diff --git a/.eslintrc.js b/.eslintrc.js index 7de339ebde5..ec5e3e3536c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -39,6 +39,7 @@ module.exports = { 'no-case-declarations': 'warn', 'prefer-regex-literals': 'warn', 'react/prop-types': 'warn', + 'react/jsx-curly-brace-presence': 'warn', // Enforce notification hooks 'no-restricted-imports': [ @@ -179,6 +180,8 @@ module.exports = { files: ['./protocol-designer/src/**/*.@(ts|tsx)'], rules: { 'opentrons/no-imports-up-the-tree-of-life': 'warn', + 'opentrons/no-margins-in-css': 'warn', + 'opentrons/no-margins-inline': 'warn', }, }, // apply application structure import requirements to app @@ -186,6 +189,23 @@ module.exports = { files: ['./app/src/**/*.@(ts|tsx)'], rules: { 'opentrons/no-imports-across-applications': 'error', + 'opentrons/no-margins-in-css': 'warn', + 'opentrons/no-margins-inline': 'warn', + }, + }, + { + files: ['./opentrons-ai-client/src/**/*.@(ts|tsx)'], + rules: { + 'opentrons/no-imports-up-the-tree-of-life': 'warn', + 'opentrons/no-margins-in-css': 'warn', + 'opentrons/no-margins-inline': 'warn', + }, + }, + { + files: ['./components/src/**/*.@(ts|tsx)'], + rules: { + 'opentrons/no-margins-in-css': 'warn', + 'opentrons/no-margins-inline': 'warn', }, }, ], diff --git a/.github/actions/odd-resource-analysis/action/index.js b/.github/actions/odd-resource-analysis/action/index.js index 9ccd2f48e5a..7941e1a2b25 100644 --- a/.github/actions/odd-resource-analysis/action/index.js +++ b/.github/actions/odd-resource-analysis/action/index.js @@ -5,9 +5,11 @@ async function run() { try { const mixpanelUser = core.getInput('mixpanel-user', { required: true }) const mixpanelSecret = core.getInput('mixpanel-secret', { required: true }) - const mixpanelProjectId = core.getInput('mixpanel-project-id', { - required: true, - }) + const mixpanelProjectId = parseInt( + core.getInput('mixpanel-project-id', { + required: true, + }) + ) const previousVersionCount = parseInt( core.getInput('previous-version-count') || '2' ) diff --git a/.github/actions/odd-resource-analysis/action/lib/helpers/mixpanel.js b/.github/actions/odd-resource-analysis/action/lib/helpers/mixpanel.js index 50013d9ffd6..a518d1f4b40 100644 --- a/.github/actions/odd-resource-analysis/action/lib/helpers/mixpanel.js +++ b/.github/actions/odd-resource-analysis/action/lib/helpers/mixpanel.js @@ -33,7 +33,7 @@ async function getMixpanelResourceMonitorDataFor({ where, }) { const params = new URLSearchParams({ - project_id: parseInt(projectId), + project_id: projectId, from_date: fromDate, to_date: toDate, event: '["resourceMonitorReport"]', diff --git a/.github/workflows/api-test-lint-deploy.yaml b/.github/workflows/api-test-lint-deploy.yaml index 6297cb7c747..e1790f28f20 100644 --- a/.github/workflows/api-test-lint-deploy.yaml +++ b/.github/workflows/api-test-lint-deploy.yaml @@ -73,8 +73,6 @@ jobs: strategy: matrix: os: ['windows-2022', 'ubuntu-22.04', 'macos-latest'] - # TODO(mc, 2022-02-24): expand this matrix to 3.8 and 3.9, - # preferably in a nightly cronjob on edge or something python: ['3.10'] with-ot-hardware: ['true', 'false'] exclude: @@ -128,6 +126,60 @@ jobs: files: ./api/coverage.xml flags: api + test-package: + name: 'installed package tests on ${{ matrix.os }}' + timeout-minutes: 5 + strategy: + matrix: + os: ['ubuntu-22.04', 'macos-latest', 'windows-2022'] + runs-on: '${{ matrix.os }}' + steps: + - uses: 'actions/checkout@v4' + - name: 'Fix actions/checkout odd handling of tags' + if: startsWith(github.ref, 'refs/tags') + run: | + git fetch -f origin ${{ github.ref }}:${{ github.ref }} + git checkout ${{ github.ref }} + - uses: 'actions/setup-python@v4' + with: + python-version: '3.10' + - name: Set up package-testing + id: setup + if: ${{ matrix.os != 'windows-2022' }} + working-directory: package-testing + shell: bash + run: make setup + - name: Set up package-testing (Windows) + id: setup-windows + if: ${{ matrix.os == 'windows-2022' }} + working-directory: package-testing + shell: pwsh + run: make setup-windows + - name: Run the tests + if: ${{ matrix.os != 'windows-2022' }} + shell: bash + id: test + working-directory: package-testing + run: make test + - name: Run the tests (Windows) + shell: pwsh + id: test-windows + working-directory: package-testing + run: make test-windows + - name: Save the test results + if: ${{ always() && steps.setup.outcome == 'success' || steps.setup-windows.outcome == 'success' }} + id: results + uses: actions/upload-artifact@v4 + with: + name: package-test-results-${{ matrix.os }} + path: package-testing/results + - name: Set job summary + if: ${{ always() }} + run: | + echo "## Opentrons Package Test Results ${{matrix.os}}" >> $GITHUB_STEP_SUMMARY + echo "### Test Outcome: Unixy ${{ steps.test.outcome }} Windows: ${{ steps.test-windows.outcome }}" >> $GITHUB_STEP_SUMMARY + echo "[Download the test results artifact](${{steps.results.outputs.artifact-url}})" >> $GITHUB_STEP_SUMMARY + deploy: name: 'deploy opentrons package' needs: [test] diff --git a/.github/workflows/odd-memory-usage-test.yaml b/.github/workflows/odd-memory-usage-test.yaml index 3302ec0203a..15b78fc79a3 100644 --- a/.github/workflows/odd-memory-usage-test.yaml +++ b/.github/workflows/odd-memory-usage-test.yaml @@ -20,5 +20,5 @@ jobs: with: mixpanel-user: ${{ secrets.MIXPANEL_INGEST_USER }} mixpanel-secret: ${{ secrets.MIXPANEL_INGEST_SECRET }} - mixpanel-project-id: ${{ secrets.OT_APP_MIXPANEL_ID }} + mixpanel-project-id: ${{ secrets.OT_MIXPANEL_PROJECT_ID }} previous-version-count: '2' \ No newline at end of file diff --git a/.gitignore b/.gitignore index 319ccc32e67..2d8b2ff20cb 100755 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,5 @@ opentrons-robot-app.tar.gz mock_dir .npm-cache/ .eslintcache + +package-testing/results diff --git a/abr-testing/Pipfile.lock b/abr-testing/Pipfile.lock index a1b677f52bb..a2f82b44925 100644 --- a/abr-testing/Pipfile.lock +++ b/abr-testing/Pipfile.lock @@ -22,108 +22,93 @@ }, "aiohappyeyeballs": { "hashes": [ - "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586", - "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572" + "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745", + "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8" ], "markers": "python_version >= '3.8'", - "version": "==2.4.3" + "version": "==2.4.4" }, "aiohttp": { "hashes": [ - "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138", - "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c", - "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24", - "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480", - "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2", - "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5", - "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a", - "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8", - "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf", - "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871", - "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486", - "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9", - "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d", - "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb", - "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68", - "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1", - "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d", - "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd", - "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1", - "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8", - "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7", - "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959", - "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7", - "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42", - "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79", - "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38", - "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a", - "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8", - "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8", - "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151", - "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6", - "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e", - "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7", - "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce", - "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b", - "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8", - "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628", - "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f", - "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a", - "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7", - "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc", - "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab", - "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b", - "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911", - "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9", - "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572", - "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554", - "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d", - "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257", - "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c", - "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b", - "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742", - "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090", - "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6", - "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc", - "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142", - "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16", - "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a", - "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28", - "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e", - "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94", - "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026", - "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb", - "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28", - "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9", - "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3", - "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f", - "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983", - "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205", - "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f", - "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa", - "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c", - "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2", - "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb", - "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67", - "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762", - "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a", - "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8", - "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a", - "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a", - "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc", - "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91", - "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23", - "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527", - "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6", - "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c", - "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7", - "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f", - "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a", - "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092", - "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414" + "sha256:012f176945af138abc10c4a48743327a92b4ca9adc7a0e078077cdb5dbab7be0", + "sha256:02c13415b5732fb6ee7ff64583a5e6ed1c57aa68f17d2bda79c04888dfdc2769", + "sha256:03b6002e20938fc6ee0918c81d9e776bebccc84690e2b03ed132331cca065ee5", + "sha256:04814571cb72d65a6899db6099e377ed00710bf2e3eafd2985166f2918beaf59", + "sha256:0580f2e12de2138f34debcd5d88894786453a76e98febaf3e8fe5db62d01c9bf", + "sha256:06a8e2ee1cbac16fe61e51e0b0c269400e781b13bcfc33f5425912391a542985", + "sha256:076bc454a7e6fd646bc82ea7f98296be0b1219b5e3ef8a488afbdd8e81fbac50", + "sha256:0c9527819b29cd2b9f52033e7fb9ff08073df49b4799c89cb5754624ecd98299", + "sha256:0dc49f42422163efb7e6f1df2636fe3db72713f6cd94688e339dbe33fe06d61d", + "sha256:14cdb5a9570be5a04eec2ace174a48ae85833c2aadc86de68f55541f66ce42ab", + "sha256:15fccaf62a4889527539ecb86834084ecf6e9ea70588efde86e8bc775e0e7542", + "sha256:24213ba85a419103e641e55c27dc7ff03536c4873470c2478cce3311ba1eee7b", + "sha256:31d5093d3acd02b31c649d3a69bb072d539d4c7659b87caa4f6d2bcf57c2fa2b", + "sha256:3691ed7726fef54e928fe26344d930c0c8575bc968c3e239c2e1a04bd8cf7838", + "sha256:386fbe79863eb564e9f3615b959e28b222259da0c48fd1be5929ac838bc65683", + "sha256:3bbbfff4c679c64e6e23cb213f57cc2c9165c9a65d63717108a644eb5a7398df", + "sha256:3de34936eb1a647aa919655ff8d38b618e9f6b7f250cc19a57a4bf7fd2062b6d", + "sha256:40d1c7a7f750b5648642586ba7206999650208dbe5afbcc5284bcec6579c9b91", + "sha256:44224d815853962f48fe124748227773acd9686eba6dc102578defd6fc99e8d9", + "sha256:47ad15a65fb41c570cd0ad9a9ff8012489e68176e7207ec7b82a0940dddfd8be", + "sha256:482cafb7dc886bebeb6c9ba7925e03591a62ab34298ee70d3dd47ba966370d2c", + "sha256:49c7dbbc1a559ae14fc48387a115b7d4bbc84b4a2c3b9299c31696953c2a5219", + "sha256:4b2c7ac59c5698a7a8207ba72d9e9c15b0fc484a560be0788b31312c2c5504e4", + "sha256:4cca22a61b7fe45da8fc73c3443150c3608750bbe27641fc7558ec5117b27fdf", + "sha256:4cfce37f31f20800a6a6620ce2cdd6737b82e42e06e6e9bd1b36f546feb3c44f", + "sha256:502a1464ccbc800b4b1995b302efaf426e8763fadf185e933c2931df7db9a199", + "sha256:53bf2097e05c2accc166c142a2090e4c6fd86581bde3fd9b2d3f9e93dda66ac1", + "sha256:593c114a2221444f30749cc5e5f4012488f56bd14de2af44fe23e1e9894a9c60", + "sha256:5d6958671b296febe7f5f859bea581a21c1d05430d1bbdcf2b393599b1cdce77", + "sha256:5ef359ebc6949e3a34c65ce20230fae70920714367c63afd80ea0c2702902ccf", + "sha256:613e5169f8ae77b1933e42e418a95931fb4867b2991fc311430b15901ed67079", + "sha256:61b9bae80ed1f338c42f57c16918853dc51775fb5cb61da70d590de14d8b5fb4", + "sha256:6362cc6c23c08d18ddbf0e8c4d5159b5df74fea1a5278ff4f2c79aed3f4e9f46", + "sha256:65a96e3e03300b41f261bbfd40dfdbf1c301e87eab7cd61c054b1f2e7c89b9e8", + "sha256:65e55ca7debae8faaffee0ebb4b47a51b4075f01e9b641c31e554fd376595c6c", + "sha256:68386d78743e6570f054fe7949d6cb37ef2b672b4d3405ce91fafa996f7d9b4d", + "sha256:68ff6f48b51bd78ea92b31079817aff539f6c8fc80b6b8d6ca347d7c02384e33", + "sha256:6ab29b8a0beb6f8eaf1e5049252cfe74adbaafd39ba91e10f18caeb0e99ffb34", + "sha256:77ae58586930ee6b2b6f696c82cf8e78c8016ec4795c53e36718365f6959dc82", + "sha256:77c4aa15a89847b9891abf97f3d4048f3c2d667e00f8a623c89ad2dccee6771b", + "sha256:78153314f26d5abef3239b4a9af20c229c6f3ecb97d4c1c01b22c4f87669820c", + "sha256:7852bbcb4d0d2f0c4d583f40c3bc750ee033265d80598d0f9cb6f372baa6b836", + "sha256:7e97d622cb083e86f18317282084bc9fbf261801b0192c34fe4b1febd9f7ae69", + "sha256:7f3dc0e330575f5b134918976a645e79adf333c0a1439dcf6899a80776c9ab39", + "sha256:80886dac673ceaef499de2f393fc80bb4481a129e6cb29e624a12e3296cc088f", + "sha256:811f23b3351ca532af598405db1093f018edf81368e689d1b508c57dcc6b6a32", + "sha256:86a5dfcc39309470bd7b68c591d84056d195428d5d2e0b5ccadfbaf25b026ebc", + "sha256:8b3cf2dc0f0690a33f2d2b2cb15db87a65f1c609f53c37e226f84edb08d10f52", + "sha256:8cc5203b817b748adccb07f36390feb730b1bc5f56683445bfe924fc270b8816", + "sha256:909af95a72cedbefe5596f0bdf3055740f96c1a4baa0dd11fd74ca4de0b4e3f1", + "sha256:974d3a2cce5fcfa32f06b13ccc8f20c6ad9c51802bb7f829eae8a1845c4019ec", + "sha256:98283b94cc0e11c73acaf1c9698dea80c830ca476492c0fe2622bd931f34b487", + "sha256:98f5635f7b74bcd4f6f72fcd85bea2154b323a9f05226a80bc7398d0c90763b0", + "sha256:99b7920e7165be5a9e9a3a7f1b680f06f68ff0d0328ff4079e5163990d046767", + "sha256:9bca390cb247dbfaec3c664326e034ef23882c3f3bfa5fbf0b56cad0320aaca5", + "sha256:9e2e576caec5c6a6b93f41626c9c02fc87cd91538b81a3670b2e04452a63def6", + "sha256:9ef405356ba989fb57f84cac66f7b0260772836191ccefbb987f414bcd2979d9", + "sha256:a55d2ad345684e7c3dd2c20d2f9572e9e1d5446d57200ff630e6ede7612e307f", + "sha256:ab7485222db0959a87fbe8125e233b5a6f01f4400785b36e8a7878170d8c3138", + "sha256:b1fc6b45010a8d0ff9e88f9f2418c6fd408c99c211257334aff41597ebece42e", + "sha256:b78f053a7ecfc35f0451d961dacdc671f4bcbc2f58241a7c820e9d82559844cf", + "sha256:b99acd4730ad1b196bfb03ee0803e4adac371ae8efa7e1cbc820200fc5ded109", + "sha256:be2b516f56ea883a3e14dda17059716593526e10fb6303189aaf5503937db408", + "sha256:beb39a6d60a709ae3fb3516a1581777e7e8b76933bb88c8f4420d875bb0267c6", + "sha256:bf3d1a519a324af764a46da4115bdbd566b3c73fb793ffb97f9111dbc684fc4d", + "sha256:c49a76c1038c2dd116fa443eba26bbb8e6c37e924e2513574856de3b6516be99", + "sha256:c5532f0441fc09c119e1dca18fbc0687e64fbeb45aa4d6a87211ceaee50a74c4", + "sha256:c6b9e6d7e41656d78e37ce754813fa44b455c3d0d0dced2a047def7dc5570b74", + "sha256:c87bf31b7fdab94ae3adbe4a48e711bfc5f89d21cf4c197e75561def39e223bc", + "sha256:cbad88a61fa743c5d283ad501b01c153820734118b65aee2bd7dbb735475ce0d", + "sha256:cf14627232dfa8730453752e9cdc210966490992234d77ff90bc8dc0dce361d5", + "sha256:db1d0b28fcb7f1d35600150c3e4b490775251dea70f894bf15c678fdd84eda6a", + "sha256:ddf5f7d877615f6a1e75971bfa5ac88609af3b74796ff3e06879e8422729fd01", + "sha256:e44a9a3c053b90c6f09b1bb4edd880959f5328cf63052503f892c41ea786d99f", + "sha256:efb15a17a12497685304b2d976cb4939e55137df7b09fa53f1b6a023f01fcb4e", + "sha256:fbbaea811a2bba171197b08eea288b9402faa2bab2ba0858eecdd0a4105753a3" ], - "markers": "python_version >= '3.8'", - "version": "==3.10.10" + "markers": "python_version >= '3.9'", + "version": "==3.11.10" }, "aionotify": { "hashes": [ @@ -141,6 +126,14 @@ "markers": "python_version >= '3.7'", "version": "==1.3.1" }, + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "anyio": { "hashes": [ "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", @@ -151,11 +144,11 @@ }, "async-timeout": { "hashes": [ - "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", - "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028" + "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", + "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3" ], - "markers": "python_version < '3.11'", - "version": "==4.0.3" + "markers": "python_version >= '3.8'", + "version": "==5.0.1" }, "attrs": { "hashes": [ @@ -167,36 +160,34 @@ }, "bcrypt": { "hashes": [ - "sha256:096a15d26ed6ce37a14c1ac1e48119660f21b24cba457f160a4b830f3fe6b5cb", - "sha256:0da52759f7f30e83f1e30a888d9163a81353ef224d82dc58eb5bb52efcabc399", - "sha256:1bb429fedbe0249465cdd85a58e8376f31bb315e484f16e68ca4c786dcc04291", - "sha256:1d84cf6d877918620b687b8fd1bf7781d11e8a0998f576c7aa939776b512b98d", - "sha256:1ee38e858bf5d0287c39b7a1fc59eec64bbf880c7d504d3a06a96c16e14058e7", - "sha256:1ff39b78a52cf03fdf902635e4c81e544714861ba3f0efc56558979dd4f09170", - "sha256:27fe0f57bb5573104b5a6de5e4153c60814c711b29364c10a75a54bb6d7ff48d", - "sha256:3413bd60460f76097ee2e0a493ccebe4a7601918219c02f503984f0a7ee0aebe", - "sha256:3698393a1b1f1fd5714524193849d0c6d524d33523acca37cd28f02899285060", - "sha256:373db9abe198e8e2c70d12b479464e0d5092cc122b20ec504097b5f2297ed184", - "sha256:39e1d30c7233cfc54f5c3f2c825156fe044efdd3e0b9d309512cc514a263ec2a", - "sha256:3bbbfb2734f0e4f37c5136130405332640a1e46e6b23e000eeff2ba8d005da68", - "sha256:3d3a6d28cb2305b43feac298774b997e372e56c7c7afd90a12b3dc49b189151c", - "sha256:5a1e8aa9b28ae28020a3ac4b053117fb51c57a010b9f969603ed885f23841458", - "sha256:61ed14326ee023917ecd093ee6ef422a72f3aec6f07e21ea5f10622b735538a9", - "sha256:655ea221910bcac76ea08aaa76df427ef8625f92e55a8ee44fbf7753dbabb328", - "sha256:762a2c5fb35f89606a9fde5e51392dad0cd1ab7ae64149a8b935fe8d79dd5ed7", - "sha256:77800b7147c9dc905db1cba26abe31e504d8247ac73580b4aa179f98e6608f34", - "sha256:8ac68872c82f1add6a20bd489870c71b00ebacd2e9134a8aa3f98a0052ab4b0e", - "sha256:8d7bb9c42801035e61c109c345a28ed7e84426ae4865511eb82e913df18f58c2", - "sha256:8f6ede91359e5df88d1f5c1ef47428a4420136f3ce97763e31b86dd8280fbdf5", - "sha256:9c1c4ad86351339c5f320ca372dfba6cb6beb25e8efc659bedd918d921956bae", - "sha256:c02d944ca89d9b1922ceb8a46460dd17df1ba37ab66feac4870f6862a1533c00", - "sha256:c52aac18ea1f4a4f65963ea4f9530c306b56ccd0c6f8c8da0c06976e34a6e841", - "sha256:cb2a8ec2bc07d3553ccebf0746bbf3d19426d1c6d1adbd4fa48925f66af7b9e8", - "sha256:cf69eaf5185fd58f268f805b505ce31f9b9fc2d64b376642164e9244540c1221", - "sha256:f4f4acf526fcd1c34e7ce851147deedd4e26e6402369304220250598b26448db" + "sha256:041fa0155c9004eb98a232d54da05c0b41d4b8e66b6fc3cb71b4b3f6144ba837", + "sha256:04e56e3fe8308a88b77e0afd20bec516f74aecf391cdd6e374f15cbed32783d6", + "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17", + "sha256:533e7f3bcf2f07caee7ad98124fab7499cb3333ba2274f7a36cf1daee7409d99", + "sha256:6765386e3ab87f569b276988742039baab087b2cdb01e809d74e74503c2faafe", + "sha256:687cf30e6681eeda39548a93ce9bfbb300e48b4d445a43db4298d2474d2a1e54", + "sha256:76132c176a6d9953cdc83c296aeaed65e1a708485fd55abf163e0d9f8f16ce0e", + "sha256:76d3e352b32f4eeb34703370e370997065d28a561e4a18afe4fef07249cb4396", + "sha256:807261df60a8b1ccd13e6599c779014a362ae4e795f5c59747f60208daddd96d", + "sha256:89df2aea2c43be1e1fa066df5f86c8ce822ab70a30e4c210968669565c0f4685", + "sha256:8ad2f4528cbf0febe80e5a3a57d7a74e6635e41af1ea5675282a33d769fba413", + "sha256:8c458cd103e6c5d1d85cf600e546a639f234964d0228909d8f8dbeebff82d526", + "sha256:8dbd0747208912b1e4ce730c6725cb56c07ac734b3629b60d4398f082ea718ad", + "sha256:909faa1027900f2252a9ca5dfebd25fc0ef1417943824783d1c8418dd7d6df4a", + "sha256:aaa2e285be097050dba798d537b6efd9b698aa88eef52ec98d23dcd6d7cf6fea", + "sha256:adadd36274510a01f33e6dc08f5824b97c9580583bd4487c564fc4617b328005", + "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f", + "sha256:b588af02b89d9fad33e5f98f7838bf590d6d692df7153647724a7f20c186f6bf", + "sha256:b7703ede632dc945ed1172d6f24e9f30f27b1b1a067f32f68bf169c5f08d0425", + "sha256:c6f5fa3775966cca251848d4d5393ab016b3afed251163c1436fefdec3b02c84", + "sha256:cde78d385d5e93ece5479a0a87f73cd6fa26b171c786a884f955e165032b262c", + "sha256:cfdf3d7530c790432046c40cda41dfee8c83e29482e6a604f8930b9930e94139", + "sha256:e158009a54c4c8bc91d5e0da80920d048f918c61a581f0a63e4e93bb556d362f", + "sha256:e84e0e6f8e40a242b11bce56c313edc2be121cec3e0ec2d76fce01f6af33c07c", + "sha256:f85b1ffa09240c89aa2e1ae9f3b1c687104f7b2b9d2098da4e923f1b7082d331" ], "markers": "python_version >= '3.7'", - "version": "==4.2.0" + "version": "==4.2.1" }, "cachetools": { "hashes": [ @@ -284,7 +275,7 @@ "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" ], - "markers": "platform_python_implementation != 'PyPy'", + "markers": "python_version >= '3.8'", "version": "==1.17.1" }, "charset-normalizer": { @@ -406,53 +397,45 @@ "markers": "python_version >= '3.7'", "version": "==8.1.7" }, - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "markers": "platform_system == 'Windows'", - "version": "==0.4.6" - }, "cryptography": { "hashes": [ - "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", - "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", - "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", - "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", - "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", - "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", - "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", - "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", - "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", - "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", - "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", - "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", - "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", - "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", - "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", - "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", - "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", - "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", - "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", - "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", - "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", - "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", - "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", - "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", - "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", - "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", - "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7" - ], - "markers": "python_version >= '3.7'", - "version": "==43.0.3" + "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", + "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", + "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", + "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", + "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", + "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", + "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", + "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", + "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", + "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", + "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", + "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", + "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", + "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", + "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", + "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", + "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", + "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", + "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", + "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", + "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", + "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", + "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", + "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", + "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", + "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", + "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" + ], + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.0" }, "exceptiongroup": { "hashes": [ "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], - "markers": "python_version < '3.11'", + "markers": "python_version >= '3.7'", "version": "==1.2.2" }, "frozenlist": { @@ -555,20 +538,20 @@ }, "google-api-core": { "hashes": [ - "sha256:26f8d76b96477db42b55fd02a33aae4a42ec8b86b98b94969b7333a2c828bf35", - "sha256:a6652b6bd51303902494998626653671703c420f6f4c88cfd3f50ed723e9d021" + "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9", + "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf" ], "markers": "python_version >= '3.7'", - "version": "==2.22.0" + "version": "==2.24.0" }, "google-api-python-client": { "hashes": [ - "sha256:4427b2f47cd88b0355d540c2c52215f68c337f3bc9d6aae1ceeae4525977504c", - "sha256:a9d26d630810ed4631aea21d1de3e42072f98240aaf184a8a1a874a371115034" + "sha256:1b420062e03bfcaa1c79e2e00a612d29a6a934151ceb3d272fe150a656dc8f17", + "sha256:a521bbbb2ec0ba9d6f307cdd64ed6e21eeac372d1bd7493a4ab5022941f784ad" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.151.0" + "version": "==2.154.0" }, "google-auth": { "hashes": [ @@ -595,11 +578,11 @@ }, "googleapis-common-protos": { "hashes": [ - "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63", - "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0" + "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c", + "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed" ], "markers": "python_version >= '3.7'", - "version": "==1.65.0" + "version": "==1.66.0" }, "gspread": { "hashes": [ @@ -633,11 +616,81 @@ }, "jsonschema": { "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" + "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", + "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566" ], - "markers": "python_version >= '3.7'", - "version": "==4.17.3" + "markers": "python_version >= '3.8'", + "version": "==4.23.0" + }, + "jsonschema-specifications": { + "hashes": [ + "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", + "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.10.1" + }, + "msgpack": { + "hashes": [ + "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982", + "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3", + "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40", + "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee", + "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693", + "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950", + "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151", + "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24", + "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305", + "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b", + "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c", + "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659", + "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d", + "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18", + "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746", + "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868", + "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2", + "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba", + "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228", + "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2", + "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273", + "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c", + "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653", + "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a", + "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596", + "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd", + "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8", + "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa", + "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85", + "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc", + "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836", + "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3", + "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58", + "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128", + "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db", + "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f", + "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77", + "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad", + "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13", + "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8", + "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b", + "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a", + "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543", + "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b", + "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce", + "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d", + "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a", + "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c", + "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f", + "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e", + "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011", + "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04", + "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480", + "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a", + "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d", + "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d" + ], + "markers": "python_version >= '3.8'", + "version": "==1.0.8" }, "multidict": { "hashes": [ @@ -776,7 +829,7 @@ "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3", "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f" ], - "markers": "python_version < '3.11'", + "markers": "python_version >= '3.9'", "version": "==1.26.4" }, "oauth2client": { @@ -817,11 +870,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pandas": { "hashes": [ @@ -874,12 +927,12 @@ }, "pandas-stubs": { "hashes": [ - "sha256:3a6f8f142105a42550be677ba741ba532621f4e0acad2155c0e7b2450f114cfa", - "sha256:d4ab618253f0acf78a5d0d2bfd6dffdd92d91a56a69bdc8144e5a5c6d25be3b5" + "sha256:74aa79c167af374fe97068acc90776c0ebec5266a6e5c69fe11e9c2cf51f2267", + "sha256:cf819383c6d9ae7d4dabf34cd47e1e45525bb2f312e6ad2939c2c204cb708acd" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==2.2.3.241009" + "version": "==2.2.3.241126" }, "paramiko": { "hashes": [ @@ -900,107 +953,91 @@ }, "propcache": { "hashes": [ - "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9", - "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763", - "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325", - "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb", - "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b", - "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09", - "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957", - "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68", - "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f", - "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798", - "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418", - "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6", - "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162", - "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f", - "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036", - "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8", - "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2", - "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110", - "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23", - "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8", - "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638", - "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a", - "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44", - "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2", - "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2", - "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850", - "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136", - "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b", - "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887", - "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89", - "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87", - "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348", - "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4", - "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861", - "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e", - "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c", - "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b", - "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb", - "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1", - "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de", - "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354", - "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563", - "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5", - "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf", - "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9", - "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12", - "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4", - "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5", - "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71", - "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9", - "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed", - "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336", - "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90", - "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063", - "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad", - "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6", - "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8", - "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e", - "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2", - "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7", - "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d", - "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d", - "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df", - "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b", - "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178", - "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2", - "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630", - "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48", - "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61", - "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89", - "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb", - "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3", - "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6", - "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562", - "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b", - "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58", - "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db", - "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99", - "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37", - "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83", - "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a", - "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d", - "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04", - "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70", - "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544", - "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394", - "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea", - "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7", - "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1", - "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793", - "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577", - "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7", - "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57", - "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d", - "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032", - "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d", - "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016", - "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504" + "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4", + "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4", + "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a", + "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f", + "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9", + "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d", + "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e", + "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6", + "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf", + "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034", + "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d", + "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16", + "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30", + "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba", + "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95", + "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d", + "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae", + "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348", + "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2", + "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", + "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce", + "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54", + "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629", + "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", + "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1", + "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b", + "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf", + "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b", + "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587", + "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097", + "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea", + "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24", + "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7", + "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541", + "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6", + "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634", + "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3", + "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d", + "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034", + "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465", + "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2", + "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf", + "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1", + "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04", + "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5", + "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583", + "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb", + "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b", + "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c", + "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958", + "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc", + "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4", + "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82", + "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e", + "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce", + "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9", + "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518", + "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536", + "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505", + "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052", + "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff", + "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1", + "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f", + "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681", + "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347", + "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af", + "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246", + "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787", + "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0", + "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f", + "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439", + "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3", + "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6", + "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca", + "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec", + "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d", + "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3", + "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16", + "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717", + "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6", + "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd", + "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212" ], - "markers": "python_version >= '3.8'", - "version": "==0.2.0" + "markers": "python_version >= '3.9'", + "version": "==0.2.1" }, "proto-plus": { "hashes": [ @@ -1012,20 +1049,20 @@ }, "protobuf": { "hashes": [ - "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24", - "sha256:135658402f71bbd49500322c0f736145731b16fc79dc8f367ab544a17eab4535", - "sha256:27b246b3723692bf1068d5734ddaf2fccc2cdd6e0c9b47fe099244d80200593b", - "sha256:3e6101d095dfd119513cde7259aa703d16c6bbdfae2554dfe5cfdbe94e32d548", - "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584", - "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b", - "sha256:70585a70fc2dd4818c51287ceef5bdba6387f88a578c86d47bb34669b5552c36", - "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135", - "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868", - "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687", - "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed" + "sha256:012ce28d862ff417fd629285aca5d9772807f15ceb1a0dbd15b88f58c776c98c", + "sha256:027fbcc48cea65a6b17028510fdd054147057fa78f4772eb547b9274e5219331", + "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34", + "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110", + "sha256:32600ddb9c2a53dedc25b8581ea0f1fd8ea04956373c0c07577ce58d312522e0", + "sha256:50879eb0eb1246e3a5eabbbe566b44b10348939b7cc1b267567e8c3d07213853", + "sha256:5a41deccfa5e745cef5c65a560c76ec0ed8e70908a67cc8f4da5fce588b50d57", + "sha256:683be02ca21a6ffe80db6dd02c0b5b2892322c59ca57fd6c872d652cb80549cb", + "sha256:8ee1461b3af56145aca2800e6a3e2f928108c749ba8feccc6f5dd0062c410c0d", + "sha256:b5ba1d0e4c8a40ae0496d0e2ecfdbb82e1776928a205106d14ad6985a09ec155", + "sha256:d473655e29c0c4bbf8b69e9a8fb54645bc289dead6d753b952e7aa660254ae18" ], "markers": "python_version >= '3.8'", - "version": "==5.28.3" + "version": "==5.29.1" }, "pyasn1": { "hashes": [ @@ -1053,52 +1090,125 @@ }, "pydantic": { "hashes": [ - "sha256:0399094464ae7f28482de22383e667625e38e1516d6b213176df1acdd0c477ea", - "sha256:076c49e24b73d346c45f9282d00dbfc16eef7ae27c970583d499f11110d9e5b0", - "sha256:07d00ca5ef0de65dd274005433ce2bb623730271d495a7d190a91c19c5679d34", - "sha256:0890fbd7fec9e151c7512941243d830b2d6076d5df159a2030952d480ab80a4e", - "sha256:0bfb5b378b78229119d66ced6adac2e933c67a0aa1d0a7adffbe432f3ec14ce4", - "sha256:0d32227ea9a3bf537a2273fd2fdb6d64ab4d9b83acd9e4e09310a777baaabb98", - "sha256:11965f421f7eb026439d4eb7464e9182fe6d69c3d4d416e464a4485d1ba61ab6", - "sha256:1fc8cc264afaf47ae6a9bcbd36c018d0c6b89293835d7fb0e5e1a95898062d59", - "sha256:2206a1752d9fac011e95ca83926a269fb0ef5536f7e053966d058316e24d929f", - "sha256:22a1794e01591884741be56c6fba157c4e99dcc9244beb5a87bd4aa54b84ea8b", - "sha256:4739c206bfb6bb2bdc78dcd40bfcebb2361add4ceac6d170e741bb914e9eff0f", - "sha256:4a5d5b877c7d3d9e17399571a8ab042081d22fe6904416a8b20f8af5909e6c8f", - "sha256:566bebdbe6bc0ac593fa0f67d62febbad9f8be5433f686dc56401ba4aab034e3", - "sha256:570ad0aeaf98b5e33ff41af75aba2ef6604ee25ce0431ecd734a28e74a208555", - "sha256:573254d844f3e64093f72fcd922561d9c5696821ff0900a0db989d8c06ab0c25", - "sha256:5d4320510682d5a6c88766b2a286d03b87bd3562bf8d78c73d63bab04b21e7b4", - "sha256:6d8a38a44bb6a15810084316ed69c854a7c06e0c99c5429f1d664ad52cec353c", - "sha256:6eb56074b11a696e0b66c7181da682e88c00e5cebe6570af8013fcae5e63e186", - "sha256:7e66aa0fa7f8aa9d0a620361834f6eb60d01d3e9cea23ca1a92cda99e6f61dac", - "sha256:7ea24e8614f541d69ea72759ff635df0e612b7dc9d264d43f51364df310081a3", - "sha256:7f31742c95e3f9443b8c6fa07c119623e61d76603be9c0d390bcf7e888acabcb", - "sha256:83ee8c9916689f8e6e7d90161e6663ac876be2efd32f61fdcfa3a15e87d4e413", - "sha256:8b2cf5e26da84f2d2dee3f60a3f1782adedcee785567a19b68d0af7e1534bd1f", - "sha256:945407f4d08cd12485757a281fca0e5b41408606228612f421aa4ea1b63a095d", - "sha256:9c46f58ef2df958ed2ea7437a8be0897d5efe9ee480818405338c7da88186fb3", - "sha256:9d7d48fbc5289efd23982a0d68e973a1f37d49064ccd36d86de4543aff21e086", - "sha256:9f28a81978e936136c44e6a70c65bde7548d87f3807260f73aeffbf76fb94c2f", - "sha256:a415b9e95fa602b10808113967f72b2da8722061265d6af69268c111c254832d", - "sha256:a82746c6d6e91ca17e75f7f333ed41d70fce93af520a8437821dec3ee52dfb10", - "sha256:ad57004e5d73aee36f1e25e4e73a4bc853b473a1c30f652dc8d86b0a987ffce3", - "sha256:c6444368b651a14c2ce2fb22145e1496f7ab23cbdb978590d47c8d34a7bc0289", - "sha256:d216f8d0484d88ab72ab45d699ac669fe031275e3fa6553e3804e69485449fa0", - "sha256:d3449633c207ec3d2d672eedb3edbe753e29bd4e22d2e42a37a2c1406564c20f", - "sha256:d5b5b7c6bafaef90cbb7dafcb225b763edd71d9e22489647ee7df49d6d341890", - "sha256:d7a8a1dd68bac29f08f0a3147de1885f4dccec35d4ea926e6e637fac03cdb4b3", - "sha256:d8d72553d2f3f57ce547de4fa7dc8e3859927784ab2c88343f1fc1360ff17a08", - "sha256:dce355fe7ae53e3090f7f5fa242423c3a7b53260747aa398b4b3aaf8b25f41c3", - "sha256:e351df83d1c9cffa53d4e779009a093be70f1d5c6bb7068584086f6a19042526", - "sha256:ec5c44e6e9eac5128a9bfd21610df3b8c6b17343285cc185105686888dc81206", - "sha256:f5bb81fcfc6d5bff62cd786cbd87480a11d23f16d5376ad2e057c02b3b44df96", - "sha256:fd34012691fbd4e67bdf4accb1f0682342101015b78327eaae3543583fcd451e", - "sha256:fea36c2065b7a1d28c6819cc2e93387b43dd5d3cf5a1e82d8132ee23f36d1f10", - "sha256:ff09600cebe957ecbb4a27496fe34c1d449e7957ed20a202d5029a71a8af2e35" + "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", + "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9" ], - "markers": "python_version >= '3.7'", - "version": "==1.10.19" + "markers": "python_version >= '3.8'", + "version": "==2.10.3" + }, + "pydantic-core": { + "hashes": [ + "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9", + "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b", + "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c", + "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529", + "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc", + "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854", + "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d", + "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278", + "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a", + "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c", + "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f", + "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27", + "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f", + "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac", + "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2", + "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97", + "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a", + "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919", + "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9", + "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4", + "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c", + "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131", + "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5", + "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd", + "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089", + "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107", + "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6", + "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60", + "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf", + "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5", + "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08", + "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05", + "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2", + "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e", + "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c", + "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17", + "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62", + "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23", + "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be", + "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067", + "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02", + "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f", + "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235", + "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840", + "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5", + "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807", + "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16", + "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c", + "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864", + "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e", + "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a", + "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35", + "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737", + "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a", + "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3", + "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52", + "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05", + "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31", + "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89", + "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de", + "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6", + "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36", + "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c", + "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154", + "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb", + "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e", + "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd", + "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3", + "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f", + "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78", + "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960", + "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618", + "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08", + "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4", + "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c", + "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c", + "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330", + "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8", + "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792", + "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025", + "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9", + "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f", + "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01", + "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337", + "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4", + "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f", + "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd", + "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51", + "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab", + "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc", + "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676", + "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381", + "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed", + "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb", + "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967", + "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073", + "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae", + "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c", + "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206", + "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b" + ], + "markers": "python_version >= '3.8'", + "version": "==2.27.1" + }, + "pydantic-settings": { + "hashes": [ + "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87", + "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0" + ], + "markers": "python_version >= '3.8'", + "version": "==2.6.1" }, "pynacl": { "hashes": [ @@ -1121,47 +1231,9 @@ "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84", "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c" ], - "markers": "python_version >= '3.1'", + "markers": "python_version >= '3.9'", "version": "==3.2.0" }, - "pyrsistent": { - "hashes": [ - "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f", - "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e", - "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958", - "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34", - "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca", - "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d", - "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d", - "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", - "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714", - "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf", - "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee", - "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8", - "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224", - "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d", - "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054", - "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656", - "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7", - "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423", - "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce", - "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e", - "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3", - "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0", - "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f", - "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", - "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce", - "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a", - "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174", - "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86", - "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f", - "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b", - "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98", - "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022" - ], - "markers": "python_version >= '3.8'", - "version": "==0.20.0" - }, "pyserial": { "hashes": [ "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", @@ -1182,9 +1254,17 @@ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==2.9.0.post0" }, + "python-dotenv": { + "hashes": [ + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" + ], + "markers": "python_version >= '3.8'", + "version": "==1.0.1" + }, "pytz": { "hashes": [ "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", @@ -1200,29 +1280,13 @@ "markers": "python_full_version >= '3.6.0'", "version": "==1.2.1" }, - "pywin32": { - "hashes": [ - "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", - "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6", - "sha256:13dcb914ed4347019fbec6697a01a0aec61019c1046c2b905410d197856326a6", - "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", - "sha256:1f696ab352a2ddd63bd07430080dd598e6369152ea13a25ebcdd2f503a38f1ff", - "sha256:3b92622e29d651c6b783e368ba7d6722b1634b8e70bd376fd7610fe1992e19de", - "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e", - "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b", - "sha256:5794e764ebcabf4ff08c555b31bd348c9025929371763b2183172ff4708152f0", - "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", - "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a", - "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920", - "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341", - "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e", - "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", - "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c", - "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", - "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4" - ], - "markers": "platform_system == 'Windows' and platform_python_implementation == 'CPython'", - "version": "==308" + "referencing": { + "hashes": [ + "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", + "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de" + ], + "markers": "python_version >= '3.8'", + "version": "==0.35.1" }, "requests": { "hashes": [ @@ -1240,6 +1304,115 @@ "markers": "python_version >= '3.4'", "version": "==2.0.0" }, + "rpds-py": { + "hashes": [ + "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518", + "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059", + "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61", + "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5", + "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9", + "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543", + "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2", + "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a", + "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d", + "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56", + "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d", + "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd", + "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b", + "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4", + "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99", + "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d", + "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd", + "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe", + "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1", + "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e", + "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f", + "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3", + "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca", + "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d", + "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e", + "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc", + "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea", + "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38", + "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b", + "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c", + "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff", + "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723", + "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e", + "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493", + "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6", + "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83", + "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091", + "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1", + "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627", + "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1", + "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728", + "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16", + "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c", + "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45", + "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7", + "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a", + "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730", + "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967", + "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25", + "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24", + "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055", + "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d", + "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0", + "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e", + "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7", + "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c", + "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f", + "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd", + "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652", + "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8", + "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11", + "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333", + "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96", + "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64", + "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b", + "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e", + "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c", + "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9", + "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec", + "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb", + "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37", + "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad", + "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9", + "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c", + "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf", + "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4", + "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f", + "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d", + "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09", + "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", + "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566", + "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74", + "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338", + "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15", + "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c", + "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648", + "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84", + "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3", + "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123", + "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520", + "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831", + "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e", + "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf", + "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b", + "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2", + "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3", + "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130", + "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b", + "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de", + "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5", + "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d", + "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00", + "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e" + ], + "markers": "python_version >= '3.9'", + "version": "==0.22.3" + }, "rsa": { "hashes": [ "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", @@ -1250,28 +1423,28 @@ }, "setuptools": { "hashes": [ - "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd", - "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686" + "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", + "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" ], - "markers": "python_version >= '3.8'", - "version": "==75.3.0" + "markers": "python_version >= '3.9'", + "version": "==75.6.0" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==1.17.0" }, "slack-sdk": { "hashes": [ - "sha256:0515fb93cd03b18de61f876a8304c4c3cef4dd3c2a3bad62d7394d2eb5a3c8e6", - "sha256:4cc44c9ffe4bb28a01fbe3264c2f466c783b893a4eca62026ab845ec7c176ff1" + "sha256:a5e74c00c99dc844ad93e501ab764a20d86fa8184bbc9432af217496f632c4ee", + "sha256:b8cccadfa3d4005a5e6529f52000d25c583f46173fda8e9136fdd2bc58923ff6" ], "index": "pypi", "markers": "python_version >= '3.6'", - "version": "==3.33.3" + "version": "==3.33.5" }, "slackclient": { "hashes": [ @@ -1348,167 +1521,162 @@ }, "wrapt": { "hashes": [ - "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", - "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", - "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", - "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", - "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", - "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", - "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", - "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", - "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", - "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", - "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", - "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", - "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", - "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", - "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", - "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", - "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", - "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", - "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", - "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", - "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", - "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", - "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", - "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", - "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", - "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", - "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", - "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", - "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", - "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", - "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", - "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", - "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", - "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", - "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", - "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", - "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", - "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", - "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", - "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", - "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", - "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", - "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", - "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", - "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", - "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", - "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", - "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", - "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", - "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", - "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", - "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", - "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", - "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", - "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", - "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", - "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", - "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", - "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", - "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", - "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", - "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", - "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", - "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", - "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", - "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", - "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", - "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", - "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", - "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" + "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d", + "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301", + "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635", + "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a", + "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed", + "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721", + "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801", + "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b", + "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1", + "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88", + "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8", + "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0", + "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f", + "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578", + "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7", + "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045", + "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada", + "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d", + "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b", + "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a", + "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977", + "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea", + "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346", + "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13", + "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22", + "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339", + "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9", + "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181", + "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c", + "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90", + "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a", + "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489", + "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f", + "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504", + "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea", + "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569", + "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4", + "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce", + "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab", + "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a", + "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f", + "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c", + "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9", + "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf", + "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d", + "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627", + "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d", + "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4", + "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c", + "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d", + "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad", + "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b", + "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33", + "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371", + "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1", + "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393", + "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106", + "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df", + "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379", + "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451", + "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b", + "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575", + "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed", + "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb", + "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838" ], - "markers": "python_version >= '3.6'", - "version": "==1.16.0" + "markers": "python_version >= '3.8'", + "version": "==1.17.0" }, "yarl": { "hashes": [ - "sha256:06157fb3c58f2736a5e47c8fcbe1afc8b5de6fb28b14d25574af9e62150fcaac", - "sha256:067a63fcfda82da6b198fa73079b1ca40b7c9b7994995b6ee38acda728b64d47", - "sha256:0b1794853124e2f663f0ea54efb0340b457f08d40a1cef78edfa086576179c91", - "sha256:0bdff5e0995522706c53078f531fb586f56de9c4c81c243865dd5c66c132c3b5", - "sha256:117ed8b3732528a1e41af3aa6d4e08483c2f0f2e3d3d7dca7cf538b3516d93df", - "sha256:14bc88baa44e1f84164a392827b5defb4fa8e56b93fecac3d15315e7c8e5d8b3", - "sha256:1654ec814b18be1af2c857aa9000de7a601400bd4c9ca24629b18486c2e35463", - "sha256:16bca6678a83657dd48df84b51bd56a6c6bd401853aef6d09dc2506a78484c7b", - "sha256:1a3b91c44efa29e6c8ef8a9a2b583347998e2ba52c5d8280dbd5919c02dfc3b5", - "sha256:1a52a1ffdd824fb1835272e125385c32fd8b17fbdefeedcb4d543cc23b332d74", - "sha256:1ce36ded585f45b1e9bb36d0ae94765c6608b43bd2e7f5f88079f7a85c61a4d3", - "sha256:299f11b44d8d3a588234adbe01112126010bd96d9139c3ba7b3badd9829261c3", - "sha256:2b24ec55fad43e476905eceaf14f41f6478780b870eda5d08b4d6de9a60b65b4", - "sha256:2d374d70fdc36f5863b84e54775452f68639bc862918602d028f89310a034ab0", - "sha256:2d9f0606baaec5dd54cb99667fcf85183a7477f3766fbddbe3f385e7fc253299", - "sha256:2e7ba4c9377e48fb7b20dedbd473cbcbc13e72e1826917c185157a137dac9df2", - "sha256:2f0a6423295a0d282d00e8701fe763eeefba8037e984ad5de44aa349002562ac", - "sha256:327828786da2006085a4d1feb2594de6f6d26f8af48b81eb1ae950c788d97f61", - "sha256:380e6c38ef692b8fd5a0f6d1fa8774d81ebc08cfbd624b1bca62a4d4af2f9931", - "sha256:3b74ff4767d3ef47ffe0cd1d89379dc4d828d4873e5528976ced3b44fe5b0a21", - "sha256:3e844be8d536afa129366d9af76ed7cb8dfefec99f5f1c9e4f8ae542279a6dc3", - "sha256:459e81c2fb920b5f5df744262d1498ec2c8081acdcfe18181da44c50f51312f7", - "sha256:46ddf6e0b975cd680eb83318aa1d321cb2bf8d288d50f1754526230fcf59ba96", - "sha256:482c122b72e3c5ec98f11457aeb436ae4aecca75de19b3d1de7cf88bc40db82f", - "sha256:561c87fea99545ef7d692403c110b2f99dced6dff93056d6e04384ad3bc46243", - "sha256:578d00c9b7fccfa1745a44f4eddfdc99d723d157dad26764538fbdda37209857", - "sha256:58c8e9620eb82a189c6c40cb6b59b4e35b2ee68b1f2afa6597732a2b467d7e8f", - "sha256:5b29beab10211a746f9846baa39275e80034e065460d99eb51e45c9a9495bcca", - "sha256:5d1d42556b063d579cae59e37a38c61f4402b47d70c29f0ef15cee1acaa64488", - "sha256:5f236cb5999ccd23a0ab1bd219cfe0ee3e1c1b65aaf6dd3320e972f7ec3a39da", - "sha256:62a91aefff3d11bf60e5956d340eb507a983a7ec802b19072bb989ce120cd948", - "sha256:64cc6e97f14cf8a275d79c5002281f3040c12e2e4220623b5759ea7f9868d6a5", - "sha256:6f4c9156c4d1eb490fe374fb294deeb7bc7eaccda50e23775b2354b6a6739934", - "sha256:7294e38f9aa2e9f05f765b28ffdc5d81378508ce6dadbe93f6d464a8c9594473", - "sha256:7615058aabad54416ddac99ade09a5510cf77039a3b903e94e8922f25ed203d7", - "sha256:7e48cdb8226644e2fbd0bdb0a0f87906a3db07087f4de77a1b1b1ccfd9e93685", - "sha256:7f63d176a81555984e91f2c84c2a574a61cab7111cc907e176f0f01538e9ff6e", - "sha256:7f6595c852ca544aaeeb32d357e62c9c780eac69dcd34e40cae7b55bc4fb1147", - "sha256:7fac95714b09da9278a0b52e492466f773cfe37651cf467a83a1b659be24bf71", - "sha256:81713b70bea5c1386dc2f32a8f0dab4148a2928c7495c808c541ee0aae614d67", - "sha256:846dd2e1243407133d3195d2d7e4ceefcaa5f5bf7278f0a9bda00967e6326b04", - "sha256:84c063af19ef5130084db70ada40ce63a84f6c1ef4d3dbc34e5e8c4febb20822", - "sha256:881764d610e3269964fc4bb3c19bb6fce55422828e152b885609ec176b41cf11", - "sha256:8994b29c462de9a8fce2d591028b986dbbe1b32f3ad600b2d3e1c482c93abad6", - "sha256:8c79e9d7e3d8a32d4824250a9c6401194fb4c2ad9a0cec8f6a96e09a582c2cc0", - "sha256:8ee427208c675f1b6e344a1f89376a9613fc30b52646a04ac0c1f6587c7e46ec", - "sha256:949681f68e0e3c25377462be4b658500e85ca24323d9619fdc41f68d46a1ffda", - "sha256:9e275792097c9f7e80741c36de3b61917aebecc08a67ae62899b074566ff8556", - "sha256:9fb815155aac6bfa8d86184079652c9715c812d506b22cfa369196ef4e99d1b4", - "sha256:a2a64e62c7a0edd07c1c917b0586655f3362d2c2d37d474db1a509efb96fea1c", - "sha256:a7ac5b4984c468ce4f4a553df281450df0a34aefae02e58d77a0847be8d1e11f", - "sha256:aa46dce75078fceaf7cecac5817422febb4355fbdda440db55206e3bd288cfb8", - "sha256:ae3476e934b9d714aa8000d2e4c01eb2590eee10b9d8cd03e7983ad65dfbfcba", - "sha256:b0341e6d9a0c0e3cdc65857ef518bb05b410dbd70d749a0d33ac0f39e81a4258", - "sha256:b40d1bf6e6f74f7c0a567a9e5e778bbd4699d1d3d2c0fe46f4b717eef9e96b95", - "sha256:b5c4804e4039f487e942c13381e6c27b4b4e66066d94ef1fae3f6ba8b953f383", - "sha256:b5d6a6c9602fd4598fa07e0389e19fe199ae96449008d8304bf5d47cb745462e", - "sha256:b5f1ac7359e17efe0b6e5fec21de34145caef22b260e978336f325d5c84e6938", - "sha256:c0167540094838ee9093ef6cc2c69d0074bbf84a432b4995835e8e5a0d984374", - "sha256:c180ac742a083e109c1a18151f4dd8675f32679985a1c750d2ff806796165b55", - "sha256:c73df5b6e8fabe2ddb74876fb82d9dd44cbace0ca12e8861ce9155ad3c886139", - "sha256:c7e177c619342e407415d4f35dec63d2d134d951e24b5166afcdfd1362828e17", - "sha256:cbad927ea8ed814622305d842c93412cb47bd39a496ed0f96bfd42b922b4a217", - "sha256:cc353841428d56b683a123a813e6a686e07026d6b1c5757970a877195f880c2d", - "sha256:cc7c92c1baa629cb03ecb0c3d12564f172218fb1739f54bf5f3881844daadc6d", - "sha256:cc7d768260f4ba4ea01741c1b5fe3d3a6c70eb91c87f4c8761bbcce5181beafe", - "sha256:d0eea830b591dbc68e030c86a9569826145df485b2b4554874b07fea1275a199", - "sha256:d216e5d9b8749563c7f2c6f7a0831057ec844c68b4c11cb10fc62d4fd373c26d", - "sha256:d401f07261dc5aa36c2e4efc308548f6ae943bfff20fcadb0a07517a26b196d8", - "sha256:d6324274b4e0e2fa1b3eccb25997b1c9ed134ff61d296448ab8269f5ac068c4c", - "sha256:d8a8b74d843c2638f3864a17d97a4acda58e40d3e44b6303b8cc3d3c44ae2d29", - "sha256:d9b6b28a57feb51605d6ae5e61a9044a31742db557a3b851a74c13bc61de5172", - "sha256:de599af166970d6a61accde358ec9ded821234cbbc8c6413acfec06056b8e860", - "sha256:e594b22688d5747b06e957f1ef822060cb5cb35b493066e33ceac0cf882188b7", - "sha256:e5b078134f48552c4d9527db2f7da0b5359abd49393cdf9794017baec7506170", - "sha256:eb6dce402734575e1a8cc0bb1509afca508a400a57ce13d306ea2c663bad1138", - "sha256:f1790a4b1e8e8e028c391175433b9c8122c39b46e1663228158e61e6f915bf06", - "sha256:f5efe0661b9fcd6246f27957f6ae1c0eb29bc60552820f01e970b4996e016004", - "sha256:f9cbfbc5faca235fbdf531b93aa0f9f005ec7d267d9d738761a4d42b744ea159", - "sha256:fbea1751729afe607d84acfd01efd95e3b31db148a181a441984ce9b3d3469da", - "sha256:fca4b4307ebe9c3ec77a084da3a9d1999d164693d16492ca2b64594340999988", - "sha256:ff5c6771c7e3511a06555afa317879b7db8d640137ba55d6ab0d0c50425cab75" + "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba", + "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193", + "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318", + "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee", + "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e", + "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1", + "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a", + "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186", + "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1", + "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50", + "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640", + "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb", + "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8", + "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc", + "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5", + "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58", + "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2", + "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393", + "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24", + "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b", + "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910", + "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c", + "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272", + "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed", + "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1", + "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04", + "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d", + "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5", + "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d", + "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889", + "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae", + "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b", + "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c", + "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576", + "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34", + "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477", + "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990", + "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2", + "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512", + "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069", + "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a", + "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6", + "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0", + "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8", + "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb", + "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa", + "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8", + "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e", + "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e", + "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985", + "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8", + "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", + "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5", + "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690", + "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10", + "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789", + "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", + "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca", + "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e", + "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5", + "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59", + "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9", + "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8", + "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db", + "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde", + "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7", + "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb", + "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3", + "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6", + "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285", + "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb", + "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8", + "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482", + "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd", + "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75", + "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760", + "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782", + "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53", + "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2", + "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1", + "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719", + "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62" ], "markers": "python_version >= '3.9'", - "version": "==1.17.1" + "version": "==1.18.3" } }, "develop": { @@ -1694,79 +1862,80 @@ }, "colorama": { "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", + "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" ], - "markers": "platform_system == 'Windows'", - "version": "==0.4.6" + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.4.4" }, "coverage": { "hashes": [ - "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376", - "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", - "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111", - "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", - "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", - "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", - "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", - "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", - "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", - "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c", - "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", - "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", - "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", - "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0", - "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", - "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", - "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", - "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", - "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", - "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", - "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", - "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", - "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", - "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", - "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", - "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", - "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", - "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", - "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", - "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901", - "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", - "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", - "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", - "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", - "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", - "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", - "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", - "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", - "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3", - "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", - "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076", - "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", - "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", - "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", - "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", - "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", - "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", - "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09", - "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", - "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", - "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f", - "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", - "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", - "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", - "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", - "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", - "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", - "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", - "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", - "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", - "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", - "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858" + "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", + "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", + "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f", + "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", + "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6", + "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae", + "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692", + "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4", + "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4", + "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", + "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d", + "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", + "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", + "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", + "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb", + "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", + "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08", + "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf", + "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b", + "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", + "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c", + "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae", + "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077", + "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00", + "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", + "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664", + "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014", + "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9", + "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6", + "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e", + "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", + "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", + "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", + "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", + "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", + "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", + "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030", + "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678", + "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", + "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902", + "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97", + "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845", + "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419", + "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464", + "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be", + "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", + "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", + "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be", + "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", + "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba", + "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", + "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", + "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4", + "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a", + "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a", + "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", + "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599", + "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", + "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", + "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", + "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1", + "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3" ], "markers": "python_version >= '3.9'", - "version": "==7.6.4" + "version": "==7.6.9" }, "flake8": { "hashes": [ @@ -1805,20 +1974,20 @@ }, "google-api-core": { "hashes": [ - "sha256:26f8d76b96477db42b55fd02a33aae4a42ec8b86b98b94969b7333a2c828bf35", - "sha256:a6652b6bd51303902494998626653671703c420f6f4c88cfd3f50ed723e9d021" + "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9", + "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf" ], "markers": "python_version >= '3.7'", - "version": "==2.22.0" + "version": "==2.24.0" }, "google-api-python-client": { "hashes": [ - "sha256:4427b2f47cd88b0355d540c2c52215f68c337f3bc9d6aae1ceeae4525977504c", - "sha256:a9d26d630810ed4631aea21d1de3e42072f98240aaf184a8a1a874a371115034" + "sha256:1b420062e03bfcaa1c79e2e00a612d29a6a934151ceb3d272fe150a656dc8f17", + "sha256:a521bbbb2ec0ba9d6f307cdd64ed6e21eeac372d1bd7493a4ab5022941f784ad" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.151.0" + "version": "==2.154.0" }, "google-api-python-client-stubs": { "hashes": [ @@ -1846,11 +2015,11 @@ }, "googleapis-common-protos": { "hashes": [ - "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63", - "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0" + "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c", + "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed" ], "markers": "python_version >= '3.7'", - "version": "==1.65.0" + "version": "==1.66.0" }, "httplib2": { "hashes": [ @@ -1928,11 +2097,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pathspec": { "hashes": [ @@ -1968,20 +2137,20 @@ }, "protobuf": { "hashes": [ - "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24", - "sha256:135658402f71bbd49500322c0f736145731b16fc79dc8f367ab544a17eab4535", - "sha256:27b246b3723692bf1068d5734ddaf2fccc2cdd6e0c9b47fe099244d80200593b", - "sha256:3e6101d095dfd119513cde7259aa703d16c6bbdfae2554dfe5cfdbe94e32d548", - "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584", - "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b", - "sha256:70585a70fc2dd4818c51287ceef5bdba6387f88a578c86d47bb34669b5552c36", - "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135", - "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868", - "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687", - "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed" + "sha256:012ce28d862ff417fd629285aca5d9772807f15ceb1a0dbd15b88f58c776c98c", + "sha256:027fbcc48cea65a6b17028510fdd054147057fa78f4772eb547b9274e5219331", + "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34", + "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110", + "sha256:32600ddb9c2a53dedc25b8581ea0f1fd8ea04956373c0c07577ce58d312522e0", + "sha256:50879eb0eb1246e3a5eabbbe566b44b10348939b7cc1b267567e8c3d07213853", + "sha256:5a41deccfa5e745cef5c65a560c76ec0ed8e70908a67cc8f4da5fce588b50d57", + "sha256:683be02ca21a6ffe80db6dd02c0b5b2892322c59ca57fd6c872d652cb80549cb", + "sha256:8ee1461b3af56145aca2800e6a3e2f928108c749ba8feccc6f5dd0062c410c0d", + "sha256:b5ba1d0e4c8a40ae0496d0e2ecfdbb82e1776928a205106d14ad6985a09ec155", + "sha256:d473655e29c0c4bbf8b69e9a8fb54645bc289dead6d753b952e7aa660254ae18" ], "markers": "python_version >= '3.8'", - "version": "==5.28.3" + "version": "==5.29.1" }, "py": { "hashes": [ @@ -2036,7 +2205,7 @@ "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84", "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c" ], - "markers": "python_version >= '3.1'", + "markers": "python_version >= '3.9'", "version": "==3.2.0" }, "pytest": { @@ -2082,11 +2251,41 @@ }, "tomli": { "hashes": [ - "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", - "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed" + "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", + "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", + "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", + "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", + "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", + "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", + "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", + "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", + "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", + "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", + "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", + "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", + "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", + "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", + "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", + "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", + "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", + "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", + "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", + "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", + "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", + "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", + "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", + "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", + "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", + "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", + "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", + "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", + "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", + "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", + "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", + "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7" ], - "markers": "python_version < '3.11'", - "version": "==2.0.2" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "types-httplib2": { "hashes": [ diff --git a/abr-testing/abr_testing/data_collection/abr_google_drive.py b/abr-testing/abr_testing/data_collection/abr_google_drive.py index 8f82567a7d1..655200745a9 100644 --- a/abr-testing/abr_testing/data_collection/abr_google_drive.py +++ b/abr-testing/abr_testing/data_collection/abr_google_drive.py @@ -34,16 +34,13 @@ def create_data_dictionary( runs_to_save: Union[Set[str], str], storage_directory: str, issue_url: str, - plate: str, - accuracy: Any, hellma_plate_standards: List[Dict[str, Any]], -) -> Tuple[List[List[Any]], List[str], List[List[Any]], List[str], List[List[Any]]]: +) -> Tuple[List[List[Any]], List[str], List[List[Any]], List[str]]: """Pull data from run files and format into a dictionary.""" runs_and_robots: List[Any] = [] runs_and_lpc: List[Dict[str, Any]] = [] headers: List[str] = [] headers_lpc: List[str] = [] - list_of_heights: List[List[Any]] = [[], [], [], [], [], [], [], []] hellma_plate_orientation = False # default hellma plate is not rotated. for filename in os.listdir(storage_directory): file_path = os.path.join(storage_directory, filename) @@ -103,12 +100,15 @@ def create_data_dictionary( run_time_min = run_time.total_seconds() / 60 except ValueError: pass # Handle datetime parsing errors if necessary + # Get protocol version # + version_number = read_robot_logs.get_protocol_version_number(file_results) if run_time_min > 0: run_row = { "Robot": robot, "Run_ID": run_id, "Protocol_Name": protocol_name, + "Protocol Version": version_number, "Software Version": software_version, "Date": start_date, "Start_Time": start_time_str, @@ -130,13 +130,10 @@ def create_data_dictionary( plate_reader_dict = read_robot_logs.plate_reader_commands( file_results, hellma_plate_standards, hellma_plate_orientation ) - list_of_heights = read_robot_logs.liquid_height_commands( - file_results, list_of_heights - ) notes = {"Note1": "", "Jira Link": issue_url} + liquid_height = read_robot_logs.get_liquid_waste_height(file_results) plate_measure = { - "Plate Measured": plate, - "End Volume Accuracy (%)": accuracy, + "Liquid Waste Height (mm)": liquid_height, "Average Temp (oC)": "", "Average RH(%)": "", } @@ -173,7 +170,6 @@ def create_data_dictionary( headers, transposed_runs_and_lpc, headers_lpc, - list_of_heights, ) @@ -211,26 +207,15 @@ def run( headers, transposed_runs_and_lpc, headers_lpc, - list_of_heights, ) = create_data_dictionary( missing_runs_from_gs, storage_directory, "", - "", - "", - hellma_plate_standards=file_values, + file_values, ) start_row = google_sheet.get_index_row() + 1 google_sheet.batch_update_cells(transposed_runs_and_robots, "A", start_row, "0") - # Record Liquid Heights Found - google_sheet_ldf = google_sheets_tool.google_sheet( - credentials_path, google_sheet_name, 2 - ) - google_sheet_ldf.get_row(1) - start_row_lhd = google_sheet_ldf.get_index_row() + 1 - google_sheet_ldf.batch_update_cells( - list_of_heights, "A", start_row_lhd, "2075262446" - ) + # Add LPC to google sheet google_sheet_lpc = google_sheets_tool.google_sheet(credentials_path, "ABR-LPC", 0) start_row_lpc = google_sheet_lpc.get_index_row() + 1 diff --git a/abr-testing/abr_testing/data_collection/abr_robot_error.py b/abr-testing/abr_testing/data_collection/abr_robot_error.py index 73cf12c6253..c2dadaae54c 100644 --- a/abr-testing/abr_testing/data_collection/abr_robot_error.py +++ b/abr-testing/abr_testing/data_collection/abr_robot_error.py @@ -11,11 +11,35 @@ import sys import json import re +from pathlib import Path import pandas as pd from statistics import mean, StatisticsError from abr_testing.tools import plate_reader +def retrieve_protocol_file( + protocol_id: str, + robot_ip: str, + storage: str, +) -> Path | str: + """Find and copy protocol file on robot with error.""" + protocol_dir = f"/var/lib/opentrons-robot-server/7.1/protocols/{protocol_id}" + + print(f"FILE TO FIND: {protocol_dir}/{protocol_id}") + # Copy protocol file found in robot oto host computer + save_dir = Path(f"{storage}/protocol_errors") + command = ["scp", "-r", f"root@{robot_ip}:{protocol_dir}", save_dir] + try: + # If file found and copied return path to file + subprocess.run(command, check=True) # type: ignore + print("File transfer successful!") + return save_dir + except subprocess.CalledProcessError as e: + print(f"Error during file transfer: {e}") + # Return empty string if file can't be copied + return "" + + def compare_current_trh_to_average( robot: str, start_time: Any, @@ -38,9 +62,13 @@ def compare_current_trh_to_average( # Find average conditions of errored time period df_all_trh = pd.DataFrame(all_trh_data) # Convert timestamps to datetime objects - df_all_trh["Timestamp"] = pd.to_datetime( - df_all_trh["Timestamp"], format="mixed", utc=True - ).dt.tz_localize(None) + print(f'TIMESTAMP: {df_all_trh["Timestamp"]}') + try: + df_all_trh["Timestamp"] = pd.to_datetime( + df_all_trh["Timestamp"], format="mixed", utc=True + ).dt.tz_localize(None) + except Exception: + print(f'The following timestamp is invalid: {df_all_trh["Timestamp"]}') # Ensure start_time is timezone-naive start_time = start_time.replace(tzinfo=None) relevant_temp_rhs = df_all_trh[ @@ -245,9 +273,10 @@ def get_user_id(user_file_path: str, assignee_name: str) -> str: return assignee_id -def get_error_runs_from_robot(ip: str) -> List[str]: +def get_error_runs_from_robot(ip: str) -> Tuple[List[str], List[str]]: """Get runs that have errors from robot.""" error_run_ids = [] + protocol_ids = [] response = requests.get( f"http://{ip}:31950/runs", headers={"opentrons-version": "3"} ) @@ -255,10 +284,13 @@ def get_error_runs_from_robot(ip: str) -> List[str]: run_list = run_data.get("data", []) for run in run_list: run_id = run["id"] + protocol_id = run["protocolId"] num_of_errors = len(run["errors"]) if not run["current"] and num_of_errors > 0: error_run_ids.append(run_id) - return error_run_ids + # Protocol ID will identify the correct folder on the robot of the protocol file + protocol_ids.append(protocol_id) + return (error_run_ids, protocol_ids) def get_robot_state( @@ -335,7 +367,7 @@ def get_robot_state( def get_run_error_info_from_robot( - ip: str, one_run: str, storage_directory: str + ip: str, one_run: str, storage_directory: str, protocol_found: bool ) -> Tuple[str, str, str, List[str], List[str], str, str]: """Get error information from robot to fill out ticket.""" description = dict() @@ -369,6 +401,9 @@ def get_run_error_info_from_robot( description["protocol_name"] = results["protocol"]["metadata"].get( "protocolName", "" ) + + # If Protocol was successfully retrieved from the robot + description["protocol_found_on_robot"] = protocol_found # Get start and end time of run start_time = datetime.strptime( results.get("startedAt", ""), "%Y-%m-%dT%H:%M:%S.%f%z" @@ -511,12 +546,21 @@ def get_run_error_info_from_robot( users_file_path = ticket.get_jira_users(storage_directory) assignee_id = get_user_id(users_file_path, assignee) run_log_file_path = "" + protocol_found = False try: - error_runs = get_error_runs_from_robot(ip) + error_runs, protocol_ids = get_error_runs_from_robot(ip) except requests.exceptions.InvalidURL: print("Invalid IP address.") sys.exit() if len(run_or_other) < 1: + # Retrieve the most recently run protocol file + protocol_files_path = retrieve_protocol_file( + protocol_ids[-1], ip, storage_directory + ) + # Set protocol_found to true if python protocol was successfully copied over + if protocol_files_path: + protocol_found = True + one_run = error_runs[-1] # Most recent run with error. ( summary, @@ -526,7 +570,9 @@ def get_run_error_info_from_robot( labels, whole_description_str, run_log_file_path, - ) = get_run_error_info_from_robot(ip, one_run, storage_directory) + ) = get_run_error_info_from_robot( + ip, one_run, storage_directory, protocol_found + ) else: ( summary, @@ -566,8 +612,15 @@ def get_run_error_info_from_robot( # OPEN TICKET issue_url = ticket.open_issue(issue_key) # MOVE FILES TO ERROR FOLDER. + print(protocol_files_path) error_files = [saved_file_path_calibration, run_log_file_path] + file_paths - error_folder_path = os.path.join(storage_directory, issue_key) + + # Move protocol file(s) to error folder + if protocol_files_path: + for file in os.listdir(protocol_files_path): + error_files.append(os.path.join(protocol_files_path, file)) + + error_folder_path = os.path.join(storage_directory, "issue_key") os.makedirs(error_folder_path, exist_ok=True) for source_file in error_files: try: @@ -577,7 +630,7 @@ def get_run_error_info_from_robot( shutil.move(source_file, destination_file) except shutil.Error: continue - # POST FILES TO TICKET + # POST ALL FILES TO TICKET list_of_files = os.listdir(error_folder_path) for file in list_of_files: file_to_attach = os.path.join(error_folder_path, file) @@ -614,28 +667,16 @@ def get_run_error_info_from_robot( headers, runs_and_lpc, headers_lpc, - list_of_heights, ) = abr_google_drive.create_data_dictionary( run_id, error_folder_path, issue_url, - "", - "", - hellma_plate_standards=file_values, + file_values, ) start_row = google_sheet.get_index_row() + 1 google_sheet.batch_update_cells(runs_and_robots, "A", start_row, "0") print("Wrote run to ABR-run-data") - # Record Liquid Heights Found - google_sheet_ldf = google_sheets_tool.google_sheet( - credentials_path, google_sheet_name, 4 - ) - start_row_lhd = google_sheet_ldf.get_index_row() + 1 - google_sheet_ldf.batch_update_cells( - list_of_heights, "A", start_row_lhd, "1795535088" - ) - print("wrote liquid heights found.") # Add LPC to google sheet google_sheet_lpc = google_sheets_tool.google_sheet( credentials_path, "ABR-LPC", 0 diff --git a/abr-testing/abr_testing/data_collection/read_robot_logs.py b/abr-testing/abr_testing/data_collection/read_robot_logs.py index 7bc83e0a54b..d4570d20110 100644 --- a/abr-testing/abr_testing/data_collection/read_robot_logs.py +++ b/abr-testing/abr_testing/data_collection/read_robot_logs.py @@ -134,7 +134,8 @@ def match_pipette_to_action( left_pipette_add = 0 for command in commandTypes: command_type = command_dict["commandType"] - command_pipette = command_dict.get("pipetteId", "") + command_params = command_dict.get("params", "") + command_pipette = command_params.get("pipetteId", "") if command_type == command and command_pipette == right_pipette: right_pipette_add = 1 elif command_type == command and command_pipette == left_pipette: @@ -213,6 +214,38 @@ def instrument_commands( return pipette_dict +def get_comment_result_by_string(file_results: Dict[str, Any], key_phrase: str) -> str: + """Get comment string based off ky phrase.""" + commandData = file_results.get("commands", "") + result_str = command_str = "" + for command in commandData: + commandType = command["commandType"] + if commandType == "comment": + command_str = command["params"].get("message", "") + try: + result_str = command_str.split(key_phrase)[1] + except IndexError: + continue + return result_str + + +def get_protocol_version_number(file_results: Dict[str, Any]) -> str: + """Get protocol version number.""" + return get_comment_result_by_string(file_results, "Protocol Version: ") + + +def get_liquid_waste_height(file_results: Dict[str, Any]) -> float: + """Find liquid waste height.""" + result_str = get_comment_result_by_string( + file_results, "Liquid Waste Total Height: " + ) + try: + height = float(result_str) + except ValueError: + height = 0.0 + return height + + def liquid_height_commands( file_results: Dict[str, Any], all_heights_list: List[List[Any]] ) -> List[List[Any]]: @@ -220,6 +253,9 @@ def liquid_height_commands( commandData = file_results.get("commands", "") robot = file_results.get("robot_name", "") run_id = file_results.get("run_id", "") + list_of_heights = [] + print(robot) + liquid_waste_height = 0.0 for command in commandData: commandType = command["commandType"] if commandType == "comment": @@ -236,16 +272,24 @@ def liquid_height_commands( well_location = str(entry.split(", ")[1].split(" ")[0]) slot_location = str(entry.split("slot ")[1].split(")")[0]) labware_name = str(entry.split("of ")[1].split(" on")[0]) - all_heights_list[0].append(robot) - all_heights_list[1].append(run_id) - all_heights_list[2].append(comment_time) - all_heights_list[3].append(labware_type) - all_heights_list[4].append(labware_name) - all_heights_list[5].append(slot_location) - all_heights_list[6].append(well_location) - all_heights_list[7].append(height) + if labware_name == "Liquid Waste": + liquid_waste_height += height + one_entry = { + "Timestamp": comment_time, + "Labware Name": labware_name, + "Labware Type": labware_type, + "Slot Location": slot_location, + "Well Location": well_location, + "All Heights (mm)": height, + } + list_of_heights.append(one_entry) except (IndexError, ValueError): continue + if len(list_of_heights) > 0: + all_heights_list[0].append(robot) + all_heights_list[1].append(run_id) + all_heights_list[2].append(list_of_heights) + all_heights_list[3].append(liquid_waste_height) return all_heights_list @@ -281,10 +325,13 @@ def plate_reader_commands( read = "yes" elif read == "yes" and commandType == "comment": result = command["params"].get("message", "") - if "result:" in result: - plate_name = result.split("result:")[0] - formatted_result = result.split("result: ")[1] - print(formatted_result) + if "result:" in result or "Result:" in result: + try: + plate_name = result.split("result:")[0] + formatted_result = result.split("result: ")[1] + except IndexError: + plate_name = result.split("Result:")[0] + formatted_result = result.split("Result: ")[1] result_dict = eval(formatted_result) result_dict_keys = list(result_dict.keys()) if len(result_dict_keys) > 1: diff --git a/abr-testing/abr_testing/data_collection/single_run_log_reader.py b/abr-testing/abr_testing/data_collection/single_run_log_reader.py index a61670e6d12..bf10347faff 100644 --- a/abr-testing/abr_testing/data_collection/single_run_log_reader.py +++ b/abr-testing/abr_testing/data_collection/single_run_log_reader.py @@ -34,14 +34,11 @@ header, runs_and_lpc, lpc_headers, - list_of_heights, ) = abr_google_drive.create_data_dictionary( run_ids_in_storage, run_log_file_path, "", - "", - "", - hellma_plate_standards=file_values, + file_values, ) print("list_of_heights not recorded.") transposed_list = list(zip(*runs_and_robots)) diff --git a/abr-testing/abr_testing/protocols/active_protocols/10_ZymoBIOMICS_Magbead_DNA_Cells_Flex.py b/abr-testing/abr_testing/protocols/active_protocols/10_ZymoBIOMICS_Magbead_DNA_Cells_Flex.py index 9631b442694..fbc77c9ed46 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/10_ZymoBIOMICS_Magbead_DNA_Cells_Flex.py +++ b/abr-testing/abr_testing/protocols/active_protocols/10_ZymoBIOMICS_Magbead_DNA_Cells_Flex.py @@ -17,7 +17,7 @@ "protocolName": "Flex ZymoBIOMICS Magbead DNA Extraction: Cells", } -requirements = {"robotType": "OT-3", "apiLevel": "2.21"} +requirements = {"robotType": "Flex", "apiLevel": "2.21"} """ Slot A1: Tips 1000 Slot A2: Tips 1000 @@ -41,11 +41,12 @@ Wells 1-12 - 9,000 ul """ -whichwash = 1 +whichwash = 0 +wash_volume_tracker = 0.0 sample_max = 48 tip1k = 0 -tip200 = 0 drop_count = 0 +m1000_tips = 0 def add_parameters(parameters: protocol_api.ParameterContext) -> None: @@ -53,28 +54,40 @@ def add_parameters(parameters: protocol_api.ParameterContext) -> None: helpers.create_hs_speed_parameter(parameters) helpers.create_single_pipette_mount_parameter(parameters) helpers.create_dot_bottom_parameter(parameters) + helpers.create_deactivate_modules_parameter(parameters) -def run(ctx: protocol_api.ProtocolContext) -> None: +def run(protocol: protocol_api.ProtocolContext) -> None: """Protocol Set Up.""" - heater_shaker_speed = ctx.params.heater_shaker_speed # type: ignore[attr-defined] - mount = ctx.params.pipette_mount # type: ignore[attr-defined] - dot_bottom = ctx.params.dot_bottom # type: ignore[attr-defined] + heater_shaker_speed = protocol.params.heater_shaker_speed # type: ignore[attr-defined] + mount = protocol.params.pipette_mount # type: ignore[attr-defined] + dot_bottom = protocol.params.dot_bottom # type: ignore[attr-defined] + deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined] + helpers.comment_protocol_version(protocol, "01") + dry_run = False TIP_TRASH = ( False # True = Used tips go in Trash, False = Used tips go back into rack ) res_type = "nest_12_reservoir_15ml" - - num_samples = 8 - wash1_vol = 500.0 - wash2_vol = wash3_vol = 900.0 - lysis_vol = 200.0 + global m1000_tips + num_samples = 96 + wash1_vol = wash2_vol = wash3_vol = 400.0 + lysis_vol = 90.0 sample_vol = 10.0 # Sample should be pelleted tissue/bacteria/cells bind_vol = 600.0 bind2_vol = 500.0 elution_vol = 75.0 + def tipcheck(m1000: InstrumentContext) -> None: + """Tip tracking function.""" + global m1000_tips + if m1000_tips >= 3 * 96: + m1000.reset_tipracks() + m1000_tips == 0 + m1000.pick_up_tip() + m1000_tips += 8 + # Protocol Parameters deepwell_type = "nest_96_wellplate_2ml_deep" @@ -97,37 +110,41 @@ def run(ctx: protocol_api.ProtocolContext) -> None: bead_vol = 25.0 starting_vol = lysis_vol + sample_vol binding_buffer_vol = bind_vol + bead_vol - ctx.load_trash_bin("A3") - h_s: HeaterShakerContext = ctx.load_module(helpers.hs_str, "D1") # type: ignore[assignment] + protocol.load_trash_bin("A3") + h_s: HeaterShakerContext = protocol.load_module( + helpers.hs_str, "D1" + ) # type: ignore[assignment] labware_name = "Samples" sample_plate, h_s_adapter = helpers.load_hs_adapter_and_labware( deepwell_type, h_s, labware_name ) h_s.close_labware_latch() - temp: TemperatureModuleContext = ctx.load_module( + temp: TemperatureModuleContext = protocol.load_module( helpers.temp_str, "D3" ) # type: ignore[assignment] elutionplate, temp_adapter = helpers.load_temp_adapter_and_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", temp, "Elution Plate" ) - magblock: MagneticBlockContext = ctx.load_module( + magblock: MagneticBlockContext = protocol.load_module( helpers.mag_str, "C1" ) # type: ignore[assignment] - waste_reservoir = ctx.load_labware("nest_1_reservoir_195ml", "B3", "Liquid Waste") + waste_reservoir = protocol.load_labware( + "nest_1_reservoir_290ml", "B3", "Liquid Waste" + ) waste = waste_reservoir.wells()[0].top() - res1 = ctx.load_labware(res_type, "D2", "reagent reservoir 1") - res2 = ctx.load_labware(res_type, "C2", "reagent reservoir 2") + res1 = protocol.load_labware(res_type, "D2", "reagent reservoir 1") + res2 = protocol.load_labware(res_type, "C2", "reagent reservoir 2") + res3 = protocol.load_labware(res_type, "B2", "reagent reservoir 3") num_cols = math.ceil(num_samples / 8) # Load tips and combine all similar boxes - tips1000 = ctx.load_labware("opentrons_flex_96_tiprack_1000ul", "A1", "Tips 1") - tips1001 = ctx.load_labware("opentrons_flex_96_tiprack_1000ul", "A2", "Tips 2") - tips1002 = ctx.load_labware("opentrons_flex_96_tiprack_1000ul", "B1", "Tips 3") - tips = [*tips1000.wells()[num_samples:96], *tips1001.wells(), *tips1002.wells()] + tips1000 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "A1", "Tips 1") + tips1001 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "A2", "Tips 2") + tips1002 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "B1", "Tips 3") tips_sn = tips1000.wells()[:num_samples] # load instruments - m1000 = ctx.load_instrument( + m1000 = protocol.load_instrument( "flex_8channel_1000", mount, tip_racks=[tips1000, tips1001, tips1002] ) @@ -135,50 +152,32 @@ def run(ctx: protocol_api.ProtocolContext) -> None: Here is where you can define the locations of your reagents. """ lysis_ = res1.wells()[0] - binding_buffer = res1.wells()[1:4] - bind2_res = res1.wells()[4:8] - wash1 = res1.wells()[6:8] - elution_solution = res1.wells()[-1] - wash2 = res2.wells()[:6] - wash3 = res2.wells()[6:] - + binding_buffer = res1.wells()[1:8] + bind2_res = res1.wells()[8:12] + all_washes = res2.wells()[1:] + elution_solution = res2.wells()[0] + all_washes.extend(res3.wells()[:2]) samples_m = sample_plate.rows()[0][:num_cols] elution_samples_m = elutionplate.rows()[0][:num_cols] # Redefine per well for liquid definitions samps = sample_plate.wells()[: (8 * num_cols)] - liquid_vols_and_wells: Dict[str, List[Dict[str, Well | List[Well] | float]]] = { "Lysis and PK": [{"well": lysis_, "volume": 12320.0}], "Beads and Binding": [{"well": binding_buffer, "volume": 11875.0}], "Binding 2": [{"well": bind2_res, "volume": 13500.0}], - "Final Elution": [{"well": elution_solution, "volume": 52000}], + "Final Elution": [{"well": elution_solution, "volume": 7500.0}], "Samples": [{"well": samps, "volume": 0.0}], - "Reagents": [{"well": res2.wells(), "volume": 9000.0}], + "Reagents": [{"well": all_washes, "volume": 9800.0}], } - flattened_list_of_wells = helpers.find_liquid_height_of_loaded_liquids( - ctx, liquid_vols_and_wells, m1000 - ) + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, m1000) m1000.flow_rate.aspirate = 300 m1000.flow_rate.dispense = 300 m1000.flow_rate.blow_out = 300 - def tiptrack(tipbox: List[Well]) -> None: - """Track Tips.""" - global tip1k - global drop_count - if tipbox == tips: - m1000.pick_up_tip(tipbox[int(tip1k)]) - tip1k = tip1k + 8 - - drop_count = drop_count + 8 - if drop_count >= 150: - drop_count = 0 - ctx.pause("Please empty the waste bin of all the tips before continuing.") - def remove_supernatant(vol: float) -> None: """Remove supernatant.""" - ctx.comment("-----Removing Supernatant-----") + protocol.comment("-----Removing Supernatant-----") m1000.flow_rate.aspirate = 30 num_trans = math.ceil(vol / 980) vol_per_trans = vol / num_trans @@ -198,7 +197,7 @@ def remove_supernatant(vol: float) -> None: m1000.flow_rate.aspirate = 300 # Transfer from Magdeck plate to H-S - helpers.move_labware_to_hs(ctx, sample_plate, h_s, h_s_adapter) + helpers.move_labware_to_hs(protocol, sample_plate, h_s, h_s_adapter) def bead_mixing( well: Well, pip: InstrumentContext, mvol: float, reps: int = 8 @@ -261,6 +260,7 @@ def mixing(well: Well, pip: InstrumentContext, mvol: float, reps: int = 8) -> No dispensing at the top and 2 cycles of aspirating from middle, dispensing at the bottom """ + pip.liquid_presence_detection = False center = well.top(5) asp = well.bottom(1) disp = well.top(-8) @@ -290,15 +290,18 @@ def mixing(well: Well, pip: InstrumentContext, mvol: float, reps: int = 8) -> No def lysis(vol: float, source: Well) -> None: """Lysis.""" - ctx.comment("-----Beginning Lysis Steps-----") - ctx.pause(msg="\n Hello \n - step 1 \n - step 2") + protocol.comment("-----Beginning Lysis Steps-----") num_transfers = math.ceil(vol / 980) - tiptrack(tips) + tipcheck(m1000) + total_lysis_aspirated = 0.0 for i in range(num_cols): src = source tvol = vol / num_transfers # Mix Shield and PK before transferring first time if i == 0: + m1000.liquid_presence_detection = ( + False # turn off liquid detection during mixing + ) for x in range(lysis_rep_1): m1000.aspirate(vol, src.bottom(1)) m1000.dispense(vol, src.bottom(8)) @@ -308,15 +311,14 @@ def lysis(vol: float, source: Well) -> None: m1000.aspirate(tvol, src.bottom(1)) m1000.air_gap(10) m1000.dispense(m1000.current_volume, samples_m[i].top()) - + total_lysis_aspirated += tvol * 8 # Mix shield and pk with samples for i in range(num_cols): if i != 0: - tiptrack(tips) + tipcheck(m1000) mixing(samples_m[i], m1000, tvol, reps=lysis_rep_2) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, lysis_incubation, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, lysis_incubation, True) def bind(vol1: float, vol2: float) -> None: """Binding. @@ -334,9 +336,9 @@ def bind(vol1: float, vol2: float) -> None: supernatant to the final clean elutions PCR plate. """ - ctx.comment("-----Beginning Binding Steps-----") + protocol.comment("-----Beginning Binding Steps-----") for i, well in enumerate(samples_m): - tiptrack(tips) + tipcheck(m1000) num_trans = math.ceil(vol1 / 980) vol_per_trans = vol1 / num_trans source = binding_buffer[i // 2] @@ -360,15 +362,19 @@ def bind(vol1: float, vol2: float) -> None: m1000.air_gap(10) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed * 0.9, bind_time_1, True) + helpers.set_hs_speed( + protocol, h_s, heater_shaker_speed * 0.9, bind_time_1, True + ) # Transfer from H-S plate to Magdeck plate - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) for bindi in np.arange( settling_time + 1, 0, -0.5 ): # Settling time delay with countdown timer - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(bindi) + " minutes left in the incubation.", ) @@ -376,8 +382,8 @@ def bind(vol1: float, vol2: float) -> None: # remove initial supernatant remove_supernatant(vol1 + starting_vol) - ctx.comment("-----Beginning Bind #2 Steps-----") - tiptrack(tips) + protocol.comment("-----Beginning Bind #2 Steps-----") + tipcheck(m1000) for i, well in enumerate(samples_m): num_trans = math.ceil(vol2 / 980) vol_per_trans = vol2 / num_trans @@ -402,20 +408,22 @@ def bind(vol1: float, vol2: float) -> None: for i in range(num_cols): if i != 0: - tiptrack(tips) + tipcheck(m1000) bead_mixing( samples_m[i], m1000, vol_per_trans, reps=3 if not dry_run else 1 ) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, bind_time_2, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, bind_time_2, True) # Transfer from H-S plate to Magdeck plate - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) for bindi in np.arange( settling_time + 1, 0, -0.5 ): # Settling time delay with countdown timer - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(bindi) + " minutes left in the incubation.", ) @@ -426,52 +434,43 @@ def bind(vol1: float, vol2: float) -> None: def wash(vol: float, source: List[Well]) -> None: """Wash Steps.""" global whichwash # Defines which wash the protocol is on to log on the app - - if source == wash1: - whichwash = 1 - const = 6 // len(source) - if source == wash2: - whichwash = 2 - const = 6 // len(source) - height = 1 - if source == wash3: - whichwash = 3 - const = 6 // len(source) - height = 1 - - ctx.comment("-----Wash #" + str(whichwash) + " is starting now------") + protocol.comment("-----Now starting Wash #" + str(whichwash) + "-----") + global wash_volume_tracker num_trans = math.ceil(vol / 980) vol_per_trans = vol / num_trans - tiptrack(tips) + tipcheck(m1000) for i, m in enumerate(samples_m): - if source == wash1: - if i == 0 or i == 3: - height = 10 - else: - height = 1 - src = source[i // const] + src = source[whichwash] for n in range(num_trans): if m1000.current_volume > 0: m1000.dispense(m1000.current_volume, src.top()) m1000.require_liquid_presence(src) m1000.transfer( vol_per_trans, - src.bottom(height), + src.bottom(dot_bottom), m.top(), air_gap=20, new_tip="never", ) + wash_volume_tracker += vol_per_trans * 8 + if wash_volume_tracker >= 9600: + whichwash += 1 + src = source[whichwash] + protocol.comment(f"new wash source {whichwash}") + wash_volume_tracker = 0.0 m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed * 0.9, wash_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed * 0.9, wash_time, True) - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) for washi in np.arange( settling_time, 0, -0.5 ): # settling time timer for washes - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(washi) @@ -483,27 +482,30 @@ def wash(vol: float, source: List[Well]) -> None: remove_supernatant(vol) def elute(vol: float) -> None: - tiptrack(tips) + tipcheck(m1000) + total_elution_vol = 0.0 for i, m in enumerate(samples_m): m1000.require_liquid_presence(elution_solution) m1000.aspirate(vol, elution_solution) m1000.air_gap(20) m1000.dispense(m1000.current_volume, m.top(-3)) + total_elution_vol += vol * 8 m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed * 0.9, wash_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed * 0.9, wash_time, True) # Transfer back to magnet - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) for elutei in np.arange(settling_time, 0, -0.5): - ctx.delay( + protocol.delay( minutes=0.5, msg="Incubating on MagDeck for " + str(elutei) + " more minutes.", ) for i, (m, e) in enumerate(zip(samples_m, elution_samples_m)): - tiptrack(tips) + tipcheck(m1000) m1000.flow_rate.dispense = 100 m1000.flow_rate.aspirate = 25 m1000.transfer( @@ -521,16 +523,23 @@ def elute(vol: float) -> None: """ lysis(lysis_vol, lysis_) bind(binding_buffer_vol, bind2_vol) - wash(wash1_vol, wash1) - wash(wash2_vol, wash2) - wash(wash3_vol, wash3) + wash(wash1_vol, all_washes) + wash(wash2_vol, all_washes) + wash(wash3_vol, all_washes) h_s.set_and_wait_for_temperature(55) for beaddry in np.arange(drybeads, 0, -0.5): - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(beaddry) + " minutes left in the drying step.", ) elute(elution_vol) h_s.deactivate_heater() - flattened_list_of_wells.extend([waste_reservoir["A1"], elutionplate["A1"]]) - helpers.find_liquid_height_of_all_wells(ctx, m1000, flattened_list_of_wells) + helpers.clean_up_plates( + m1000, + [elutionplate, sample_plate, res1, res3, res2], + waste_reservoir["A1"], + 1000, + ) + helpers.find_liquid_height_of_all_wells(protocol, m1000, [waste_reservoir["A1"]]) + if deactivate_modules_bool: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/active_protocols/11_Dynabeads_IP_Flex_96well_RIT.py b/abr-testing/abr_testing/protocols/active_protocols/11_Dynabeads_IP_Flex_96well_RIT.py index e885ec45a5e..44db654cc1f 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/11_Dynabeads_IP_Flex_96well_RIT.py +++ b/abr-testing/abr_testing/protocols/active_protocols/11_Dynabeads_IP_Flex_96well_RIT.py @@ -1,5 +1,5 @@ """Immunoprecipitation by Dynabeads.""" -from opentrons.protocol_api import ProtocolContext, ParameterContext, Well +from opentrons.protocol_api import ProtocolContext, ParameterContext, Well, Labware from opentrons.protocol_api.module_contexts import ( HeaterShakerContext, TemperatureModuleContext, @@ -15,7 +15,7 @@ } requirements = { - "robotType": "OT-3", + "robotType": "Flex", "apiLevel": "2.21", } @@ -25,6 +25,7 @@ def add_parameters(parameters: ParameterContext) -> None: helpers.create_hs_speed_parameter(parameters) helpers.create_two_pipette_mount_parameters(parameters) helpers.create_dot_bottom_parameter(parameters) + helpers.create_deactivate_modules_parameter(parameters) NUM_COL = 12 @@ -34,7 +35,7 @@ def add_parameters(parameters: ParameterContext) -> None: BEADS_VOL = 50 AB_VOL = 50 SAMPLE_VOL = 200 -WASH_TIMES = 3 +WASH_TIMES = 6 WASH_VOL = 200 ELUTION_VOL = 50 @@ -48,64 +49,78 @@ def add_parameters(parameters: ParameterContext) -> None: TIP_TRASH = False -def run(ctx: ProtocolContext) -> None: +def run(protocol: ProtocolContext) -> None: """Protocol.""" # defining variables inside def run - heater_shaker_speed = ctx.params.heater_shaker_speed # type: ignore[attr-defined] - ASP_HEIGHT = ctx.params.dot_bottom # type: ignore[attr-defined] - single_channel_mount = ctx.params.pipette_mount_1 # type: ignore[attr-defined] - eight_channel_mount = ctx.params.pipette_mount_2 # type: ignore[attr-defined] + heater_shaker_speed = protocol.params.heater_shaker_speed # type: ignore[attr-defined] + ASP_HEIGHT = protocol.params.dot_bottom # type: ignore[attr-defined] + single_channel_mount = protocol.params.pipette_mount_1 # type: ignore[attr-defined] + eight_channel_mount = protocol.params.pipette_mount_2 # type: ignore[attr-defined] + deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined] + helpers.comment_protocol_version(protocol, "01") + MIX_SPEED = heater_shaker_speed MIX_SEC = 10 # if on deck: - INCUBATION_SPEEND = heater_shaker_speed * 0.5 + INCUBATION_SPEED = heater_shaker_speed * 0.5 INCUBATION_MIN = 60 # load labware - sample_plate = ctx.load_labware("nest_96_wellplate_2ml_deep", "B2", "samples") - wash_res = ctx.load_labware("nest_12_reservoir_15ml", "B1", "wash") - reagent_res = ctx.load_labware( + sample_plate_1 = protocol.load_labware( + "nest_96_wellplate_2ml_deep", "B2", "sample plate 1" + ) + sample_plate_2 = protocol.load_labware( + "nest_96_wellplate_2ml_deep", "C4", "sample plate 2" + ) + + wash_res = protocol.load_labware("nest_12_reservoir_15ml", "B1", "wash") + reagent_res = protocol.load_labware( "opentrons_15_tuberack_nest_15ml_conical", "C3", "reagents" ) - waste_res = ctx.load_labware("nest_1_reservoir_290ml", "D2", "waste") + waste_res = protocol.load_labware("nest_1_reservoir_290ml", "D2", "Liquid Waste") - tips = ctx.load_labware("opentrons_flex_96_tiprack_1000ul", "B3") - tips_sample = ctx.load_labware( + tips = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "B3") + tips_sample = protocol.load_labware( "opentrons_flex_96_tiprack_1000ul", "A2", "sample tips" ) tips_sample_loc = tips_sample.wells()[:95] if READY_FOR_SDSPAGE == 0: - tips_elu = ctx.load_labware( + tips_elu = protocol.load_labware( "opentrons_flex_96_tiprack_1000ul", "A1", "elution tips" ) tips_elu_loc = tips_elu.wells()[:95] - tips_reused = ctx.load_labware( + tips_reused = protocol.load_labware( "opentrons_flex_96_tiprack_1000ul", "C2", "reused tips" ) tips_reused_loc = tips_reused.wells()[:95] - p1000 = ctx.load_instrument( + p1000 = protocol.load_instrument( "flex_8channel_1000", eight_channel_mount, tip_racks=[tips] ) - p1000_single = ctx.load_instrument( + p1000_single = protocol.load_instrument( "flex_1channel_1000", single_channel_mount, tip_racks=[tips] ) - h_s: HeaterShakerContext = ctx.load_module(helpers.hs_str, "D1") # type: ignore[assignment] + h_s: HeaterShakerContext = protocol.load_module( + helpers.hs_str, "D1" + ) # type: ignore[assignment] working_plate, h_s_adapter = helpers.load_hs_adapter_and_labware( "nest_96_wellplate_2ml_deep", h_s, "Working Plate" ) if READY_FOR_SDSPAGE == 0: - temp: TemperatureModuleContext = ctx.load_module( + temp: TemperatureModuleContext = protocol.load_module( helpers.temp_str, "D3" ) # type: ignore[assignment] final_plate, temp_adapter = helpers.load_temp_adapter_and_labware( "nest_96_wellplate_2ml_deep", temp, "Final Plate" ) - mag: MagneticBlockContext = ctx.load_module(helpers.mag_str, "C1") # type: ignore[assignment] + mag: MagneticBlockContext = protocol.load_module( + helpers.mag_str, "C1" + ) # type: ignore[assignment] # liquids - samples = sample_plate.rows()[0][:NUM_COL] # 1 + samples1 = sample_plate_1.rows()[0][:NUM_COL] # 1 + samples2 = sample_plate_2.rows()[0][:NUM_COL] # 1 beads = reagent_res.wells()[0] # 2 ab = reagent_res.wells()[1] # 3 elu = reagent_res.wells()[2] # 4 @@ -119,14 +134,15 @@ def run(ctx: ProtocolContext) -> None: liquid_vols_and_wells: Dict[ str, List[Dict[str, Union[Well, List[Well], float]]] ] = { - "Beads": [{"well": beads, "volume": 4900.0}], - "AB": [{"well": ab, "volume": 4900.0}], - "Elution": [{"well": elu, "volume": 4900.0}], - "Wash": [{"well": wash, "volume": 750.0}], - "Samples": [{"well": samples, "volume": 250.0}], + "Beads": [{"well": beads, "volume": 9800.0}], + "AB": [{"well": ab, "volume": 9800.0}], + "Elution": [{"well": elu, "volume": 9800.0}], + "Wash": [{"well": wash, "volume": 1500.0}], + "Samples 1": [{"well": samples1, "volume": 250.0}], + "Samples 2": [{"well": samples2, "volume": 250.0}], } helpers.find_liquid_height_of_loaded_liquids( - ctx, liquid_vols_and_wells, p1000_single + protocol, liquid_vols_and_wells, p1000_single ) def transfer_plate_to_plate( @@ -193,9 +209,6 @@ def discard(vol3: float, start: List[Well]) -> None: """Discard function.""" global waste_vol global waste_vol_chk - if waste_vol_chk >= WASTE_VOL_MAX: - ctx.pause("Empty Liquid Waste") - waste_vol_chk = 0 waste_vol = 0.0 for k in range(NUM_COL): p1000.pick_up_tip(tips_reused_loc[k * 8]) @@ -210,64 +223,77 @@ def discard(vol3: float, start: List[Well]) -> None: waste_vol_chk = waste_vol_chk + waste_vol # protocol - - # Add beads, samples and antibody solution - h_s.close_labware_latch() - transfer_well_to_plate(BEADS_VOL, beads, working_wells, 2) - - helpers.move_labware_from_hs_to_destination(ctx, working_plate, h_s, mag) - - ctx.delay(minutes=MAG_DELAY_MIN) - discard(BEADS_VOL * 1.1, working_cols) - - helpers.move_labware_to_hs(ctx, working_plate, h_s, h_s_adapter) - - transfer_plate_to_plate(SAMPLE_VOL, samples, working_cols, 1) - transfer_well_to_plate(AB_VOL, ab, working_wells, 3) - - h_s.set_and_wait_for_shake_speed(rpm=MIX_SPEED) - ctx.delay(seconds=MIX_SEC) - - h_s.set_and_wait_for_shake_speed(rpm=INCUBATION_SPEEND) - ctx.delay(seconds=INCUBATION_MIN * 60) - h_s.deactivate_shaker() - - helpers.move_labware_from_hs_to_destination(ctx, working_plate, h_s, mag) - - ctx.delay(minutes=MAG_DELAY_MIN) - vol_total = SAMPLE_VOL + AB_VOL - discard(vol_total * 1.1, working_cols) - - # Wash - for _ in range(WASH_TIMES): - helpers.move_labware_to_hs(ctx, working_plate, h_s, h_s_adapter) - - transfer_well_to_plate(WASH_VOL, wash, working_cols, 5) - helpers.set_hs_speed(ctx, h_s, MIX_SPEED, MIX_SEC / 60, True) - helpers.move_labware_from_hs_to_destination(ctx, working_plate, h_s, mag) - ctx.delay(minutes=MAG_DELAY_MIN) - discard(WASH_VOL * 1.1, working_cols) - - # Elution - helpers.move_labware_to_hs(ctx, working_plate, h_s, h_s_adapter) - - transfer_well_to_plate(ELUTION_VOL, elu, working_wells, 4) - if READY_FOR_SDSPAGE == 1: - ctx.pause("Seal the Working Plate") - h_s.set_and_wait_for_temperature(70) - helpers.set_hs_speed(ctx, h_s, MIX_SPEED, (MIX_SEC / 60) + 10, True) - h_s.deactivate_heater() - h_s.open_labware_latch() - ctx.pause("Protocol Complete") - - elif READY_FOR_SDSPAGE == 0: - helpers.set_hs_speed(ctx, h_s, MIX_SPEED, (MIX_SEC / 60) + 2, True) - - temp.set_temperature(4) - helpers.move_labware_from_hs_to_destination(ctx, working_plate, h_s, mag) - ctx.delay(minutes=MAG_DELAY_MIN) - transfer_plate_to_plate(ELUTION_VOL * 1.1, working_cols, final_cols, 6) - temp.deactivate() - end_wells_to_probe = [reagent_res["A1"], reagent_res["B1"], reagent_res["C1"]] - end_wells_to_probe.extend(wash_res.wells()) - helpers.find_liquid_height_of_all_wells(ctx, p1000_single, end_wells_to_probe) + def run(sample_plate: Labware) -> None: + """Protocol.""" + # Add beads, samples and antibody solution + samples = sample_plate.rows()[0][:NUM_COL] # 1 + h_s.close_labware_latch() + transfer_well_to_plate(BEADS_VOL, beads, working_wells, 2) + + helpers.move_labware_from_hs_to_destination(protocol, working_plate, h_s, mag) + + protocol.delay(minutes=MAG_DELAY_MIN) + discard(BEADS_VOL * 1.1, working_cols) + + helpers.move_labware_to_hs(protocol, working_plate, h_s, h_s_adapter) + + transfer_plate_to_plate(SAMPLE_VOL, samples, working_cols, 1) + transfer_well_to_plate(AB_VOL, ab, working_wells, 3) + + h_s.set_and_wait_for_shake_speed(rpm=MIX_SPEED) + protocol.delay(seconds=MIX_SEC) + + h_s.set_and_wait_for_shake_speed(rpm=INCUBATION_SPEED) + protocol.delay(seconds=INCUBATION_MIN * 60) + h_s.deactivate_shaker() + + helpers.move_labware_from_hs_to_destination(protocol, working_plate, h_s, mag) + + protocol.delay(minutes=MAG_DELAY_MIN) + vol_total = SAMPLE_VOL + AB_VOL + discard(vol_total * 1.1, working_cols) + + # Wash + for _ in range(WASH_TIMES): + helpers.move_labware_to_hs(protocol, working_plate, h_s, h_s_adapter) + + transfer_well_to_plate(WASH_VOL, wash, working_cols, 5) + helpers.set_hs_speed(protocol, h_s, MIX_SPEED, MIX_SEC / 60, True) + helpers.move_labware_from_hs_to_destination( + protocol, working_plate, h_s, mag + ) + protocol.delay(minutes=MAG_DELAY_MIN) + discard(WASH_VOL * 1.1, working_cols) + # Elution + helpers.move_labware_to_hs(protocol, working_plate, h_s, h_s_adapter) + transfer_well_to_plate(ELUTION_VOL, elu, working_wells, 4) + if READY_FOR_SDSPAGE == 1: + protocol.pause("Seal the Working Plate") + h_s.set_and_wait_for_temperature(70) + helpers.set_hs_speed(protocol, h_s, MIX_SPEED, (MIX_SEC / 60) + 10, True) + h_s.deactivate_heater() + h_s.open_labware_latch() + protocol.pause("Protocol Complete") + elif READY_FOR_SDSPAGE == 0: + helpers.set_hs_speed(protocol, h_s, MIX_SPEED, (MIX_SEC / 60) + 2, True) + + temp.set_temperature(4) + helpers.move_labware_from_hs_to_destination( + protocol, working_plate, h_s, mag + ) + protocol.delay(minutes=MAG_DELAY_MIN) + transfer_plate_to_plate(ELUTION_VOL * 1.1, working_cols, final_cols, 6) + temp.deactivate() + helpers.clean_up_plates(p1000_single, [sample_plate], waste, 1000) + helpers.move_labware_to_hs(protocol, working_plate, h_s, h_s_adapter) + + run(sample_plate_1) + # swap plates + protocol.move_labware(sample_plate_1, "B4", True) + protocol.move_labware(sample_plate_2, "B2", True) + run(sample_plate_2) + + helpers.clean_up_plates(p1000_single, [wash_res], waste, 1000) + helpers.find_liquid_height_of_all_wells(protocol, p1000_single, [waste_res["A1"]]) + if deactivate_modules_bool: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/active_protocols/12_KAPA HyperPlus Library Prep.py b/abr-testing/abr_testing/protocols/active_protocols/12_KAPA HyperPlus Library Prep.py index 75658f11438..ff38e8cf7c7 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/12_KAPA HyperPlus Library Prep.py +++ b/abr-testing/abr_testing/protocols/active_protocols/12_KAPA HyperPlus Library Prep.py @@ -18,7 +18,7 @@ metadata = { "protocolName": "KAPA HyperPlus Library Preparation", - "author": "Your Name ", + "author": "Tony Ngumah ", } requirements = {"robotType": "Flex", "apiLevel": "2.21"} @@ -41,6 +41,7 @@ def add_parameters(parameters: ParameterContext) -> None: helpers.create_disposable_lid_parameter(parameters) helpers.create_tc_lid_deck_riser_parameter(parameters) helpers.create_two_pipette_mount_parameters(parameters) + helpers.create_deactivate_modules_parameter(parameters) parameters.add_int( variable_name="num_samples", display_name="number of samples", @@ -68,22 +69,25 @@ def add_parameters(parameters: ParameterContext) -> None: ) -def run(ctx: ProtocolContext) -> None: +def run(protocol: ProtocolContext) -> None: """Protocol.""" USE_GRIPPER = True - trash_tips = ctx.params.trash_tips # type: ignore[attr-defined] - dry_run = ctx.params.dry_run # type: ignore[attr-defined] - pipette_1000_mount = ctx.params.pipette_mount_1 # type: ignore[attr-defined] - pipette_50_mount = ctx.params.pipette_mount_2 # type: ignore[attr-defined] - deck_riser = ctx.params.deck_riser # type: ignore[attr-defined] + deactivate_mods = protocol.params.deactivate_modules # type: ignore[attr-defined] + trash_tips = protocol.params.trash_tips # type: ignore[attr-defined] + dry_run = protocol.params.dry_run # type: ignore[attr-defined] + pipette_1000_mount = protocol.params.pipette_mount_1 # type: ignore[attr-defined] + pipette_50_mount = protocol.params.pipette_mount_2 # type: ignore[attr-defined] + deck_riser = protocol.params.deck_riser # type: ignore[attr-defined] + helpers.comment_protocol_version(protocol, "01") + REUSE_ETOH_TIPS = True REUSE_RSB_TIPS = ( True # Reuse tips for RSB buffer (adding RSB, mixing, and transferring) ) REUSE_REMOVE_TIPS = True # Reuse tips for supernatant removal - num_samples = ctx.params.num_samples # type: ignore[attr-defined] - PCRCYCLES = ctx.params.PCR_CYCLES # type: ignore[attr-defined] - disposable_lid = ctx.params.disposable_lid # type: ignore[attr-defined] + num_samples = protocol.params.num_samples # type: ignore[attr-defined] + PCRCYCLES = protocol.params.PCR_CYCLES # type: ignore[attr-defined] + disposable_lid = protocol.params.disposable_lid # type: ignore[attr-defined] Fragmentation_time = 10 ligation_tc_time = 15 used_lids: List[Labware] = [] @@ -111,10 +115,10 @@ def run(ctx: ProtocolContext) -> None: # etoh_vol = 400.0 # Importing Labware, Modules and Instruments - magblock: MagneticBlockContext = ctx.load_module( + magblock: MagneticBlockContext = protocol.load_module( helpers.mag_str, "D2" ) # type: ignore[assignment] - temp_mod: TemperatureModuleContext = ctx.load_module( + temp_mod: TemperatureModuleContext = protocol.load_module( helpers.temp_str, "B3" ) # type: ignore[assignment] temp_plate, temp_adapter = helpers.load_temp_adapter_and_labware( @@ -125,7 +129,7 @@ def run(ctx: ProtocolContext) -> None: if not dry_run: temp_mod.set_temperature(4) - tc_mod: ThermocyclerContext = ctx.load_module(helpers.tc_str) # type: ignore[assignment] + tc_mod: ThermocyclerContext = protocol.load_module(helpers.tc_str) # type: ignore[assignment] # Just in case tc_mod.open_lid() @@ -134,32 +138,32 @@ def run(ctx: ProtocolContext) -> None: ) samples_flp = FLP_plate.rows()[0][:num_cols] - sample_plate = ctx.load_labware( + sample_plate = protocol.load_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", "D1", "Sample Plate 1" ) - sample_plate_2 = ctx.load_labware( + sample_plate_2 = protocol.load_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", "B2", "Sample Plate 2" ) samples_2 = sample_plate_2.rows()[0][:num_cols] samples = sample_plate.rows()[0][:num_cols] - reservoir = ctx.load_labware( + reservoir = protocol.load_labware( "nest_96_wellplate_2ml_deep", "C2", "Beads + Buffer + Ethanol" ) # Load tipracks - tiprack_50_1 = ctx.load_labware("opentrons_flex_96_tiprack_50ul", "A3") - tiprack_50_2 = ctx.load_labware("opentrons_flex_96_tiprack_50ul", "A2") + tiprack_50_1 = protocol.load_labware("opentrons_flex_96_tiprack_50ul", "A3") + tiprack_50_2 = protocol.load_labware("opentrons_flex_96_tiprack_50ul", "A2") - tiprack_200_1 = ctx.load_labware("opentrons_flex_96_tiprack_200ul", "C1") - tiprack_200_2 = ctx.load_labware("opentrons_flex_96_tiprack_200ul", "C3") + tiprack_200_1 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "C1") + tiprack_200_2 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "C3") if trash_tips: - ctx.load_waste_chute() + protocol.load_waste_chute() unused_lids: List[Labware] = [] # Load TC Lids if disposable_lid: - unused_lids = helpers.load_disposable_lids(ctx, 5, ["C4"], deck_riser) + unused_lids = helpers.load_disposable_lids(protocol, 5, ["C4"], deck_riser) # Import Global Variables global tip50 @@ -168,12 +172,12 @@ def run(ctx: ProtocolContext) -> None: global p200_rack_count tip_count = {1000: 0, 50: 0} - p200 = ctx.load_instrument( + p200 = protocol.load_instrument( "flex_8channel_1000", pipette_1000_mount, tip_racks=[tiprack_200_1, tiprack_200_2], ) - p50 = ctx.load_instrument( + p50 = protocol.load_instrument( "flex_8channel_50", pipette_50_mount, tip_racks=[tiprack_50_1, tiprack_50_2] ) @@ -224,7 +228,7 @@ def run(ctx: ProtocolContext) -> None: waste2 = reservoir.columns()[7] waste2_res = waste2[0] - helpers.find_liquid_height_of_loaded_liquids(ctx, liquid_vols_and_wells, p50) + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, p50) def tip_track(pipette: InstrumentContext, tip_count: Dict) -> None: """Track tip usage.""" @@ -248,19 +252,19 @@ def run_tag_profile( ) -> Tuple[List[Labware], List[Labware]]: """Run Tag Profile.""" # Presetting Thermocycler Temps - ctx.comment( + protocol.comment( "****Starting Fragmentation Profile (37C for 10 minutes with 100C lid)****" ) tc_mod.set_lid_temperature(100) tc_mod.set_block_temperature(37) # Move Plate to TC - ctx.comment("****Moving Plate to Pre-Warmed TC Module Block****") - ctx.move_labware(sample_plate, tc_mod, use_gripper=USE_GRIPPER) + protocol.comment("****Moving Plate to Pre-Warmed TC Module Block****") + protocol.move_labware(sample_plate, tc_mod, use_gripper=USE_GRIPPER) if disposable_lid: lid_on_plate, unused_lids, used_lids = helpers.use_disposable_lid_with_tc( - ctx, unused_lids, used_lids, sample_plate, tc_mod + protocol, unused_lids, used_lids, sample_plate, tc_mod ) else: tc_mod.close_lid() @@ -271,13 +275,13 @@ def run_tag_profile( if disposable_lid: if len(used_lids) <= 1: - ctx.move_labware(lid_on_plate, "D4", use_gripper=True) + protocol.move_labware(lid_on_plate, "D4", use_gripper=True) else: - ctx.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) + protocol.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) # #Move Plate to H-S - ctx.comment("****Moving Plate off of TC****") + protocol.comment("****Moving Plate off of TC****") - ctx.move_labware(sample_plate, "D1", use_gripper=USE_GRIPPER) + protocol.move_labware(sample_plate, "D1", use_gripper=USE_GRIPPER) return unused_lids, used_lids def run_er_profile( @@ -285,19 +289,19 @@ def run_er_profile( ) -> Tuple[List[Labware], List[Labware]]: """End Repair Profile.""" # Presetting Thermocycler Temps - ctx.comment( + protocol.comment( "****Starting End Repair Profile (65C for 30 minutes with 100C lid)****" ) tc_mod.set_lid_temperature(100) tc_mod.set_block_temperature(65) # Move Plate to TC - ctx.comment("****Moving Plate to Pre-Warmed TC Module Block****") - ctx.move_labware(sample_plate, tc_mod, use_gripper=USE_GRIPPER) + protocol.comment("****Moving Plate to Pre-Warmed TC Module Block****") + protocol.move_labware(sample_plate, tc_mod, use_gripper=USE_GRIPPER) if disposable_lid: lid_on_plate, unused_lids, used_lids = helpers.use_disposable_lid_with_tc( - ctx, unused_lids, used_lids, sample_plate, tc_mod + protocol, unused_lids, used_lids, sample_plate, tc_mod ) else: tc_mod.close_lid() @@ -311,13 +315,13 @@ def run_er_profile( if disposable_lid: # move lid if len(used_lids) <= 1: - ctx.move_labware(lid_on_plate, "C4", use_gripper=True) + protocol.move_labware(lid_on_plate, "C4", use_gripper=True) else: - ctx.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) + protocol.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) # #Move Plate to H-S - ctx.comment("****Moving Plate off of TC****") + protocol.comment("****Moving Plate off of TC****") - ctx.move_labware(sample_plate, "D1", use_gripper=USE_GRIPPER) + protocol.move_labware(sample_plate, "D1", use_gripper=USE_GRIPPER) return unused_lids, used_lids def run_ligation_profile( @@ -325,20 +329,20 @@ def run_ligation_profile( ) -> Tuple[List[Labware], List[Labware]]: """Run Ligation Profile.""" # Presetting Thermocycler Temps - ctx.comment( + protocol.comment( "****Starting Ligation Profile (20C for 15 minutes with 100C lid)****" ) tc_mod.set_lid_temperature(100) tc_mod.set_block_temperature(20) # Move Plate to TC - ctx.comment("****Moving Plate to Pre-Warmed TC Module Block****") + protocol.comment("****Moving Plate to Pre-Warmed TC Module Block****") - ctx.move_labware(sample_plate, tc_mod, use_gripper=USE_GRIPPER) + protocol.move_labware(sample_plate, tc_mod, use_gripper=USE_GRIPPER) if disposable_lid: lid_on_plate, unused_lids, used_lids = helpers.use_disposable_lid_with_tc( - ctx, unused_lids, used_lids, sample_plate, tc_mod + protocol, unused_lids, used_lids, sample_plate, tc_mod ) else: tc_mod.close_lid() @@ -353,14 +357,14 @@ def run_ligation_profile( tc_mod.open_lid() if disposable_lid: if len(used_lids) <= 1: - ctx.move_labware(lid_on_plate, "C4", use_gripper=True) + protocol.move_labware(lid_on_plate, "C4", use_gripper=True) else: - ctx.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) + protocol.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) # #Move Plate to H-S - ctx.comment("****Moving Plate off of TC****") + protocol.comment("****Moving Plate off of TC****") - ctx.move_labware(sample_plate, "D1", use_gripper=USE_GRIPPER) + protocol.move_labware(sample_plate, "D1", use_gripper=USE_GRIPPER) return unused_lids, used_lids def run_amplification_profile( @@ -368,27 +372,27 @@ def run_amplification_profile( ) -> Tuple[List[Labware], List[Labware]]: """Run Amplification Profile.""" # Presetting Thermocycler Temps - ctx.comment( + protocol.comment( "Amplification Profile (37C for 5 min, 50C for 5 min with 100C lid)" ) tc_mod.set_lid_temperature(100) tc_mod.set_block_temperature(98) # Move Plate to TC - ctx.comment("****Moving Sample Plate onto TC****") - ctx.move_labware(sample_plate_2, tc_mod, use_gripper=USE_GRIPPER) + protocol.comment("****Moving Sample Plate onto TC****") + protocol.move_labware(sample_plate_2, tc_mod, use_gripper=USE_GRIPPER) if not dry_run: tc_mod.set_lid_temperature(105) if disposable_lid: lid_on_plate, unused_lids, used_lids = helpers.use_disposable_lid_with_tc( - ctx, unused_lids, used_lids, sample_plate_2, tc_mod + protocol, unused_lids, used_lids, sample_plate_2, tc_mod ) else: tc_mod.close_lid() if not dry_run: helpers.perform_pcr( - ctx, + protocol, tc_mod, initial_denature_time_sec=45, denaturation_time_sec=15, @@ -401,16 +405,16 @@ def run_amplification_profile( tc_mod.open_lid() if disposable_lid: if len(used_lids) <= 1: - ctx.move_labware(lid_on_plate, "C4", use_gripper=True) + protocol.move_labware(lid_on_plate, "C4", use_gripper=True) else: - ctx.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) + protocol.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) # Move Sample Plate to H-S - ctx.comment("****Moving Sample Plate back to H-S****") - ctx.move_labware(sample_plate_2, "D1", use_gripper=USE_GRIPPER) + protocol.comment("****Moving Sample Plate back to H-S****") + protocol.move_labware(sample_plate_2, "D1", use_gripper=USE_GRIPPER) # get FLP plate out of the way - ctx.comment("****Moving FLP Plate back to TC****") - ctx.move_labware(FLP_plate, tc_mod, use_gripper=USE_GRIPPER) + protocol.comment("****Moving FLP Plate back to TC****") + protocol.move_labware(FLP_plate, tc_mod, use_gripper=USE_GRIPPER) return unused_lids, used_lids def mix_beads( @@ -463,39 +467,39 @@ def mix_beads( def remove_supernatant(well: Well, vol: float, waste_: Well, column: int) -> None: """Remove supernatant.""" - ctx.comment("-------Removing " + str(vol) + "ul of Supernatant-------") + protocol.comment("-------Removing " + str(vol) + "ul of Supernatant-------") p200.flow_rate.aspirate = 15 num_trans = math.ceil(vol / 190) vol_per_trans = vol / num_trans tip_track(p200, tip_count) for x in range(num_trans): p200.aspirate(vol_per_trans / 2, well.bottom(0.2)) - ctx.delay(seconds=1) + protocol.delay(seconds=1) p200.aspirate(vol_per_trans / 2, well.bottom(0.2)) p200.air_gap(10) p200.dispense(p200.current_volume, waste_) p200.air_gap(10) if REUSE_REMOVE_TIPS: p200.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") else: if trash_tips: p200.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p200.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") p200.flow_rate.aspirate = 150 def Fragmentation( unused_lids: List[Labware], used_lids: List[Labware] ) -> Tuple[List[Labware], List[Labware]]: """Fragmentation Function.""" - ctx.comment("-------Starting Fragmentation-------") + protocol.comment("-------Starting Fragmentation-------") for i in range(num_cols): - ctx.comment("Mixing and Transfering beads to column " + str(i + 1)) + protocol.comment("Mixing and Transfering beads to column " + str(i + 1)) p50.flow_rate.dispense = 15 tip_track(p50, tip_count) @@ -512,10 +516,10 @@ def Fragmentation( p50.flow_rate.dispense = 150 if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") unused_lids, used_lids = run_tag_profile( unused_lids, used_lids @@ -526,11 +530,11 @@ def end_repair( unused_lids: List[Labware], used_lids: List[Labware] ) -> Tuple[List[Labware], List[Labware]]: """End Repair Function.""" - ctx.comment("-------Starting end_repair-------") + protocol.comment("-------Starting end_repair-------") for i in range(num_cols): - ctx.comment( + protocol.comment( "**** Mixing and Transfering beads to column " + str(i + 1) + " ****" ) @@ -549,10 +553,10 @@ def end_repair( p50.flow_rate.dispense = 150 if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") unused_lids, used_lids = run_er_profile( unused_lids, used_lids @@ -565,8 +569,8 @@ def index_ligation( unused_lids: List[Labware], used_lids: List[Labware] ) -> Tuple[List[Labware], List[Labware]]: """Index Ligation.""" - ctx.comment("-------Ligating Indexes-------") - ctx.comment("-------Adding and Mixing ELM-------") + protocol.comment("-------Ligating Indexes-------") + protocol.comment("-------Adding and Mixing ELM-------") for i in samples: tip_track(p50, tip_count) p50.aspirate(ligation_vol, ligation_res) @@ -581,13 +585,13 @@ def index_ligation( p50.flow_rate.dispense = 150 if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") # Add and mix adapters - ctx.comment("-------Adding and Mixing Adapters-------") + protocol.comment("-------Adding and Mixing Adapters-------") for i_well, x_well in zip(samples, adapters): tip_track(p50, tip_count) p50.aspirate(adapter_vol, x_well) @@ -600,10 +604,10 @@ def index_ligation( p50.dispense(40, i_well.bottom(8)) if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") p50.flow_rate.aspirate = 150 p50.flow_rate.dispense = 150 @@ -613,13 +617,13 @@ def index_ligation( def lib_cleanup() -> None: """Litigation Clean up.""" - ctx.comment("-------Starting Cleanup-------") - ctx.comment("-------Adding and Mixing Cleanup Beads-------") + protocol.comment("-------Starting Cleanup-------") + protocol.comment("-------Adding and Mixing Cleanup Beads-------") # Move FLP plate off magnetic module if it's there if FLP_plate.parent == magblock: - ctx.comment("****Moving FLP Plate off Magnetic Module****") - ctx.move_labware(FLP_plate, tc_mod, use_gripper=USE_GRIPPER) + protocol.comment("****Moving FLP Plate off Magnetic Module****") + protocol.move_labware(FLP_plate, tc_mod, use_gripper=USE_GRIPPER) for x, i in enumerate(samples): mix_beads(p200, bead_res, bead_vol_1, 7 if x == 0 else 2, x) @@ -636,22 +640,22 @@ def lib_cleanup() -> None: p200.flow_rate.dispense = 150 if trash_tips: p200.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p200.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") - ctx.delay( + protocol.delay( minutes=bead_inc, msg="Please wait " + str(bead_inc) + " minutes while samples incubate at RT.", ) - ctx.comment("****Moving Labware to Magnet for Pelleting****") - ctx.move_labware(sample_plate, magblock, use_gripper=USE_GRIPPER) + protocol.comment("****Moving Labware to Magnet for Pelleting****") + protocol.move_labware(sample_plate, magblock, use_gripper=USE_GRIPPER) - ctx.delay(minutes=4.5, msg="Time for Pelleting") + protocol.delay(minutes=4.5, msg="Time for Pelleting") for col, i in enumerate(samples): remove_supernatant(i, 130, waste1_res, col) @@ -661,7 +665,7 @@ def lib_cleanup() -> None: p200.flow_rate.aspirate = 75 p200.flow_rate.dispense = 75 for y in range(2 if not dry_run else 1): - ctx.comment(f"-------Wash # {y+1} with Ethanol-------") + protocol.comment(f"-------Wash # {y+1} with Ethanol-------") if y == 0: # First wash this_res = etoh1_res this_waste_res = waste1_res @@ -672,42 +676,42 @@ def lib_cleanup() -> None: p200.aspirate(150, this_res) p200.air_gap(10) p200.dispense(p200.current_volume, i.top()) - ctx.delay(seconds=1) + protocol.delay(seconds=1) p200.air_gap(10) if not REUSE_ETOH_TIPS: p200.drop_tip() if trash_tips else p200.return_tip() - ctx.delay(seconds=10) + protocol.delay(seconds=10) # Remove the ethanol wash for x, i in enumerate(samp_list): tip_track(p200, tip_count) p200.aspirate(155, i) p200.air_gap(10) p200.dispense(p200.current_volume, this_waste_res) - ctx.delay(seconds=1) + protocol.delay(seconds=1) p200.air_gap(10) if trash_tips: p200.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p200.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") p200.flow_rate.aspirate = 150 p200.flow_rate.dispense = 150 # Wash complete, move on to drying steps. - ctx.delay(minutes=2, msg="Allow 3 minutes for residual ethanol to dry") + protocol.delay(minutes=2, msg="Allow 3 minutes for residual ethanol to dry") # Return Plate to H-S from Magnet - ctx.comment("****Moving sample plate off of Magnet****") - ctx.move_labware(sample_plate, "D1", use_gripper=USE_GRIPPER) + protocol.comment("****Moving sample plate off of Magnet****") + protocol.move_labware(sample_plate, "D1", use_gripper=USE_GRIPPER) # Adding RSB and Mixing for col, i in enumerate(samp_list): - ctx.comment(f"****Adding RSB to Columns: {col+1}****") + protocol.comment(f"****Adding RSB to Columns: {col+1}****") tip_track(p50, tip_count) p50.aspirate(rsb_vol_1, rsb_res) p50.air_gap(5) @@ -723,23 +727,23 @@ def lib_cleanup() -> None: p50.air_gap(5) if REUSE_RSB_TIPS: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") else: if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") - ctx.delay( + protocol.delay( minutes=3, msg="Allow 3 minutes for incubation and liquid aggregation." ) - ctx.comment("****Move Samples to Magnet for Pelleting****") - ctx.move_labware(sample_plate, magblock, use_gripper=USE_GRIPPER) + protocol.comment("****Move Samples to Magnet for Pelleting****") + protocol.move_labware(sample_plate, magblock, use_gripper=USE_GRIPPER) - ctx.delay(minutes=2, msg="Please allow 2 minutes for beads to pellet.") + protocol.delay(minutes=2, msg="Please allow 2 minutes for beads to pellet.") p200.flow_rate.aspirate = 10 for i_int, (s, e) in enumerate(zip(samp_list, samples_2)): @@ -750,31 +754,31 @@ def lib_cleanup() -> None: p50.air_gap(5) if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") # move new sample plate to D1 or heatershaker - ctx.comment("****Moving sample plate off of Magnet****") - ctx.move_labware(sample_plate_2, "D1", use_gripper=USE_GRIPPER) + protocol.comment("****Moving sample plate off of Magnet****") + protocol.move_labware(sample_plate_2, "D1", use_gripper=USE_GRIPPER) # Keep Sample PLate 1 to B2 - ctx.comment("****Moving Sample_plate_1 Plate off magnet to B2****") - ctx.move_labware(sample_plate, "B2", use_gripper=USE_GRIPPER) + protocol.comment("****Moving Sample_plate_1 Plate off magnet to B2****") + protocol.move_labware(sample_plate, "B2", use_gripper=USE_GRIPPER) - ctx.comment("****Moving FLP Plate off TC****") - ctx.move_labware(FLP_plate, magblock, use_gripper=USE_GRIPPER) + protocol.comment("****Moving FLP Plate off TC****") + protocol.move_labware(FLP_plate, magblock, use_gripper=USE_GRIPPER) def lib_amplification( unused_lids: List[Labware], used_lids: List[Labware] ) -> Tuple[List[Labware], List[Labware]]: """Library Amplification.""" - ctx.comment("-------Starting lib_amplification-------") + protocol.comment("-------Starting lib_amplification-------") for i in range(num_cols): - ctx.comment( + protocol.comment( "**** Mixing and Transfering beads to column " + str(i + 1) + " ****" ) mix_beads( @@ -795,10 +799,10 @@ def lib_amplification( p50.flow_rate.dispense = 150 if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") unused_lids, used_lids = run_amplification_profile( unused_lids, used_lids @@ -807,8 +811,8 @@ def lib_amplification( def lib_cleanup_2() -> None: """Final Library Clean up.""" - ctx.comment("-------Starting Cleanup-------") - ctx.comment("-------Adding and Mixing Cleanup Beads-------") + protocol.comment("-------Starting Cleanup-------") + protocol.comment("-------Adding and Mixing Cleanup Beads-------") for x, i in enumerate(samples_2): mix_beads(p200, bead_res, bead_vol_2, 7 if x == 0 else 2, x) tip_track(p200, tip_count) @@ -826,22 +830,22 @@ def lib_cleanup_2() -> None: p200.flow_rate.dispense = 150 if trash_tips: p200.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p200.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") - ctx.delay( + protocol.delay( minutes=bead_inc, msg="Please wait " + str(bead_inc) + " minutes while samples incubate at RT.", ) - ctx.comment("****Moving Labware to Magnet for Pelleting****") - ctx.move_labware(sample_plate_2, magblock, use_gripper=USE_GRIPPER) + protocol.comment("****Moving Labware to Magnet for Pelleting****") + protocol.move_labware(sample_plate_2, magblock, use_gripper=USE_GRIPPER) - ctx.delay(minutes=4.5, msg="Time for Pelleting") + protocol.delay(minutes=4.5, msg="Time for Pelleting") for col, i in enumerate(samples_2): remove_supernatant(i, 130, waste1_res, col) @@ -851,7 +855,7 @@ def lib_cleanup_2() -> None: p200.flow_rate.aspirate = 75 p200.flow_rate.dispense = 75 for y in range(2 if not dry_run else 1): - ctx.comment(f"-------Wash # {y+1} with Ethanol-------") + protocol.comment(f"-------Wash # {y+1} with Ethanol-------") if y == 0: # First wash this_res = etoh1_res this_waste_res = waste1_res @@ -862,41 +866,41 @@ def lib_cleanup_2() -> None: p200.aspirate(150, this_res) p200.air_gap(10) p200.dispense(p200.current_volume, i.top()) - ctx.delay(seconds=1) + protocol.delay(seconds=1) p200.air_gap(10) if not REUSE_ETOH_TIPS: p200.drop_tip() if trash_tips else p200.return_tip() - ctx.delay(seconds=10) + protocol.delay(seconds=10) # Remove the ethanol wash for x, i in enumerate(samp_list_2): tip_track(p200, tip_count) p200.aspirate(155, i) p200.air_gap(10) p200.dispense(p200.current_volume, this_waste_res) - ctx.delay(seconds=1) + protocol.delay(seconds=1) p200.air_gap(10) if trash_tips: p200.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p200.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") p200.flow_rate.aspirate = 150 p200.flow_rate.dispense = 150 # Washes Complete, Move on to Drying Steps - ctx.delay(minutes=3, msg="Allow 3 minutes for residual ethanol to dry") + protocol.delay(minutes=3, msg="Allow 3 minutes for residual ethanol to dry") - ctx.comment("****Moving sample plate off of Magnet****") - ctx.move_labware(sample_plate_2, "D1", use_gripper=USE_GRIPPER) + protocol.comment("****Moving sample plate off of Magnet****") + protocol.move_labware(sample_plate_2, "D1", use_gripper=USE_GRIPPER) # Adding RSB and Mixing for col, i in enumerate(samp_list_2): - ctx.comment(f"****Adding RSB to Columns: {col+1}****") + protocol.comment(f"****Adding RSB to Columns: {col+1}****") tip_track(p50, tip_count) p50.aspirate(rsb_vol_2, rsb_res) p50.air_gap(5) @@ -912,23 +916,23 @@ def lib_cleanup_2() -> None: p50.air_gap(5) if REUSE_RSB_TIPS: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") else: if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") - ctx.delay( + protocol.delay( minutes=3, msg="Allow 3 minutes for incubation and liquid aggregation." ) - ctx.comment("****Move Samples to Magnet for Pelleting****") - ctx.move_labware(sample_plate_2, magblock, use_gripper=USE_GRIPPER) + protocol.comment("****Move Samples to Magnet for Pelleting****") + protocol.move_labware(sample_plate_2, magblock, use_gripper=USE_GRIPPER) - ctx.delay(minutes=2, msg="Please allow 2 minutes for beads to pellet.") + protocol.delay(minutes=2, msg="Please allow 2 minutes for beads to pellet.") p200.flow_rate.aspirate = 10 for i_int, (s, e) in enumerate(zip(samp_list_2, samples_flp)): @@ -939,10 +943,10 @@ def lib_cleanup_2() -> None: p50.air_gap(5) if trash_tips: p50.drop_tip() - ctx.comment("****Dropping Tip in Waste shoot****") + protocol.comment("****Dropping Tip in Waste shoot****") else: p50.return_tip() - ctx.comment("****Dropping Tip Back in Tip Box****") + protocol.comment("****Dropping Tip Back in Tip Box****") # Set Block Temp for Final Plate tc_mod.set_block_temperature(4) @@ -963,4 +967,6 @@ def lib_cleanup_2() -> None: waste2_res = waste2[0] end_probed_wells = [waste1_res, waste2_res] - helpers.find_liquid_height_of_all_wells(ctx, p50, end_probed_wells) + helpers.find_liquid_height_of_all_wells(protocol, p50, end_probed_wells) + if deactivate_mods: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/active_protocols/1_Simple Normalize Long Right.py b/abr-testing/abr_testing/protocols/active_protocols/1_Simple Normalize Long Right.py index 525a82c3095..6162e6ab34e 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/1_Simple Normalize Long Right.py +++ b/abr-testing/abr_testing/protocols/active_protocols/1_Simple Normalize Long Right.py @@ -4,10 +4,12 @@ ParameterContext, Labware, SINGLE, + ALL, InstrumentContext, Well, ) from abr_testing.protocols import helpers +from typing import List, Dict metadata = { "protocolName": "Simple Normalize Long with LPD and Single Tip", @@ -15,16 +17,14 @@ "source": "Protocol Library", } -requirements = { - "robotType": "Flex", - "apiLevel": "2.21", -} +requirements = {"robotType": "Flex", "apiLevel": "2.21"} def add_parameters(parameters: ParameterContext) -> None: """Parameters.""" helpers.create_single_pipette_mount_parameter(parameters) - helpers.create_tip_size_parameter(parameters) + helpers.create_csv_parameter(parameters) + helpers.create_dot_bottom_parameter(parameters) def get_next_tip_by_row(tip_rack: Labware, pipette: InstrumentContext) -> Well | None: @@ -79,149 +79,68 @@ def get_next_tip_by_row(tip_rack: Labware, pipette: InstrumentContext) -> Well | def run(protocol: ProtocolContext) -> None: """Protocol.""" - tip_type = protocol.params.tip_size # type: ignore[attr-defined] + dot_bottom = protocol.params.dot_bottom # type: ignore[attr-defined] mount_pos = protocol.params.pipette_mount # type: ignore[attr-defined] + all_data = protocol.params.parameters_csv.parse_as_csv() # type: ignore[attr-defined] + data = all_data[1:] + helpers.comment_protocol_version(protocol, "01") # DECK SETUP AND LABWARE protocol.comment("THIS IS A NO MODULE RUN") - tiprack_x_1 = protocol.load_labware(tip_type, "D1") - tiprack_x_2 = protocol.load_labware(tip_type, "D2") - tiprack_x_3 = protocol.load_labware(tip_type, "B1") + tiprack_x_1 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "D1") + tiprack_x_2 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "D2") + tiprack_x_3 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "A1") sample_plate_1 = protocol.load_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", "D3" ) reservoir = protocol.load_labware("nest_12_reservoir_15ml", "B3") + waste_reservoir = protocol.load_labware( + "nest_1_reservoir_195ml", "C1", "Liquid Waste" + ) sample_plate_2 = protocol.load_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", "C2" ) sample_plate_3 = protocol.load_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", "B2" ) + sample_plate_4 = protocol.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", "A2" + ) protocol.load_trash_bin("A3") - # reagent + # reagentg146 Dye_1 = reservoir["A1"] Dye_2 = reservoir["A2"] Dye_3 = reservoir["A3"] Diluent_1 = reservoir["A4"] Diluent_2 = reservoir["A5"] + Diluent_3 = reservoir["A6"] # pipette p1000 = protocol.load_instrument( "flex_8channel_1000", mount_pos, liquid_presence_detection=True ) + p1000_single = protocol.load_instrument( + "flex_1channel_1000", + "right", + liquid_presence_detection=True, + tip_racks=[tiprack_x_2, tiprack_x_3], + ) # LOAD LIQUIDS liquid_volumes = [675.0, 675.0, 675.0, 675.0, 675.0] - wells = [Dye_1, Dye_2, Dye_3, Diluent_1, Diluent_2] + wells = [Dye_1, Dye_2, Dye_3, Diluent_1, Diluent_2, Diluent_3] helpers.load_wells_with_water(protocol, wells, liquid_volumes) - + liquid_vols_and_wells: Dict[str, List[Dict[str, Well | List[Well] | float]]] = { + "Dye": [{"well": [Dye_1, Dye_2, Dye_3], "volume": 675.0}], + "Diluent": [{"well": [Diluent_1, Diluent_2, Diluent_3], "volume": 675.0}], + } current_rack = tiprack_x_1 # CONFIGURE SINGLE LAYOUT - p1000.configure_nozzle_layout( - style=SINGLE, start="H1", tip_racks=[tiprack_x_1, tiprack_x_2, tiprack_x_3] + p1000.configure_nozzle_layout(style=SINGLE, start="H1", tip_racks=[tiprack_x_1]) + helpers.find_liquid_height_of_loaded_liquids( + protocol, liquid_vols_and_wells, p1000_single ) - helpers.find_liquid_height_of_all_wells(protocol, p1000, wells) - sample_quant_csv = """ - sample_plate_1, Sample_well,DYE,DILUENT - sample_plate_1,A1,0,100 - sample_plate_1,B1,5,95 - sample_plate_1,C1,10,90 - sample_plate_1,D1,20,80 - sample_plate_1,E1,40,60 - sample_plate_1,F1,15,40 - sample_plate_1,G1,40,20 - sample_plate_1,H1,40,0 - sample_plate_1,A2,35,65 - sample_plate_1,B2,38,42 - sample_plate_1,C2,42,58 - sample_plate_1,D2,32,8 - sample_plate_1,E2,38,12 - sample_plate_1,F2,26,74 - sample_plate_1,G2,31,69 - sample_plate_1,H2,46,4 - sample_plate_1,A3,47,13 - sample_plate_1,B3,42,18 - sample_plate_1,C3,46,64 - sample_plate_1,D3,48,22 - sample_plate_1,E3,26,74 - sample_plate_1,F3,34,66 - sample_plate_1,G3,43,37 - sample_plate_1,H3,20,80 - sample_plate_1,A4,44,16 - sample_plate_1,B4,49,41 - sample_plate_1,C4,48,42 - sample_plate_1,D4,44,16 - sample_plate_1,E4,47,53 - sample_plate_1,F4,47,33 - sample_plate_1,G4,42,48 - sample_plate_1,H4,39,21 - sample_plate_1,A5,30,20 - sample_plate_1,B5,36,14 - sample_plate_1,C5,31,59 - sample_plate_1,D5,38,52 - sample_plate_1,E5,36,4 - sample_plate_1,F5,32,28 - sample_plate_1,G5,35,55 - sample_plate_1,H5,39,1 - sample_plate_1,A6,31,59 - sample_plate_1,B6,20,80 - sample_plate_1,C6,38,2 - sample_plate_1,D6,34,46 - sample_plate_1,E6,30,70 - sample_plate_1,F6,32,58 - sample_plate_1,G6,21,79 - sample_plate_1,H6,38,52 - sample_plate_1,A7,33,27 - sample_plate_1,B7,34,16 - sample_plate_1,C7,40,60 - sample_plate_1,D7,34,26 - sample_plate_1,E7,30,20 - sample_plate_1,F7,44,56 - sample_plate_1,G7,26,74 - sample_plate_1,H7,45,55 - sample_plate_1,A8,39,1 - sample_plate_1,B8,38,2 - sample_plate_1,C8,34,66 - sample_plate_1,D8,39,11 - sample_plate_1,E8,46,54 - sample_plate_1,F8,37,63 - sample_plate_1,G8,38,42 - sample_plate_1,H8,34,66 - sample_plate_1,A9,44,56 - sample_plate_1,B9,39,11 - sample_plate_1,C9,30,70 - sample_plate_1,D9,37,33 - sample_plate_1,E9,46,54 - sample_plate_1,F9,39,21 - sample_plate_1,G9,29,41 - sample_plate_1,H9,23,77 - sample_plate_1,A10,26,74 - sample_plate_1,B10,39,1 - sample_plate_1,C10,31,49 - sample_plate_1,D10,38,62 - sample_plate_1,E10,29,1 - sample_plate_1,F10,21,79 - sample_plate_1,G10,29,41 - sample_plate_1,H10,28,42 - sample_plate_1,A11,15,55 - sample_plate_1,B11,28,72 - sample_plate_1,C11,11,49 - sample_plate_1,D11,34,66 - sample_plate_1,E11,27,73 - sample_plate_1,F11,30,40 - sample_plate_1,G11,33,67 - sample_plate_1,H11,31,39 - sample_plate_1,A12,39,31 - sample_plate_1,B12,47,53 - sample_plate_1,C12,46,54 - sample_plate_1,D12,13,7 - sample_plate_1,E12,34,46 - sample_plate_1,F12,45,35 - sample_plate_1,G12,28,42 - sample_plate_1,H12,37,63 - """ - data = [r.split(",") for r in sample_quant_csv.strip().splitlines() if r][1:] for X in range(1): protocol.comment("==============================================") protocol.comment("Adding Dye Sample Plate 1") @@ -232,8 +151,8 @@ def run(protocol: ProtocolContext) -> None: well = get_next_tip_by_row(current_rack, p1000) p1000.pick_up_tip(well) while current < len(data): - CurrentWell = str(data[current][1]) - DyeVol = float(data[current][2]) + CurrentWell = str(data[current][0]) + DyeVol = float(data[current][1]) if DyeVol != 0 and DyeVol < 100: p1000.liquid_presence_detection = False p1000.transfer( @@ -245,7 +164,7 @@ def run(protocol: ProtocolContext) -> None: if DyeVol > 20: wells.append(sample_plate_1.wells_by_name()[CurrentWell]) current += 1 - p1000.blow_out() + p1000.blow_out(location=waste_reservoir["A1"]) p1000.touch_tip() p1000.drop_tip() p1000.liquid_presence_detection = True @@ -256,34 +175,34 @@ def run(protocol: ProtocolContext) -> None: current = 0 while current < len(data): - CurrentWell = str(data[current][1]) + CurrentWell = str(data[current][0]) DilutionVol = float(data[current][2]) if DilutionVol != 0 and DilutionVol < 100: well = get_next_tip_by_row(current_rack, p1000) p1000.pick_up_tip(well) - p1000.aspirate(DilutionVol, Diluent_1.bottom(z=2)) + p1000.aspirate(DilutionVol, Diluent_1.bottom(z=dot_bottom)) p1000.dispense( DilutionVol, sample_plate_1.wells_by_name()[CurrentWell].top(z=0.2) ) if DilutionVol > 20: wells.append(sample_plate_1.wells_by_name()[CurrentWell]) - p1000.blow_out() + p1000.blow_out(location=waste_reservoir["A1"]) p1000.touch_tip() p1000.drop_tip() current += 1 + protocol.comment("Changing pipette configuration to 8ch.") + protocol.comment("==============================================") protocol.comment("Adding Dye Sample Plate 2") protocol.comment("==============================================") - current = 0 - well = get_next_tip_by_row(tiprack_x_2, p1000) - p1000.pick_up_tip(well) + p1000_single.pick_up_tip() while current < len(data): - CurrentWell = str(data[current][1]) - DyeVol = float(data[current][2]) + CurrentWell = str(data[current][0]) + DyeVol = float(data[current][1]) if DyeVol != 0 and DyeVol < 100: - p1000.transfer( + p1000_single.transfer( DyeVol, Dye_2.bottom(z=2), sample_plate_2.wells_by_name()[CurrentWell].top(z=1), @@ -292,9 +211,9 @@ def run(protocol: ProtocolContext) -> None: if DyeVol > 20: wells.append(sample_plate_2.wells_by_name()[CurrentWell]) current += 1 - p1000.blow_out() - p1000.touch_tip() - p1000.drop_tip() + p1000_single.blow_out(location=waste_reservoir["A1"]) + p1000_single.touch_tip() + p1000_single.return_tip() protocol.comment("==============================================") protocol.comment("Adding Diluent Sample Plate 2") @@ -302,20 +221,19 @@ def run(protocol: ProtocolContext) -> None: current = 0 while current < len(data): - CurrentWell = str(data[current][1]) + CurrentWell = str(data[current][0]) DilutionVol = float(data[current][2]) if DilutionVol != 0 and DilutionVol < 100: - well = get_next_tip_by_row(tiprack_x_2, p1000) - p1000.pick_up_tip(well) - p1000.aspirate(DilutionVol, Diluent_2.bottom(z=2)) - p1000.dispense( + p1000_single.pick_up_tip() + p1000_single.aspirate(DilutionVol, Diluent_2.bottom(z=dot_bottom)) + p1000_single.dispense( DilutionVol, sample_plate_2.wells_by_name()[CurrentWell].top(z=0.2) ) if DilutionVol > 20: wells.append(sample_plate_2.wells_by_name()[CurrentWell]) - p1000.blow_out() - p1000.touch_tip() - p1000.drop_tip() + p1000_single.blow_out(location=waste_reservoir["A1"]) + p1000_single.touch_tip() + p1000_single.return_tip() current += 1 protocol.comment("==============================================") @@ -323,14 +241,13 @@ def run(protocol: ProtocolContext) -> None: protocol.comment("==============================================") current = 0 - well = get_next_tip_by_row(tiprack_x_3, p1000) - p1000.pick_up_tip(well) + p1000_single.pick_up_tip() while current < len(data): - CurrentWell = str(data[current][1]) - DyeVol = float(data[current][2]) + CurrentWell = str(data[current][0]) + DyeVol = float(data[current][1]) if DyeVol != 0 and DyeVol < 100: - p1000.liquid_presence_detection = False - p1000.transfer( + p1000_single.liquid_presence_detection = False + p1000_single.transfer( DyeVol, Dye_3.bottom(z=2), sample_plate_3.wells_by_name()[CurrentWell].top(z=1), @@ -341,14 +258,85 @@ def run(protocol: ProtocolContext) -> None: if DyeVol > 20: wells.append(sample_plate_3.wells_by_name()[CurrentWell]) current += 1 - p1000.liquid_presence_detection = True - p1000.blow_out() - p1000.touch_tip() - p1000.drop_tip() + p1000_single.liquid_presence_detection = True + p1000_single.blow_out(location=waste_reservoir["A1"]) + p1000_single.touch_tip() + p1000_single.return_tip() protocol.comment("==============================================") protocol.comment("Adding Diluent Sample Plate 3") protocol.comment("==============================================") + current = 0 + while current < len(data): + CurrentWell = str(data[current][0]) + DilutionVol = float(data[current][2]) + if DilutionVol != 0 and DilutionVol < 100: + p1000_single.pick_up_tip() + p1000_single.aspirate(DilutionVol, Diluent_3.bottom(z=dot_bottom)) + p1000_single.dispense( + DilutionVol, sample_plate_3.wells_by_name()[CurrentWell].top(z=0.2) + ) + if DilutionVol > 20: + wells.append(sample_plate_3.wells_by_name()[CurrentWell]) + p1000_single.blow_out(location=waste_reservoir["A1"]) + p1000_single.touch_tip() + p1000_single.return_tip() + current += 1 + protocol.comment("==============================================") + protocol.comment("Adding Dye Sample Plate 4") + protocol.comment("==============================================") + p1000_single.reset_tipracks() current = 0 - # Probe heights - helpers.find_liquid_height_of_all_wells(protocol, p1000, wells) + p1000_single.pick_up_tip() + while current < len(data): + CurrentWell = str(data[current][0]) + DyeVol = float(data[current][1]) + if DyeVol != 0 and DyeVol < 100: + p1000_single.liquid_presence_detection = False + p1000_single.transfer( + DyeVol, + Dye_3.bottom(z=2), + sample_plate_4.wells_by_name()[CurrentWell].top(z=1), + blow_out=True, + blowout_location="destination well", + new_tip="never", + ) + if DyeVol > 20: + wells.append(sample_plate_4.wells_by_name()[CurrentWell]) + current += 1 + p1000_single.liquid_presence_detection = True + p1000_single.blow_out(location=waste_reservoir["A1"]) + p1000_single.touch_tip() + p1000_single.return_tip() + protocol.comment("==============================================") + protocol.comment("Adding Diluent Sample Plate 4") + protocol.comment("==============================================") + current = 0 + while current < len(data): + CurrentWell = str(data[current][0]) + DilutionVol = float(data[current][2]) + if DilutionVol != 0 and DilutionVol < 100: + p1000_single.pick_up_tip() + p1000_single.aspirate(DilutionVol, Diluent_3.bottom(z=dot_bottom)) + p1000_single.dispense( + DilutionVol, sample_plate_4.wells_by_name()[CurrentWell].top(z=0.2) + ) + if DilutionVol > 20: + wells.append(sample_plate_4.wells_by_name()[CurrentWell]) + p1000_single.blow_out(location=waste_reservoir["A1"]) + p1000_single.touch_tip() + p1000_single.return_tip() + current += 1 + + current = 0 + # Probe heights + p1000.configure_nozzle_layout(style=ALL, tip_racks=[tiprack_x_3]) + helpers.clean_up_plates( + p1000, + [sample_plate_1, sample_plate_2, sample_plate_3, sample_plate_4], + waste_reservoir["A1"], + 200, + ) + helpers.find_liquid_height_of_all_wells( + protocol, p1000_single, [waste_reservoir["A1"]] + ) diff --git a/abr-testing/abr_testing/protocols/active_protocols/2_BMS_PCR_Protocol.py b/abr-testing/abr_testing/protocols/active_protocols/2_BMS_PCR_Protocol.py index 24e7358f6e1..3b11b51b7fe 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/2_BMS_PCR_Protocol.py +++ b/abr-testing/abr_testing/protocols/active_protocols/2_BMS_PCR_Protocol.py @@ -5,7 +5,7 @@ ThermocyclerContext, TemperatureModuleContext, ) -from opentrons.protocol_api import SINGLE, Well +from opentrons.protocol_api import SINGLE, Well, ALL from abr_testing.protocols import helpers from typing import List, Dict @@ -14,7 +14,7 @@ "protocolName": "PCR Protocol with TC Auto Sealing Lid", "author": "Rami Farawi None: @@ -23,81 +23,79 @@ def add_parameters(parameters: ParameterContext) -> None: helpers.create_disposable_lid_parameter(parameters) helpers.create_csv_parameter(parameters) helpers.create_tc_lid_deck_riser_parameter(parameters) + helpers.create_deactivate_modules_parameter(parameters) -def run(ctx: ProtocolContext) -> None: +def run(protocol: ProtocolContext) -> None: """Protocol.""" - pipette_mount = ctx.params.pipette_mount # type: ignore[attr-defined] - disposable_lid = ctx.params.disposable_lid # type: ignore[attr-defined] - parsed_csv = ctx.params.parameters_csv.parse_as_csv() # type: ignore[attr-defined] - deck_riser = ctx.params.deck_riser # type: ignore[attr-defined] + pipette_mount = protocol.params.pipette_mount # type: ignore[attr-defined] + disposable_lid = protocol.params.disposable_lid # type: ignore[attr-defined] + parsed_csv = protocol.params.parameters_csv.parse_as_csv() # type: ignore[attr-defined] + deck_riser = protocol.params.deck_riser # type: ignore[attr-defined] + deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined] + helpers.comment_protocol_version(protocol, "01") rxn_vol = 50 real_mode = True # DECK SETUP AND LABWARE - tc_mod: ThermocyclerContext = ctx.load_module( + tc_mod: ThermocyclerContext = protocol.load_module( helpers.tc_str ) # type: ignore[assignment] tc_mod.open_lid() tc_mod.set_lid_temperature(105) - temp_mod: TemperatureModuleContext = ctx.load_module( + temp_mod: TemperatureModuleContext = protocol.load_module( helpers.temp_str, location="D3" ) # type: ignore[assignment] reagent_rack = temp_mod.load_labware( - "opentrons_24_aluminumblock_nest_1.5ml_snapcap" - ) # check if 2mL - - dest_plate = tc_mod.load_labware( - "opentrons_96_wellplate_200ul_pcr_full_skirt" - ) # do I change this to tough plate if they run pcr? - - source_plate = ctx.load_labware( - "opentrons_96_wellplate_200ul_pcr_full_skirt", location="D1" - ) # do I change this to their plate? + "opentrons_24_aluminumblock_nest_1.5ml_snapcap", "Reagent Rack" + ) + dest_plate_1 = tc_mod.load_labware( + "opentrons_96_wellplate_200ul_pcr_full_skirt", "Destination Plate 1" + ) + source_plate_1 = protocol.load_labware( + "opentrons_96_wellplate_200ul_pcr_full_skirt", "D1", "DNA Plate 1" + ) + waste = protocol.load_labware("nest_1_reservoir_195ml", "D2", "Liquid Waste") + liquid_waste = waste["A1"] tiprack_50 = [ - ctx.load_labware("opentrons_flex_96_tiprack_50ul", slot) for slot in [8, 9] + protocol.load_labware("opentrons_flex_96_tiprack_50ul", slot) for slot in [8, 9] ] # Opentrons tough pcr auto sealing lids if disposable_lid: - unused_lids = helpers.load_disposable_lids(ctx, 3, ["C3"], deck_riser) + unused_lids = helpers.load_disposable_lids(protocol, 3, ["C3"], deck_riser) used_lids: List[Labware] = [] # LOAD PIPETTES - p50 = ctx.load_instrument( + p50 = protocol.load_instrument( "flex_8channel_50", pipette_mount, tip_racks=tiprack_50, liquid_presence_detection=True, ) p50.configure_nozzle_layout(style=SINGLE, start="A1", tip_racks=tiprack_50) - ctx.load_trash_bin("A3") + protocol.load_trash_bin("A3") temp_mod.set_temperature(4) # LOAD LIQUIDS water: Well = reagent_rack["B1"] mmx_pic: List[Well] = reagent_rack.rows()[0] - dna_pic: List[Well] = source_plate.wells() + dna_pic: List[Well] = source_plate_1.wells() liquid_vols_and_wells: Dict[str, List[Dict[str, Well | List[Well] | float]]] = { - "Water": [{"well": water, "volume": 1500.0}], - "Mastermix": [{"well": mmx_pic, "volume": 1500.0}], - "DNA": [{"well": dna_pic, "volume": 50.0}], + "Water": [{"well": water, "volume": 500.0}], + "Mastermix": [{"well": mmx_pic, "volume": 500.0}], + "DNA": [{"well": dna_pic, "volume": 100.0}], } - helpers.load_wells_with_custom_liquids(ctx, liquid_vols_and_wells) - wells_to_probe = [[water], mmx_pic, dna_pic] - wells_to_probe_flattened = [ - well for list_of_wells in wells_to_probe for well in list_of_wells - ] - helpers.find_liquid_height_of_all_wells(ctx, p50, wells_to_probe_flattened) + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, p50) # adding water - ctx.comment("\n\n----------ADDING WATER----------\n") + protocol.comment("\n\n----------ADDING WATER----------\n") p50.pick_up_tip() - # p50.aspirate(40, water) # prewet - # p50.dispense(40, water) + p50.aspirate(40, water) # prewet + p50.dispense(40, water) parsed_csv = parsed_csv[1:] num_of_rows = len(parsed_csv) for row_index in range(num_of_rows): @@ -112,13 +110,13 @@ def run(ctx: ProtocolContext) -> None: p50.configure_for_volume(water_vol) p50.aspirate(water_vol, water) - p50.dispense(water_vol, dest_plate[dest_well], rate=0.5) + p50.dispense(water_vol, dest_plate_1[dest_well], rate=0.5) p50.configure_for_volume(50) - # p50.blow_out() + p50.blow_out() p50.drop_tip() # adding Mastermix - ctx.comment("\n\n----------ADDING MASTERMIX----------\n") + protocol.comment("\n\n----------ADDING MASTERMIX----------\n") for i, row in enumerate(parsed_csv): p50.pick_up_tip() mmx_vol = row[3] @@ -144,8 +142,8 @@ def run(ctx: ProtocolContext) -> None: break p50.configure_for_volume(mmx_vol) p50.aspirate(mmx_vol, reagent_rack[mmx_tube]) - p50.dispense(mmx_vol, dest_plate[dest_well].top()) - ctx.delay(seconds=2) + p50.dispense(mmx_vol, dest_plate_1[dest_well].top()) + protocol.delay(seconds=2) p50.blow_out() p50.touch_tip() p50.configure_for_volume(50) @@ -154,7 +152,7 @@ def run(ctx: ProtocolContext) -> None: p50.drop_tip() # adding DNA - ctx.comment("\n\n----------ADDING DNA----------\n") + protocol.comment("\n\n----------ADDING DNA----------\n") for row in parsed_csv: dna_vol = row[2] if dna_vol.lower() == "x": @@ -168,29 +166,28 @@ def run(ctx: ProtocolContext) -> None: if dna_vol == 0: break p50.configure_for_volume(dna_vol) - p50.aspirate(dna_vol, source_plate[dest_and_source_well]) - p50.dispense(dna_vol, dest_plate[dest_and_source_well], rate=0.5) + p50.aspirate(dna_vol, source_plate_1[dest_and_source_well]) + p50.dispense(dna_vol, dest_plate_1[dest_and_source_well], rate=0.5) p50.mix( 10, 0.7 * rxn_vol if 0.7 * rxn_vol < 30 else 30, - dest_plate[dest_and_source_well], + dest_plate_1[dest_and_source_well], ) p50.drop_tip() p50.configure_for_volume(50) - wells_to_probe_flattened.append(dest_plate[dest_well]) - ctx.comment("\n\n-----------Running PCR------------\n") + protocol.comment("\n\n-----------Running PCR------------\n") if real_mode: if disposable_lid: lid_on_plate, unused_lids, used_lids = helpers.use_disposable_lid_with_tc( - ctx, unused_lids, used_lids, dest_plate, tc_mod + protocol, unused_lids, used_lids, dest_plate_1, tc_mod ) else: tc_mod.close_lid() helpers.perform_pcr( - ctx, + protocol, tc_mod, initial_denature_time_sec=120, denaturation_time_sec=10, @@ -205,9 +202,16 @@ def run(ctx: ProtocolContext) -> None: tc_mod.open_lid() if disposable_lid: if len(used_lids) <= 1: - ctx.move_labware(lid_on_plate, "C2", use_gripper=True) + protocol.move_labware(lid_on_plate, "C2", use_gripper=True) else: - ctx.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) + protocol.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) p50.drop_tip() p50.configure_nozzle_layout(style=SINGLE, start="A1", tip_racks=tiprack_50) - helpers.find_liquid_height_of_all_wells(ctx, p50, wells_to_probe_flattened) + mmx_pic.append(water) + # Empty plates into liquid waste + p50.configure_nozzle_layout(style=ALL, tip_racks=tiprack_50) + helpers.clean_up_plates(p50, [source_plate_1, dest_plate_1], liquid_waste, 50) + # Probe liquid waste + helpers.find_liquid_height_of_all_wells(protocol, p50, [liquid_waste]) + if deactivate_modules_bool: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/active_protocols/3_Tartrazine Protocol.py b/abr-testing/abr_testing/protocols/active_protocols/3_Tartrazine Protocol.py index 66db85468f4..9916ef7f7fc 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/3_Tartrazine Protocol.py +++ b/abr-testing/abr_testing/protocols/active_protocols/3_Tartrazine Protocol.py @@ -1,5 +1,10 @@ """Tartrazine Protocol.""" -from opentrons.protocol_api import ProtocolContext, ParameterContext, Well +from opentrons.protocol_api import ( + ProtocolContext, + ParameterContext, + Well, + InstrumentContext, +) from abr_testing.protocols import helpers from opentrons.protocol_api.module_contexts import ( AbsorbanceReaderContext, @@ -23,101 +28,172 @@ def add_parameters(parameters: ParameterContext) -> None: parameters.add_int( variable_name="number_of_plates", display_name="Number of Plates", - default=4, + default=1, minimum=1, maximum=4, ) + helpers.create_channel_parameter(parameters) + helpers.create_plate_reader_compatible_labware_parameter(parameters) -def run(ctx: ProtocolContext) -> None: +def run(protocol: ProtocolContext) -> None: """Protocol.""" - number_of_plates = ctx.params.number_of_plates # type: ignore [attr-defined] + # Load parameters + number_of_plates = protocol.params.number_of_plates # type: ignore [attr-defined] + channels = protocol.params.channels # type: ignore [attr-defined] + plate_type = protocol.params.labware_plate_reader_compatible # type: ignore [attr-defined] + + helpers.comment_protocol_version(protocol, "01") # Plate Reader - plate_reader: AbsorbanceReaderContext = ctx.load_module( + plate_reader: AbsorbanceReaderContext = protocol.load_module( helpers.abs_mod_str, "A3" ) # type: ignore[assignment] - hs: HeaterShakerContext = ctx.load_module(helpers.hs_str, "A1") # type: ignore[assignment] - hs_adapter = hs.load_adapter("opentrons_universal_flat_adapter") - tube_rack = ctx.load_labware( - "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical", "C2", "Reagent Tube" - ) - tartrazine_tube = tube_rack["A3"] - water_tube_1 = tube_rack["A4"] - water_tube_2 = tube_rack["B3"] - sample_plate_1 = ctx.load_labware( - "corning_96_wellplate_360ul_flat", "D1", "Sample Plate 1" - ) - sample_plate_2 = ctx.load_labware( - "corning_96_wellplate_360ul_flat", "D2", "Sample Plate 2" + hs: HeaterShakerContext = protocol.load_module(helpers.hs_str, "A1") # type: ignore[assignment] + # Load Plates based off of number_of_plates parameter + available_deck_slots = ["D1", "D2", "C1", "B1"] + sample_plate_list = [] + for plate_num, slot in zip(range(number_of_plates), available_deck_slots): + plate = protocol.load_labware(plate_type, slot, f"Sample Plate {plate_num + 1}") + sample_plate_list.append(plate) + available_tip_rack_slots = ["D3", "C3", "B3", "B2"] + # LOAD PIPETTES AND TIP RACKS + # 50 CHANNEL + tip_racks_50 = [] + for plate_num, slot_2 in zip(range(number_of_plates), available_tip_rack_slots): + tiprack_50 = protocol.load_labware("opentrons_flex_96_tiprack_50ul", slot_2) + tip_racks_50.append(tiprack_50) + p50 = protocol.load_instrument( + f"flex_{channels}_50", "left", tip_racks=tip_racks_50 ) - sample_plate_3 = ctx.load_labware( - "corning_96_wellplate_360ul_flat", "C1", "Sample Plate 3" - ) - sample_plate_4 = ctx.load_labware( - "corning_96_wellplate_360ul_flat", "B1", "Sample Plate 4" + # 1000 CHANNEL + tiprack_1000_1 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "A2") + p1000 = protocol.load_instrument( + f"flex_{channels}_1000", "right", tip_racks=[tiprack_1000_1] ) + # DETERMINE RESERVOIR BASED OFF # OF PIPETTE CHANNELS + # 1 CHANNEL = TUBE RACK + if p50.active_channels == 1: + reservoir = protocol.load_labware( + "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical", "C2", "Reservoir" + ) + water_max_vol = reservoir["A3"].max_volume - 500 + reservoir_wells = reservoir.wells()[6:] # Skip first 4 bc they are 15ml + else: + # 8 CHANNEL = 12 WELL RESERVOIR + reservoir = protocol.load_labware("nest_12_reservoir_15ml", "C2", "Reservoir") + water_max_vol = reservoir["A1"].max_volume - 500 + reservoir_wells = reservoir.wells()[ + 1: + ] # Skip A1 as it's reserved for tartrazine - sample_plate_list = [sample_plate_1, sample_plate_2, sample_plate_3, sample_plate_4] - tiprack_50_1 = ctx.load_labware("opentrons_flex_96_tiprack_50ul", "D3") - tiprack_50_2 = ctx.load_labware("opentrons_flex_96_tiprack_50ul", "C3") - tiprack_50_3 = ctx.load_labware("opentrons_flex_96_tiprack_50ul", "B3") - tiprack_1000_1 = ctx.load_labware("opentrons_flex_96_tiprack_1000ul", "A2") - tip_racks = [tiprack_50_1, tiprack_50_2, tiprack_50_3] + # LABEL RESERVOIR WELLS AND DETERMINE NEEDED LIQUID + tartrazine_well = reservoir["A1"] + # NEEDED TARTRAZINE + needed_tartrazine: float = ( + float(number_of_plates) * 96.0 + ) * 10.0 + 1000.0 # loading extra as a safety factor + # NEEDED WATER + needed_water: float = ( + float(number_of_plates) * 96.0 * 250 + ) # loading extra as a safety factor + # CALCULATING NEEDED # OF WATER WELLS + needed_wells = round(needed_water / water_max_vol) + water_wells = [] + for i in range(needed_wells + 1): + water_wells.append(reservoir_wells[i]) - # Pipette - p50 = ctx.load_instrument("flex_1channel_50", "left", tip_racks=tip_racks) - p1000 = ctx.load_instrument( - "flex_1channel_1000", "right", tip_racks=[tiprack_1000_1] - ) + def _mix_tartrazine(pipette: InstrumentContext, well_to_probe: Well) -> None: + """Mix Tartrazine.""" + # Mix step is needed to ensure tartrazine does not settle between plates. + pipette.pick_up_tip() + top_of_tartrazine = helpers.find_liquid_height(pipette, well_to_probe) + for i in range(20): + p50.aspirate(1, well_to_probe.bottom(z=1)) + p50.dispense(1, well_to_probe.bottom(z=top_of_tartrazine + 1)) + pipette.return_tip() - # Probe wells + # LOAD LIQUIDS AND PROBE WELLS liquid_vols_and_wells: Dict[str, List[Dict[str, Well | List[Well] | float]]] = { - "Tartrazine": [{"well": tartrazine_tube, "volume": 45.0}], - "Water": [{"well": [water_tube_1, water_tube_2], "volume": 45.0}], + "Tartrazine": [{"well": tartrazine_well, "volume": needed_tartrazine}], + "Water": [{"well": water_wells, "volume": water_max_vol}], } - helpers.find_liquid_height_of_loaded_liquids(ctx, liquid_vols_and_wells, p50) - + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, p50) + tip_count = 1 * p50.active_channels # number of 50 ul tip uses. + p50.reset_tipracks() i = 0 all_percent_error_dict = {} cv_dict = {} - vol = 0.0 - tip_count = 0 + vol = 0.0 # counter to track available water volume + water_tip_count = 0 * p1000.active_channels # number of 1000 ul tip uses + well_num = 0 # index of well being used for water for sample_plate in sample_plate_list[:number_of_plates]: - deck_locations = ["D1", "D2", "C1", "B1"] - p1000.pick_up_tip() - for well in sample_plate.wells(): - if vol < 45000: - tube_of_choice = water_tube_1 + return_location = sample_plate.parent + # Mix Tartrazine to ensure no settling as occurred + _mix_tartrazine(p50, tartrazine_well) + tip_count += 1 * p50.active_channels + # Determine list of wells to probe + if p50.active_channels == 1: + well_list = sample_plate.wells() + elif p50.active_channels == 8: + well_list = sample_plate.rows()[0] + for well in well_list: + p1000.pick_up_tip() + # Determine which water well to aspirate from. + if vol < water_max_vol - 6000: + well_of_choice = water_wells[well_num] else: - tube_of_choice = water_tube_2 + well_num += 1 + well_of_choice = water_wells[well_num] + vol = 0.0 p50.pick_up_tip() - p1000.aspirate(190, tube_of_choice) - p1000.air_gap(5) - p1000.dispense(5, well.top()) + p1000.aspirate(190, well_of_choice) + p1000.air_gap(10) + p1000.dispense(10, well.top()) p1000.dispense(190, well) - vol += 190 - height = helpers.find_liquid_height(p50, tartrazine_tube) - p50.aspirate(10, tartrazine_tube.bottom(z=height)) + # Two blow outs ensures water is completely removed from pipette + p1000.blow_out(well.top()) + protocol.delay(minutes=0.1) + p1000.blow_out(well.top()) + vol += 190 * p1000.active_channels + # Probe to find liquid height of tartrazine to ensure correct amount is aspirated + height = helpers.find_liquid_height(p50, tartrazine_well) + if height <= 0.0: + # If a negative tartrazine height is found, + # the protocol will pause, prompt a refill, and reprobe. + protocol.pause("Fill tartrazine") + height = helpers.find_liquid_height(p50, tartrazine_well) + p50.aspirate(10, tartrazine_well.bottom(z=height), rate=0.15) p50.air_gap(5) p50.dispense(5, well.top()) - p50.dispense(10, well.bottom(z=0.5)) + p50.dispense(10, well.bottom(z=0.5), rate=0.15) + p50.blow_out() + protocol.delay(minutes=0.1) p50.blow_out() p50.return_tip() - tip_count += 1 - if tip_count >= (96 * 3): + tip_count += p50.active_channels + if tip_count >= (96 * len(tip_racks_50)): p50.reset_tipracks() - p1000.return_tip() - helpers.move_labware_to_hs(ctx, sample_plate, hs, hs_adapter) - helpers.set_hs_speed(ctx, hs, 1500, 2.0, True) + tip_count = 0 + p1000.return_tip() + water_tip_count += p1000.active_channels + if water_tip_count >= 96: + p1000.reset_tipracks() + water_tip_count = 0 + # Move labware to heater shaker to be mixed + helpers.move_labware_to_hs(protocol, sample_plate, hs, hs) + helpers.set_hs_speed(protocol, hs, 1500, 2.0, True) hs.open_labware_latch() + # Initialize plate reader plate_reader.close_lid() plate_reader.initialize("single", [450]) plate_reader.open_lid() - ctx.move_labware(sample_plate, plate_reader, use_gripper=True) + # Move sample plate into plate reader + protocol.move_labware(sample_plate, plate_reader, use_gripper=True) sample_plate_name = "sample plate_" + str(i + 1) csv_string = sample_plate_name + "_" + str(datetime.now()) plate_reader.close_lid() result = plate_reader.read(csv_string) + # Calculate CV and % error of expected value. for wavelength in result: dict_of_wells = result[wavelength] readings_and_wells = dict_of_wells.items() @@ -145,11 +221,15 @@ def run(ctx: ProtocolContext) -> None: "SD": standard_deviation, "Avg Percent Error": avg_percent_error, } + # Move Plate back to original location all_percent_error_dict[sample_plate_name] = percent_error_dict plate_reader.open_lid() - ctx.move_labware(sample_plate, deck_locations[i], use_gripper=True) + protocol.comment( + f"------plate {sample_plate}. {cv_dict[sample_plate_name]}------" + ) + protocol.move_labware(sample_plate, return_location, use_gripper=True) i += 1 # Print percent error dictionary - ctx.comment("Percent Error: " + str(all_percent_error_dict)) + protocol.comment("Percent Error: " + str(all_percent_error_dict)) # Print cv dictionary - ctx.comment("Plate Reader result: " + str(cv_dict)) + protocol.comment("Plate Reader Result: " + str(cv_dict)) diff --git a/abr-testing/abr_testing/protocols/active_protocols/4_Illumina DNA Enrichment.py b/abr-testing/abr_testing/protocols/active_protocols/4_Illumina DNA Enrichment.py new file mode 100644 index 00000000000..ff9a9807c92 --- /dev/null +++ b/abr-testing/abr_testing/protocols/active_protocols/4_Illumina DNA Enrichment.py @@ -0,0 +1,1016 @@ +"""DVT1ABR4: Illumina DNA Enrichment.""" +from opentrons.protocol_api import ( + ParameterContext, + ProtocolContext, + Labware, + Well, + InstrumentContext, +) +from opentrons import types +from abr_testing.protocols import helpers +from opentrons.protocol_api.module_contexts import ( + HeaterShakerContext, + MagneticBlockContext, + ThermocyclerContext, + TemperatureModuleContext, +) +from opentrons.hardware_control.modules.types import ThermocyclerStep +from typing import List, Dict + + +metadata = { + "protocolName": "Illumina DNA Enrichment v4 with TC Auto Sealing Lid", + "author": "Opentrons ", + "source": "Protocol Library", +} + +requirements = { + "robotType": "Flex", + "apiLevel": "2.21", +} + +# SCRIPT SETTINGS +DRYRUN = False # True = skip incubation times, shorten mix, for testing purposes +USE_GRIPPER = True # True = Uses Gripper, False = Manual Move +TIP_TRASH = False # True = Used tips go in Trash, False = Used tips go back into rack +HYBRID_PAUSE = True # True = sets a pause on the Hybridization + +# PROTOCOL SETTINGS +COLUMNS = 4 # 1-4 +HYBRIDDECK = True +HYBRIDTIME = 1.6 # Hours + +# PROTOCOL BLOCKS +STEP_VOLPOOL = 0 +STEP_HYB = 0 +STEP_CAPTURE = 1 +STEP_WASH = 1 +STEP_PCR = 1 +STEP_PCRDECK = 1 +STEP_CLEANUP = 1 + +p200_tips = 0 +p50_tips = 0 +total_waste_volume = 0.0 + + +RUN = 1 + + +def add_parameters(parameters: ParameterContext) -> None: + """Add parameters.""" + helpers.create_hs_speed_parameter(parameters) + helpers.create_dot_bottom_parameter(parameters) + helpers.create_disposable_lid_parameter(parameters) + helpers.create_tc_lid_deck_riser_parameter(parameters) + helpers.create_disposable_lid_trash_location(parameters) + helpers.create_deactivate_modules_parameter(parameters) + + +def run(protocol: ProtocolContext) -> None: + """Protocol.""" + heater_shaker_speed = protocol.params.heater_shaker_speed # type: ignore[attr-defined] + dot_bottom = protocol.params.dot_bottom # type: ignore[attr-defined] + disposable_lid = protocol.params.disposable_lid # type: ignore[attr-defined] + deck_riser = protocol.params.deck_riser # type: ignore[attr-defined] + trash_lid = protocol.params.trash_lid # type: ignore[attr-defined] + deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined] + helpers.comment_protocol_version(protocol, "01") + + unused_lids: List[Labware] = [] + used_lids: List[Labware] = [] + global p200_tips + global p50_tips + + protocol.comment("THIS IS A DRY RUN") if DRYRUN else protocol.comment( + "THIS IS A REACTION RUN" + ) + protocol.comment("USED TIPS WILL GO IN TRASH") if TIP_TRASH else protocol.comment( + "USED TIPS WILL BE RE-RACKED" + ) + + # DECK SETUP AND LABWARE + # ========== FIRST ROW =========== + heatershaker: HeaterShakerContext = protocol.load_module( + helpers.hs_str, "1" + ) # type: ignore[assignment] + heatershaker.close_labware_latch() + sample_plate_2 = heatershaker.load_labware( + "thermoscientificnunc_96_wellplate_1300ul" + ) + reservoir = protocol.load_labware("nest_96_wellplate_2ml_deep", "2", "Liquid Waste") + temp_block: TemperatureModuleContext = protocol.load_module( + helpers.temp_str, "3" + ) # type: ignore[assignment] + reagent_plate, temp_adapter = helpers.load_temp_adapter_and_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", temp_block, "Reagent Plate" + ) + # ========== SECOND ROW ========== + MAG_PLATE_SLOT: MagneticBlockContext = protocol.load_module( + helpers.mag_str, "C1" + ) # type: ignore[assignment] + tiprack_200_1 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "5") + tiprack_50_1 = protocol.load_labware("opentrons_flex_96_tiprack_50ul", "6") + # Opentrons tough pcr auto sealing lids + if disposable_lid: + unused_lids = helpers.load_disposable_lids(protocol, 3, ["C4"], deck_riser) + # ========== THIRD ROW =========== + thermocycler: ThermocyclerContext = protocol.load_module( + helpers.tc_str + ) # type: ignore[assignment] + sample_plate_1 = thermocycler.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt" + ) + thermocycler.open_lid() + tiprack_200_2 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "8") + tiprack_50_2 = protocol.load_labware("opentrons_flex_96_tiprack_50ul", "9") + # ========== FOURTH ROW ========== + tiprack_200_3 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "11") + trash_bin = protocol.load_trash_bin("A3") + # reagent + AMPure = reservoir["A1"] + SMB = reservoir["A2"] + + EtOH = reservoir["A4"] + RSB = reservoir["A5"] + Liquid_trash_well_1 = reservoir["A9"] + Liquid_trash_well_2 = reservoir["A10"] + Liquid_trash_well_3 = reservoir["A11"] + Liquid_trash_well_4 = reservoir["A12"] + liquid_trash_list = { + Liquid_trash_well_1: 0.0, + Liquid_trash_well_2: 0.0, + Liquid_trash_well_3: 0.0, + Liquid_trash_well_4: 0.0, + } + + def trash_liquid( + protocol: ProtocolContext, + pipette: InstrumentContext, + vol_to_trash: float, + liquid_trash_list: Dict[Well, float], + ) -> None: + """Determine which wells to use as liquid waste.""" + remaining_volume = vol_to_trash + max_capacity = 1500.0 + # Determine liquid waste location depending on current total volume + # Distribute the liquid volume sequentially + for well, current_volume in liquid_trash_list.items(): + if remaining_volume <= 0.0: + break + available_capacity = max_capacity - current_volume + if available_capacity < remaining_volume: + continue + pipette.dispense(remaining_volume, well.top()) + protocol.delay(minutes=0.1) + pipette.blow_out(well.top()) + liquid_trash_list[well] += remaining_volume + if pipette.current_volume <= 0.0: + break + + # Will Be distributed during the protocol + EEW_1 = sample_plate_2.wells_by_name()["A9"] + EEW_2 = sample_plate_2.wells_by_name()["A10"] + EEW_3 = sample_plate_2.wells_by_name()["A11"] + EEW_4 = sample_plate_2.wells_by_name()["A12"] + + NHB2 = reagent_plate.wells_by_name()["A1"] + Panel = reagent_plate.wells_by_name()["A2"] + EHB2 = reagent_plate.wells_by_name()["A3"] + Elute = reagent_plate.wells_by_name()["A4"] + ET2 = reagent_plate.wells_by_name()["A5"] + PPC = reagent_plate.wells_by_name()["A6"] + EPM = reagent_plate.wells_by_name()["A7"] + + # pipette + p1000 = protocol.load_instrument( + "flex_8channel_1000", + "left", + tip_racks=[tiprack_200_1, tiprack_200_2, tiprack_200_3], + ) + p50 = protocol.load_instrument( + "flex_8channel_50", "right", tip_racks=[tiprack_50_1, tiprack_50_2] + ) + reagent_plate.columns()[3] + # Load liquids and probe + liquid_vols_and_wells: Dict[str, List[Dict[str, Well | List[Well] | float]]] = { + "Reagents": [ + {"well": reagent_plate.columns()[3], "volume": 75.0}, + {"well": reagent_plate.columns()[4], "volume": 15.0}, + {"well": reagent_plate.columns()[5], "volume": 20.0}, + {"well": reagent_plate.columns()[6], "volume": 65.0}, + ], + "AMPure": [{"well": reservoir.columns()[0], "volume": 120.0}], + "SMB": [{"well": reservoir.columns()[1], "volume": 750.0}], + "EtOH": [{"well": reservoir.columns()[3], "volume": 900.0}], + "RSB": [{"well": reservoir.columns()[4], "volume": 96.0}], + "Wash": [ + {"well": sample_plate_2.columns()[8], "volume": 1000.0}, + {"well": sample_plate_2.columns()[9], "volume": 1000.0}, + {"well": sample_plate_2.columns()[10], "volume": 1000.0}, + {"well": sample_plate_2.columns()[11], "volume": 1000.0}, + ], + "Samples": [{"well": sample_plate_1.wells(), "volume": 150.0}], + } + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, p50) + # tip and sample tracking + if COLUMNS == 1: + column_1_list = ["A1"] # Plate 1 + column_2_list = ["A1"] # Plate 2 + column_3_list = ["A4"] # Plate 2 + column_4_list = ["A4"] # Plate 1 + column_5_list = ["A7"] # Plate 2 + column_6_list = ["A7"] # Plate 1 + WASHES = [EEW_1] + if COLUMNS == 2: + column_1_list = ["A1", "A2"] # Plate 1 + column_2_list = ["A1", "A2"] # Plate 2 + column_3_list = ["A4", "A5"] # Plate 2 + column_4_list = ["A4", "A5"] # Plate 1 + column_5_list = ["A7", "A8"] # Plate 2 + column_6_list = ["A7", "A8"] # Plate 1 + WASHES = [EEW_1, EEW_2] + if COLUMNS == 3: + column_1_list = ["A1", "A2", "A3"] # Plate 1 + column_2_list = ["A1", "A2", "A3"] # Plate 2 + column_3_list = ["A4", "A5", "A6"] # Plate 2 + column_4_list = ["A4", "A5", "A6"] # Plate 1 + column_5_list = ["A7", "A8", "A9"] # Plate 2 + column_6_list = ["A7", "A8", "A9"] # Plate 1 + WASHES = [EEW_1, EEW_2, EEW_3] + if COLUMNS == 4: + column_1_list = ["A1", "A2", "A3", "A4"] # Plate 1 + column_2_list = ["A1", "A2", "A3", "A4"] # Plate 2 + column_3_list = ["A5", "A6", "A7", "A8"] # Plate 2 + column_4_list = ["A5", "A6", "A7", "A8"] # Plate 1 + column_5_list = ["A9", "A10", "A11", "A12"] # Plate 2 + column_6_list = ["A9", "A10", "A11", "A12"] # Plate 1 + WASHES = [EEW_1, EEW_2, EEW_3, EEW_4] + + def tipcheck() -> None: + """Tip tracking function.""" + if p200_tips >= 3 * 12: + p1000.reset_tipracks() + p200_tips == 0 + if p50_tips >= 2 * 12: + p50.reset_tipracks() + p50_tips == 0 + + # commands + for loop in range(RUN): + thermocycler.open_lid() + heatershaker.open_labware_latch() + if DRYRUN is False: + if STEP_HYB == 1: + protocol.comment("SETTING THERMO and TEMP BLOCK Temperature") + thermocycler.set_block_temperature(4) + thermocycler.set_lid_temperature(100) + temp_block.set_temperature(4) + else: + protocol.comment("SETTING THERMO and TEMP BLOCK Temperature") + thermocycler.set_block_temperature(58) + thermocycler.set_lid_temperature(58) + heatershaker.set_and_wait_for_temperature(58) + heatershaker.close_labware_latch() + + # Sample Plate contains 30ul of DNA + + if STEP_VOLPOOL == 1: + protocol.comment("==============================================") + protocol.comment("--> Quick Vol Pool") + protocol.comment("==============================================") + + if STEP_HYB == 1: + protocol.comment("==============================================") + protocol.comment("--> HYB") + protocol.comment("==============================================") + + protocol.comment("--> Adding NHB2") + NHB2Vol = 50 + for loop, X in enumerate(column_1_list): + p50.pick_up_tip() + p50.aspirate(NHB2Vol, NHB2.bottom(z=dot_bottom)) # original = () + p50.dispense( + NHB2Vol, sample_plate_1[X].bottom(z=dot_bottom) + ) # original = () + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + protocol.comment("--> Adding Panel") + PanelVol = 10 + for loop, X in enumerate(column_1_list): + p50.pick_up_tip() + p50.aspirate(PanelVol, Panel.bottom(z=dot_bottom)) # original = () + p50.dispense( + PanelVol, sample_plate_1[X].bottom(z=dot_bottom) + ) # original = () + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + protocol.comment("--> Adding EHB2") + EHB2Vol = 10 + EHB2MixRep = 10 if DRYRUN is False else 1 + EHB2MixVol = 90 + for loop, X in enumerate(column_1_list): + p1000.pick_up_tip() + p1000.aspirate(EHB2Vol, EHB2.bottom(z=dot_bottom)) # original = () + p1000.dispense( + EHB2Vol, sample_plate_1[X].bottom(z=dot_bottom) + ) # original = () + p1000.move_to(sample_plate_1[X].bottom(z=dot_bottom)) # original = () + p1000.mix(EHB2MixRep, EHB2MixVol) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p50_tips += 1 + tipcheck() + + if HYBRIDDECK: + protocol.comment("Hybridize on Deck") + if disposable_lid: + ( + lid_on_plate, + unused_lids, + used_lids, + ) = helpers.use_disposable_lid_with_tc( + protocol, unused_lids, used_lids, sample_plate_1, thermocycler + ) + else: + thermocycler.close_lid() + if DRYRUN is False: + profile_TAGSTOP: List[ThermocyclerStep] = [ + {"temperature": 98, "hold_time_minutes": 5}, + {"temperature": 97, "hold_time_minutes": 1}, + {"temperature": 95, "hold_time_minutes": 1}, + {"temperature": 93, "hold_time_minutes": 1}, + {"temperature": 91, "hold_time_minutes": 1}, + {"temperature": 89, "hold_time_minutes": 1}, + {"temperature": 87, "hold_time_minutes": 1}, + {"temperature": 85, "hold_time_minutes": 1}, + {"temperature": 83, "hold_time_minutes": 1}, + {"temperature": 81, "hold_time_minutes": 1}, + {"temperature": 79, "hold_time_minutes": 1}, + {"temperature": 77, "hold_time_minutes": 1}, + {"temperature": 75, "hold_time_minutes": 1}, + {"temperature": 73, "hold_time_minutes": 1}, + {"temperature": 71, "hold_time_minutes": 1}, + {"temperature": 69, "hold_time_minutes": 1}, + {"temperature": 67, "hold_time_minutes": 1}, + {"temperature": 65, "hold_time_minutes": 1}, + {"temperature": 63, "hold_time_minutes": 1}, + {"temperature": 62, "hold_time_minutes": HYBRIDTIME * 60}, + ] + thermocycler.execute_profile( + steps=profile_TAGSTOP, repetitions=1, block_max_volume=100 + ) + thermocycler.set_block_temperature(62) + if HYBRID_PAUSE: + protocol.comment("HYBRIDIZATION PAUSED") + thermocycler.set_block_temperature(10) + thermocycler.open_lid() + if disposable_lid: + if trash_lid: + protocol.move_labware(lid_on_plate, trash_bin, use_gripper=True) + elif len(used_lids) <= 1: + protocol.move_labware(lid_on_plate, "B4", use_gripper=True) + else: + protocol.move_labware( + lid_on_plate, used_lids[-2], use_gripper=True + ) + else: + protocol.comment("Hybridize off Deck") + + if STEP_CAPTURE == 1: + protocol.comment("==============================================") + protocol.comment("--> Capture") + protocol.comment("==============================================") + # Standard Setup + + if DRYRUN is False: + protocol.comment("SETTING THERMO and TEMP BLOCK Temperature") + thermocycler.set_block_temperature(58) + thermocycler.set_lid_temperature(58) + + if DRYRUN is False: + heatershaker.set_and_wait_for_temperature(58) + + protocol.comment("--> Transfer Hybridization") + TransferSup = 100 + for loop, X in enumerate(column_1_list): + p1000.pick_up_tip() + p1000.move_to(sample_plate_1[X].bottom(z=0.5)) + p1000.aspirate(TransferSup + 1, rate=0.25) + p1000.dispense( + TransferSup + 1, sample_plate_2[column_2_list[loop]].bottom(z=1) + ) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + if disposable_lid: + ( + lid_on_plate, + unused_lids, + used_lids, + ) = helpers.use_disposable_lid_with_tc( + protocol, + unused_lids, + used_lids, + sample_plate_1, + thermocycler, + ) + else: + thermocycler.close_lid() + + protocol.comment("--> ADDING SMB") + SMBVol = 250 + SMBMixRPM = heater_shaker_speed + SMBMixRep = 5.0 if DRYRUN is False else 0.1 # minutes + SMBPremix = 3 if DRYRUN is False else 1 + # ============================== + for loop, X in enumerate(column_2_list): + p1000.pick_up_tip() + p1000.mix(SMBPremix, 200, SMB.bottom(z=1)) + p1000.aspirate(SMBVol / 2, SMB.bottom(z=1), rate=0.25) + p1000.dispense(SMBVol / 2, sample_plate_2[X].top(z=-7), rate=0.25) + p1000.aspirate(SMBVol / 2, SMB.bottom(z=1), rate=0.25) + p1000.dispense(SMBVol / 2, sample_plate_2[X].bottom(z=1), rate=0.25) + p1000.default_speed = 5 + p1000.move_to(sample_plate_2[X].bottom(z=5)) + for Mix in range(2): + p1000.aspirate(100, rate=0.5) + p1000.move_to(sample_plate_2[X].bottom(z=1)) + p1000.aspirate(80, rate=0.5) + p1000.dispense(80, rate=0.5) + p1000.move_to(sample_plate_2[X].bottom(z=5)) + p1000.dispense(100, rate=0.5) + Mix += 1 + p1000.blow_out(sample_plate_2[X].top(z=-7)) + p1000.default_speed = 400 + p1000.move_to(sample_plate_2[X].top(z=5)) + p1000.move_to(sample_plate_2[X].top(z=0)) + p1000.move_to(sample_plate_2[X].top(z=5)) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + # ============================== + helpers.set_hs_speed(protocol, heatershaker, SMBMixRPM, SMBMixRep, True) + + # GRIPPER MOVE sample_plate_2 FROM heatershaker TO MAGPLATE + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate_2, heatershaker, MAG_PLATE_SLOT + ) + + thermocycler.open_lid() + if disposable_lid: + if trash_lid: + protocol.move_labware(lid_on_plate, trash_bin, use_gripper=True) + elif len(used_lids) <= 1: + protocol.move_labware(lid_on_plate, "B4", use_gripper=True) + else: + protocol.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) + + if DRYRUN is False: + protocol.delay(minutes=2) + + protocol.comment("==============================================") + protocol.comment("--> WASH") + protocol.comment("==============================================") + # Setting Labware to Resume at Cleanup 1 + + protocol.comment("--> Remove SUPERNATANT") + for loop, X in enumerate(column_2_list): + p1000.pick_up_tip() + p1000.move_to(sample_plate_2[X].bottom(4)) + p1000.aspirate(200, rate=0.25) + trash_liquid(protocol, p1000, 200.0, liquid_trash_list) + p1000.move_to(sample_plate_2[X].bottom(0.5)) + p1000.aspirate(200, rate=0.25) + trash_liquid(protocol, p1000, 200.0, liquid_trash_list) + p1000.aspirate(20) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + # GRIPPER MOVE sample_plate_2 FROM MAGPLATE TO heatershaker + helpers.move_labware_to_hs( + protocol, sample_plate_2, heatershaker, heatershaker + ) + + protocol.comment("--> Repeating 6 washes") + washreps = 6 + washcount = 0 + for wash in range(washreps): + + protocol.comment("--> Adding EEW") + EEWVol = 200 + for loop, X in enumerate(column_2_list): + p1000.pick_up_tip() + p1000.aspirate( + EEWVol, WASHES[loop].bottom(z=dot_bottom) + ) # original = () + p1000.dispense( + EEWVol, sample_plate_2[X].bottom(z=dot_bottom) + ) # original = () + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + helpers.set_hs_speed( + protocol, heatershaker, int(heater_shaker_speed * 0.9), 4.0, True + ) + heatershaker.open_labware_latch() + + if DRYRUN is False: + protocol.delay(seconds=5 * 60) + + # GRIPPER MOVE sample_plate_2 FROM heatershaker TO MAGPLATE + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate_2, heatershaker, MAG_PLATE_SLOT + ) + + if DRYRUN is False: + protocol.delay(seconds=1 * 60) + + protocol.comment("--> Removing Supernatant") + RemoveSup = 200 + for loop, X in enumerate(column_2_list): + p1000.pick_up_tip() + p1000.move_to(sample_plate_2[X].bottom(z=3.5)) + p1000.aspirate(RemoveSup - 100, rate=0.25) + protocol.delay(minutes=0.1) + p1000.move_to(sample_plate_2[X].bottom(z=0.5)) + p1000.aspirate(100, rate=0.25) + trash_liquid(protocol, p1000, RemoveSup, liquid_trash_list) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + # ============================================================================================ + # GRIPPER MOVE sample_plate_2 FROM MAGPLATE TO heatershaker + helpers.move_labware_to_hs( + protocol, sample_plate_2, heatershaker, heatershaker + ) + washcount += 1 + + protocol.comment("--> Adding EEW") + EEWVol = 200 + for loop, X in enumerate(column_2_list): + p1000.pick_up_tip() + p1000.aspirate( + EEWVol, WASHES[loop].bottom(z=dot_bottom) + ) # original = () + p1000.dispense( + EEWVol, sample_plate_2[X].bottom(z=dot_bottom) + ) # original = () + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + helpers.set_hs_speed( + protocol, heatershaker, int(heater_shaker_speed * 0.9), 4.0, True + ) + + if DRYRUN is False: + protocol.delay(seconds=1 * 60) + + protocol.comment("--> Transfer Hybridization") + TransferSup = 200 + for loop, X in enumerate(column_2_list): + p1000.pick_up_tip() + p1000.move_to(sample_plate_2[X].bottom(z=0.5)) + p1000.aspirate(TransferSup, rate=0.25) + p1000.dispense( + TransferSup, sample_plate_2[column_3_list[loop]].bottom(z=1) + ) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + if DRYRUN is False: + protocol.delay(seconds=5 * 60) + + # GRIPPER MOVE sample_plate_2 FROM heatershaker TO MAGPLATE + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate_2, heatershaker, MAG_PLATE_SLOT + ) + if DRYRUN is False: + protocol.delay(seconds=1 * 60) + + protocol.comment("--> Removing Supernatant") + RemoveSup = 200 + for loop, X in enumerate(column_3_list): + p1000.pick_up_tip() + p1000.move_to(sample_plate_2[X].bottom(z=3.5)) + p1000.aspirate(RemoveSup - 100, rate=0.25) + protocol.delay(minutes=0.1) + p1000.move_to(sample_plate_2[X].bottom(z=0.5)) + p1000.aspirate(100, rate=0.25) + p1000.move_to(sample_plate_2[X].top(z=0.5)) + trash_liquid(protocol, p1000, 100, liquid_trash_list) + p1000.aspirate(20) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + protocol.comment("--> Removing Residual") + for loop, X in enumerate(column_3_list): + p50.pick_up_tip() + p50.move_to(sample_plate_2[X].bottom(z=dot_bottom)) # original = z=0 + p50.aspirate(50, rate=0.25) + p50.default_speed = 200 + trash_liquid(protocol, p50, 50, liquid_trash_list) + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + protocol.comment("==============================================") + protocol.comment("--> ELUTE") + protocol.comment("==============================================") + + protocol.comment("--> Adding Elute") + EluteVol = 23 + for loop, X in enumerate(column_3_list): + p50.pick_up_tip() + p50.aspirate(EluteVol, Elute.bottom(z=dot_bottom)) # original = () + p50.dispense( + EluteVol, sample_plate_2[X].bottom(z=dot_bottom) + ) # original = () + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + # ============================================================================================ + # GRIPPER MOVE sample_plate_2 FROM MAGPLATE TO heatershaker + helpers.move_labware_to_hs( + protocol, sample_plate_2, heatershaker, heatershaker + ) + # ============================================================================================ + helpers.set_hs_speed( + protocol, heatershaker, int(heater_shaker_speed * 0.9), 2.0, True + ) + heatershaker.open_labware_latch() + + if DRYRUN is False: + protocol.delay(minutes=2) + + # ============================================================================================ + # GRIPPER MOVE sample_plate_2 FROM heatershaker TO MAGPLATE + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate_2, heatershaker, MAG_PLATE_SLOT + ) + protocol.comment("--> Transfer Elution") + TransferSup = 21 + for loop, X in enumerate(column_3_list): + p50.pick_up_tip() + p50.move_to(sample_plate_2[X].bottom(z=0.5)) + p50.aspirate(TransferSup + 1, rate=0.25) + p50.dispense( + TransferSup + 1, sample_plate_1[column_4_list[loop]].bottom(z=1) + ) + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + protocol.comment("--> Adding ET2") + ET2Vol = 4 + ET2MixRep = 10 if DRYRUN is False else 1 + ET2MixVol = 20 + for loop, X in enumerate(column_4_list): + p50.pick_up_tip() + p50.aspirate(ET2Vol, ET2.bottom(z=dot_bottom)) # original = () + p50.dispense( + ET2Vol, sample_plate_1[X].bottom(z=dot_bottom) + ) # original = () + p50.move_to(sample_plate_1[X].bottom(z=dot_bottom)) # original = () + p50.mix(ET2MixRep, ET2MixVol) + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + if STEP_PCR == 1: + protocol.comment("==============================================") + protocol.comment("--> AMPLIFICATION") + protocol.comment("==============================================") + + protocol.comment("--> Adding PPC") + PPCVol = 5 + for loop, X in enumerate(column_4_list): + p50.pick_up_tip() + p50.aspirate(PPCVol, PPC.bottom(z=dot_bottom)) # original = () + p50.dispense( + PPCVol, sample_plate_1[X].bottom(z=dot_bottom) + ) # original = () + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + protocol.comment("--> Adding EPM") + EPMVol = 20 + EPMMixRep = 10 if DRYRUN is False else 1 + EPMMixVol = 45 + for loop, X in enumerate(column_4_list): + p50.pick_up_tip() + p50.aspirate(EPMVol, EPM.bottom(z=dot_bottom)) # original = () + p50.dispense( + EPMVol, sample_plate_1[X].bottom(z=dot_bottom) + ) # original = () + p50.move_to(sample_plate_1[X].bottom(z=dot_bottom)) # original = () + p50.mix(EPMMixRep, EPMMixVol) + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + if DRYRUN is False: + heatershaker.deactivate_heater() + + if STEP_PCRDECK == 1: + if DRYRUN is False: + if DRYRUN is False: + if disposable_lid: + ( + lid_on_plate, + unused_lids, + used_lids, + ) = helpers.use_disposable_lid_with_tc( + protocol, + unused_lids, + used_lids, + sample_plate_1, + thermocycler, + ) + else: + thermocycler.close_lid() + profile_PCR_1: List[ThermocyclerStep] = [ + {"temperature": 98, "hold_time_seconds": 45} + ] + thermocycler.execute_profile( + steps=profile_PCR_1, repetitions=1, block_max_volume=50 + ) + profile_PCR_2: List[ThermocyclerStep] = [ + {"temperature": 98, "hold_time_seconds": 30}, + {"temperature": 60, "hold_time_seconds": 30}, + {"temperature": 72, "hold_time_seconds": 30}, + ] + thermocycler.execute_profile( + steps=profile_PCR_2, repetitions=12, block_max_volume=50 + ) + profile_PCR_3: List[ThermocyclerStep] = [ + {"temperature": 72, "hold_time_minutes": 1} + ] + thermocycler.execute_profile( + steps=profile_PCR_3, repetitions=1, block_max_volume=50 + ) + thermocycler.set_block_temperature(10) + + thermocycler.open_lid() + if disposable_lid: + if trash_lid: + protocol.move_labware(lid_on_plate, trash_bin, use_gripper=True) + elif len(used_lids) <= 1: + protocol.move_labware(lid_on_plate, "B4", use_gripper=True) + else: + protocol.move_labware( + lid_on_plate, used_lids[-2], use_gripper=True + ) + + if STEP_CLEANUP == 1: + protocol.comment("==============================================") + protocol.comment("--> Cleanup") + protocol.comment("==============================================") + + # GRIPPER MOVE sample_plate_2 FROM MAGPLATE TO heatershaker + helpers.move_labware_to_hs( + protocol, sample_plate_2, heatershaker, heatershaker + ) + + protocol.comment("--> Transfer Elution") + TransferSup = 45 + for loop, X in enumerate(column_4_list): + p50.pick_up_tip() + p50.move_to(sample_plate_1[X].bottom(z=0.5)) + p50.aspirate(TransferSup + 1, rate=0.25) + p50.dispense( + TransferSup + 1, sample_plate_2[column_5_list[loop]].bottom(z=1) + ) + p50.return_tip() if TIP_TRASH is False else p50.drop_tip() + p50_tips += 1 + tipcheck() + + protocol.comment("--> ADDING AMPure (0.8x)") + AMPureVol = 40.5 + AMPureMixRep = 5.0 if DRYRUN is False else 0.1 + AMPurePremix = 3 if DRYRUN is False else 1 + # ========NEW SINGLE TIP DISPENSE=========== + for loop, X in enumerate(column_5_list): + p1000.pick_up_tip() + p1000.mix(AMPurePremix, AMPureVol + 10, AMPure.bottom(z=1)) + p1000.aspirate(AMPureVol, AMPure.bottom(z=1), rate=0.25) + p1000.dispense(AMPureVol, sample_plate_2[X].bottom(z=1), rate=0.25) + p1000.default_speed = 5 + p1000.move_to(sample_plate_2[X].bottom(z=5)) + for Mix in range(2): + p1000.aspirate(60, rate=0.5) + p1000.move_to(sample_plate_2[X].bottom(z=1)) + p1000.aspirate(60, rate=0.5) + p1000.dispense(60, rate=0.5) + p1000.move_to(sample_plate_2[X].bottom(z=5)) + p1000.dispense(30, rate=0.5) + Mix += 1 + p1000.blow_out(sample_plate_2[X].top(z=2)) + p1000.default_speed = 400 + p1000.move_to(sample_plate_2[X].top(z=5)) + p1000.move_to(sample_plate_2[X].top(z=0)) + p1000.move_to(sample_plate_2[X].top(z=5)) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + # ========NEW HS MIX========================= + helpers.set_hs_speed( + protocol, + heatershaker, + int(heater_shaker_speed * 0.9), + AMPureMixRep, + True, + ) + + # GRIPPER MOVE PLATE FROM HEATER SHAKER TO MAG PLATE + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate_2, heatershaker, MAG_PLATE_SLOT + ) + + if DRYRUN is False: + protocol.delay(minutes=4) + + protocol.comment("--> Removing Supernatant") + RemoveSup = 200 + for loop, X in enumerate(column_5_list): + p1000.pick_up_tip() + p1000.move_to(sample_plate_2[X].bottom(z=3.5)) + p1000.aspirate(RemoveSup - 100, rate=0.25) + protocol.delay(minutes=0.1) + p1000.move_to(sample_plate_2[X].bottom(z=0.5)) + p1000.aspirate(100, rate=0.25) + p1000.default_speed = 5 + p1000.move_to(sample_plate_2[X].top(z=2)) + p1000.default_speed = 200 + trash_liquid(protocol, p1000, 200, liquid_trash_list) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + for X_times in range(2): + protocol.comment("--> ETOH Wash") + ETOHMaxVol = 150 + for loop, X in enumerate(column_5_list): + p1000.pick_up_tip() + p1000.aspirate(ETOHMaxVol, EtOH.bottom(z=1)) + p1000.move_to(EtOH.top(z=0)) + p1000.move_to(EtOH.top(z=-5)) + p1000.move_to(EtOH.top(z=0)) + p1000.move_to(sample_plate_2[X].top(z=-2)) + p1000.dispense(ETOHMaxVol, rate=1) + protocol.delay(minutes=0.1) + p1000.blow_out() + p1000.move_to(sample_plate_2[X].top(z=5)) + p1000.move_to(sample_plate_2[X].top(z=0)) + p1000.move_to(sample_plate_2[X].top(z=5)) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + if DRYRUN is False: + protocol.delay(minutes=0.5) + + protocol.comment("--> Remove ETOH Wash") + for loop, X in enumerate(column_5_list): + p1000.pick_up_tip() + p1000.move_to(sample_plate_2[X].bottom(z=3.5)) + p1000.aspirate(RemoveSup - 100, rate=0.25) + protocol.delay(minutes=0.1) + p1000.move_to(sample_plate_2[X].bottom(z=0.5)) + p1000.aspirate(100, rate=0.25) + p1000.default_speed = 5 + p1000.move_to(sample_plate_2[X].top(z=2)) + p1000.default_speed = 200 + trash_liquid(protocol, p1000, RemoveSup, liquid_trash_list) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + if DRYRUN is False: + protocol.delay(minutes=2) + + protocol.comment("--> Removing Residual ETOH") + for loop, X in enumerate(column_5_list): + p1000.pick_up_tip() + p1000.move_to( + sample_plate_2[X].bottom(z=dot_bottom) + ) # original = (z=0) + p1000.aspirate(50, rate=0.25) + p1000.default_speed = 200 + trash_liquid(protocol, p1000, 50, liquid_trash_list) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + + if DRYRUN is False: + protocol.delay(minutes=1) + + # GRIPPER MOVE PLATE FROM MAG PLATE TO HEATER SHAKER + helpers.move_labware_to_hs( + protocol, sample_plate_2, heatershaker, heatershaker + ) + + protocol.comment("--> Adding RSB") + RSBVol = 32 + RSBMixRep = 1.0 if DRYRUN is False else 0.1 # minutes + for loop, X in enumerate(column_5_list): + p1000.pick_up_tip() + p1000.aspirate(RSBVol, RSB.bottom(z=1)) + + p1000.move_to( + ( + sample_plate_2.wells_by_name()[X] + .center() + .move(types.Point(x=1.3 * 0.8, y=0, z=-4)) + ) + ) + p1000.dispense(RSBVol, rate=1) + p1000.move_to(sample_plate_2.wells_by_name()[X].bottom(z=1)) + p1000.aspirate(RSBVol, rate=1) + p1000.move_to( + ( + sample_plate_2.wells_by_name()[X] + .center() + .move(types.Point(x=0, y=1.3 * 0.8, z=-4)) + ) + ) + p1000.dispense(RSBVol, rate=1) + p1000.move_to(sample_plate_2.wells_by_name()[X].bottom(z=1)) + p1000.aspirate(RSBVol, rate=1) + p1000.move_to( + ( + sample_plate_2.wells_by_name()[X] + .center() + .move(types.Point(x=1.3 * -0.8, y=0, z=-4)) + ) + ) + p1000.dispense(RSBVol, rate=1) + p1000.move_to(sample_plate_2.wells_by_name()[X].bottom(z=1)) + p1000.aspirate(RSBVol, rate=1) + p1000.move_to( + ( + sample_plate_2.wells_by_name()[X] + .center() + .move(types.Point(x=0, y=1.3 * -0.8, z=-4)) + ) + ) + p1000.dispense(RSBVol, rate=1) + p1000.move_to(sample_plate_2.wells_by_name()[X].bottom(z=1)) + p1000.aspirate(RSBVol, rate=1) + p1000.dispense(RSBVol, rate=1) + + p1000.blow_out(sample_plate_2.wells_by_name()[X].center()) + p1000.move_to(sample_plate_2.wells_by_name()[X].top(z=5)) + p1000.move_to(sample_plate_2.wells_by_name()[X].top(z=0)) + p1000.move_to(sample_plate_2.wells_by_name()[X].top(z=5)) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + if DRYRUN is False: + helpers.set_hs_speed( + protocol, + heatershaker, + int(heater_shaker_speed * 0.8), + RSBMixRep, + True, + ) + + # GRIPPER MOVE PLATE FROM HEATER SHAKER TO MAG PLATE + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate_2, heatershaker, MAG_PLATE_SLOT + ) + + if DRYRUN is False: + protocol.delay(minutes=3) + + protocol.comment("--> Transferring Supernatant") + TransferSup = 30 + for loop, X in enumerate(column_5_list): + p1000.pick_up_tip() + p1000.move_to(sample_plate_2[X].bottom(z=0.5)) + p1000.aspirate(TransferSup + 1, rate=0.25) + p1000.dispense( + TransferSup + 1, sample_plate_1[column_6_list[loop]].bottom(z=1) + ) + p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() + p200_tips += 1 + tipcheck() + liquids_to_probe_at_end = [ + Liquid_trash_well_1, + Liquid_trash_well_2, + Liquid_trash_well_3, + Liquid_trash_well_4, + ] + helpers.find_liquid_height_of_all_wells(protocol, p50, liquids_to_probe_at_end) + if deactivate_modules_bool: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/active_protocols/5_96ch complex protocol with single tip Pick Up.py b/abr-testing/abr_testing/protocols/active_protocols/5_96ch complex protocol with single tip Pick Up.py index dc40db7f177..ca7506cf6f0 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/5_96ch complex protocol with single tip Pick Up.py +++ b/abr-testing/abr_testing/protocols/active_protocols/5_96ch complex protocol with single tip Pick Up.py @@ -1,14 +1,14 @@ """96 ch Test Single Tip and Gripper Moves.""" from opentrons.protocol_api import ( - ALL, + COLUMN, SINGLE, + ALL, ParameterContext, ProtocolContext, Labware, ) from opentrons.protocol_api.module_contexts import ( HeaterShakerContext, - MagneticBlockContext, ThermocyclerContext, TemperatureModuleContext, ) @@ -21,7 +21,7 @@ } requirements = { - "robotType": "OT-3", + "robotType": "Flex", "apiLevel": "2.21", } @@ -45,25 +45,31 @@ def add_parameters(parameters: ParameterContext) -> None: helpers.create_dot_bottom_parameter(parameters) helpers.create_disposable_lid_parameter(parameters) helpers.create_tc_lid_deck_riser_parameter(parameters) + helpers.create_deactivate_modules_parameter(parameters) -def run(ctx: ProtocolContext) -> None: +def run(protocol: ProtocolContext) -> None: """Protocol.""" - b = ctx.params.dot_bottom # type: ignore[attr-defined] - TIPRACK_96_NAME = ctx.params.tip_size # type: ignore[attr-defined] - disposable_lid = ctx.params.disposable_lid # type: ignore[attr-defined] - deck_riser = ctx.params.deck_riser # type: ignore[attr-defined] + b = protocol.params.dot_bottom # type: ignore[attr-defined] + TIPRACK_96_NAME = protocol.params.tip_size # type: ignore[attr-defined] + disposable_lid = protocol.params.disposable_lid # type: ignore[attr-defined] + deck_riser = protocol.params.deck_riser # type: ignore[attr-defined] + deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined] - waste_chute = ctx.load_waste_chute() + waste_chute = protocol.load_waste_chute() + helpers.comment_protocol_version(protocol, "01") - thermocycler: ThermocyclerContext = ctx.load_module(helpers.tc_str) # type: ignore[assignment] - mag: MagneticBlockContext = ctx.load_module(helpers.mag_str, "A3") # type: ignore[assignment] - h_s: HeaterShakerContext = ctx.load_module(helpers.hs_str, "D1") # type: ignore[assignment] - temperature_module: TemperatureModuleContext = ctx.load_module( + thermocycler: ThermocyclerContext = protocol.load_module( + helpers.tc_str + ) # type: ignore[assignment] + h_s: HeaterShakerContext = protocol.load_module( + helpers.hs_str, "D1" + ) # type: ignore[assignment] + temperature_module: TemperatureModuleContext = protocol.load_module( helpers.temp_str, "C1" ) # type: ignore[assignment] if disposable_lid: - unused_lids = helpers.load_disposable_lids(ctx, 3, ["A4"], deck_riser) + unused_lids = helpers.load_disposable_lids(protocol, 3, ["A2"], deck_riser) used_lids: List[Labware] = [] thermocycler.open_lid() h_s.open_labware_latch() @@ -75,16 +81,15 @@ def run(ctx: ProtocolContext) -> None: adapters = [temperature_module_adapter, h_s_adapter] - source_reservoir = ctx.load_labware(RESERVOIR_NAME, "D2") - dest_pcr_plate = ctx.load_labware(PCR_PLATE_96_NAME, "C2") + source_reservoir = protocol.load_labware(RESERVOIR_NAME, "D2") + dest_pcr_plate = protocol.load_labware(PCR_PLATE_96_NAME, "C2") + liquid_waste = protocol.load_labware("nest_1_reservoir_195ml", "B2", "Liquid Waste") - tip_rack_1 = ctx.load_labware( - TIPRACK_96_NAME, "A2", adapter=TIPRACK_96_ADAPTER_NAME + tip_rack_1 = protocol.load_labware( + TIPRACK_96_NAME, "A3", adapter="opentrons_flex_96_tiprack_adapter" ) - tip_rack_adapter = tip_rack_1.parent - - tip_rack_2 = ctx.load_labware(TIPRACK_96_NAME, "C3") - tip_rack_3 = ctx.load_labware(TIPRACK_96_NAME, "C4") + tip_rack_2 = protocol.load_labware(TIPRACK_96_NAME, "C3") + tip_rack_3 = protocol.load_labware(TIPRACK_96_NAME, "C4") tip_racks = [ tip_rack_1, @@ -92,14 +97,16 @@ def run(ctx: ProtocolContext) -> None: tip_rack_3, ] - pipette_96_channel = ctx.load_instrument( + pipette_96_channel = protocol.load_instrument( PIPETTE_96_CHANNEL_NAME, mount="left", tip_racks=tip_racks, liquid_presence_detection=True, ) - water = ctx.define_liquid(name="water", description="H₂O", display_color="#42AB2D") + water = protocol.define_liquid( + name="water", description="H₂O", display_color="#42AB2D" + ) source_reservoir.wells_by_name()["A1"].load_liquid(liquid=water, volume=29000) def run_moves( @@ -123,7 +130,7 @@ def move_to_locations( def reset_labware() -> None: """Reset the labware to the reset location.""" - ctx.move_labware( + protocol.move_labware( labware_to_move, reset_location, use_gripper=use_gripper ) @@ -131,7 +138,9 @@ def reset_labware() -> None: return for location in move_locations: - ctx.move_labware(labware_to_move, location, use_gripper=use_gripper) + protocol.move_labware( + labware_to_move, location, use_gripper=use_gripper + ) if reset_after_each_move: reset_labware() @@ -161,14 +170,13 @@ def test_gripper_moves() -> None: def deck_moves(labware: Labware, reset_location: str) -> None: """Function to perform the movement of labware.""" deck_move_sequence = [ - ["B2"], # Deck Moves + ["B3"], # Deck Moves ["C3"], # Staging Area Slot 3 Moves ["C4", "D4"], # Staging Area Slot 4 Moves [ thermocycler, temperature_module_adapter, h_s_adapter, - mag, ], # Module Moves ] @@ -177,14 +185,13 @@ def deck_moves(labware: Labware, reset_location: str) -> None: def staging_area_slot_3_moves(labware: Labware, reset_location: str) -> None: """Function to perform the movement of labware, starting w/ staging area slot 3.""" staging_area_slot_3_move_sequence = [ - ["B2", "C2"], # Deck Moves + ["B3", "C2"], # Deck Moves [], # Don't have Staging Area Slot 3 open ["C4", "D4"], # Staging Area Slot 4 Moves [ thermocycler, temperature_module_adapter, h_s_adapter, - mag, ], # Module Moves ] @@ -198,14 +205,13 @@ def staging_area_slot_3_moves(labware: Labware, reset_location: str) -> None: def staging_area_slot_4_moves(labware: Labware, reset_location: str) -> None: """Function to perform the movement of labware, starting with staging area slot 4.""" staging_area_slot_4_move_sequence = [ - ["C2", "B2"], # Deck Moves + ["C2", "B3"], # Deck Moves ["C3"], # Staging Area Slot 3 Moves ["C4"], # Staging Area Slot 4 Moves [ thermocycler, temperature_module_adapter, h_s_adapter, - mag, ], # Module Moves ] @@ -219,7 +225,7 @@ def staging_area_slot_4_moves(labware: Labware, reset_location: str) -> None: def module_moves(labware: Labware, module_locations: List) -> None: """Function to perform the movement of labware, starting on a module.""" module_move_sequence = [ - ["C2", "B2"], # Deck Moves + ["C2", "B3"], # Deck Moves ["C3"], # Staging Area Slot 3 Moves ["C4", "D4"], # Staging Area Slot 4 Moves ] @@ -229,7 +235,7 @@ def module_moves(labware: Labware, module_locations: List) -> None: labware_move_to_locations.remove(module_starting_location) all_sequences = module_move_sequence.copy() all_sequences.append(labware_move_to_locations) - ctx.move_labware( + protocol.move_labware( labware, module_starting_location, use_gripper=USING_GRIPPER ) run_moves( @@ -242,27 +248,27 @@ def module_moves(labware: Labware, module_locations: List) -> None: deck_moves(dest_pcr_plate, DECK_MOVE_RESET_LOCATION) - ctx.move_labware( + protocol.move_labware( dest_pcr_plate, STAGING_AREA_SLOT_3_RESET_LOCATION, use_gripper=USING_GRIPPER, ) staging_area_slot_3_moves(dest_pcr_plate, STAGING_AREA_SLOT_3_RESET_LOCATION) - ctx.move_labware( + protocol.move_labware( dest_pcr_plate, STAGING_AREA_SLOT_4_RESET_LOCATION, use_gripper=USING_GRIPPER, ) staging_area_slot_4_moves(dest_pcr_plate, STAGING_AREA_SLOT_4_RESET_LOCATION) - module_locations = [thermocycler, mag] + adapters + module_locations = [thermocycler] + adapters module_moves(dest_pcr_plate, module_locations) - ctx.move_labware(dest_pcr_plate, thermocycler, use_gripper=USING_GRIPPER) + protocol.move_labware(dest_pcr_plate, thermocycler, use_gripper=USING_GRIPPER) def test_manual_moves() -> None: """Test manual moves.""" - ctx.move_labware(source_reservoir, "D4", use_gripper=USING_GRIPPER) + protocol.move_labware(source_reservoir, "D4", use_gripper=USING_GRIPPER) def test_pipetting() -> None: """Test pipetting.""" @@ -279,64 +285,59 @@ def test_single_tip_pickup_usage() -> None: well_position = f"{row}{col}" pipette_96_channel.pick_up_tip(tip_rack_2) - pipette_96_channel.aspirate(5, source_reservoir[well_position]) - pipette_96_channel.touch_tip() + pipette_96_channel.aspirate(45, source_reservoir[well_position]) + pipette_96_channel.air_gap(5) pipette_96_channel.dispense( - 5, dest_pcr_plate[well_position].bottom(b) + 25, dest_pcr_plate[well_position].bottom(b) ) + pipette_96_channel.blow_out(location=liquid_waste["A1"]) pipette_96_channel.drop_tip() tip_count += 1 # leave this dropping in waste chute, do not use get_disposal_preference # want to test partial drop - ctx.move_labware(tip_rack_2, waste_chute, use_gripper=USING_GRIPPER) + protocol.move_labware(tip_rack_2, waste_chute, use_gripper=USING_GRIPPER) + + def test_column_tip_rack_usage() -> None: + """Column Tip Pick Up.""" + list_of_columns = list(range(1, 13)) + pipette_96_channel.configure_nozzle_layout( + style=COLUMN, start="A12", tip_racks=[tip_rack_3] + ) + protocol.comment("------------------------------") + protocol.comment(f"channels {pipette_96_channel.active_channels}") + protocol.move_labware(tip_rack_3, "C3", use_gripper=USING_GRIPPER) + for well in list_of_columns: + tiprack_well = "A" + str(well) + well_name = "A" + str(well) + pipette_96_channel.liquid_presence_detection = True + pipette_96_channel.pick_up_tip(tip_rack_3[tiprack_well]) + pipette_96_channel.aspirate(45, source_reservoir[well_name]) + pipette_96_channel.liquid_presence_detection = False + pipette_96_channel.air_gap(5) + pipette_96_channel.dispense(25, dest_pcr_plate[tiprack_well].bottom(b)) + pipette_96_channel.blow_out(location=liquid_waste["A1"]) + pipette_96_channel.drop_tip() + protocol.move_labware(tip_rack_3, waste_chute, use_gripper=USING_GRIPPER) def test_full_tip_rack_usage() -> None: """Full Tip Pick Up.""" - pipette_96_channel.configure_nozzle_layout(style=ALL, start="A1") + pipette_96_channel.configure_nozzle_layout( + style=ALL, tip_racks=[tip_rack_1] + ) + protocol.comment(f"channels {pipette_96_channel.active_channels}") pipette_96_channel.liquid_presence_detection = True - pipette_96_channel.pick_up_tip(tip_rack_1["A1"]) - - pipette_96_channel.aspirate(5, source_reservoir["A1"]) - pipette_96_channel.touch_tip() - + pipette_96_channel.pick_up_tip() + pipette_96_channel.aspirate(45, source_reservoir["A1"]) pipette_96_channel.liquid_presence_detection = False - pipette_96_channel.air_gap(height=30) - pipette_96_channel.blow_out(waste_chute) - - pipette_96_channel.aspirate(5, source_reservoir["A1"]) - pipette_96_channel.touch_tip() - - pipette_96_channel.air_gap(height=30) - pipette_96_channel.blow_out() - - pipette_96_channel.aspirate(10, source_reservoir["A1"]) - pipette_96_channel.touch_tip() - - pipette_96_channel.dispense(10, dest_pcr_plate["A1"].bottom(b)) - pipette_96_channel.mix(repetitions=5, volume=15) + pipette_96_channel.air_gap(5) + pipette_96_channel.dispense(25, dest_pcr_plate["A1"].bottom(b)) + pipette_96_channel.blow_out(location=liquid_waste["A1"]) pipette_96_channel.return_tip() - - ctx.move_labware(tip_rack_1, waste_chute, use_gripper=USING_GRIPPER) - ctx.move_labware(tip_rack_3, tip_rack_adapter, use_gripper=USING_GRIPPER) - - pipette_96_channel.pick_up_tip(tip_rack_3["A1"]) - pipette_96_channel.transfer( - volume=10, - source=source_reservoir["A1"], - dest=dest_pcr_plate["A1"], - new_tip="never", - touch_tip=True, - blow_out=True, - blowout_location="trash", - mix_before=(3, 5), - mix_after=(1, 5), - ) - pipette_96_channel.return_tip() - - ctx.move_labware(tip_rack_3, waste_chute, use_gripper=USING_GRIPPER) + pipette_96_channel.reset_tipracks() test_single_tip_pickup_usage() + test_column_tip_rack_usage() test_full_tip_rack_usage() def test_module_usage(unused_lids: List[Labware], used_lids: List[Labware]) -> None: @@ -351,14 +352,14 @@ def test_thermocycler( unused_lids, used_lids, ) = helpers.use_disposable_lid_with_tc( - ctx, unused_lids, used_lids, dest_pcr_plate, thermocycler + protocol, unused_lids, used_lids, dest_pcr_plate, thermocycler ) thermocycler.set_block_temperature(4) thermocycler.set_lid_temperature(105) # Close lid thermocycler.close_lid() helpers.perform_pcr( - ctx, + protocol, thermocycler, initial_denature_time_sec=45, denaturation_time_sec=30, @@ -374,9 +375,9 @@ def test_thermocycler( thermocycler.open_lid() if disposable_lid: if len(used_lids) <= 1: - ctx.move_labware(lid_on_plate, "B3", use_gripper=True) + protocol.move_labware(lid_on_plate, waste_chute, use_gripper=True) else: - ctx.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) + protocol.move_labware(lid_on_plate, used_lids[-2], use_gripper=True) thermocycler.deactivate() def test_h_s() -> None: @@ -397,16 +398,21 @@ def test_temperature_module() -> None: temperature_module.set_temperature(10) temperature_module.deactivate() - def test_mag() -> None: - """Tests magnetic block.""" - pass - test_thermocycler(unused_lids, used_lids) test_h_s() test_temperature_module() - test_mag() test_pipetting() test_gripper_moves() test_module_usage(unused_lids, used_lids) test_manual_moves() + protocol.move_labware(source_reservoir, "C2", use_gripper=True) + helpers.clean_up_plates( + pipette_96_channel, [dest_pcr_plate, source_reservoir], liquid_waste["A1"], 50 + ) + pipette_96_channel.reset_tipracks() + helpers.find_liquid_height_of_all_wells( + protocol, pipette_96_channel, [liquid_waste["A1"]] + ) + if deactivate_modules_bool: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/active_protocols/6_Omega_HDQ_DNA_Cells-Flex_96_channel.py b/abr-testing/abr_testing/protocols/active_protocols/6_Omega_HDQ_DNA_Cells-Flex_96_channel.py index 89f643729fb..894f80dcdea 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/6_Omega_HDQ_DNA_Cells-Flex_96_channel.py +++ b/abr-testing/abr_testing/protocols/active_protocols/6_Omega_HDQ_DNA_Cells-Flex_96_channel.py @@ -29,22 +29,31 @@ def add_parameters(parameters: ParameterContext) -> None: """Parameters.""" helpers.create_dot_bottom_parameter(parameters) + helpers.create_deactivate_modules_parameter(parameters) + parameters.add_int( + variable_name="number_of_runs", + display_name="Number of Runs", + default=2, + minimum=1, + maximum=4, + ) # Start protocol -def run(ctx: ProtocolContext) -> None: +def run(protocol: ProtocolContext) -> None: """Protocol.""" - dot_bottom = ctx.params.dot_bottom # type: ignore[attr-defined] - + dot_bottom = protocol.params.dot_bottom # type: ignore[attr-defined] + deactivate_modules = protocol.params.deactivate_modules # type: ignore[attr-defined] + number_of_runs = protocol.params.number_of_runs # type: ignore[attr-defined] dry_run = False tip_mixing = False wash_vol = 600.0 AL_vol = 230.0 - bind_vol = 320.0 + bind_vol = 300.0 sample_vol = 180.0 elution_vol = 100.0 - + helpers.comment_protocol_version(protocol, "01") # Same for all HDQ Extractions deepwell_type = "nest_96_wellplate_2ml_deep" if not dry_run: @@ -59,56 +68,56 @@ def run(ctx: ProtocolContext) -> None: binding_buffer_vol = bead_vol + bind_vol starting_vol = AL_total_vol + sample_vol - h_s: HeaterShakerContext = ctx.load_module(helpers.hs_str, "D1") # type: ignore[assignment] + h_s: HeaterShakerContext = protocol.load_module( + helpers.hs_str, "D1" + ) # type: ignore[assignment] sample_plate, h_s_adapter = helpers.load_hs_adapter_and_labware( deepwell_type, h_s, "Sample Plate" ) h_s.close_labware_latch() samples_m = sample_plate.wells()[0] - # NOTE: MAG BLOCK will be on slot 6 - - temp: TemperatureModuleContext = ctx.load_module( + temp: TemperatureModuleContext = protocol.load_module( helpers.temp_str, "A3" ) # type: ignore[assignment] elutionplate, tempblock = helpers.load_temp_adapter_and_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", temp, "Elution Plate/Reservoir" ) - magblock: MagneticBlockContext = ctx.load_module( + magblock: MagneticBlockContext = protocol.load_module( "magneticBlockV1", "C1" ) # type: ignore[assignment] - liquid_waste = ctx.load_labware("nest_1_reservoir_195ml", "B3", "Liquid Waste") + liquid_waste = protocol.load_labware("nest_1_reservoir_195ml", "B3", "Liquid Waste") waste = liquid_waste.wells()[0].top() - lysis_reservoir = ctx.load_labware(deepwell_type, "D2", "Lysis reservoir") + lysis_reservoir = protocol.load_labware(deepwell_type, "D2", "Lysis reservoir") lysis_res = lysis_reservoir.wells()[0] - bind_reservoir = ctx.load_labware( + bind_reservoir = protocol.load_labware( deepwell_type, "C2", "Beads and binding reservoir" ) bind_res = bind_reservoir.wells()[0] - wash1_reservoir = ctx.load_labware(deepwell_type, "C3", "Wash 1 reservoir") + wash1_reservoir = protocol.load_labware(deepwell_type, "C3", "Wash 1 reservoir") wash1_res = wash1_reservoir.wells()[0] - wash2_reservoir = ctx.load_labware(deepwell_type, "B1", "Wash 2 reservoir") + wash2_reservoir = protocol.load_labware(deepwell_type, "B1", "Wash 2 reservoir") wash2_res = wash2_reservoir.wells()[0] elution_res = elutionplate.wells()[0] # Load Pipette and tip racks # Load tips - tiprack_1 = ctx.load_labware( + tiprack_1 = protocol.load_labware( "opentrons_flex_96_tiprack_1000ul", "A1", adapter="opentrons_flex_96_tiprack_adapter", ) tips = tiprack_1.wells()[0] - tiprack_2 = ctx.load_labware( + tiprack_2 = protocol.load_labware( "opentrons_flex_96_tiprack_1000ul", "A2", adapter="opentrons_flex_96_tiprack_adapter", ) tips1 = tiprack_2.wells()[0] # load 96 channel pipette - pip: InstrumentContext = ctx.load_instrument( + pip: InstrumentContext = protocol.load_instrument( "flex_96channel_1000", mount="left", tip_racks=[tiprack_1, tiprack_2] ) # Load Liquids and probe @@ -127,7 +136,7 @@ def run(ctx: ProtocolContext) -> None: "Samples": [{"well": sample_plate.wells(), "volume": sample_vol}], } - helpers.find_liquid_height_of_loaded_liquids(ctx, liquid_vols_and_wells, pip) + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, pip) pip.flow_rate.aspirate = 50 pip.flow_rate.dispense = 150 @@ -211,141 +220,215 @@ def bead_mix(vol: float, plate: Well, reps: int = 5) -> None: pip.flow_rate.aspirate = 150 pip.flow_rate.dispense = 200 - # Start Protocol - temp.set_temperature(inc_temp) - # Transfer and mix lysis - pip.pick_up_tip(tips) - pip.aspirate(AL_total_vol, lysis_res) - pip.dispense(AL_total_vol, samples_m) - resuspend_pellet(400, samples_m, reps=4 if not dry_run else 1) - if not tip_mixing: - pip.return_tip() - - # Mix, then heat - ctx.comment("Lysis Mixing") - helpers.set_hs_speed(ctx, h_s, 1800, 10, False) - if not dry_run: - h_s.set_and_wait_for_temperature(55) - ctx.delay( - minutes=10 if not dry_run else 0.25, - msg="Please allow another 10 minutes of 55C incubation to complete lysis.", - ) - h_s.deactivate_shaker() - - # Transfer and mix bind&beads - pip.pick_up_tip(tips) - bead_mix(binding_buffer_vol, bind_res, reps=4 if not dry_run else 1) - pip.aspirate(binding_buffer_vol, bind_res) - pip.dispense(binding_buffer_vol, samples_m) - bead_mix(binding_buffer_vol + starting_vol, samples_m, reps=4 if not dry_run else 1) - if not tip_mixing: - pip.return_tip() - pip.home() - - # Shake for binding incubation - ctx.comment("Binding incubation") - helpers.set_hs_speed(ctx, h_s, 1800, 10, True) - - # Transfer plate to magnet - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) - - ctx.delay( - minutes=settling_time, - msg="Please wait " + str(settling_time) + " minute(s) for beads to pellet.", - ) + def protocol_function() -> None: + # Start Protocol + temp.set_temperature(inc_temp) + # Transfer and mix lysis + pip.pick_up_tip(tips) + pip.aspirate(AL_total_vol, lysis_res) + pip.dispense(AL_total_vol, samples_m) + resuspend_pellet(200, samples_m, reps=4 if not dry_run else 1) + if not tip_mixing: + pip.return_tip() - # Remove Supernatant and move off magnet - pip.pick_up_tip(tips) - pip.aspirate(1000, samples_m.bottom(dot_bottom)) - pip.dispense(1000, waste) - if starting_vol + binding_buffer_vol > 1000: - pip.aspirate(1000, samples_m.bottom(dot_bottom)) - pip.dispense(1000, waste) - pip.return_tip() - - # Transfer plate from magnet to H/S - helpers.move_labware_to_hs(ctx, sample_plate, h_s, h_s_adapter) - - # Washes - for i in range(num_washes if not dry_run else 1): - if i == 0 or i == 1: - wash_res = wash1_res - else: - wash_res = wash2_res + # Mix, then heat + protocol.comment("Lysis Mixing") + helpers.set_hs_speed(protocol, h_s, 1800, 10, False) + if not dry_run: + h_s.set_and_wait_for_temperature(55) + protocol.delay( + minutes=10 if not dry_run else 0.25, + msg="Please allow another 10 minutes of 55C incubation to complete lysis.", + ) + h_s.deactivate_shaker() + # Transfer and mix bind&beads pip.pick_up_tip(tips) - pip.aspirate(wash_vol, wash_res) - pip.dispense(wash_vol, samples_m) + bead_mix(binding_buffer_vol, bind_res, reps=4 if not dry_run else 1) + pip.aspirate(binding_buffer_vol, bind_res) + pip.dispense(binding_buffer_vol, samples_m) + bead_mix( + binding_buffer_vol + starting_vol, samples_m, reps=4 if not dry_run else 1 + ) if not tip_mixing: pip.return_tip() - helpers.set_hs_speed(ctx, h_s, 1800, 5, True) + pip.home() + + # Shake for binding incubation + protocol.comment("Binding incubation") + helpers.set_hs_speed(protocol, h_s, 1800, 10, True) # Transfer plate to magnet - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) - ctx.delay( + protocol.delay( minutes=settling_time, msg="Please wait " + str(settling_time) + " minute(s) for beads to pellet.", ) # Remove Supernatant and move off magnet pip.pick_up_tip(tips) - pip.aspirate(1000, samples_m.bottom(dot_bottom)) - pip.dispense(1000, bind_res.top()) - if wash_vol > 1000: - pip.aspirate(1000, samples_m.bottom(dot_bottom)) - pip.dispense(1000, bind_res.top()) + pip.aspirate(550, samples_m.bottom(dot_bottom)) + pip.dispense(550, waste) + if starting_vol + binding_buffer_vol > 1000: + pip.aspirate(550, samples_m.bottom(dot_bottom)) + pip.dispense(550, waste) pip.return_tip() # Transfer plate from magnet to H/S - helpers.move_labware_to_hs(ctx, sample_plate, h_s, h_s_adapter) + helpers.move_labware_to_hs(protocol, sample_plate, h_s, h_s_adapter) + + # Washes + for i in range(num_washes if not dry_run else 1): + if i == 0 or i == 1: + wash_res = wash1_res + else: + wash_res = wash2_res + + pip.pick_up_tip(tips) + pip.aspirate(wash_vol, wash_res) + pip.dispense(wash_vol, samples_m) + if not tip_mixing: + pip.return_tip() + helpers.set_hs_speed(protocol, h_s, 1800, 5, True) + + # Transfer plate to magnet + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) + + protocol.delay( + minutes=settling_time, + msg="Please wait " + + str(settling_time) + + " minute(s) for beads to pellet.", + ) + + # Remove Supernatant and move off magnet + pip.pick_up_tip(tips) + pip.aspirate(473, samples_m.bottom(dot_bottom)) + pip.dispense(473, bind_res.top()) + if wash_vol > 1000: + pip.aspirate(473, samples_m.bottom(dot_bottom)) + pip.dispense(473, bind_res.top()) + pip.return_tip() - # Dry beads - if dry_run: - drybeads = 0.5 - else: - drybeads = 10 - # Number of minutes you want to dry for - for beaddry in np.arange(drybeads, 0, -0.5): - ctx.delay( - minutes=0.5, - msg="There are " + str(beaddry) + " minutes left in the drying step.", - ) + # Transfer plate from magnet to H/S + helpers.move_labware_to_hs(protocol, sample_plate, h_s, h_s_adapter) - # Elution - pip.pick_up_tip(tips1) - pip.aspirate(elution_vol, elution_res) - pip.dispense(elution_vol, samples_m) - resuspend_pellet(elution_vol, samples_m, reps=3 if not dry_run else 1) - if not tip_mixing: - pip.return_tip() - pip.home() + # Dry beads + if dry_run: + drybeads = 0.5 + else: + drybeads = 10 + # Number of minutes you want to dry for + for beaddry in np.arange(drybeads, 0, -0.5): + protocol.delay( + minutes=0.5, + msg="There are " + str(beaddry) + " minutes left in the drying step.", + ) + + # Elution + pip.pick_up_tip(tips1) + pip.aspirate(elution_vol, elution_res) + pip.dispense(elution_vol, samples_m) + resuspend_pellet(elution_vol, samples_m, reps=3 if not dry_run else 1) + if not tip_mixing: + pip.return_tip() + pip.home() - helpers.set_hs_speed(ctx, h_s, 2000, 5, True) + helpers.set_hs_speed(protocol, h_s, 2000, 5, True) - # Transfer plate to magnet - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + # Transfer plate to magnet + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) - ctx.delay( - minutes=settling_time, - msg="Please wait " + str(settling_time) + " minute(s) for beads to pellet.", - ) + protocol.delay( + minutes=settling_time, + msg="Please wait " + str(settling_time) + " minute(s) for beads to pellet.", + ) + + pip.pick_up_tip(tips1) + pip.aspirate(elution_vol, samples_m) + pip.dispense(elution_vol, elutionplate.wells()[0]) + pip.return_tip() + + pip.home() + pip.reset_tipracks() + + # Empty Plates + pip.pick_up_tip() + pip.aspirate(500, samples_m) + pip.dispense(500, liquid_waste["A1"].top()) + pip.aspirate(500, wash1_res) + pip.dispense(500, liquid_waste["A1"].top()) + pip.aspirate(500, wash2_res) + pip.dispense(500, liquid_waste["A1"].top()) + pip.return_tip() + helpers.find_liquid_height_of_all_wells(protocol, pip, [liquid_waste["A1"]]) + helpers.move_labware_to_hs(protocol, sample_plate, h_s, h_s_adapter) + + def setup() -> None: + pip.pick_up_tip() + pip.transfer( + volume=250, + source=liquid_waste["A1"].bottom(z=2), + dest=lysis_reservoir["A1"], + blow_out=True, + blowout_location="source well", + new_tip="never", + trash=False, + ) + pip.transfer( + 1700, + liquid_waste["A1"].bottom(z=2), + wash1_reservoir["A1"], + blow_out=True, + blowout_location="source well", + new_tip="never", + trash=False, + ) + pip.transfer( + 1100, + bind_reservoir["A1"].bottom(z=2), + wash2_reservoir["A1"], + blow_out=True, + blowout_location="source well", + new_tip="never", + trash=False, + ) + pip.transfer( + 100, + liquid_waste["A1"].bottom(z=2), + sample_plate["A1"], + blow_out=True, + blowout_location="source well", + new_tip="never", + trash=False, + ) + pip.return_tip() - pip.pick_up_tip(tips1) - pip.aspirate(elution_vol, samples_m) - pip.dispense(elution_vol, elutionplate.wells()[0]) - pip.return_tip() - - pip.home() - pip.reset_tipracks() - - # Empty Plates - pip.pick_up_tip() - pip.aspirate(1000, samples_m) - pip.dispense(1000, liquid_waste["A1"].top()) - pip.aspirate(1000, wash1_res) - pip.dispense(1000, liquid_waste["A1"].top()) - pip.aspirate(1000, wash2_res) - pip.dispense(1000, liquid_waste["A1"].top()) - pip.return_tip() - helpers.find_liquid_height_of_all_wells(ctx, pip, [liquid_waste["A1"]]) + def clean() -> None: + plates_to_clean = [ + sample_plate, + elutionplate, + wash2_reservoir, + wash1_reservoir, + liquid_waste, + ] + helpers.clean_up_plates(pip, plates_to_clean, liquid_waste["A1"], 1000) + + for i in range(number_of_runs): + protocol_function() + + pip.reset_tipracks() + if i < number_of_runs - 1: + setup() + pip.reset_tipracks() + clean() + if deactivate_modules: + helpers.deactivate_modules(protocol) + helpers.find_liquid_height_of_all_wells(protocol, pip, [liquid_waste["A1"]]) diff --git a/abr-testing/abr_testing/protocols/active_protocols/7_HDQ_DNA_Bacteria_Flex.py b/abr-testing/abr_testing/protocols/active_protocols/7_HDQ_DNA_Bacteria_Flex.py index aa33079f553..4350888b0d6 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/7_HDQ_DNA_Bacteria_Flex.py +++ b/abr-testing/abr_testing/protocols/active_protocols/7_HDQ_DNA_Bacteria_Flex.py @@ -22,7 +22,7 @@ } requirements = { - "robotType": "OT-3", + "robotType": "Flex", "apiLevel": "2.21", } """ @@ -57,18 +57,22 @@ def add_parameters(parameters: ParameterContext) -> None: helpers.create_single_pipette_mount_parameter(parameters) helpers.create_hs_speed_parameter(parameters) helpers.create_dot_bottom_parameter(parameters) + helpers.create_deactivate_modules_parameter(parameters) -def run(ctx: ProtocolContext) -> None: +def run(protocol: ProtocolContext) -> None: """Protocol.""" - heater_shaker_speed = ctx.params.heater_shaker_speed # type: ignore[attr-defined] - mount = ctx.params.pipette_mount # type: ignore[attr-defined] - dot_bottom = ctx.params.dot_bottom # type: ignore[attr-defined] + heater_shaker_speed = protocol.params.heater_shaker_speed # type: ignore[attr-defined] + mount = protocol.params.pipette_mount # type: ignore[attr-defined] + dot_bottom = protocol.params.dot_bottom # type: ignore[attr-defined] + deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined] + helpers.comment_protocol_version(protocol, "01") + dry_run = False TIP_TRASH = False res_type = "nest_12_reservoir_22ml" - num_samples = 8 + num_samples = 96 wash1_vol = 600.0 wash2_vol = 600.0 wash3_vol = 600.0 @@ -95,37 +99,48 @@ def run(ctx: ProtocolContext) -> None: starting_vol = AL_vol + sample_vol binding_buffer_vol = bind_vol + bead_vol - ctx.load_trash_bin("A3") - h_s: HeaterShakerContext = ctx.load_module(helpers.hs_str, "D1") # type: ignore[assignment] + protocol.load_trash_bin("A3") + h_s: HeaterShakerContext = protocol.load_module( + helpers.hs_str, "D1" + ) # type: ignore[assignment] sample_plate, h_s_adapter = helpers.load_hs_adapter_and_labware( deepwell_type, h_s, "Sample Plate" ) h_s.close_labware_latch() - temp: TemperatureModuleContext = ctx.load_module( + temp: TemperatureModuleContext = protocol.load_module( helpers.temp_str, "D3" ) # type: ignore[assignment] elutionplate, temp_adapter = helpers.load_temp_adapter_and_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", temp, "Elution Plate" ) - magnetic_block: MagneticBlockContext = ctx.load_module( + magnetic_block: MagneticBlockContext = protocol.load_module( helpers.mag_str, "C1" ) # type: ignore[assignment] - waste_reservoir = ctx.load_labware("nest_1_reservoir_195ml", "B3", "Liquid Waste") + waste_reservoir = protocol.load_labware( + "nest_1_reservoir_195ml", "B3", "Liquid Waste" + ) waste = waste_reservoir.wells()[0].top() - res1 = ctx.load_labware(res_type, "D2", "Reagent Reservoir 1") + res1 = protocol.load_labware(res_type, "D2", "Reagent Reservoir 1") num_cols = math.ceil(num_samples / 8) - # Load tips and combine all similar boxes - tips1000 = ctx.load_labware("opentrons_flex_96_tiprack_1000ul", "A1", "Tips 1") - tips1001 = ctx.load_labware("opentrons_flex_96_tiprack_1000ul", "A2", "Tips 2") - tips1002 = ctx.load_labware("opentrons_flex_96_tiprack_1000ul", "B1", "Tips 3") - tips = [*tips1000.wells()[num_samples:96], *tips1001.wells(), *tips1002.wells()] + tips1000 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "A1", "Tips 1") + tips1001 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "A2", "Tips 2") + tips1002 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "B1", "Tips 3") + tips1003 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "B2", "Tips 4") + tips1004 = protocol.load_labware("opentrons_flex_96_tiprack_1000ul", "C2", "Tips 5") + + tips = [ + *tips1000.wells()[num_samples:96], + *tips1001.wells(), + *tips1002.wells(), + *tips1003.wells(), + ] tips_sn = tips1000.wells()[:num_samples] # load instruments - m1000 = ctx.load_instrument( - "flex_8channel_1000", mount, tip_racks=[tips1000, tips1001, tips1002] + m1000 = protocol.load_instrument( + "flex_8channel_1000", mount, tip_racks=[tips1000, tips1001, tips1002, tips1003] ) """ @@ -158,7 +173,7 @@ def run(ctx: ProtocolContext) -> None: m1000.flow_rate.aspirate = 300 m1000.flow_rate.dispense = 300 m1000.flow_rate.blow_out = 300 - helpers.find_liquid_height_of_loaded_liquids(ctx, liquid_vols_and_wells, m1000) + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, m1000) def tiptrack(tipbox: List[Well]) -> None: """Track Tips.""" @@ -167,14 +182,15 @@ def tiptrack(tipbox: List[Well]) -> None: if tipbox == tips: m1000.pick_up_tip(tipbox[int(tip1k)]) tip1k = tip1k + 8 + if tip1k >= len(tipbox): + tip1k = 0 drop_count = drop_count + 8 if drop_count >= 150: drop_count = 0 - ctx.pause("Empty Waste Bin.") def remove_supernatant(vol: float) -> None: """Remove supernatants.""" - ctx.comment("-----Removing Supernatant-----") + protocol.comment("-----Removing Supernatant-----") m1000.flow_rate.aspirate = 150 num_trans = math.ceil(vol / 980) vol_per_trans = vol / num_trans @@ -192,7 +208,7 @@ def remove_supernatant(vol: float) -> None: m1000.air_gap(20) m1000.drop_tip(tips_sn[8 * i]) if TIP_TRASH else m1000.return_tip() m1000.flow_rate.aspirate = 300 - helpers.move_labware_to_hs(ctx, sample_plate, h_s, h_s_adapter) + helpers.move_labware_to_hs(protocol, sample_plate, h_s, h_s_adapter) def bead_mixing( well: Well, pip: InstrumentContext, mvol: float, reps: int = 8 @@ -284,7 +300,7 @@ def mixing(well: Well, pip: InstrumentContext, mvol: float, reps: int = 8) -> No def A_lysis(vol: float, source: Well) -> None: """A Lysis.""" - ctx.comment("-----Mixing then transferring AL buffer-----") + protocol.comment("-----Mixing then transferring AL buffer-----") num_transfers = math.ceil(vol / 980) tiptrack(tips) for i in range(num_cols): @@ -303,7 +319,6 @@ def A_lysis(vol: float, source: Well) -> None: m1000.require_liquid_presence(src) m1000.aspirate(tvol, src.bottom(1)) m1000.dispense(tvol, src.bottom(4)) - m1000.require_liquid_presence(src) m1000.aspirate(tvol, src.bottom(height)) m1000.air_gap(10) m1000.dispense(m1000.current_volume, samples_m[i].top()) @@ -318,12 +333,12 @@ def A_lysis(vol: float, source: Well) -> None: m1000.air_gap(20) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - ctx.comment("-----Mixing then Heating AL and Sample-----") + protocol.comment("-----Mixing then Heating AL and Sample-----") - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, A_lysis_time_1, False) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, A_lysis_time_1, False) if not dry_run: h_s.set_and_wait_for_temperature(55) - ctx.delay( + protocol.delay( minutes=A_lysis_time_2, msg="Incubating at 55C " + str(heater_shaker_speed) @@ -347,17 +362,17 @@ def bind(vol: float) -> None: supernatant to the final clean elutions PCR plate. """ - ctx.comment("-----Beginning Bind Steps-----") + protocol.comment("-----Beginning Bind Steps-----") tiptrack(tips) for i, well in enumerate(samples_m): num_trans = math.ceil(vol / 980) vol_per_trans = vol / num_trans - source = binding_buffer[i // 3] + source = binding_buffer[i // 7] if i == 0: reps = 6 if not dry_run else 1 else: reps = 1 - ctx.comment("-----Mixing Beads in Reservoir-----") + protocol.comment("-----Mixing Beads in Reservoir-----") bead_mixing(source, m1000, vol_per_trans, reps=reps if not dry_run else 1) # Transfer beads and binding from source to H-S plate for t in range(num_trans): @@ -370,7 +385,7 @@ def bind(vol: float) -> None: if t < num_trans - 1: m1000.air_gap(20) - ctx.comment("-----Mixing Beads in Plate-----") + protocol.comment("-----Mixing Beads in Plate-----") for i in range(num_cols): if i != 0: tiptrack(tips) @@ -379,19 +394,19 @@ def bind(vol: float) -> None: ) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - ctx.comment("-----Incubating Beads and Bind on H-S-----") + protocol.comment("-----Incubating Beads and Bind on H-S-----") speed_val = heater_shaker_speed * 0.9 - helpers.set_hs_speed(ctx, h_s, speed_val, bind_time, True) + helpers.set_hs_speed(protocol, h_s, speed_val, bind_time, True) # Transfer from H-S plate to Magdeck plate helpers.move_labware_from_hs_to_destination( - ctx, sample_plate, h_s, magnetic_block + protocol, sample_plate, h_s, magnetic_block ) for bindi in np.arange( settling_time + 1, 0, -0.5 ): # Settling time delay with countdown timer - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(bindi) + " minutes left in the incubation.", ) @@ -410,29 +425,29 @@ def wash(vol: float, source: List[Well]) -> None: if source == wash3: whichwash = 3 - ctx.comment("-----Beginning Wash #" + str(whichwash) + "-----") + protocol.comment("-----Beginning Wash #" + str(whichwash) + "-----") num_trans = math.ceil(vol / 980) vol_per_trans = vol / num_trans tiptrack(tips) for i, m in enumerate(samples_m): - src = source[i // 2] + src = source[i // 4] for n in range(num_trans): if m1000.current_volume > 0: m1000.dispense(m1000.current_volume, src.top()) m1000.transfer(vol_per_trans, src, m.top(), air_gap=20, new_tip="never") m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, elute_wash_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, elute_wash_time, True) helpers.move_labware_from_hs_to_destination( - ctx, sample_plate, h_s, magnetic_block + protocol, sample_plate, h_s, magnetic_block ) for washi in np.arange( settling_time, 0, -0.5 ): # settling time timer for washes - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(washi) @@ -445,7 +460,7 @@ def wash(vol: float, source: List[Well]) -> None: def elute(vol: float) -> None: """Elution Function.""" - ctx.comment("-----Beginning Elution Steps-----") + protocol.comment("-----Beginning Elution Steps-----") tiptrack(tips) for i, (m, e) in enumerate(zip(samples_m, elution_samples_m)): m1000.flow_rate.aspirate = 25 @@ -457,15 +472,15 @@ def elute(vol: float) -> None: h_s.set_and_wait_for_shake_speed(heater_shaker_speed * 1.1) speed_val = heater_shaker_speed * 1.1 - helpers.set_hs_speed(ctx, h_s, speed_val, elute_wash_time, True) + helpers.set_hs_speed(protocol, h_s, speed_val, elute_wash_time, True) # Transfer back to magnet helpers.move_labware_from_hs_to_destination( - ctx, sample_plate, h_s, magnetic_block + protocol, sample_plate, h_s, magnetic_block ) for elutei in np.arange(settling_time, 0, -0.5): - ctx.delay( + protocol.delay( minutes=0.5, msg="Incubating on MagDeck for " + str(elutei) + " more minutes.", ) @@ -495,7 +510,7 @@ def elute(vol: float) -> None: else: drybeads = 0.5 for beaddry in np.arange(drybeads, 0, -0.5): - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(beaddry) + " minutes left in the drying step.", ) @@ -504,7 +519,9 @@ def elute(vol: float) -> None: # Probe wells end_wells_with_liquid = [ waste_reservoir.wells()[0], - res1.wells()[0], - elutionplate.wells()[0], ] - helpers.find_liquid_height_of_all_wells(ctx, m1000, end_wells_with_liquid) + m1000.tip_racks = [tips1004] + helpers.clean_up_plates(m1000, [res1, elutionplate], waste_reservoir["A1"], 1000) + helpers.find_liquid_height_of_all_wells(protocol, m1000, end_wells_with_liquid) + if deactivate_modules_bool: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/active_protocols/8_Illumina and Plate Reader.py b/abr-testing/abr_testing/protocols/active_protocols/8_Illumina and Plate Reader.py index 4894cae41d4..3d8c664956c 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/8_Illumina and Plate Reader.py +++ b/abr-testing/abr_testing/protocols/active_protocols/8_Illumina and Plate Reader.py @@ -1,5 +1,11 @@ """Illumina DNA Prep and Plate Reader Test.""" -from opentrons.protocol_api import ParameterContext, ProtocolContext, Labware +from opentrons.protocol_api import ( + ParameterContext, + ProtocolContext, + Labware, + Well, + InstrumentContext, +) from abr_testing.protocols import helpers from opentrons.protocol_api.module_contexts import ( AbsorbanceReaderContext, @@ -10,9 +16,10 @@ ) from datetime import datetime from opentrons.hardware_control.modules.types import ThermocyclerStep -from typing import List +from typing import List, Dict from opentrons import types + metadata = { "protocolName": "Illumina DNA Prep and Plate Reader Test", "author": "Platform Expansion", @@ -31,7 +38,7 @@ HYBRID_PAUSE = True # True = sets a pause on the Hybridization # PROTOCOL SETTINGS -COLUMNS = 3 # 1-3 +COLUMNS = 4 # 1-4 HYBRIDDECK = True HYBRIDTIME = 1.6 # Hours @@ -55,6 +62,8 @@ def add_parameters(parameters: ParameterContext) -> None: """Add Parameters.""" helpers.create_hs_speed_parameter(parameters) helpers.create_dot_bottom_parameter(parameters) + helpers.create_deactivate_modules_parameter(parameters) + helpers.create_plate_reader_compatible_labware_parameter(parameters) parameters.add_bool( variable_name="plate_orientation", display_name="Hellma Plate Orientation", @@ -103,6 +112,10 @@ def run(protocol: ProtocolContext) -> None: heater_shaker_speed = protocol.params.heater_shaker_speed # type: ignore[attr-defined] dot_bottom = protocol.params.dot_bottom # type: ignore[attr-defined] plate_orientation = protocol.params.plate_orientation # type: ignore[attr-defined] + deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined] + plate_type = protocol.params.labware_plate_reader_compatible # type: ignore [attr-defined] + helpers.comment_protocol_version(protocol, "01") + plate_name_str = "hellma_plate_" + str(plate_orientation) global p200_tips global p50_tips @@ -115,7 +128,9 @@ def run(protocol: ProtocolContext) -> None: tiprack_50_2 = protocol.load_labware("opentrons_flex_96_tiprack_50ul", "A3") # MODULES + LABWARE # Reservoir - reservoir = protocol.load_labware("nest_96_wellplate_2ml_deep", "D2") + reservoir = protocol.load_labware( + "nest_96_wellplate_2ml_deep", "D2", "Liquid Waste" + ) # Heatershaker heatershaker: HeaterShakerContext = protocol.load_module( helpers.hs_str, "D1" @@ -123,6 +138,7 @@ def run(protocol: ProtocolContext) -> None: sample_plate_2 = heatershaker.load_labware( "thermoscientificnunc_96_wellplate_1300ul" ) + heatershaker.close_labware_latch() # Magnetic Block mag_block: MagneticBlockContext = protocol.load_module( helpers.mag_str, "C1" @@ -133,6 +149,7 @@ def run(protocol: ProtocolContext) -> None: sample_plate_1 = thermocycler.load_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt" ) + thermocycler.open_lid() # Temperature Module temp_block: TemperatureModuleContext = protocol.load_module( helpers.temp_str, "B3" @@ -144,7 +161,7 @@ def run(protocol: ProtocolContext) -> None: plate_reader: AbsorbanceReaderContext = protocol.load_module( helpers.abs_mod_str, PLATE_READER_SLOT ) # type: ignore[assignment] - hellma_plate = protocol.load_labware("hellma_reference_plate", HELLMA_PLATE_SLOT) + hellma_plate = protocol.load_labware(plate_type, HELLMA_PLATE_SLOT) # PIPETTES p1000 = protocol.load_instrument( "flex_8channel_1000", @@ -154,6 +171,28 @@ def run(protocol: ProtocolContext) -> None: p50 = protocol.load_instrument( "flex_8channel_50", "right", tip_racks=[tiprack_50_1, tiprack_50_2] ) + + # Load liquids and probe + liquid_vols_and_wells: Dict[str, List[Dict[str, Well | List[Well] | float]]] = { + "Reagents": [ + {"well": reagent_plate.columns()[3], "volume": 75.0}, + {"well": reagent_plate.columns()[4], "volume": 15.0}, + {"well": reagent_plate.columns()[5], "volume": 20.0}, + {"well": reagent_plate.columns()[6], "volume": 65.0}, + ], + "AMPure": [{"well": reservoir.columns()[0], "volume": 120.0}], + "SMB": [{"well": reservoir.columns()[1], "volume": 750.0}], + "EtOH": [{"well": reservoir.columns()[3], "volume": 900.0}], + "RSB": [{"well": reservoir.columns()[4], "volume": 96.0}], + "Wash": [ + {"well": sample_plate_2.columns()[9], "volume": 1000.0}, + {"well": sample_plate_2.columns()[10], "volume": 1000.0}, + {"well": sample_plate_2.columns()[11], "volume": 1000.0}, + ], + "Samples": [{"well": sample_plate_1.wells(), "volume": 150.0}], + } + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, p50) + # reagent AMPure = reservoir["A1"] SMB = reservoir["A2"] @@ -165,11 +204,42 @@ def run(protocol: ProtocolContext) -> None: Liquid_trash_well_2 = reservoir["A10"] Liquid_trash_well_3 = reservoir["A11"] Liquid_trash_well_4 = reservoir["A12"] + liquid_trash_list = { + Liquid_trash_well_1: 0.0, + Liquid_trash_well_2: 0.0, + Liquid_trash_well_3: 0.0, + Liquid_trash_well_4: 0.0, + } + + def trash_liquid( + protocol: ProtocolContext, + pipette: InstrumentContext, + vol_to_trash: float, + liquid_trash_list: Dict[Well, float], + ) -> None: + """Determine which wells to use as liquid waste.""" + remaining_volume = vol_to_trash + max_capacity = 1500.0 + # Determine liquid waste location depending on current total volume + # Distribute the liquid volume sequentially + for well, current_volume in liquid_trash_list.items(): + if remaining_volume <= 0.0: + break + available_capacity = max_capacity - current_volume + if available_capacity < remaining_volume: + continue + pipette.dispense(remaining_volume, well.top()) + protocol.delay(minutes=0.1) + pipette.blow_out(well.top()) + liquid_trash_list[well] += remaining_volume + if pipette.current_volume <= 0.0: + break # Will Be distributed during the protocol - EEW_1 = sample_plate_2.wells_by_name()["A10"] - EEW_2 = sample_plate_2.wells_by_name()["A11"] - EEW_3 = sample_plate_2.wells_by_name()["A12"] + EEW_1 = sample_plate_2.wells_by_name()["A9"] + EEW_2 = sample_plate_2.wells_by_name()["A10"] + EEW_3 = sample_plate_2.wells_by_name()["A11"] + EEW_4 = sample_plate_2.wells_by_name()["A12"] NHB2 = reagent_plate.wells_by_name()["A1"] Panel = reagent_plate.wells_by_name()["A2"] @@ -206,6 +276,14 @@ def run(protocol: ProtocolContext) -> None: column_5_list = ["A7", "A8", "A9"] # Plate 2 column_6_list = ["A7", "A8", "A9"] # Plate 1 WASHES = [EEW_1, EEW_2, EEW_3] + if COLUMNS == 4: + column_1_list = ["A1", "A2", "A3", "A4"] # Plate 1 + column_2_list = ["A1", "A2", "A3", "A4"] # Plate 2 + column_3_list = ["A5", "A6", "A7", "A8"] # Plate 2 + column_4_list = ["A5", "A6", "A7", "A8"] # Plate 1 + column_5_list = ["A9", "A10", "A11", "A12"] # Plate 2 + column_6_list = ["A9", "A10", "A11", "A12"] # Plate 1 + WASHES = [EEW_1, EEW_2, EEW_3, EEW_4] def tipcheck() -> None: """Check tips.""" @@ -231,9 +309,7 @@ def tipcheck() -> None: thermocycler.set_block_temperature(58) thermocycler.set_lid_temperature(58) heatershaker.set_and_wait_for_temperature(58) - protocol.pause("Ready") heatershaker.close_labware_latch() - Liquid_trash = Liquid_trash_well_1 # Sample Plate contains 30ul of DNA @@ -409,20 +485,14 @@ def tipcheck() -> None: p1000.pick_up_tip() p1000.move_to(sample_plate_2[X].bottom(4)) p1000.aspirate(200, rate=0.25) - p1000.dispense(200, Liquid_trash.top(z=-7)) + trash_liquid(protocol, p1000, 200, liquid_trash_list) p1000.move_to(sample_plate_2[X].bottom(0.5)) p1000.aspirate(200, rate=0.25) - p1000.dispense(200, Liquid_trash.top(z=-7)) - p1000.move_to(Liquid_trash.top(z=-7)) - protocol.delay(minutes=0.1) - p1000.blow_out(Liquid_trash.top(z=-7)) - p1000.aspirate(20) + trash_liquid(protocol, p1000, 200, liquid_trash_list) p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() p200_tips += 1 tipcheck() - Liquid_trash = Liquid_trash_well_2 - # ============================================================================================ # GRIPPER MOVE sample_plate_2 FROM MAGPLATE TO heatershaker helpers.move_labware_to_hs( @@ -469,9 +539,6 @@ def tipcheck() -> None: if DRYRUN is False: protocol.delay(seconds=1 * 60) - if washcount > 2: - Liquid_trash = Liquid_trash_well_3 - protocol.comment("--> Removing Supernatant") RemoveSup = 200 for loop, X in enumerate(column_2_list): @@ -482,10 +549,7 @@ def tipcheck() -> None: p1000.move_to(sample_plate_2[X].bottom(z=0.5)) p1000.aspirate(100, rate=0.25) p1000.move_to(sample_plate_2[X].top(z=0.5)) - p1000.dispense(200, Liquid_trash.top(z=-7)) - protocol.delay(minutes=0.1) - p1000.blow_out(Liquid_trash.top(z=-7)) - p1000.aspirate(20) + trash_liquid(protocol, p1000, 200, liquid_trash_list) p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() p200_tips += 1 tipcheck() @@ -554,10 +618,7 @@ def tipcheck() -> None: p1000.move_to(sample_plate_2[X].bottom(z=0.5)) p1000.aspirate(100, rate=0.25) p1000.move_to(sample_plate_2[X].top(z=0.5)) - p1000.dispense(200, Liquid_trash.top(z=-7)) - protocol.delay(minutes=0.1) - p1000.blow_out(Liquid_trash.top(z=-7)) - p1000.aspirate(20) + trash_liquid(protocol, p1000, 200, liquid_trash_list) p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() p200_tips += 1 tipcheck() @@ -568,12 +629,7 @@ def tipcheck() -> None: p50.move_to(sample_plate_2[X].bottom(z=dot_bottom)) # original = z=0 p50.aspirate(50, rate=0.25) p50.default_speed = 200 - p50.dispense(50, Liquid_trash.top(z=-7)) - protocol.delay(minutes=0.1) - p50.blow_out() - p50.default_speed = 400 - p50.move_to(Liquid_trash.top(z=-7)) - p50.move_to(Liquid_trash.top(z=0)) + trash_liquid(protocol, p50, 50, liquid_trash_list) p50.return_tip() if TIP_TRASH is False else p50.drop_tip() p50_tips += 1 tipcheck() @@ -722,8 +778,6 @@ def tipcheck() -> None: p50_tips += 1 tipcheck() - Liquid_trash = Liquid_trash_well_4 - protocol.comment("--> ADDING AMPure (0.8x)") AMPureVol = 40.5 AMPureMixRep = 5 * 60 if DRYRUN is False else 0.1 * 60 @@ -777,12 +831,7 @@ def tipcheck() -> None: p1000.default_speed = 5 p1000.move_to(sample_plate_2[X].top(z=2)) p1000.default_speed = 200 - p1000.dispense(200, Liquid_trash.top(z=-7)) - protocol.delay(minutes=0.1) - p1000.blow_out() - p1000.default_speed = 400 - p1000.move_to(Liquid_trash.top(z=-7)) - p1000.move_to(Liquid_trash.top(z=0)) + trash_liquid(protocol, p1000, 200, liquid_trash_list) p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() p200_tips += 1 tipcheck() @@ -821,12 +870,7 @@ def tipcheck() -> None: p1000.default_speed = 5 p1000.move_to(sample_plate_2[X].top(z=2)) p1000.default_speed = 200 - p1000.dispense(200, Liquid_trash.top(z=-7)) - protocol.delay(minutes=0.1) - p1000.blow_out() - p1000.default_speed = 400 - p1000.move_to(Liquid_trash.top(z=-7)) - p1000.move_to(Liquid_trash.top(z=0)) + trash_liquid(protocol, p1000, 200, liquid_trash_list) p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() p200_tips += 1 tipcheck() @@ -842,12 +886,7 @@ def tipcheck() -> None: ) # original = (z=0) p1000.aspirate(50, rate=0.25) p1000.default_speed = 200 - p1000.dispense(50, Liquid_trash.top(z=-7)) - protocol.delay(minutes=0.1) - p1000.blow_out() - p1000.default_speed = 400 - p1000.move_to(Liquid_trash.top(z=-7)) - p1000.move_to(Liquid_trash.top(z=0)) + trash_liquid(protocol, p1000, 50, liquid_trash_list) p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() p200_tips += 1 tipcheck() @@ -945,4 +984,13 @@ def tipcheck() -> None: p1000.return_tip() if TIP_TRASH is False else p1000.drop_tip() p200_tips += 1 tipcheck() + liquids_to_probe_at_end = [ + Liquid_trash_well_1, + Liquid_trash_well_2, + Liquid_trash_well_3, + Liquid_trash_well_4, + ] + helpers.find_liquid_height_of_all_wells(protocol, p50, liquids_to_probe_at_end) plate_reader_actions(protocol, plate_reader, hellma_plate, plate_name_str) + if deactivate_modules_bool: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/active_protocols/9_Magmax_RNA_Cells_Flex.py b/abr-testing/abr_testing/protocols/active_protocols/9_Magmax_RNA_Cells_Flex.py index 09201e58314..c44e8111490 100644 --- a/abr-testing/abr_testing/protocols/active_protocols/9_Magmax_RNA_Cells_Flex.py +++ b/abr-testing/abr_testing/protocols/active_protocols/9_Magmax_RNA_Cells_Flex.py @@ -24,7 +24,7 @@ } requirements = { - "robotType": "OT-3", + "robotType": "Flex", "apiLevel": "2.21", } """ @@ -44,15 +44,17 @@ Reservoir 1: Well 1 - 8120 ul -Well 2 - 6400 ul -Well 3-7 - 8550 ul +Well 2 - 8120 ul +Well 3 - 12800 ul +Well 4-12 - 9500 ul + """ -whichwash = 1 -sample_max = 48 -tip = 0 +whichwash = 0 +tip_pick_up = 0 drop_count = 0 waste_vol = 0 +wash_volume_tracker = 0.0 # Start protocol @@ -61,22 +63,25 @@ def add_parameters(parameters: ParameterContext) -> None: helpers.create_dot_bottom_parameter(parameters) helpers.create_single_pipette_mount_parameter(parameters) helpers.create_hs_speed_parameter(parameters) + helpers.create_deactivate_modules_parameter(parameters) -def run(ctx: ProtocolContext) -> None: +def run(protocol: ProtocolContext) -> None: """Protocol.""" dry_run = False inc_lysis = True res_type = "nest_12_reservoir_15ml" TIP_TRASH = False - num_samples = 48 + num_samples = 96 wash_vol = 150.0 lysis_vol = 140.0 stop_vol = 100.0 - elution_vol = dnase_vol = 50.0 - heater_shaker_speed = ctx.params.heater_shaker_speed # type: ignore[attr-defined] - dot_bottom = ctx.params.dot_bottom # type: ignore[attr-defined] - pipette_mount = ctx.params.pipette_mount # type: ignore[attr-defined] + elution_vol = dnase_vol = 55.0 + heater_shaker_speed = protocol.params.heater_shaker_speed # type: ignore[attr-defined] + dot_bottom = protocol.params.dot_bottom # type: ignore[attr-defined] + pipette_mount = protocol.params.pipette_mount # type: ignore[attr-defined] + deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined] + helpers.comment_protocol_version(protocol, "01") # Protocol Parameters deepwell_type = "nest_96_wellplate_2ml_deep" @@ -93,63 +98,67 @@ def run(ctx: ProtocolContext) -> None: drybeads = elute_time = 0.25 bind_time = wash_time = dnase_time = stop_time = 0.25 bead_vol = 20.0 - ctx.load_trash_bin("A3") - h_s: HeaterShakerContext = ctx.load_module(helpers.hs_str, "D1") # type: ignore[assignment] + protocol.load_trash_bin("A3") + h_s: HeaterShakerContext = protocol.load_module( + helpers.hs_str, "D1" + ) # type: ignore[assignment] sample_plate, h_s_adapter = helpers.load_hs_adapter_and_labware( deepwell_type, h_s, "Sample Plate" ) h_s.close_labware_latch() - temp: TemperatureModuleContext = ctx.load_module( + temp: TemperatureModuleContext = protocol.load_module( helpers.temp_str, "D3" ) # type: ignore[assignment] elutionplate, temp_adapter = helpers.load_temp_adapter_and_labware( "armadillo_96_wellplate_200ul_pcr_full_skirt", temp, "Elution Plate" ) temp.set_temperature(4) - magblock: MagneticBlockContext = ctx.load_module( + magblock: MagneticBlockContext = protocol.load_module( helpers.mag_str, "C1" ) # type: ignore[assignment] - waste_reservoir = ctx.load_labware("nest_1_reservoir_195ml", "B3", "Liquid Waste") + waste_reservoir = protocol.load_labware( + "nest_1_reservoir_195ml", "B3", "Liquid Waste" + ) waste = waste_reservoir.wells()[0].top() - res1 = ctx.load_labware(res_type, "D2", "reagent reservoir 1") + res1 = protocol.load_labware(res_type, "D2", "reagent reservoir 1") num_cols = math.ceil(num_samples / 8) # Load tips and combine all similar boxes - tips200 = ctx.load_labware("opentrons_flex_96_tiprack_200ul", "A1", "Tips 1") - tips201 = ctx.load_labware("opentrons_flex_96_tiprack_200ul", "A2", "Tips 2") - tips202 = ctx.load_labware("opentrons_flex_96_tiprack_200ul", "B1", "Tips 3") - tips203 = ctx.load_labware("opentrons_flex_96_tiprack_200ul", "B2", "Tips 4") - tips = [ - *tips200.wells()[num_samples:96], - *tips201.wells(), - *tips202.wells(), - *tips203.wells(), - ] + tips200 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "A1", "Tips 1") + tips201 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "A2", "Tips 2") + tips202 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "B1", "Tips 3") + tips203 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "B2", "Tips 4") + tips204 = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "C2", "Tips 5") + tips_sn = tips200.wells()[:num_samples] # load P1000M pipette - m1000 = ctx.load_instrument( + m1000 = protocol.load_instrument( "flex_8channel_1000", pipette_mount, - tip_racks=[tips200, tips201, tips202, tips203], + tip_racks=[tips200, tips201, tips202, tips203, tips204], ) # Load Liquid Locations in Reservoir elution_solution = elutionplate.rows()[0][:num_cols] - dnase1 = elutionplate.rows()[0][num_cols : 2 * num_cols] - lysis_ = res1.wells()[0] - stopreaction = res1.wells()[1] - wash1 = res1.wells()[2] - wash2 = res1.wells()[3] - wash3 = res1.wells()[4] - wash4 = res1.wells()[5] - wash5 = res1.wells()[6] - + dnase1 = elutionplate.rows()[0][:num_cols] + lysis_ = res1.wells()[0:2] + stopreaction = res1.wells()[2] + wash1 = res1.wells()[3] + wash2 = res1.wells()[4] + wash3 = res1.wells()[5] + wash4 = res1.wells()[6] + wash5 = res1.wells()[7] + wash6 = res1.wells()[8] + wash7 = res1.wells()[9] + wash8 = res1.wells()[10] + wash9 = res1.wells()[11] + all_washes = res1.wells()[3:12] """ Here is where you can define the locations of your reagents. """ samples_m = sample_plate.rows()[0][:num_cols] # 20ul beads each well - cells_m = sample_plate.rows()[0][num_cols : 2 * num_cols] + cells_m = sample_plate.rows()[0][:num_cols] elution_samples_m = elutionplate.rows()[0][:num_cols] # Do the same for color mapping beads_ = sample_plate.wells()[: (8 * num_cols)] @@ -163,36 +172,42 @@ def run(ctx: ProtocolContext) -> None: "Sample": [{"well": cells_, "volume": 0.0}], "DNAse": [{"well": dnase1_, "volume": dnase_vol}], "Elution Buffer": [{"well": elution_samps, "volume": elution_vol}], - "Lysis": [{"well": lysis_, "volume": lysis_vol}], - "Wash 1": [{"well": wash1, "volume": wash_vol}], - "Wash 2": [{"well": wash2, "volume": wash_vol}], - "Wash 3": [{"well": wash3, "volume": wash_vol}], - "Wash 4": [{"well": wash4, "volume": wash_vol}], - "Wash 5": [{"well": wash5, "volume": wash_vol}], - "Stop": [{"well": stopreaction, "volume": stop_vol}], + "Lysis": [{"well": lysis_, "volume": 8120.0}], + "Stop": [{"well": stopreaction, "volume": 6400.0}], + "Wash 1": [{"well": wash1, "volume": 9500.0}], + "Wash 2": [{"well": wash2, "volume": 9500.0}], + "Wash 3": [{"well": wash3, "volume": 9500.0}], + "Wash 4": [{"well": wash4, "volume": 9500.0}], + "Wash 5": [{"well": wash5, "volume": 9500.0}], + "Wash 6": [{"well": wash6, "volume": 9500.0}], + "Wash 7": [{"well": wash7, "volume": 9500.0}], + "Wash 8": [{"well": wash8, "volume": 9500.0}], + "Wash 9": [{"well": wash9, "volume": 9500.0}], } - helpers.find_liquid_height_of_loaded_liquids(ctx, liquid_vols_and_wells, m1000) + helpers.find_liquid_height_of_loaded_liquids(protocol, liquid_vols_and_wells, m1000) m1000.flow_rate.aspirate = 50 m1000.flow_rate.dispense = 150 m1000.flow_rate.blow_out = 300 - def tiptrack(pip: InstrumentContext, tipbox: List[Well]) -> None: + def tiptrack(pip: InstrumentContext) -> None: """Tip Track.""" - global tip + global tip_pick_up global drop_count - pip.pick_up_tip(tipbox[int(tip)]) - tip = tip + 8 + pip.pick_up_tip() + tip_pick_up += 1 drop_count = drop_count + 8 if drop_count >= 250: drop_count = 0 if TIP_TRASH: - ctx.pause("Empty Trash bin.") + protocol.pause("Empty Trash bin.") + if tip_pick_up >= 59: + pip.reset_tipracks() def remove_supernatant(vol: float) -> None: """Remove Supernatant.""" - ctx.comment("-----Removing Supernatant-----") + protocol.comment("-----Removing Supernatant-----") m1000.flow_rate.aspirate = 30 num_trans = math.ceil(vol / 180) vol_per_trans = vol / num_trans @@ -211,7 +226,7 @@ def remove_supernatant(vol: float) -> None: m1000.drop_tip(tips_sn[8 * i]) if TIP_TRASH else m1000.return_tip() m1000.flow_rate.aspirate = 300 # Move Plate From Magnet to H-S - helpers.move_labware_to_hs(ctx, sample_plate, h_s, h_s_adapter) + helpers.move_labware_to_hs(protocol, sample_plate, h_s, h_s_adapter) def bead_mixing( well: Well, pip: InstrumentContext, mvol: float, reps: int = 8 @@ -301,32 +316,39 @@ def mixing(well: Well, pip: InstrumentContext, mvol: float, reps: int = 8) -> No pip.flow_rate.aspirate = 300 pip.flow_rate.dispense = 300 - def lysis(vol: float, source: Well) -> None: + def lysis(vol: float, source: List[Well]) -> None: """Lysis Steps.""" - ctx.comment("-----Beginning lysis steps-----") + tvol_total = 0.0 + protocol.comment("-----Beginning lysis steps-----") num_transfers = math.ceil(vol / 180) - tiptrack(m1000, tips) + tiptrack(m1000) + src = source[0] for i in range(num_cols): - src = source tvol = vol / num_transfers for t in range(num_transfers): m1000.require_liquid_presence(src) m1000.aspirate(tvol, src.bottom(1)) m1000.dispense(m1000.current_volume, cells_m[i].top(-3)) + tvol_total += tvol * 8 + if tvol_total > 8000.0: + protocol.comment("-----Changing to second lysis well.------") + src = source[1] + protocol.comment(f"new source {src}") + tvol_total = 0.0 # mix after adding all reagent to wells with cells for i in range(num_cols): if i != 0: - tiptrack(m1000, tips) + tiptrack(m1000) for x in range(8 if not dry_run else 1): m1000.aspirate(tvol * 0.75, cells_m[i].bottom(dot_bottom)) m1000.dispense(tvol * 0.75, cells_m[i].bottom(8)) if x == 3: - ctx.delay(minutes=0.0167) + protocol.delay(minutes=0.0167) m1000.blow_out(cells_m[i].bottom(1)) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, lysis_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, lysis_time, True) def bind() -> None: """Bind. @@ -344,10 +366,10 @@ def bind() -> None: supernatant to the final clean elutions PCR plate. """ - ctx.comment("-----Beginning bind steps-----") + protocol.comment("-----Beginning bind steps-----") for i, well in enumerate(samples_m): # Transfer cells+lysis/bind to wells with beads - tiptrack(m1000, tips) + tiptrack(m1000) m1000.aspirate(185, cells_m[i].bottom(dot_bottom)) m1000.air_gap(10) m1000.dispense(m1000.current_volume, well.bottom(8)) @@ -355,15 +377,17 @@ def bind() -> None: bead_mixing(well, m1000, 130, reps=5 if not dry_run else 1) m1000.air_gap(10) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, bind_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, bind_time, True) # Transfer from H-S plate to Magdeck plate - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) for bindi in np.arange( settling_time, 0, -0.5 ): # Settling time delay with countdown timer - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(bindi) + " minutes left in the incubation.", ) @@ -371,45 +395,43 @@ def bind() -> None: # remove initial supernatant remove_supernatant(180) - def wash(vol: float, source: Well) -> None: + def wash(vol: float, source: List[Well]) -> None: """Wash Function.""" global whichwash # Defines which wash the protocol is on to log on the app - - if source == wash1: - whichwash = 1 - if source == wash2: - whichwash = 2 - if source == wash3: - whichwash = 3 - if source == wash4: - whichwash = 4 - - ctx.comment("-----Now starting Wash #" + str(whichwash) + "-----") - - tiptrack(m1000, tips) + protocol.comment("-----Now starting Wash #" + str(whichwash) + "-----") + global wash_volume_tracker + tiptrack(m1000) num_trans = math.ceil(vol / 180) vol_per_trans = vol / num_trans for i, m in enumerate(samples_m): - src = source + src = source[whichwash] for n in range(num_trans): m1000.aspirate(vol_per_trans, src) m1000.air_gap(10) m1000.dispense(m1000.current_volume, m.top(-2)) - ctx.delay(seconds=2) + protocol.delay(seconds=2) m1000.blow_out(m.top(-2)) + wash_volume_tracker += vol_per_trans * 8 + if wash_volume_tracker > 9600: + whichwash += 1 + src = source[whichwash] + protocol.comment(f"new wash source {whichwash}") + wash_volume_tracker = 0.0 m1000.air_gap(10) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() # Shake for 5 minutes to mix wash with beads - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, wash_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, wash_time, True) # Transfer from H-S plate to Magdeck plate - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) for washi in np.arange( settling_time, 0, -0.5 ): # settling time timer for washes - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(washi) @@ -419,13 +441,14 @@ def wash(vol: float, source: Well) -> None: ) remove_supernatant(vol) + protocol.comment(f"final wash source {whichwash}") def dnase(vol: float, source: List[Well]) -> None: """Steps for DNAseI.""" - ctx.comment("-----DNAseI Steps Beginning-----") + protocol.comment("-----DNAseI Steps Beginning-----") num_trans = math.ceil(vol / 180) vol_per_trans = vol / num_trans - tiptrack(m1000, tips) + tiptrack(m1000) for i, m in enumerate(samples_m): src = source[i] m1000.flow_rate.aspirate = 10 @@ -442,17 +465,17 @@ def dnase(vol: float, source: List[Well]) -> None: # Is this mixing needed? \/\/\/ for i in range(num_cols): if i != 0: - tiptrack(m1000, tips) + tiptrack(m1000) mixing(samples_m[i], m1000, 45, reps=5 if not dry_run else 1) m1000.drop_tip() if TIP_TRASH else m1000.return_tip() # Shake for 10 minutes to mix DNAseI - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, dnase_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, dnase_time, True) def stop_reaction(vol: float, source: Well) -> None: """Adding stop solution.""" - ctx.comment("-----Adding Stop Solution-----") - tiptrack(m1000, tips) + protocol.comment("-----Adding Stop Solution-----") + tiptrack(m1000) num_trans = math.ceil(vol / 180) vol_per_trans = vol / num_trans for i, m in enumerate(samples_m): @@ -467,13 +490,15 @@ def stop_reaction(vol: float, source: Well) -> None: m1000.drop_tip() if TIP_TRASH else m1000.return_tip() # Shake for 3 minutes to mix wash with beads - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, stop_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, stop_time, True) # Transfer from H-S plate to Magdeck plate - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) for stop in np.arange(settling_time, 0, -0.5): - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(stop) + " minutes left in this incubation.", ) @@ -482,8 +507,8 @@ def stop_reaction(vol: float, source: Well) -> None: def elute(vol: float) -> None: """Elution.""" - ctx.comment("-----Elution Beginning-----") - tiptrack(m1000, tips) + protocol.comment("-----Elution Beginning-----") + tiptrack(m1000) m1000.flow_rate.aspirate = 10 for i, m in enumerate(samples_m): loc = m.top(-2) @@ -498,7 +523,7 @@ def elute(vol: float) -> None: # Is this mixing needed? \/\/\/ for i in range(num_cols): if i != 0: - tiptrack(m1000, tips) + tiptrack(m1000) for mixes in range(10): m1000.aspirate(elution_vol - 10, samples_m[i]) m1000.dispense(elution_vol - 10, samples_m[i].bottom(10)) @@ -510,20 +535,22 @@ def elute(vol: float) -> None: m1000.drop_tip() if TIP_TRASH else m1000.return_tip() # Shake for 3 minutes to mix wash with beads - helpers.set_hs_speed(ctx, h_s, heater_shaker_speed, elute_time, True) + helpers.set_hs_speed(protocol, h_s, heater_shaker_speed, elute_time, True) # Transfer from H-S plate to Magdeck plate - helpers.move_labware_from_hs_to_destination(ctx, sample_plate, h_s, magblock) + helpers.move_labware_from_hs_to_destination( + protocol, sample_plate, h_s, magblock + ) for elutei in np.arange(settling_time, 0, -0.5): - ctx.delay( + protocol.delay( minutes=0.5, msg="Incubating on MagDeck for " + str(elutei) + " more minutes.", ) - ctx.comment("-----Trasnferring Sample to Elution Plate-----") + protocol.comment("-----Trasnferring Sample to Elution Plate-----") for i, (m, e) in enumerate(zip(samples_m, elution_samples_m)): - tiptrack(m1000, tips) + tiptrack(m1000) loc = m.bottom(dot_bottom) m1000.transfer(vol, loc, e.bottom(5), air_gap=20, new_tip="never") m1000.blow_out(e.top(-2)) @@ -537,23 +564,27 @@ def elute(vol: float) -> None: if inc_lysis: lysis(lysis_vol, lysis_) bind() - wash(wash_vol, wash1) - wash(wash_vol, wash2) + wash(wash_vol, all_washes) + wash(wash_vol, all_washes) + wash(wash_vol, all_washes) # dnase1 treatment dnase(dnase_vol, dnase1) stop_reaction(stop_vol, stopreaction) # Resume washes - wash(wash_vol, wash3) - wash(wash_vol, wash4) - wash(wash_vol, wash5) + wash(wash_vol, all_washes) + wash(wash_vol, all_washes) + wash(wash_vol, all_washes) for beaddry in np.arange(drybeads, 0, -0.5): - ctx.delay( + protocol.delay( minutes=0.5, msg="There are " + str(beaddry) + " minutes left in the drying step.", ) elute(elution_vol) - - end_list_of_wells_to_probe = [waste_reservoir["A1"], res1["A1"]] - end_list_of_wells_to_probe.extend(elution_samples_m) - helpers.find_liquid_height_of_all_wells(ctx, m1000, end_list_of_wells_to_probe) + end_list_of_wells_to_probe = [waste_reservoir["A1"]] + helpers.clean_up_plates( + m1000, [elutionplate, sample_plate], waste_reservoir["A1"], 200 + ) + helpers.find_liquid_height_of_all_wells(protocol, m1000, end_list_of_wells_to_probe) + if deactivate_modules_bool: + helpers.deactivate_modules(protocol) diff --git a/abr-testing/abr_testing/protocols/csv_parameters/1_samplevols.csv b/abr-testing/abr_testing/protocols/csv_parameters/1_samplevols.csv new file mode 100644 index 00000000000..132b4dc70fb --- /dev/null +++ b/abr-testing/abr_testing/protocols/csv_parameters/1_samplevols.csv @@ -0,0 +1,97 @@ +Well,Dye,Diluent +A1,0,100 +B1,5,95 +C1,10,90 +D1,20,80 +E1,40,60 +F1,15,40 +G1,40,20 +H1,40,0 +A2,35,65 +B2,38,42 +C2,42,58 +D2,32,8 +E2,38,12 +F2,26,74 +G2,31,69 +H2,46,4 +A3,47,13 +B3,42,18 +C3,46,64 +D3,48,22 +E3,26,74 +F3,34,66 +G3,43,37 +H3,20,80 +A4,44,16 +B4,49,41 +C4,48,42 +D4,44,16 +E4,47,53 +F4,47,33 +G4,42,48 +H4,39,21 +A5,30,20 +B5,36,14 +C5,31,59 +D5,38,52 +E5,36,4 +F5,32,28 +G5,35,55 +H5,39,1 +A6,31,59 +B6,20,80 +C6,38,2 +D6,34,46 +E6,30,70 +F6,32,58 +G6,21,79 +H6,38,52 +A7,33,27 +B7,34,16 +C7,40,60 +D7,34,26 +E7,30,20 +F7,44,56 +G7,26,74 +H7,45,55 +A8,39,1 +B8,38,2 +C8,34,66 +D8,39,11 +E8,46,54 +F8,37,63 +G8,38,42 +H8,34,66 +A9,44,56 +B9,39,11 +C9,30,70 +D9,37,33 +E9,46,54 +F9,39,21 +G9,29,41 +H9,23,77 +A10,26,74 +B10,39,1 +C10,31,49 +D10,38,62 +E10,29,1 +F10,21,79 +G10,29,41 +H10,28,42 +A11,15,55 +B11,28,72 +C11,11,49 +D11,34,66 +E11,27,73 +F11,30,40 +G11,33,67 +H11,31,39 +A12,39,31 +B12,47,53 +C12,46,54 +D12,13,7 +E12,34,46 +F12,45,35 +G12,28,42 +H12,37,63 \ No newline at end of file diff --git a/abr-testing/abr_testing/protocols/csv_parameters/2_samplevols.csv b/abr-testing/abr_testing/protocols/csv_parameters/2_samplevols.csv index fa50562e68b..424aae072c3 100644 --- a/abr-testing/abr_testing/protocols/csv_parameters/2_samplevols.csv +++ b/abr-testing/abr_testing/protocols/csv_parameters/2_samplevols.csv @@ -6,20 +6,92 @@ D1,3,7,40,A1 E1,2,8,40,A2 F1,1,9,40,A2 G1,5,5,40,A2 -H1,3,7,40,A3 +H1,3,7,40,A2 A2,3,7,40,A3 B2,3,7,40,A3 C2,3,7,40,A3 D2,3,7,40,A3 -E2,3,7,40,A3 -F2,3,7,40,A3 -G2,3,7,40,A3 -H2,3,7,40,A3 -A3,3,7,40,A3 -B3,3,7,40,A3 -C3,3,7,45,A3 -D3,3,7,45,A3 -E3,3,5,45,A3 -F3,3,5,45,A3 +E2,3,7,40,A4 +F2,3,7,40,A4 +G2,3,7,40,A4 +H2,3,7,40,A4 +A3,3,7,40,A5 +B3,3,7,40,A5 +C3,3,7,45,A5 +D3,3,7,45,A5 +E3,3,5,45,A6 +F3,3,5,45,A6 G3,3,5,45,A6 -H3,3,4,45,A5 \ No newline at end of file +H3,3,4,45,A6 +A4,3,7,40,A1 +B4,0,10,40,A1 +C4,10,0,40,A1 +D4,3,7,40,A1 +E4,2,8,40,A2 +F4,1,9,40,A2 +G4,5,5,40,A2 +H4,3,7,40,A2 +A5,3,7,40,A3 +B5,3,7,40,A3 +C5,3,7,40,A3 +D5,3,7,40,A3 +E5,3,7,40,A4 +F5,3,7,40,A4 +G5,3,7,40,A4 +H5,3,7,40,A4 +A6,3,7,40,A5 +B6,3,7,40,A5 +C6,3,7,45,A5 +D6,3,7,45,A5 +E6,3,5,45,A6 +F6,3,5,45,A6 +G6,3,5,45,A6 +H6,3,4,45,A6 +A7,3,7,40,A1 +B7,0,10,40,A1 +C7,10,0,40,A1 +D7,3,7,40,A1 +E7,2,8,40,A2 +F7,1,9,40,A2 +G7,5,5,40,A2 +H7,3,7,40,A2 +A8,3,7,40,A3 +B8,3,7,40,A3 +C8,3,7,40,A3 +D8,3,7,40,A3 +E8,3,7,40,A4 +F8,3,7,40,A4 +G8,3,7,40,A4 +H8,3,7,40,A4 +A9,3,7,40,A5 +B9,3,7,40,A5 +C9,3,7,45,A5 +D9,3,7,45,A5 +E9,3,5,45,A6 +F9,3,5,45,A6 +G9,3,5,45,A6 +H9,3,4,45,A6 +A10,3,7,40,A1 +B10,0,10,40,A1 +C10,10,0,40,A1 +D10,3,7,40,A1 +E10,2,8,40,A2 +F10,1,9,40,A2 +G10,5,5,40,A2 +H10,3,7,40,A2 +A11,3,7,40,A3 +B11,3,7,40,A3 +C11,3,7,40,A3 +D11,3,7,40,A3 +E11,3,7,40,A4 +F11,3,7,40,A4 +G11,3,7,40,A4 +H11,3,7,40,A4 +A12,3,7,40,A5 +B12,3,7,40,A5 +C12,3,7,45,A5 +D12,3,7,45,A5 +E12,3,5,45,A6 +F12,3,5,45,A6 +G12,3,5,45,A6 +H12,3,4,45,A6 diff --git a/abr-testing/abr_testing/protocols/helpers.py b/abr-testing/abr_testing/protocols/helpers.py index 12abbfa9b3f..31a1d1a9244 100644 --- a/abr-testing/abr_testing/protocols/helpers.py +++ b/abr-testing/abr_testing/protocols/helpers.py @@ -7,14 +7,15 @@ ParameterContext, Well, ) -from typing import Tuple from opentrons.protocol_api.module_contexts import ( HeaterShakerContext, MagneticBlockContext, ThermocyclerContext, TemperatureModuleContext, + MagneticModuleContext, + AbsorbanceReaderContext, ) -from typing import List, Union, Dict +from typing import List, Union, Dict, Tuple from opentrons.hardware_control.modules.types import ThermocyclerStep from opentrons_shared_data.errors.exceptions import PipetteLiquidNotFoundError @@ -106,7 +107,62 @@ def load_temp_adapter_and_labware( return labware_on_temp_mod, temp_adapter +# FUNCTIONS FOR COMMON COMMENTS + + +def comment_protocol_version(protocol: ProtocolContext, version: str) -> None: + """Comment version number of protocol.""" + protocol.comment(f"Protocol Version: {version}") + + # FUNCTIONS FOR LOADING COMMON PARAMETERS +def create_channel_parameter(parameters: ParameterContext) -> None: + """Create pipette channel parameter.""" + parameters.add_str( + variable_name="channels", + display_name="Number of Pipette Channels", + choices=[ + {"display_name": "1 Channel", "value": "1channel"}, + {"display_name": "8 Channel", "value": "8channel"}, + ], + default="8channel", + ) + + +def create_pipette_parameters(parameters: ParameterContext) -> None: + """Create parameter for pipettes.""" + # NOTE: Place function inside def add_parameters(parameters) in protocol. + # NOTE: Copy ctx.params.left mount, ctx.params.right_mount # type: ignore[attr-defined] + # to get result + # Left Mount + parameters.add_str( + variable_name="left_mount", + display_name="Left Mount", + description="Pipette Type on Left Mount.", + choices=[ + {"display_name": "8ch 50ul", "value": "flex_8channel_50"}, + {"display_name": "8ch 1000ul", "value": "flex_8channel_1000"}, + {"display_name": "1ch 50ul", "value": "flex_1channel_50"}, + {"display_name": "1ch 1000ul", "value": "flex_1channel_1000"}, + {"display_name": "96ch 1000ul", "value": "flex_96channel_1000"}, + {"display_name": "None", "value": "none"}, + ], + default="flex_8channel_1000", + ) + # Right Mount + parameters.add_str( + variable_name="right_mount", + display_name="Right Mount", + description="Pipette Type on Right Mount.", + choices=[ + {"display_name": "8ch 50ul", "value": "flex_8channel_50"}, + {"display_name": "8ch 1000ul", "value": "flex_8channel_1000"}, + {"display_name": "1ch 50ul", "value": "flex_1channel_50"}, + {"display_name": "1ch 1000ul", "value": "flex_1channel_1000"}, + {"display_name": "None", "value": "none"}, + ], + default="none", + ) def create_single_pipette_mount_parameter(parameters: ParameterContext) -> None: @@ -163,6 +219,16 @@ def create_disposable_lid_parameter(parameters: ParameterContext) -> None: ) +def create_disposable_lid_trash_location(parameters: ParameterContext) -> None: + """Create a parameter for lid placement after use.""" + parameters.add_bool( + variable_name="trash_lid", + display_name="Trash Disposable Lid", + description="True means trash lid, false means keep on deck.", + default=True, + ) + + def create_tc_lid_deck_riser_parameter(parameters: ParameterContext) -> None: """Create parameter for tc lid deck riser.""" parameters.add_bool( @@ -184,7 +250,7 @@ def create_tip_size_parameter(parameters: ParameterContext) -> None: {"display_name": "200 µL", "value": "opentrons_flex_96_tiprack_200ul"}, {"display_name": "1000 µL", "value": "opentrons_flex_96_tiprack_1000ul"}, ], - default="opentrons_flex_96_tiprack_1000ul", + default="opentrons_flex_96_tiprack_50ul", ) @@ -224,6 +290,25 @@ def create_hs_speed_parameter(parameters: ParameterContext) -> None: ) +def create_plate_reader_compatible_labware_parameter( + parameters: ParameterContext, +) -> None: + """Create parameter for flat bottom plates compatible with plate reader.""" + parameters.add_str( + variable_name="labware_plate_reader_compatible", + display_name="Plate Reader Labware", + default="nest_96_wellplate_200ul_flat", + choices=[ + { + "display_name": "Corning_96well", + "value": "corning_96_wellplate_360ul_flat", + }, + {"display_name": "Hellma Plate", "value": "hellma_reference_plate"}, + {"display_name": "Nest_96well", "value": "nest_96_wellplate_200ul_flat"}, + ], + ) + + def create_tc_compatible_labware_parameter(parameters: ParameterContext) -> None: """Create parameter for labware type compatible with thermocycler.""" parameters.add_str( @@ -249,7 +334,33 @@ def create_tc_compatible_labware_parameter(parameters: ParameterContext) -> None ) +def create_deactivate_modules_parameter(parameters: ParameterContext) -> None: + """Create parameter for deactivating modules at the end fof run.""" + parameters.add_bool( + variable_name="deactivate_modules", + display_name="Deactivate Modules", + description="deactivate all modules at end of run", + default=True, + ) + + # FUNCTIONS FOR COMMON MODULE SEQUENCES +def deactivate_modules(protocol: ProtocolContext) -> None: + """Deactivate all loaded modules.""" + print("Deactivating Modules") + modules = protocol.loaded_modules + + if modules: + for module in modules.values(): + if isinstance(module, HeaterShakerContext): + module.deactivate_shaker() + module.deactivate_heater() + elif isinstance(module, TemperatureModuleContext): + module.deactivate() + elif isinstance(module, MagneticModuleContext): + module.disengage() + elif isinstance(module, ThermocyclerContext): + module.deactivate() def move_labware_from_hs_to_destination( @@ -314,6 +425,39 @@ def use_disposable_lid_with_tc( # FUNCTIONS FOR COMMON PIPETTE COMMAND SEQUENCES +def clean_up_plates( + pipette: InstrumentContext, + list_of_labware: List[Labware], + liquid_waste: Well, + tip_size: int, +) -> None: + """Aspirate liquid from labware and dispense into liquid waste.""" + pipette.pick_up_tip() + pipette.liquid_presence_detection = False + num_of_active_channels = pipette.active_channels + for labware in list_of_labware: + if num_of_active_channels == 8: + list_of_wells = labware.rows()[0] + elif num_of_active_channels == 1: + list_of_wells = labware.wells() + elif num_of_active_channels == 96: + list_of_wells = [labware.wells()[0]] + for well in list_of_wells: + vol_removed = 0.0 + while well.max_volume > vol_removed: + pipette.aspirate(tip_size, well) + pipette.dispense( + tip_size, + liquid_waste.top(), + ) + pipette.blow_out(liquid_waste.top()) + vol_removed += pipette.max_volume + if pipette.channels != num_of_active_channels: + pipette.drop_tip() + else: + pipette.return_tip() + + def find_liquid_height(pipette: InstrumentContext, well_to_probe: Well) -> float: """Find liquid height of well.""" try: @@ -372,6 +516,18 @@ def load_wells_with_custom_liquids( well.load_liquid(liquid, volume) +def comment_height_of_specific_labware( + protocol: ProtocolContext, labware_name: str, dict_of_labware_heights: Dict +) -> None: + """Comment height found of specific labware.""" + total_height = 0.0 + for key in dict_of_labware_heights.keys(): + if key[0] == labware_name: + height = dict_of_labware_heights[key] + total_height += height + protocol.comment(f"Liquid Waste Total Height: {total_height}") + + def find_liquid_height_of_all_wells( protocol: ProtocolContext, pipette: InstrumentContext, @@ -382,7 +538,7 @@ def find_liquid_height_of_all_wells( pipette.pick_up_tip() pip_channels = pipette.active_channels for well in wells: - labware_name = well.parent.load_name + labware_name = well.parent.name total_number_of_wells_in_plate = len(well.parent.wells()) # if pip_channels is > 1 and total_wells > 12 - only probe 1st row. if ( @@ -402,6 +558,9 @@ def find_liquid_height_of_all_wells( pipette.reset_tipracks() msg = f"result: {dict_of_labware_heights}" protocol.comment(msg=msg) + comment_height_of_specific_labware( + protocol, "Liquid Waste", dict_of_labware_heights + ) return dict_of_labware_heights @@ -423,6 +582,8 @@ def find_liquid_height_of_loaded_liquids( entry["well"] if isinstance(entry["well"], list) else [entry["well"]] ) ] + if pipette.active_channels == 96: + wells = [well for well in wells if well.display_name.split(" ")[0] == "A1"] find_liquid_height_of_all_wells(ctx, pipette, wells) return wells @@ -463,6 +624,14 @@ def load_wells_with_water( "#C0C0C0", ] +# Modules with deactivate +ModuleTypes = Union[ + TemperatureModuleContext, + ThermocyclerContext, + HeaterShakerContext, + MagneticModuleContext, + AbsorbanceReaderContext, +] # THERMOCYCLER PROFILES @@ -501,6 +670,3 @@ def perform_pcr( thermocycler.execute_profile( steps=final_extension_profile, repetitions=1, block_max_volume=50 ) - - -# TODO: Create dictionary of labware, module, and adapter. diff --git a/abr-testing/abr_testing/protocols/liquid_setups/10_ZymoBIOMICS Magbead Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/10_ZymoBIOMICS Magbead Liquid Setup.py index 422102e4321..7fed5d5d052 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/10_ZymoBIOMICS Magbead Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/10_ZymoBIOMICS Magbead Liquid Setup.py @@ -13,7 +13,7 @@ requirements = { "robotType": "Flex", - "apiLevel": "2.20", + "apiLevel": "2.21", } @@ -26,14 +26,17 @@ def run(protocol: protocol_api.ProtocolContext) -> None: p1000, ) = load_common_liquid_setup_labware_and_instruments(protocol) - res1 = protocol.load_labware("nest_12_reservoir_15ml", "C3", "R1") - res2 = protocol.load_labware("nest_12_reservoir_15ml", "B3", "R2") + res1 = protocol.load_labware("nest_12_reservoir_15ml", "D3", "Reagent Reservoir 1") + res2 = protocol.load_labware("nest_12_reservoir_15ml", "C3", "Reagent Reservoir 2") + res3 = protocol.load_labware("nest_12_reservoir_15ml", "B3", "Reagent Reservoir 3") lysis_and_pk = 12320 / 8 beads_and_binding = 11875 / 8 binding2 = 13500 / 8 - wash2 = 9000 / 8 + wash2 = 9800 / 8 wash2_list = [wash2] * 12 + final_elution = 7500 / 8 + # Fill up Plates # Res1 p1000.transfer( @@ -42,7 +45,10 @@ def run(protocol: protocol_api.ProtocolContext) -> None: beads_and_binding, beads_and_binding, beads_and_binding, - binding2, + beads_and_binding, + beads_and_binding, + beads_and_binding, + beads_and_binding, binding2, binding2, binding2, @@ -58,6 +64,9 @@ def run(protocol: protocol_api.ProtocolContext) -> None: res1["A6"].top(), res1["A7"].top(), res1["A8"].top(), + res1["A9"].top(), + res1["A10"].top(), + res1["A11"].top(), res1["A12"].top(), ], blow_out=True, @@ -66,10 +75,19 @@ def run(protocol: protocol_api.ProtocolContext) -> None: ) # Res2 p1000.transfer( - volume=wash2_list, - source=source_reservoir["A1"], + volume=[final_elution] + wash2_list[:11], + source=[source_reservoir["A1"]] * 12, dest=res2.wells(), blow_out=True, blowout_location="source well", trash=False, ) + # Res 3 + p1000.transfer( + volume=[wash2, wash2], + source=[source_reservoir["A1"], source_reservoir["A1"]], + dest=[res3["A1"], res3["A2"]], + blow_out=True, + blowout_location="source well", + trash=False, + ) diff --git a/abr-testing/abr_testing/protocols/liquid_setups/11_Dynabeads RIT Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/11_Dynabeads RIT Liquid Setup.py index 112aec315b5..2d722d410e5 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/11_Dynabeads RIT Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/11_Dynabeads RIT Liquid Setup.py @@ -13,7 +13,7 @@ requirements = { "robotType": "Flex", - "apiLevel": "2.20", + "apiLevel": "2.21", } @@ -27,8 +27,11 @@ def run(protocol: protocol_api.ProtocolContext) -> None: ) = load_common_liquid_setup_labware_and_instruments(protocol) reservoir_wash = protocol.load_labware("nest_12_reservoir_15ml", "D2", "Reservoir") - sample_plate = protocol.load_labware( - "nest_96_wellplate_2ml_deep", "C3", "Sample Plate" + sample_plate1 = protocol.load_labware( + "nest_96_wellplate_2ml_deep", "C3", "Sample Plate 1" + ) + sample_plate2 = protocol.load_labware( + "nest_96_wellplate_2ml_deep", "B3", "Sample Plate 2" ) columns = [ @@ -52,10 +55,24 @@ def run(protocol: protocol_api.ProtocolContext) -> None: p1000.dispense(750, reservoir_wash[i].top()) p1000.blow_out(location=source_reservoir["A1"].top()) p1000.return_tip() + # 1 column 6000 uL + p1000.pick_up_tip() + for i in columns: + p1000.aspirate(750, source_reservoir["A1"].bottom(z=0.5)) + p1000.dispense(750, reservoir_wash[i].top()) + p1000.blow_out(location=source_reservoir["A1"].top()) + p1000.return_tip() + # Nest 96 Deep Well Plate 2 mL: 250 uL per well + p1000.pick_up_tip() + for n in columns: + p1000.aspirate(250, source_reservoir["A1"].bottom(z=0.5)) + p1000.dispense(250, sample_plate1[n].bottom(z=1)) + p1000.blow_out(location=source_reservoir["A1"].top()) + p1000.return_tip() # Nest 96 Deep Well Plate 2 mL: 250 uL per well p1000.pick_up_tip() for n in columns: p1000.aspirate(250, source_reservoir["A1"].bottom(z=0.5)) - p1000.dispense(250, sample_plate[n].bottom(z=1)) + p1000.dispense(250, sample_plate2[n].bottom(z=1)) p1000.blow_out(location=source_reservoir["A1"].top()) p1000.return_tip() diff --git a/abr-testing/abr_testing/protocols/liquid_setups/12_KAPA HyperPlus Library Prep Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/12_KAPA HyperPlus Library Prep Liquid Setup.py index 2575caecf6e..688533ffd55 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/12_KAPA HyperPlus Library Prep Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/12_KAPA HyperPlus Library Prep Liquid Setup.py @@ -13,7 +13,7 @@ requirements = { "robotType": "Flex", - "apiLevel": "2.20", + "apiLevel": "2.21", } diff --git a/abr-testing/abr_testing/protocols/liquid_setups/1_Simple normalize long Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/1_Simple normalize long Liquid Setup.py index 2d995fede39..a99bb0568cf 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/1_Simple normalize long Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/1_Simple normalize long Liquid Setup.py @@ -12,7 +12,7 @@ requirements = { "robotType": "Flex", - "apiLevel": "2.20", + "apiLevel": "2.21", } @@ -26,14 +26,22 @@ def run(protocol: protocol_api.ProtocolContext) -> None: ) = load_common_liquid_setup_labware_and_instruments(protocol) reservoir = protocol.load_labware("nest_12_reservoir_15ml", "D2", "Reservoir") # Transfer Liquid - vol = 5400 / 8 + vol = 6175 / 8 columns = ["A1", "A2", "A3", "A4", "A5"] for i in columns: p1000.transfer( vol, - source=source_reservoir["A1"].bottom(z=0.5), + source=source_reservoir["A1"].bottom(z=2), dest=reservoir[i].top(), blowout=True, blowout_location="source well", trash=False, ) + p1000.transfer( + 8500 / 8, + source=source_reservoir["A1"].bottom(z=2), + dest=reservoir["A6"], + blowout=True, + blowout_location="source well", + trash=False, + ) diff --git a/abr-testing/abr_testing/protocols/liquid_setups/2_BMS_PCR_protocol Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/2_BMS_PCR_protocol Liquid Setup.py index a6c71b563d4..1ffefc48f19 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/2_BMS_PCR_protocol Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/2_BMS_PCR_protocol Liquid Setup.py @@ -7,13 +7,13 @@ metadata = { "protocolName": "DVT1ABR2 Liquids: BMS PCR Protocol", - "author": "Rhyann clarke ", + "author": "Rhyann Clarke ", "source": "Protocol Library", } requirements = { "robotType": "Flex", - "apiLevel": "2.20", + "apiLevel": "2.21", } @@ -33,9 +33,9 @@ def run(protocol: protocol_api.ProtocolContext) -> None: ) # Steps # Dispense into plate 1 - p1000.transfer(50, source_reservoir["A1"], pcr_plate_1.wells(), trash=False) + p1000.transfer(100, source_reservoir["A1"], pcr_plate_1.wells(), trash=False) # Dispense p1000.configure_nozzle_layout(protocol_api.SINGLE, start="H1", tip_racks=[tip_rack]) - p1000.transfer(1500, source_reservoir["A1"], snap_caps["B1"]) - p1000.transfer(1500, source_reservoir["A1"], snap_caps.rows()[0]) + p1000.transfer(1000, source_reservoir["A1"], snap_caps["B1"]) + p1000.transfer(1000, source_reservoir["A1"], snap_caps.rows()[0]) diff --git a/abr-testing/abr_testing/protocols/liquid_setups/3_Tartrazine Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/3_Tartrazine Liquid Setup.py index 9e0b29a03ed..f0941bb398b 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/3_Tartrazine Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/3_Tartrazine Liquid Setup.py @@ -1,8 +1,6 @@ """Plate Filler Protocol for Tartrazine Protocol.""" from opentrons import protocol_api -from abr_testing.protocols.helpers import ( - load_common_liquid_setup_labware_and_instruments, -) +from abr_testing.protocols import helpers metadata = { "protocolName": "DVT1ABR3 Liquids: Tartrazine Protocol", @@ -12,36 +10,77 @@ requirements = { "robotType": "Flex", - "apiLevel": "2.20", + "apiLevel": "2.21", } +def add_parameters(parameters: protocol_api.ParameterContext) -> None: + """Add parameters.""" + parameters.add_int( + variable_name="number_of_plates", + display_name="Number of Plates", + default=4, + minimum=1, + maximum=4, + ) + helpers.create_channel_parameter(parameters) + + def run(protocol: protocol_api.ProtocolContext) -> None: """Protocol.""" + number_of_plates = protocol.params.number_of_plates # type: ignore [attr-defined] + channels = protocol.params.channels # type: ignore [attr-defined] # Initiate Labware ( source_reservoir, tip_rack, p1000, - ) = load_common_liquid_setup_labware_and_instruments(protocol) - reagent_tube = protocol.load_labware( - "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical", "D3", "Reagent Tube" - ) - p1000.configure_nozzle_layout( - style=protocol_api.SINGLE, start="H1", tip_racks=[tip_rack] - ) - # Transfer Liquid - p1000.transfer( - 45000, - source_reservoir["A1"], - reagent_tube["B3"].top(), - blowout=True, - blowout_location="source well", - ) - p1000.transfer( - 45000, - source_reservoir["A1"], - reagent_tube["A4"].top(), - blowout=True, - blowout_location="source well", - ) + ) = helpers.load_common_liquid_setup_labware_and_instruments(protocol) + if channels == "1channel": + reagent_tube = protocol.load_labware( + "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical", "D3", "Reagent Tube" + ) + p1000.configure_nozzle_layout( + style=protocol_api.SINGLE, start="H1", tip_racks=[tip_rack] + ) + # Transfer Liquid + p1000.transfer( + 45000, + source_reservoir["A1"], + reagent_tube["B3"].top(), + blowout=True, + blowout_location="source well", + ) + p1000.transfer( + 45000, + source_reservoir["A1"], + reagent_tube["A4"].top(), + blowout=True, + blowout_location="source well", + ) + elif channels == "8channel": + reservoir = protocol.load_labware("nest_12_reservoir_15ml", "D3", "Reservoir") + water_max_vol = reservoir["A1"].max_volume - 500 + reservoir_wells = reservoir.wells()[ + 1: + ] # Skip A1 as it's reserved for tartrazine + # NEEDED WATER + needed_water: float = ( + float(number_of_plates) * 96.0 * 250.0 + ) # loading extra as a safety factor + # CALCULATING NEEDED # OF WATER WELLS + needed_wells = round(needed_water / water_max_vol) + water_wells = [] + for i in range(needed_wells + 1): + water_wells.append(reservoir_wells[i]) + # Create lists of volumes and source that matches wells to fill + water_max_vol_list = [water_max_vol] * len(water_wells) + source_list = [source_reservoir["A1"]] * len(water_wells) + p1000.transfer( + water_max_vol_list, + source_list, + water_wells, + blowout=True, + blowout_locaiton="source", + trash=False, + ) diff --git a/abr-testing/abr_testing/protocols/liquid_setups/4_Illumina DNA Enrichment Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/4_Illumina DNA Enrichment Liquid Setup.py index 18aee383ace..937c9f4dafd 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/4_Illumina DNA Enrichment Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/4_Illumina DNA Enrichment Liquid Setup.py @@ -12,7 +12,7 @@ requirements = { "robotType": "Flex", - "apiLevel": "2.20", + "apiLevel": "2.21", } @@ -27,7 +27,7 @@ def run(protocol: protocol_api.ProtocolContext) -> None: reservoir_1 = protocol.load_labware( "nest_96_wellplate_2ml_deep", "D2", "Reservoir 1" ) # Reservoir - reservoir_2 = protocol.load_labware( + sample_plate_2 = protocol.load_labware( "thermoscientificnunc_96_wellplate_1300ul", "D3", "Sample Plate 2" ) # Reservoir sample_plate_1 = protocol.load_labware( @@ -57,10 +57,10 @@ def run(protocol: protocol_api.ProtocolContext) -> None: volume=[120, 750, 900, 96], source=source_reservoir["A1"], dest=[ - reservoir_1["A1"].top(), - reservoir_1["A2"].top(), - reservoir_1["A4"].top(), - reservoir_1["A5"].top(), + reservoir_1["A1"].top(), # AMPure + reservoir_1["A2"].top(), # SMB + reservoir_1["A4"].top(), # EtOH + reservoir_1["A5"].top(), # RSB ], blow_out=True, blowout_location="source well", @@ -68,10 +68,10 @@ def run(protocol: protocol_api.ProtocolContext) -> None: ) # Reservoir 2 Plate Prep: dispense liquid into columns 1-9 total 3690 ul - reservoir_2_wells = reservoir_2.wells() + reservoir_2_wells = sample_plate_1.wells() list_of_locations = [well_location.top() for well_location in reservoir_2_wells] p1000.transfer( - volume=[50, 50, 50, 50, 50, 50, 330, 330, 330, 800, 800, 800], + volume=[150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150], source=source_reservoir["A1"], dest=list_of_locations, blow_out=True, @@ -80,9 +80,14 @@ def run(protocol: protocol_api.ProtocolContext) -> None: ) # Sample Plate Prep: total 303 - dest_list = [sample_plate_1["A1"], sample_plate_1["A2"], sample_plate_1["A3"]] + dest_list = [ + sample_plate_2["A9"], + sample_plate_2["A10"], + sample_plate_2["A11"], + sample_plate_2["A12"], + ] p1000.transfer( - volume=[101, 101, 101], + volume=[1000, 1000, 1000, 1000], source=source_reservoir["A1"], dest=dest_list, blow_out=True, diff --git a/abr-testing/abr_testing/protocols/liquid_setups/5_96ch Complex Protocol Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/5_96ch Complex Protocol Liquid Setup.py index cd263318442..e7a726b6b46 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/5_96ch Complex Protocol Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/5_96ch Complex Protocol Liquid Setup.py @@ -5,14 +5,14 @@ ) metadata = { - "protocolName": "DVT2ABR5 and 6 Liquids: 96ch Complex Protocol", + "protocolName": "DVT2ABR5 Liquids: 96ch Complex Protocol", "author": "Rhyann clarke ", "source": "Protocol Library", } requirements = { "robotType": "Flex", - "apiLevel": "2.16", + "apiLevel": "2.21", } @@ -29,7 +29,7 @@ def run(protocol: protocol_api.ProtocolContext) -> None: "nest_96_wellplate_2ml_deep", "D2", "Reservoir" ) # Reservoir - vol = 500 + vol = 1000 column_list = [ "A1", @@ -45,9 +45,9 @@ def run(protocol: protocol_api.ProtocolContext) -> None: "A11", "A12", ] + p1000.pick_up_tip() for i in column_list: - p1000.pick_up_tip() p1000.aspirate(vol, source_reservoir["A1"].bottom(z=0.5)) p1000.dispense(vol, reservoir[i].top()) p1000.blow_out(location=source_reservoir["A1"].top()) - p1000.return_tip() + p1000.return_tip() diff --git a/abr-testing/abr_testing/protocols/liquid_setups/7_HDQ DNA Bacteria Extraction Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/7_HDQ DNA Bacteria Extraction Liquid Setup.py index 4addbd5c7e8..309f9916d03 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/7_HDQ DNA Bacteria Extraction Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/7_HDQ DNA Bacteria Extraction Liquid Setup.py @@ -13,7 +13,7 @@ requirements = { "robotType": "Flex", - "apiLevel": "2.16", + "apiLevel": "2.21", } @@ -55,9 +55,22 @@ def run(protocol: protocol_api.ProtocolContext) -> None: # Sample Plate p1000.transfer( - volume=180, + volume=200, source=source_reservoir["A1"].bottom(z=0.5), - dest=sample_plate["A1"].top(), + dest=[ + sample_plate["A1"].top(), + sample_plate["A2"].top(), + sample_plate["A3"].top(), + sample_plate["A4"].top(), + sample_plate["A5"].top(), + sample_plate["A6"].top(), + sample_plate["A7"].top(), + sample_plate["A8"].top(), + sample_plate["A9"].top(), + sample_plate["A10"].top(), + sample_plate["A11"].top(), + sample_plate["A12"].top(), + ], blowout=True, blowout_location="source well", trash=False, @@ -66,7 +79,20 @@ def run(protocol: protocol_api.ProtocolContext) -> None: p1000.transfer( volume=100, source=source_reservoir["A1"].bottom(z=0.5), - dest=elution_plate["A1"].top(), + dest=[ + elution_plate["A1"].top(), + elution_plate["A2"].top(), + elution_plate["A3"].top(), + elution_plate["A4"].top(), + elution_plate["A5"].top(), + elution_plate["A6"].top(), + elution_plate["A7"].top(), + elution_plate["A8"].top(), + elution_plate["A9"].top(), + elution_plate["A10"].top(), + elution_plate["A11"].top(), + elution_plate["A12"].top(), + ], blowout=True, blowout_location="source well", trash=False, diff --git a/abr-testing/abr_testing/protocols/liquid_setups/9_Thermo MagMax RNA Extraction Liquid Setup.py b/abr-testing/abr_testing/protocols/liquid_setups/9_Thermo MagMax RNA Extraction Liquid Setup.py index c6ded28719d..e935063ed16 100644 --- a/abr-testing/abr_testing/protocols/liquid_setups/9_Thermo MagMax RNA Extraction Liquid Setup.py +++ b/abr-testing/abr_testing/protocols/liquid_setups/9_Thermo MagMax RNA Extraction Liquid Setup.py @@ -13,7 +13,7 @@ requirements = { "robotType": "Flex", - "apiLevel": "2.16", + "apiLevel": "2.21", } @@ -34,15 +34,28 @@ def run(protocol: protocol_api.ProtocolContext) -> None: ) # Volumes + lysis = 8120 / 8 + stop_reaction_vol = 6400 / 8 elution_vol = 55 - well1 = 8120 / 8 - well2 = 6400 / 8 - well3_7 = 8550 / 8 + well4_12 = 9500 / 8 sample_vol = 100 # Reservoir p1000.transfer( - volume=[well1, well2, well3_7, well3_7, well3_7, well3_7, well3_7], + volume=[ + lysis, + lysis, + stop_reaction_vol, + well4_12, + well4_12, + well4_12, + well4_12, + well4_12, + well4_12, + well4_12, + well4_12, + well4_12, + ], source=source_reservoir["A1"].bottom(z=0.2), dest=[ res1["A1"].top(), @@ -52,6 +65,11 @@ def run(protocol: protocol_api.ProtocolContext) -> None: res1["A5"].top(), res1["A6"].top(), res1["A7"].top(), + res1["A8"].top(), + res1["A9"].top(), + res1["A10"].top(), + res1["A11"].top(), + res1["A12"].top(), ], blow_out=True, blowout_location="source well", @@ -94,7 +112,20 @@ def run(protocol: protocol_api.ProtocolContext) -> None: ) # Sample Plate p1000.transfer( - volume=[sample_vol, sample_vol, sample_vol, sample_vol, sample_vol, sample_vol], + volume=[ + sample_vol, + sample_vol, + sample_vol, + sample_vol, + sample_vol, + sample_vol, + sample_vol, + sample_vol, + sample_vol, + sample_vol, + sample_vol, + sample_vol, + ], source=source_reservoir["A1"].bottom(z=0.2), dest=[ sample_plate["A1"].top(), @@ -103,6 +134,12 @@ def run(protocol: protocol_api.ProtocolContext) -> None: sample_plate["A4"].top(), sample_plate["A5"].top(), sample_plate["A6"].top(), + sample_plate["A7"].top(), + sample_plate["A8"].top(), + sample_plate["A9"].top(), + sample_plate["A10"].top(), + sample_plate["A11"].top(), + sample_plate["A12"].top(), ], blow_out=True, blowout_location="source well", diff --git a/abr-testing/abr_testing/protocols/test_protocols/tc_lid_x_offset_test.py b/abr-testing/abr_testing/protocols/test_protocols/tc_lid_x_offset_test.py index df85453ff28..9dd4ee7b578 100644 --- a/abr-testing/abr_testing/protocols/test_protocols/tc_lid_x_offset_test.py +++ b/abr-testing/abr_testing/protocols/test_protocols/tc_lid_x_offset_test.py @@ -1,12 +1,9 @@ """Protocol to Test the Stacking and Movement of Tough Auto Seal Lid.""" -from opentrons.protocol_api import ( - ParameterContext, - ProtocolContext, -) +from opentrons.protocol_api import ParameterContext, ProtocolContext, Labware from opentrons.protocol_api.module_contexts import ( ThermocyclerContext, ) -from abr_testing.protocols import helpers +from typing import List metadata = {"protocolName": "5 Stack Test"} @@ -23,8 +20,8 @@ def add_parameters(parameters: ParameterContext) -> None: default=5, ) parameters.add_float( - variable_name="x_offset", - display_name="X Offset", + variable_name="num_offset", + display_name="Numerical Offset", choices=[ {"display_name": "0.0", "value": 0.0}, {"display_name": "0.1", "value": 0.1}, @@ -56,41 +53,73 @@ def add_parameters(parameters: ParameterContext) -> None: description="Turn on to make offset negative.", default=False, ) + parameters.add_str( + variable_name="offset", + display_name="Offset", + choices=[ + {"display_name": "Z", "value": "Z"}, + {"display_name": "Y", "value": "Y"}, + {"display_name": "X", "value": "X"}, + ], + default="X", + ) + parameters.add_bool( + variable_name="thermocycler_bool", display_name="thermocycler", default=False + ) def run(protocol: ProtocolContext) -> None: """Runs protocol that moves lids and stacks them.""" # Load Parameters - lids_in_stack = protocol.params.lids_in_a_stack # type: ignore[attr-defined] - x_offset = protocol.params.x_offset # type: ignore[attr-defined] + lids_in_stack: int = protocol.params.lids_in_a_stack # type: ignore[attr-defined] + num_offset = protocol.params.num_offset # type: ignore[attr-defined] + + offset = protocol.params.offset # type: ignore[attr-defined] negative = protocol.params.negative # type: ignore[attr-defined] + thermocycler_bool = protocol.params.thermocycler_bool # type: ignore[attr-defined] if negative: - x_offset = x_offset * -1 + num_offset = num_offset * -1 + # Thermocycler - thermocycler: ThermocyclerContext = protocol.load_module( - "thermocyclerModuleV2" - ) # type: ignore[assignment] - plate_in_cycler = thermocycler.load_labware( - "armadillo_96_wellplate_200ul_pcr_full_skirt" - ) - thermocycler.open_lid() + if thermocycler_bool: + thermocycler: ThermocyclerContext = protocol.load_module( + "thermocyclerModuleV2" + ) # type: ignore[assignment] + plate_in_cycler: Labware = thermocycler.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt" + ) + thermocycler.open_lid() + else: + plate_in_cycler = protocol.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", "D2" + ) # Load Lids - lid_stack_1 = helpers.load_disposable_lids(protocol, lids_in_stack, ["D2"]) - lid_stack_2 = helpers.load_disposable_lids(protocol, lids_in_stack, ["C2"]) - lid_stack_3 = helpers.load_disposable_lids(protocol, lids_in_stack, ["B2"]) - lid_stack_4 = helpers.load_disposable_lids(protocol, lids_in_stack, ["C3"]) - lid_stack_5 = helpers.load_disposable_lids(protocol, lids_in_stack, ["B3"]) - - drop_offset = {"x": x_offset, "y": 0, "z": 0} + deck_riser_adapter = protocol.load_adapter("opentrons_flex_deck_riser", "D3") + unused_lids: List[Labware] = [ + deck_riser_adapter.load_labware("opentrons_tough_pcr_auto_sealing_lid") + ] + if lids_in_stack > 1: + for i in range(lids_in_stack - 1): + unused_lids.append( + unused_lids[-1].load_labware("opentrons_tough_pcr_auto_sealing_lid") + ) + unused_lids.reverse() + pick_up_offset = { + "X": {"x": num_offset, "y": 0, "z": 0}, + "Y": {"x": 0, "y": num_offset, "z": 0}, + "Z": {"x": 0, "y": 0, "z": num_offset}, + } slot = 0 - lids = [lid_stack_1, lid_stack_2, lid_stack_3, lid_stack_4, lid_stack_5] - for lid_list in lids: - lid_to_move = lid_list[0] - - lid_to_move_back_to = lid_list[1] - protocol.comment(f"Offset {x_offset}, Lid # {slot+1}") - # move lid to plate in thermocycler - protocol.move_labware( - lid_to_move, plate_in_cycler, use_gripper=True, drop_offset=drop_offset - ) - protocol.move_labware(lid_to_move, lid_to_move_back_to, use_gripper=True) + if len(unused_lids) > 1: + lid_to_move_back_to = unused_lids[1] # stack back on top + else: + lid_to_move_back_to = deck_riser_adapter + protocol.comment(f"{offset} Offset {num_offset}, Lid # {slot+1}") + # move lid to plate in thermocycler + protocol.move_labware( + unused_lids[0], + plate_in_cycler, + use_gripper=True, + pick_up_offset=pick_up_offset[offset], + ) + protocol.move_labware(unused_lids[0], lid_to_move_back_to, use_gripper=True) diff --git a/abr-testing/abr_testing/tools/abr_scale.py b/abr-testing/abr_testing/tools/abr_scale.py index a35fee93fbf..e91abf114b2 100644 --- a/abr-testing/abr_testing/tools/abr_scale.py +++ b/abr-testing/abr_testing/tools/abr_scale.py @@ -149,14 +149,11 @@ def get_most_recent_run_and_record( headers, runs_and_lpc, headers_lpc, - list_of_heights, ) = abr_google_drive.create_data_dictionary( most_recent_run_id, storage_directory, "", - labware, - accuracy, - hellma_plate_standards=hellma_file_values, + hellma_file_values, ) google_sheet_abr_data = google_sheets_tool.google_sheet( credentials_path, "ABR-run-data", tab_number=0 @@ -164,15 +161,6 @@ def get_most_recent_run_and_record( start_row = google_sheet_abr_data.get_index_row() + 1 google_sheet_abr_data.batch_update_cells(runs_and_robots, "A", start_row, "0") print("Wrote run to ABR-run-data") - # Add liquid height detection to abr sheet - google_sheet_ldf = google_sheets_tool.google_sheet( - credentials_path, "ABR-run-data", 4 - ) - start_row_lhd = google_sheet_ldf.get_index_row() + 1 - google_sheet_ldf.batch_update_cells( - list_of_heights, "A", start_row_lhd, "1795535088" - ) - print("Wrote found liquid heights to ABR-run-data") # Add LPC to google sheet google_sheet_lpc = google_sheets_tool.google_sheet( credentials_path, "ABR-LPC", tab_number=0 diff --git a/abr-testing/abr_testing/tools/abr_setup.py b/abr-testing/abr_testing/tools/abr_setup.py index 224ba5bf120..d17773965b3 100644 --- a/abr-testing/abr_testing/tools/abr_setup.py +++ b/abr-testing/abr_testing/tools/abr_setup.py @@ -42,7 +42,7 @@ def clean_sheet(sheet_name: str, credentials: str) -> Any: # Check if the date is older than the cutoff if formatted_date < cutoff_date: rem_rows.append(row_id) - if len(rem_rows) > 2000: + if len(rem_rows) > 1500: break if len(rem_rows) == 0: # No more rows to remove @@ -54,8 +54,8 @@ def clean_sheet(sheet_name: str, credentials: str) -> Any: print("deleted rows") except Exception: print("could not delete rows") - traceback.print_exc() - sys.exit(1) + return + clean_sheet(sheet_name, credentials) diff --git a/abr-testing/abr_testing/tools/make_push.py b/abr-testing/abr_testing/tools/make_push.py index 28a69b11103..665831158bc 100644 --- a/abr-testing/abr_testing/tools/make_push.py +++ b/abr-testing/abr_testing/tools/make_push.py @@ -1,7 +1,8 @@ """Push one or more folders to one or more robots.""" import subprocess -import multiprocessing import json +from typing import List +from multiprocessing import Process, Queue global folders # Opentrons folders that can be pushed to robot @@ -13,13 +14,13 @@ ] -def push_subroutine(cmd: str) -> None: +def push_subroutine(cmd: str, queue: Queue) -> None: """Pushes specified folder to specified robot.""" try: subprocess.run(cmd) + queue.put(f"{cmd}: SUCCESS!\n") except Exception: - print("failed to push folder") - raise + queue.put(f"{cmd}: FAILED\n") def main(folder_to_push: str, robot_to_push: str) -> int: @@ -27,6 +28,8 @@ def main(folder_to_push: str, robot_to_push: str) -> int: cmd = "make -C {folder} push-ot3 host={ip}" robot_ip_path = "" push_cmd = "" + processes: List[Process] = [] + queue: Queue = Queue() folder_int = int(folder_to_push) if folders[folder_int].lower() == "abr-testing + hardware-testing": if robot_to_push.lower() == "all": @@ -41,20 +44,16 @@ def main(folder_to_push: str, robot_to_push: str) -> int: for folder_name in folders[:-2]: # Push abr-testing and hardware-testing folders to all robots for robot in robot_ips: - print_proc = multiprocessing.Process( - target=print, args=(f"Pushing {folder_name} to {robot}!\n\n",) - ) - print_proc.start() - print_proc.join() push_cmd = cmd.format(folder=folder_name, ip=robot) - process = multiprocessing.Process( - target=push_subroutine, args=(push_cmd,) + process = Process( + target=push_subroutine, + args=( + push_cmd, + queue, + ), ) process.start() - process.join() - print_proc = multiprocessing.Process(target=print, args=("Done!\n\n",)) - print_proc.start() - print_proc.join() + processes.append(process) else: if folder_int == (len(folders) - 1): @@ -72,18 +71,15 @@ def main(folder_to_push: str, robot_to_push: str) -> int: # Push folder to robots for robot in robot_ips: - print_proc = multiprocessing.Process( - target=print, args=(f"Pushing {folder_name} to {robot}!\n\n",) - ) - print_proc.start() - print_proc.join() push_cmd = cmd.format(folder=folder_name, ip=robot) - process = multiprocessing.Process(target=push_subroutine, args=(push_cmd,)) + process = Process(target=push_subroutine, args=(push_cmd, queue)) process.start() - process.join() - print_proc = multiprocessing.Process(target=print, args=("Done!\n\n",)) - print_proc.start() - print_proc.join() + processes.append(process) + + for process in processes: + process.join() + result = queue.get() + print(f"\n{result}") return 0 diff --git a/abr-testing/abr_testing/tools/module_control.py b/abr-testing/abr_testing/tools/module_control.py new file mode 100644 index 00000000000..5bc1f5cfb1d --- /dev/null +++ b/abr-testing/abr_testing/tools/module_control.py @@ -0,0 +1,138 @@ +"""Interface with opentrons modules!""" +from serial import Serial # type: ignore[import-untyped] +import asyncio +import subprocess +from typing import Any + +# Generic +_READ_ALL = "readall" +_READ_LINE = "read" +_DONE = "done" + +# TC commands +_MOVE_SEAL = "ms" +_MOVE_LID = "ml" +tc_gcode_shortcuts = { + "status": "M119", + _MOVE_SEAL: "M241.D", # move seal motor + _MOVE_LID: "M240.D", # move lid stepper motor + "ol": "M126", # open lid + "cl": "M127", # close lid + "sw": "M901.D", # status of all switches + "lt": "M141.D", # get lid temperature + "pt": "M105.D", # get plate temperature +} + +# HS Commands +hs_gcode_shortcuts = { + "srpm": "M3 S{rpm}", # Set RPM + "grpm": "M123", # Get RPM + "home": "G28", # Home + "deactivate": "M106", # Deactivate +} + +gcode_shortcuts = tc_gcode_shortcuts | hs_gcode_shortcuts + + +async def message_read(dev: Serial) -> Any: + """Read message.""" + response = dev.readline().decode() + while not response: + await asyncio.sleep(1) + response = dev.readline().decode() + return response + + +async def message_return(dev: Serial) -> Any: + """Wait until message becomes available.""" + try: + response = await asyncio.wait_for(message_read(dev), timeout=30) + return response + except asyncio.exceptions.TimeoutError: + print("response timed out.") + return "" + + +async def handle_module_gcode_shortcut( + dev: Serial, command: str, in_commands: bool, output: str = "" +) -> None: + """Handle debugging commands that require followup.""" + if in_commands: + if command == _MOVE_SEAL: + distance = input("enter distance in steps => ") + dev.write( + f"{gcode_shortcuts[command]} {distance}\n".encode() + ) # (+) -> retract, (-) -> engage + # print(await message_return(dev)) + elif command == _MOVE_LID: + distance = input( + "enter angular distance in degrees => " + ) # (+) -> open, (-) -> close + dev.write(f"{gcode_shortcuts[command]} {distance}\n".encode()) + # print(await message_return(dev)) + # everything else + else: + dev.write(f"{gcode_shortcuts[command]}\n".encode()) + else: + dev.write(f"{command}\n".encode()) + try: + mr = await message_return(dev) + print(mr) + except TypeError: + print("Invalid input") + return + + if output: + try: + with open(output, "a") as result_file: + if "OK" in mr: + status = command + ": SUCCESS" + else: + status = command + ": FAILURE" + result_file.write(status) + result_file.write(f" {mr}") + result_file.close() + except FileNotFoundError: + print(f"cannot open file: {output}") + + +async def comms_loop(dev: Serial, commands: list, output: str = "") -> bool: + """Loop for commands.""" + _exit = False + try: + command = commands.pop(0) + except IndexError: + command = input("\n>>> ") + if command == _READ_ALL: + print(dev.readlines()) + elif command == _READ_LINE: + print(dev.readline()) + elif command == _DONE: + _exit = True + elif command in gcode_shortcuts: + await handle_module_gcode_shortcut(dev, command, True, output) + else: + await handle_module_gcode_shortcut(dev, command, False, output) + return _exit + + +async def _main(module: str, commands: list = [], output: str = "") -> bool: + """Main process.""" + module_name = ( + subprocess.check_output(["find", "/dev/", "-name", f"*{module}*"]) + .decode() + .strip() + ) + if not module_name: + print(f"{module} not found. Exiting.") + return False + dev = Serial(f"{module_name}", 9600, timeout=2) + _exit = False + while not _exit: + _exit = await comms_loop(dev, commands, output) + dev.close() + return True + + +if __name__ == "__main__": + asyncio.run(_main("heatershaker")) diff --git a/abr-testing/abr_testing/tools/test_modules.py b/abr-testing/abr_testing/tools/test_modules.py new file mode 100644 index 00000000000..8c372fbff53 --- /dev/null +++ b/abr-testing/abr_testing/tools/test_modules.py @@ -0,0 +1,155 @@ +"""Modules Tests Script!""" +import asyncio +import time +from datetime import datetime +import os +import module_control # type: ignore +from typing import Any, Tuple, Dict +import traceback + +# To run: +# SSH into robot +# cd /opt/opentrons-robot-server/abr-testing/tools +# python3 test_modules.py + + +async def tc_test_1(module: str, path_to_file: str) -> None: + """Thermocycler Test 1 Open and Close Lid.""" + duration = int(input("How long to run this test for? (in seconds): ")) + start = time.time() + while time.time() - start < duration: + try: + await (tc_open_lid(module, path_to_file)) + except asyncio.TimeoutError: + return + time.sleep(5) + try: + await (tc_close_lid(module, path_to_file)) + except asyncio.TimeoutError: + return + time.sleep(5) + + +async def hs_test_1(module: str, path_to_file: str) -> None: + """Heater Shaker Test 1. (Home and Shake).""" + duration = int(input("How long to run this test for? (in seconds): ")) + rpm = input("Target RPM (200-3000): ") + start = time.time() + while time.time() - start < duration: + try: + await (hs_test_home(module, path_to_file)) + except asyncio.TimeoutError: + return + time.sleep(5) + try: + await (hs_test_set_shake(module, rpm, path_to_file)) + except asyncio.TimeoutError: + return + time.sleep(10) + try: + await (hs_test_set_shake(module, "0", path_to_file)) + except asyncio.TimeoutError: + return + time.sleep(10) + + +async def input_codes(module: str, path_to_file: str) -> None: + """Opens serial for manual code input.""" + await module_control._main(module, output=path_to_file) + + +hs_tests: Dict[str, Tuple[Any, str]] = { + "Test 1": (hs_test_1, "Repeatedly home heater shaker then set shake speed"), + "Input GCodes": (input_codes, "Input g codes"), +} + +tc_tests: Dict[str, Tuple[Any, str]] = { + "Test 1": (tc_test_1, "Repeatedly open and close TC lid"), + "Input GCodes": (input_codes, "Input g codes"), +} + +global modules + +modules = { + "heatershaker": hs_tests, + "thermocycler": tc_tests, +} + + +async def main(module: str) -> None: + """Select test to be run.""" + # Select test to run + # Set directory for tests + BASE_DIRECTORY = "/userfs/data/testing_data/" + if not os.path.exists(BASE_DIRECTORY): + os.makedirs(BASE_DIRECTORY) + tests = modules[module] + for i, test in enumerate(tests.keys()): + function, description = tests[test] + print(f"{i}) {test} : {description}") + selected_test = int(input("Please select a test: ")) + try: + function, description = tests[list(tests.keys())[selected_test]] + test_dir = BASE_DIRECTORY + f"{module}/test/{list(tests.keys())[selected_test]}" + print(f"{i}, {description}") + print(f"TEST DIR: {test_dir}") + date = datetime.now() + filename = f"results_{datetime.strftime(date, '%Y-%m-%d_%H:%M:%S')}.txt" + output_file = os.path.join(test_dir, filename) + try: + if not os.path.exists(test_dir): + os.makedirs(test_dir) + open(output_file, "a").close() + except Exception: + traceback.print_exc() + print(f"PATH: {output_file} ") + await (function(module, output_file)) + except Exception: + print("Failed to run test") + traceback.print_exc() + + +# HS Test Functions +async def hs_test_home(module: str, path_to_file: str) -> None: + """Home heater shaker.""" + hs_gcodes = module_control.hs_gcode_shortcuts + home_gcode = hs_gcodes["home"] + await (module_control._main(module, [home_gcode, "done"], path_to_file)) + + +async def hs_test_set_shake(module: str, rpm: str, path_to_file: str) -> None: + """Shake heater shaker at specified speed.""" + hs_gcodes = module_control.hs_gcode_shortcuts + set_shake_gcode = hs_gcodes["srpm"].format(rpm=rpm) + await (module_control._main(module, [set_shake_gcode, "done"], path_to_file)) + + +async def hs_deactivate(module: str, path_to_file: str) -> None: + """Deactivate Heater Shaker.""" + hs_gcodes = module_control.hs_gcode_shortcuts + deactivate_gcode = hs_gcodes["deactivate"] + await (module_control._main(module, [deactivate_gcode, "done"], path_to_file)) + + +# TC Test Functions +async def tc_open_lid(module: str, path_to_file: str) -> None: + """Open thermocycler lid.""" + tc_gcodes = module_control.tc_gcode_shortcuts + open_lid_gcode = tc_gcodes["ol"] + await (module_control._main(module, [open_lid_gcode, "done"], path_to_file)) + + +async def tc_close_lid(module: str, path_to_file: str) -> None: + """Open thermocycler lid.""" + tc_gcodes = module_control.tc_gcode_shortcuts + close_lid_gcode = tc_gcodes["cl"] + await (module_control._main(module, [close_lid_gcode, "done"], path_to_file)) + + +if __name__ == "__main__": + print("Modules:") + for i, module in enumerate(modules): + print(f"{i}) {module}") + module_int = int(input("Please select a module: ")) + module = list(modules.keys())[module_int] + asyncio.run(main(module)) diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[004ebb2b82][OT2_S_v2_11_P10S_P300M_MM_TC1_TM_Swift].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[004ebb2b82][OT2_S_v2_11_P10S_P300M_MM_TC1_TM_Swift].json index 1e59022b2a8..8dc5a722eab 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[004ebb2b82][OT2_S_v2_11_P10S_P300M_MM_TC1_TM_Swift].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[004ebb2b82][OT2_S_v2_11_P10S_P300M_MM_TC1_TM_Swift].json @@ -6876,9 +6876,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -6890,9 +6890,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -6911,9 +6911,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -6945,9 +6945,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -6978,9 +6978,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7011,9 +7011,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7045,9 +7045,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7079,9 +7079,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7113,9 +7113,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7176,9 +7176,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7208,9 +7208,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7238,9 +7238,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -7252,9 +7252,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -7273,9 +7273,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7307,9 +7307,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7340,9 +7340,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7373,9 +7373,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7407,9 +7407,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7441,9 +7441,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7475,9 +7475,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7538,9 +7538,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7570,9 +7570,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7600,9 +7600,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -7614,9 +7614,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -7635,9 +7635,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7669,9 +7669,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7702,9 +7702,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7735,9 +7735,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7769,9 +7769,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7803,9 +7803,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7837,9 +7837,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7900,9 +7900,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -7932,9 +7932,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7962,9 +7962,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -7976,9 +7976,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -7997,9 +7997,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8031,9 +8031,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8064,9 +8064,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8097,9 +8097,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8131,9 +8131,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8165,9 +8165,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8199,9 +8199,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8262,9 +8262,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8294,9 +8294,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -8324,9 +8324,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -8338,9 +8338,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -8359,9 +8359,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8393,9 +8393,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8426,9 +8426,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8459,9 +8459,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8493,9 +8493,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8527,9 +8527,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8561,9 +8561,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8624,9 +8624,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8656,9 +8656,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -8686,9 +8686,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -8700,9 +8700,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -8721,9 +8721,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8755,9 +8755,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8788,9 +8788,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8821,9 +8821,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8855,9 +8855,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8889,9 +8889,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8923,9 +8923,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -8986,9 +8986,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9018,9 +9018,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -9048,9 +9048,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -9062,9 +9062,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -9083,9 +9083,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9117,9 +9117,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9150,9 +9150,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9183,9 +9183,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9217,9 +9217,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9251,9 +9251,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9285,9 +9285,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9348,9 +9348,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9380,9 +9380,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -9410,9 +9410,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -9424,9 +9424,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -9445,9 +9445,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9479,9 +9479,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9512,9 +9512,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9545,9 +9545,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9579,9 +9579,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9613,9 +9613,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9647,9 +9647,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9710,9 +9710,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9742,9 +9742,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -9892,9 +9892,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -9906,9 +9906,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -9927,9 +9927,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9961,9 +9961,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -9994,9 +9994,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10026,9 +10026,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -10056,9 +10056,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -10070,9 +10070,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -10091,9 +10091,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10125,9 +10125,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10159,9 +10159,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10193,9 +10193,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10227,9 +10227,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10276,9 +10276,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10309,9 +10309,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10342,9 +10342,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10376,9 +10376,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10410,9 +10410,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10444,9 +10444,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10477,9 +10477,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10509,9 +10509,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -10554,9 +10554,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -10568,9 +10568,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -10589,9 +10589,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10623,9 +10623,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10672,9 +10672,9 @@ "volume": 19.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10706,9 +10706,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10739,9 +10739,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -10769,9 +10769,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -10783,9 +10783,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -10804,9 +10804,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10837,9 +10837,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -10912,9 +10912,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -10926,9 +10926,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -10947,9 +10947,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10981,9 +10981,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11014,9 +11014,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11046,9 +11046,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -11091,9 +11091,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -11105,9 +11105,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -11126,9 +11126,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11160,9 +11160,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11193,9 +11193,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11225,9 +11225,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -11255,9 +11255,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -11269,9 +11269,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -11290,9 +11290,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11324,9 +11324,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11357,9 +11357,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11389,9 +11389,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -11419,9 +11419,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -11433,9 +11433,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -11454,9 +11454,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11488,9 +11488,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11521,9 +11521,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11553,9 +11553,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -11583,9 +11583,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -11597,9 +11597,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -11618,9 +11618,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11652,9 +11652,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11685,9 +11685,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11717,9 +11717,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -11747,9 +11747,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -11761,9 +11761,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -11782,9 +11782,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11816,9 +11816,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11849,9 +11849,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11881,9 +11881,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -11911,9 +11911,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -11925,9 +11925,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -11946,9 +11946,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11980,9 +11980,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12013,9 +12013,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12045,9 +12045,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -12075,9 +12075,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -12089,9 +12089,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -12110,9 +12110,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12144,9 +12144,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12177,9 +12177,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12209,9 +12209,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -12239,9 +12239,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -12253,9 +12253,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -12274,9 +12274,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12308,9 +12308,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12341,9 +12341,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12373,9 +12373,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -12403,9 +12403,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -12417,9 +12417,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -12438,9 +12438,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12472,9 +12472,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12506,9 +12506,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12540,9 +12540,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12573,9 +12573,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -12603,9 +12603,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -12617,9 +12617,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -12638,9 +12638,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12672,9 +12672,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12705,9 +12705,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12737,9 +12737,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -12767,9 +12767,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -12781,9 +12781,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -12802,9 +12802,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12836,9 +12836,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12870,9 +12870,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12904,9 +12904,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12938,9 +12938,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12972,9 +12972,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13005,9 +13005,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13037,9 +13037,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -13067,9 +13067,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -13081,9 +13081,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -13102,9 +13102,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13136,9 +13136,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13169,9 +13169,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13201,9 +13201,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -13231,9 +13231,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -13245,9 +13245,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -13266,9 +13266,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13300,9 +13300,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13334,9 +13334,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13368,9 +13368,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13402,9 +13402,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13436,9 +13436,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13469,9 +13469,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13501,9 +13501,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -13531,9 +13531,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -13545,9 +13545,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -13566,9 +13566,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13600,9 +13600,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13633,9 +13633,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13665,9 +13665,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -13695,9 +13695,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -13709,9 +13709,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -13730,9 +13730,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13764,9 +13764,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13798,9 +13798,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13832,9 +13832,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13866,9 +13866,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13900,9 +13900,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13933,9 +13933,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13965,9 +13965,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -13995,9 +13995,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -14009,9 +14009,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -14030,9 +14030,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14064,9 +14064,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14097,9 +14097,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14129,9 +14129,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14159,9 +14159,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -14173,9 +14173,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -14194,9 +14194,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14228,9 +14228,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14262,9 +14262,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14296,9 +14296,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14330,9 +14330,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14364,9 +14364,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14397,9 +14397,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14429,9 +14429,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14459,9 +14459,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -14473,9 +14473,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -14494,9 +14494,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14528,9 +14528,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14561,9 +14561,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14593,9 +14593,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14623,9 +14623,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -14637,9 +14637,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -14658,9 +14658,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14692,9 +14692,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14726,9 +14726,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14760,9 +14760,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14794,9 +14794,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14828,9 +14828,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14861,9 +14861,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14893,9 +14893,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14923,9 +14923,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -14937,9 +14937,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -14958,9 +14958,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14992,9 +14992,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15025,9 +15025,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15057,9 +15057,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15087,9 +15087,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -15101,9 +15101,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -15122,9 +15122,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15156,9 +15156,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15190,9 +15190,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15224,9 +15224,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15258,9 +15258,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15292,9 +15292,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15325,9 +15325,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15357,9 +15357,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15387,9 +15387,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -15401,9 +15401,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -15422,9 +15422,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15456,9 +15456,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15489,9 +15489,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15521,9 +15521,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15551,9 +15551,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -15565,9 +15565,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -15586,9 +15586,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15620,9 +15620,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15654,9 +15654,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15688,9 +15688,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15722,9 +15722,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15756,9 +15756,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15789,9 +15789,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15821,9 +15821,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15851,9 +15851,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -15865,9 +15865,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -15886,9 +15886,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15920,9 +15920,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15953,9 +15953,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15985,9 +15985,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16015,9 +16015,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -16029,9 +16029,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 35.910000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -16050,9 +16050,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16084,9 +16084,9 @@ "volume": 7.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16118,9 +16118,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16152,9 +16152,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16186,9 +16186,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16220,9 +16220,9 @@ "volume": 8.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16253,9 +16253,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16285,9 +16285,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16405,9 +16405,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -16419,9 +16419,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -16440,9 +16440,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16474,9 +16474,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16507,9 +16507,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16539,9 +16539,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[011481812b][OT2_S_v2_7_P20S_None_Walkthrough].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[011481812b][OT2_S_v2_7_P20S_None_Walkthrough].json index 387b9f0e3f5..3a1a9bca236 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[011481812b][OT2_S_v2_7_P20S_None_Walkthrough].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[011481812b][OT2_S_v2_7_P20S_None_Walkthrough].json @@ -2348,9 +2348,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -2362,9 +2362,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 30.950000000000003, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -2398,9 +2398,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2432,9 +2432,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2466,9 +2466,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2500,9 +2500,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2534,9 +2534,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2568,9 +2568,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2602,9 +2602,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2636,9 +2636,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2670,9 +2670,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2704,9 +2704,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2738,9 +2738,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2772,9 +2772,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2806,9 +2806,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2840,9 +2840,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2874,9 +2874,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2908,9 +2908,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2942,9 +2942,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -2976,9 +2976,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3010,9 +3010,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3059,9 +3059,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3093,9 +3093,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3142,9 +3142,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3176,9 +3176,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3210,9 +3210,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3243,9 +3243,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3276,9 +3276,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3310,9 +3310,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3343,9 +3343,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3376,9 +3376,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3410,9 +3410,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3443,9 +3443,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3475,9 +3475,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -3505,9 +3505,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -3519,9 +3519,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 30.950000000000003, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -3555,9 +3555,9 @@ "volume": 4.444444444444445, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3589,9 +3589,9 @@ "volume": 6.666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3638,9 +3638,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3672,9 +3672,9 @@ "volume": 4.444444444444445, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3706,9 +3706,9 @@ "volume": 6.666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3755,9 +3755,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3789,9 +3789,9 @@ "volume": 4.444444444444445, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3823,9 +3823,9 @@ "volume": 6.666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3872,9 +3872,9 @@ "volume": 13.333333333333334, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3906,9 +3906,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3940,9 +3940,9 @@ "volume": 2.5, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -3974,9 +3974,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4008,9 +4008,9 @@ "volume": 2.5, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4056,9 +4056,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4088,9 +4088,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -4118,9 +4118,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -4132,9 +4132,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 30.950000000000003, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -4153,9 +4153,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4187,9 +4187,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4221,9 +4221,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4255,9 +4255,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4289,9 +4289,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4323,9 +4323,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4357,9 +4357,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4391,9 +4391,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4425,9 +4425,9 @@ "volume": 13.333333333333332, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4458,9 +4458,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -4488,9 +4488,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -4502,9 +4502,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 30.950000000000003, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -4523,9 +4523,9 @@ "volume": 14.333333333333332, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4557,9 +4557,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4591,9 +4591,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4625,9 +4625,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4659,9 +4659,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4693,9 +4693,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4727,9 +4727,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4761,9 +4761,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4795,9 +4795,9 @@ "volume": 1.6666666666666667, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4828,9 +4828,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -4860,9 +4860,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0190369ce5][Flex_S_v2_16_P1000_96_GRIP_HS_MB_TC_TM_DeckConfiguration1NoFixtures].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0190369ce5][Flex_S_v2_16_P1000_96_GRIP_HS_MB_TC_TM_DeckConfiguration1NoFixtures].json index df7062d67b5..3d1cdd1d3c1 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0190369ce5][Flex_S_v2_16_P1000_96_GRIP_HS_MB_TC_TM_DeckConfiguration1NoFixtures].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0190369ce5][Flex_S_v2_16_P1000_96_GRIP_HS_MB_TC_TM_DeckConfiguration1NoFixtures].json @@ -10789,9 +10789,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -10921,9 +10921,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -11121,9 +11121,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -11253,9 +11253,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0903a95825][Flex_S_v2_19_QIASeq_FX_48x].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0903a95825][Flex_S_v2_19_QIASeq_FX_48x].json index d1d7e581f85..a7e09aba292 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0903a95825][Flex_S_v2_19_QIASeq_FX_48x].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0903a95825][Flex_S_v2_19_QIASeq_FX_48x].json @@ -20183,9 +20183,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20583,9 +20583,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20983,9 +20983,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -23990,9 +23990,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -24487,9 +24487,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -24984,9 +24984,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -26804,9 +26804,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27301,9 +27301,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27798,9 +27798,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27956,9 +27956,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28086,9 +28086,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28216,9 +28216,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28498,9 +28498,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28694,9 +28694,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28890,9 +28890,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29265,9 +29265,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29509,9 +29509,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29753,9 +29753,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35050,9 +35050,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35547,9 +35547,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36044,9 +36044,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -37850,9 +37850,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -38333,9 +38333,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -38816,9 +38816,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -41809,9 +41809,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42292,9 +42292,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42775,9 +42775,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42933,9 +42933,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -43063,9 +43063,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -43193,9 +43193,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44732,9 +44732,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44962,9 +44962,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45192,9 +45192,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -56010,9 +56010,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -56507,9 +56507,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -57004,9 +57004,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -58824,9 +58824,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -59321,9 +59321,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -59818,9 +59818,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -62861,9 +62861,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -63358,9 +63358,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -63855,9 +63855,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -64013,9 +64013,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -64143,9 +64143,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -64273,9 +64273,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -64589,9 +64589,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -64819,9 +64819,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -65049,9 +65049,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -65424,9 +65424,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -65668,9 +65668,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -65912,9 +65912,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0a9ef592c8][Flex_S_v2_18_Illumina_DNA_Prep_48x].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0a9ef592c8][Flex_S_v2_18_Illumina_DNA_Prep_48x].json index f9343e792c6..705c9658155 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0a9ef592c8][Flex_S_v2_18_Illumina_DNA_Prep_48x].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0a9ef592c8][Flex_S_v2_18_Illumina_DNA_Prep_48x].json @@ -14067,9 +14067,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14199,9 +14199,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14331,9 +14331,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14910,9 +14910,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15342,9 +15342,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15774,9 +15774,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19117,9 +19117,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19549,9 +19549,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19981,9 +19981,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22137,9 +22137,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22569,9 +22569,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -23001,9 +23001,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -26344,9 +26344,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -26776,9 +26776,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27208,9 +27208,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27588,9 +27588,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27940,9 +27940,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28292,9 +28292,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36140,9 +36140,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36540,9 +36540,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36940,9 +36940,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39946,9 +39946,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40346,9 +40346,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40746,9 +40746,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42565,9 +42565,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42965,9 +42965,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -43365,9 +43365,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -43745,9 +43745,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44097,9 +44097,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44449,9 +44449,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46022,9 +46022,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46286,9 +46286,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46550,9 +46550,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46845,9 +46845,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -47009,9 +47009,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -47173,9 +47173,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0c4ae179bb][OT2_S_v2_15_P300M_P20S_HS_TC_TM_SmokeTestV3].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0c4ae179bb][OT2_S_v2_15_P300M_P20S_HS_TC_TM_SmokeTestV3].json index c5a57c18fc4..84e33cd17bd 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0c4ae179bb][OT2_S_v2_15_P300M_P20S_HS_TC_TM_SmokeTestV3].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[0c4ae179bb][OT2_S_v2_15_P300M_P20S_HS_TC_TM_SmokeTestV3].json @@ -11844,9 +11844,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15853,9 +15853,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16208,9 +16208,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16562,9 +16562,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16840,9 +16840,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16939,9 +16939,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[134037b2aa][OT2_X_v6_P300M_P20S_HS_MM_TM_TC_AllMods].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[134037b2aa][OT2_X_v6_P300M_P20S_HS_MM_TM_TC_AllMods].json index 19b3454722f..a0e4e4b52b4 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[134037b2aa][OT2_X_v6_P300M_P20S_HS_MM_TM_TC_AllMods].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[134037b2aa][OT2_X_v6_P300M_P20S_HS_MM_TM_TC_AllMods].json @@ -6112,9 +6112,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -6147,8 +6147,8 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -6181,8 +6181,8 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -6214,9 +6214,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -6244,9 +6244,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -6279,8 +6279,8 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -6313,8 +6313,8 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -6346,9 +6346,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -6376,9 +6376,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -6411,8 +6411,8 @@ "volume": 25.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -6445,8 +6445,8 @@ "volume": 25.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -6478,9 +6478,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -6508,9 +6508,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -6543,8 +6543,8 @@ "volume": 25.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -6577,8 +6577,8 @@ "volume": 25.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -6610,9 +6610,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -6640,9 +6640,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -6675,8 +6675,8 @@ "volume": 25.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -6709,8 +6709,8 @@ "volume": 25.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -6742,9 +6742,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -6772,9 +6772,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -6807,8 +6807,8 @@ "volume": 25.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -6841,8 +6841,8 @@ "volume": 25.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -6874,9 +6874,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -6904,9 +6904,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -6939,8 +6939,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -6973,8 +6973,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -7006,9 +7006,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7036,9 +7036,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -7071,8 +7071,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -7105,8 +7105,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -7138,9 +7138,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7168,9 +7168,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -7203,8 +7203,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -7237,8 +7237,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -7270,9 +7270,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7300,9 +7300,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -7335,8 +7335,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -7369,8 +7369,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -7402,9 +7402,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7432,9 +7432,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -7467,8 +7467,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -7501,8 +7501,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -7534,9 +7534,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7564,9 +7564,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -7599,8 +7599,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -7633,8 +7633,8 @@ "volume": 22.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -7666,9 +7666,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[194e3c49bb][pl_Normalization_with_PCR].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[194e3c49bb][pl_Normalization_with_PCR].json index 1dad0a643db..15d4ad5cd55 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[194e3c49bb][pl_Normalization_with_PCR].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[194e3c49bb][pl_Normalization_with_PCR].json @@ -6021,9 +6021,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -6636,9 +6636,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -7478,9 +7478,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -8322,9 +8322,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -9166,9 +9166,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[19c783e363][Flex_X_v8_P1000_96_HS_GRIP_TC_TM_GripperCollisionWithTips].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[19c783e363][Flex_X_v8_P1000_96_HS_GRIP_TC_TM_GripperCollisionWithTips].json index 7a817b9879d..bc40fdbb1d4 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[19c783e363][Flex_X_v8_P1000_96_HS_GRIP_TC_TM_GripperCollisionWithTips].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[19c783e363][Flex_X_v8_P1000_96_HS_GRIP_TC_TM_GripperCollisionWithTips].json @@ -12022,9 +12022,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -12057,8 +12057,8 @@ "volume": 100.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -12091,8 +12091,8 @@ "volume": 100.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -12165,9 +12165,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -12200,8 +12200,8 @@ "volume": 100.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -12234,8 +12234,8 @@ "volume": 100.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", @@ -12322,9 +12322,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -12357,8 +12357,8 @@ "volume": 200.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 1.0 }, "origin": "bottom", @@ -12391,8 +12391,8 @@ "volume": 200.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, + "x": 0.0, + "y": 0.0, "z": 0.5 }, "origin": "bottom", diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1b73b8a126][pl_Dynabeads_IP_Flex_96well_RIT_final].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1b73b8a126][pl_Dynabeads_IP_Flex_96well_RIT_final].json index d4ea924e585..29df1897867 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1b73b8a126][pl_Dynabeads_IP_Flex_96well_RIT_final].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1b73b8a126][pl_Dynabeads_IP_Flex_96well_RIT_final].json @@ -16970,9 +16970,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -17262,9 +17262,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -17492,9 +17492,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -17722,9 +17722,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -17952,9 +17952,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -18182,9 +18182,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -18412,9 +18412,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -18642,9 +18642,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -18872,9 +18872,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19102,9 +19102,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19332,9 +19332,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19562,9 +19562,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19792,9 +19792,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20103,9 +20103,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20366,9 +20366,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20629,9 +20629,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20892,9 +20892,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21155,9 +21155,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21418,9 +21418,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21681,9 +21681,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21944,9 +21944,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22207,9 +22207,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22470,9 +22470,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22733,9 +22733,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22996,9 +22996,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29040,9 +29040,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29408,9 +29408,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29638,9 +29638,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29868,9 +29868,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -30098,9 +30098,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -30328,9 +30328,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -30558,9 +30558,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -30788,9 +30788,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31018,9 +31018,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31248,9 +31248,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31478,9 +31478,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31708,9 +31708,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31938,9 +31938,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34042,9 +34042,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34393,9 +34393,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34623,9 +34623,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34853,9 +34853,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35083,9 +35083,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35313,9 +35313,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35543,9 +35543,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35773,9 +35773,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36003,9 +36003,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36233,9 +36233,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36463,9 +36463,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36693,9 +36693,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36923,9 +36923,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39027,9 +39027,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39378,9 +39378,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39608,9 +39608,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39838,9 +39838,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40068,9 +40068,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40298,9 +40298,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40528,9 +40528,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40758,9 +40758,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40988,9 +40988,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -41218,9 +41218,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -41448,9 +41448,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -41678,9 +41678,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -41908,9 +41908,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44012,9 +44012,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44363,9 +44363,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44593,9 +44593,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44823,9 +44823,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45053,9 +45053,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45283,9 +45283,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45513,9 +45513,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45743,9 +45743,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45973,9 +45973,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46203,9 +46203,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46433,9 +46433,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46663,9 +46663,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46893,9 +46893,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -52985,9 +52985,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1c19a2055c][OT2_S_v2_4_P300M_None_MM_TM_Zymo].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1c19a2055c][OT2_S_v2_4_P300M_None_MM_TM_Zymo].json index fc41febf592..25c3ac466ec 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1c19a2055c][OT2_S_v2_4_P300M_None_MM_TM_Zymo].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1c19a2055c][OT2_S_v2_4_P300M_None_MM_TM_Zymo].json @@ -10682,9 +10682,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -10696,9 +10696,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -10717,9 +10717,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10751,9 +10751,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10785,9 +10785,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10819,9 +10819,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10853,9 +10853,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10887,9 +10887,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10921,9 +10921,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10955,9 +10955,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -10989,9 +10989,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11023,9 +11023,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11057,9 +11057,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11091,9 +11091,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11125,9 +11125,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11159,9 +11159,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11193,9 +11193,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11227,9 +11227,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11261,9 +11261,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11295,9 +11295,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11329,9 +11329,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11363,9 +11363,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11397,9 +11397,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11431,9 +11431,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11465,9 +11465,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11499,9 +11499,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11533,9 +11533,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11567,9 +11567,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11601,9 +11601,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11635,9 +11635,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11669,9 +11669,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11703,9 +11703,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11737,9 +11737,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11771,9 +11771,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11805,9 +11805,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11839,9 +11839,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11873,9 +11873,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11907,9 +11907,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11941,9 +11941,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -11975,9 +11975,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12009,9 +12009,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12043,9 +12043,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12077,9 +12077,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12111,9 +12111,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12145,9 +12145,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12179,9 +12179,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12213,9 +12213,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12247,9 +12247,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12281,9 +12281,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12315,9 +12315,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12349,9 +12349,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12383,9 +12383,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12417,9 +12417,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12451,9 +12451,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12485,9 +12485,9 @@ "volume": 3.333333333333343, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12519,9 +12519,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12553,9 +12553,9 @@ "volume": 13.333333333333343, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12587,9 +12587,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12621,9 +12621,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12655,9 +12655,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12689,9 +12689,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12723,9 +12723,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12757,9 +12757,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12791,9 +12791,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12825,9 +12825,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12859,9 +12859,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12893,9 +12893,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12927,9 +12927,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12961,9 +12961,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -12995,9 +12995,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13029,9 +13029,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13063,9 +13063,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13097,9 +13097,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13131,9 +13131,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13165,9 +13165,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13199,9 +13199,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13233,9 +13233,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13267,9 +13267,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13301,9 +13301,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13335,9 +13335,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13369,9 +13369,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13403,9 +13403,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13437,9 +13437,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13471,9 +13471,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13505,9 +13505,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13539,9 +13539,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13573,9 +13573,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13607,9 +13607,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13641,9 +13641,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13675,9 +13675,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13709,9 +13709,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13743,9 +13743,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13777,9 +13777,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13811,9 +13811,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13845,9 +13845,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13879,9 +13879,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13913,9 +13913,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13947,9 +13947,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -13981,9 +13981,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14015,9 +14015,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14049,9 +14049,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14083,9 +14083,9 @@ "volume": 3.333333333333343, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14117,9 +14117,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14151,9 +14151,9 @@ "volume": 13.333333333333343, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14185,9 +14185,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14219,9 +14219,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14253,9 +14253,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14287,9 +14287,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14321,9 +14321,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14355,9 +14355,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14389,9 +14389,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14423,9 +14423,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14457,9 +14457,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14491,9 +14491,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14525,9 +14525,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14559,9 +14559,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14593,9 +14593,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14627,9 +14627,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14661,9 +14661,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14695,9 +14695,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14729,9 +14729,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14763,9 +14763,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14797,9 +14797,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14831,9 +14831,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14865,9 +14865,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14899,9 +14899,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14933,9 +14933,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -14967,9 +14967,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15001,9 +15001,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15035,9 +15035,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15069,9 +15069,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15103,9 +15103,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15137,9 +15137,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15171,9 +15171,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15205,9 +15205,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15239,9 +15239,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15273,9 +15273,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15307,9 +15307,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15341,9 +15341,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15375,9 +15375,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15409,9 +15409,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15443,9 +15443,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15477,9 +15477,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15511,9 +15511,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15545,9 +15545,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15579,9 +15579,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15613,9 +15613,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15647,9 +15647,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15681,9 +15681,9 @@ "volume": 3.333333333333343, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15715,9 +15715,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15749,9 +15749,9 @@ "volume": 13.333333333333343, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15782,9 +15782,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15815,9 +15815,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -15848,9 +15848,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15921,9 +15921,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -15935,9 +15935,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -15971,9 +15971,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16005,9 +16005,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16039,9 +16039,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16073,9 +16073,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16107,9 +16107,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16141,9 +16141,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16175,9 +16175,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16209,9 +16209,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16243,9 +16243,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16277,9 +16277,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16311,9 +16311,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16345,9 +16345,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16379,9 +16379,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16413,9 +16413,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16447,9 +16447,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16481,9 +16481,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16515,9 +16515,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16549,9 +16549,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16583,9 +16583,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16617,9 +16617,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16651,9 +16651,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16685,9 +16685,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16719,9 +16719,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16753,9 +16753,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16787,9 +16787,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16821,9 +16821,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16855,9 +16855,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16889,9 +16889,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16923,9 +16923,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16957,9 +16957,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -16991,9 +16991,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17025,9 +17025,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17059,9 +17059,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17093,9 +17093,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17127,9 +17127,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17161,9 +17161,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17195,9 +17195,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17229,9 +17229,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17263,9 +17263,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17297,9 +17297,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17331,9 +17331,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17365,9 +17365,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17399,9 +17399,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17433,9 +17433,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17467,9 +17467,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17501,9 +17501,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17535,9 +17535,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17569,9 +17569,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17603,9 +17603,9 @@ "volume": 6.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17637,9 +17637,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17671,9 +17671,9 @@ "volume": 16.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17704,9 +17704,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17737,9 +17737,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17771,9 +17771,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17820,9 +17820,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17854,9 +17854,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17888,9 +17888,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17922,9 +17922,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17956,9 +17956,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -17990,9 +17990,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18024,9 +18024,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18058,9 +18058,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18092,9 +18092,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18126,9 +18126,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18160,9 +18160,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18194,9 +18194,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18228,9 +18228,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18262,9 +18262,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18296,9 +18296,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18330,9 +18330,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18364,9 +18364,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18398,9 +18398,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18432,9 +18432,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18466,9 +18466,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18500,9 +18500,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18534,9 +18534,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18568,9 +18568,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18602,9 +18602,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18636,9 +18636,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18670,9 +18670,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18704,9 +18704,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18738,9 +18738,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18772,9 +18772,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18806,9 +18806,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18840,9 +18840,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18874,9 +18874,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18908,9 +18908,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18942,9 +18942,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -18976,9 +18976,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19010,9 +19010,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19044,9 +19044,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19078,9 +19078,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19112,9 +19112,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19146,9 +19146,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19180,9 +19180,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19214,9 +19214,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19248,9 +19248,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19282,9 +19282,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19316,9 +19316,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19350,9 +19350,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19384,9 +19384,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19418,9 +19418,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19452,9 +19452,9 @@ "volume": 6.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19486,9 +19486,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19520,9 +19520,9 @@ "volume": 16.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19553,9 +19553,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19586,9 +19586,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19620,9 +19620,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19669,9 +19669,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19703,9 +19703,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19737,9 +19737,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19771,9 +19771,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19805,9 +19805,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19839,9 +19839,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19873,9 +19873,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19907,9 +19907,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19941,9 +19941,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -19975,9 +19975,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20009,9 +20009,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20043,9 +20043,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20077,9 +20077,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20111,9 +20111,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20145,9 +20145,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20179,9 +20179,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20213,9 +20213,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20247,9 +20247,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20281,9 +20281,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20315,9 +20315,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20349,9 +20349,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20383,9 +20383,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20417,9 +20417,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20451,9 +20451,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20485,9 +20485,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20519,9 +20519,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20553,9 +20553,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20587,9 +20587,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20621,9 +20621,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20655,9 +20655,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20689,9 +20689,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20723,9 +20723,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20757,9 +20757,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20791,9 +20791,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20825,9 +20825,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20859,9 +20859,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20893,9 +20893,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20927,9 +20927,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20961,9 +20961,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -20995,9 +20995,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21029,9 +21029,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21063,9 +21063,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21097,9 +21097,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21131,9 +21131,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21165,9 +21165,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21199,9 +21199,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21233,9 +21233,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21267,9 +21267,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21301,9 +21301,9 @@ "volume": 6.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21335,9 +21335,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21369,9 +21369,9 @@ "volume": 16.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21402,9 +21402,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21435,9 +21435,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21469,9 +21469,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21518,9 +21518,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21552,9 +21552,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21586,9 +21586,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21620,9 +21620,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21654,9 +21654,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21688,9 +21688,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21722,9 +21722,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21756,9 +21756,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21790,9 +21790,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21824,9 +21824,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21858,9 +21858,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21892,9 +21892,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21926,9 +21926,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21960,9 +21960,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -21994,9 +21994,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22028,9 +22028,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22062,9 +22062,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22096,9 +22096,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22130,9 +22130,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22164,9 +22164,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22198,9 +22198,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22232,9 +22232,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22266,9 +22266,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22300,9 +22300,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22334,9 +22334,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22368,9 +22368,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22402,9 +22402,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22436,9 +22436,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22470,9 +22470,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22504,9 +22504,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22538,9 +22538,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22572,9 +22572,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22606,9 +22606,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22640,9 +22640,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22674,9 +22674,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22708,9 +22708,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22742,9 +22742,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22776,9 +22776,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22810,9 +22810,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22844,9 +22844,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22878,9 +22878,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22912,9 +22912,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22946,9 +22946,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -22980,9 +22980,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23014,9 +23014,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23048,9 +23048,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23082,9 +23082,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23116,9 +23116,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23150,9 +23150,9 @@ "volume": 6.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23184,9 +23184,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23218,9 +23218,9 @@ "volume": 16.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23251,9 +23251,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23284,9 +23284,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23318,9 +23318,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23367,9 +23367,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23401,9 +23401,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23435,9 +23435,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23469,9 +23469,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23503,9 +23503,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23537,9 +23537,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23571,9 +23571,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23605,9 +23605,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23639,9 +23639,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23673,9 +23673,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23707,9 +23707,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23741,9 +23741,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23775,9 +23775,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23809,9 +23809,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23843,9 +23843,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23877,9 +23877,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23911,9 +23911,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23945,9 +23945,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -23979,9 +23979,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24013,9 +24013,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24047,9 +24047,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24081,9 +24081,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24115,9 +24115,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24149,9 +24149,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24183,9 +24183,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24217,9 +24217,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24251,9 +24251,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24285,9 +24285,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24319,9 +24319,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24353,9 +24353,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24387,9 +24387,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24421,9 +24421,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24455,9 +24455,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24489,9 +24489,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24523,9 +24523,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24557,9 +24557,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24591,9 +24591,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24625,9 +24625,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24659,9 +24659,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24693,9 +24693,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24727,9 +24727,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24761,9 +24761,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24795,9 +24795,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24829,9 +24829,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24863,9 +24863,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24897,9 +24897,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24931,9 +24931,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24965,9 +24965,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -24999,9 +24999,9 @@ "volume": 6.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25033,9 +25033,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25067,9 +25067,9 @@ "volume": 16.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25100,9 +25100,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25133,9 +25133,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25166,9 +25166,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -25211,9 +25211,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -25225,9 +25225,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -25246,9 +25246,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25280,9 +25280,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25314,9 +25314,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25348,9 +25348,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25382,9 +25382,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25416,9 +25416,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25450,9 +25450,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25484,9 +25484,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25518,9 +25518,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25552,9 +25552,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25586,9 +25586,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25620,9 +25620,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25654,9 +25654,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25688,9 +25688,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25722,9 +25722,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25756,9 +25756,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25790,9 +25790,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25824,9 +25824,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25858,9 +25858,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25892,9 +25892,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25926,9 +25926,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25960,9 +25960,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -25994,9 +25994,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26028,9 +26028,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26062,9 +26062,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26096,9 +26096,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26130,9 +26130,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26164,9 +26164,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26198,9 +26198,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26232,9 +26232,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26266,9 +26266,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26300,9 +26300,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26334,9 +26334,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26368,9 +26368,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26402,9 +26402,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26436,9 +26436,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26470,9 +26470,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26504,9 +26504,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26538,9 +26538,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26572,9 +26572,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26606,9 +26606,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26640,9 +26640,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26674,9 +26674,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26708,9 +26708,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26742,9 +26742,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26776,9 +26776,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26810,9 +26810,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26844,9 +26844,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26878,9 +26878,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26912,9 +26912,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26946,9 +26946,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -26980,9 +26980,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27014,9 +27014,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27048,9 +27048,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27082,9 +27082,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27116,9 +27116,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27150,9 +27150,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27184,9 +27184,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27218,9 +27218,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27252,9 +27252,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27286,9 +27286,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27320,9 +27320,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27354,9 +27354,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27388,9 +27388,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27422,9 +27422,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27456,9 +27456,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27490,9 +27490,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27524,9 +27524,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27558,9 +27558,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27592,9 +27592,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27626,9 +27626,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27660,9 +27660,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27694,9 +27694,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27728,9 +27728,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27762,9 +27762,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27796,9 +27796,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27830,9 +27830,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27864,9 +27864,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27898,9 +27898,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27932,9 +27932,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -27966,9 +27966,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28000,9 +28000,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28034,9 +28034,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28068,9 +28068,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28102,9 +28102,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28136,9 +28136,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28170,9 +28170,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28204,9 +28204,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28238,9 +28238,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28272,9 +28272,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28306,9 +28306,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28340,9 +28340,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28374,9 +28374,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28408,9 +28408,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28442,9 +28442,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28476,9 +28476,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28510,9 +28510,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28544,9 +28544,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28578,9 +28578,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28612,9 +28612,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28646,9 +28646,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28680,9 +28680,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28714,9 +28714,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28748,9 +28748,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28782,9 +28782,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28816,9 +28816,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28850,9 +28850,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28884,9 +28884,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28918,9 +28918,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28952,9 +28952,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -28986,9 +28986,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29020,9 +29020,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29054,9 +29054,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29088,9 +29088,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29122,9 +29122,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29156,9 +29156,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29190,9 +29190,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29224,9 +29224,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29258,9 +29258,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29292,9 +29292,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29326,9 +29326,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29360,9 +29360,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29394,9 +29394,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29428,9 +29428,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29462,9 +29462,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29496,9 +29496,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29530,9 +29530,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29564,9 +29564,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29598,9 +29598,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29632,9 +29632,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29666,9 +29666,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29700,9 +29700,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29734,9 +29734,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29768,9 +29768,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29802,9 +29802,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29836,9 +29836,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29870,9 +29870,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29904,9 +29904,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29938,9 +29938,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -29972,9 +29972,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30006,9 +30006,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30040,9 +30040,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30074,9 +30074,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30108,9 +30108,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30142,9 +30142,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30176,9 +30176,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30210,9 +30210,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30244,9 +30244,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30278,9 +30278,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30312,9 +30312,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30346,9 +30346,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30380,9 +30380,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30414,9 +30414,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30448,9 +30448,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30482,9 +30482,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30516,9 +30516,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30550,9 +30550,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30599,9 +30599,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30633,9 +30633,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30667,9 +30667,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30701,9 +30701,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30735,9 +30735,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30769,9 +30769,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30803,9 +30803,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30837,9 +30837,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30871,9 +30871,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30905,9 +30905,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30939,9 +30939,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -30973,9 +30973,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31007,9 +31007,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31041,9 +31041,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31075,9 +31075,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31109,9 +31109,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31143,9 +31143,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31177,9 +31177,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31211,9 +31211,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31245,9 +31245,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31279,9 +31279,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31313,9 +31313,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31347,9 +31347,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31381,9 +31381,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31415,9 +31415,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31449,9 +31449,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31483,9 +31483,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31517,9 +31517,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31551,9 +31551,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31585,9 +31585,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31619,9 +31619,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31653,9 +31653,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31687,9 +31687,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31721,9 +31721,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31755,9 +31755,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31789,9 +31789,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31823,9 +31823,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31857,9 +31857,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31891,9 +31891,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31925,9 +31925,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31958,9 +31958,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -31991,9 +31991,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32024,9 +32024,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -32084,9 +32084,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -32098,9 +32098,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -32134,9 +32134,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32168,9 +32168,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32202,9 +32202,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32236,9 +32236,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32270,9 +32270,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32304,9 +32304,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32338,9 +32338,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32372,9 +32372,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32406,9 +32406,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32440,9 +32440,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32474,9 +32474,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32508,9 +32508,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32542,9 +32542,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32576,9 +32576,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32610,9 +32610,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32644,9 +32644,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32678,9 +32678,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32712,9 +32712,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32746,9 +32746,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32780,9 +32780,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32814,9 +32814,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32848,9 +32848,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32882,9 +32882,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32916,9 +32916,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32950,9 +32950,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -32984,9 +32984,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33018,9 +33018,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33052,9 +33052,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33086,9 +33086,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33120,9 +33120,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33154,9 +33154,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33188,9 +33188,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33222,9 +33222,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33256,9 +33256,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33290,9 +33290,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33324,9 +33324,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33358,9 +33358,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33392,9 +33392,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33426,9 +33426,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33460,9 +33460,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33494,9 +33494,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33528,9 +33528,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33562,9 +33562,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33596,9 +33596,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33630,9 +33630,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33664,9 +33664,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33698,9 +33698,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33732,9 +33732,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33766,9 +33766,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33800,9 +33800,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33834,9 +33834,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33867,9 +33867,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33900,9 +33900,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33934,9 +33934,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -33983,9 +33983,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34017,9 +34017,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34051,9 +34051,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34085,9 +34085,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34119,9 +34119,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34153,9 +34153,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34187,9 +34187,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34221,9 +34221,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34255,9 +34255,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34289,9 +34289,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34323,9 +34323,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34357,9 +34357,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34391,9 +34391,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34425,9 +34425,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34459,9 +34459,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34493,9 +34493,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34527,9 +34527,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34561,9 +34561,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34595,9 +34595,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34629,9 +34629,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34663,9 +34663,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34697,9 +34697,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34731,9 +34731,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34765,9 +34765,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34799,9 +34799,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34833,9 +34833,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34867,9 +34867,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34901,9 +34901,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34935,9 +34935,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -34969,9 +34969,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35003,9 +35003,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35037,9 +35037,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35071,9 +35071,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35105,9 +35105,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35139,9 +35139,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35173,9 +35173,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35207,9 +35207,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35241,9 +35241,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35275,9 +35275,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35309,9 +35309,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35343,9 +35343,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35377,9 +35377,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35411,9 +35411,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35445,9 +35445,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35479,9 +35479,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35513,9 +35513,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35547,9 +35547,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35581,9 +35581,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35615,9 +35615,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35649,9 +35649,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35683,9 +35683,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35716,9 +35716,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35749,9 +35749,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35783,9 +35783,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35832,9 +35832,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35866,9 +35866,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35900,9 +35900,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35934,9 +35934,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -35968,9 +35968,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36002,9 +36002,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36036,9 +36036,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36070,9 +36070,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36104,9 +36104,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36138,9 +36138,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36172,9 +36172,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36206,9 +36206,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36240,9 +36240,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36274,9 +36274,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36308,9 +36308,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36342,9 +36342,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36376,9 +36376,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36410,9 +36410,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36444,9 +36444,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36478,9 +36478,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36512,9 +36512,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36546,9 +36546,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36580,9 +36580,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36614,9 +36614,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36648,9 +36648,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36682,9 +36682,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36716,9 +36716,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36750,9 +36750,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36784,9 +36784,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36818,9 +36818,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36852,9 +36852,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36886,9 +36886,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36920,9 +36920,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36954,9 +36954,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -36988,9 +36988,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37022,9 +37022,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37056,9 +37056,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37090,9 +37090,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37124,9 +37124,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37158,9 +37158,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37192,9 +37192,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37226,9 +37226,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37260,9 +37260,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37294,9 +37294,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37328,9 +37328,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37362,9 +37362,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37396,9 +37396,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37430,9 +37430,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37464,9 +37464,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37498,9 +37498,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37532,9 +37532,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37565,9 +37565,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37598,9 +37598,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37631,9 +37631,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -37676,9 +37676,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -37690,9 +37690,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -37711,9 +37711,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37745,9 +37745,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37779,9 +37779,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37813,9 +37813,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37847,9 +37847,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37881,9 +37881,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37915,9 +37915,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37949,9 +37949,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -37983,9 +37983,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38017,9 +38017,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38051,9 +38051,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38085,9 +38085,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38119,9 +38119,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38153,9 +38153,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38187,9 +38187,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38221,9 +38221,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38255,9 +38255,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38289,9 +38289,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38323,9 +38323,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38357,9 +38357,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38391,9 +38391,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38425,9 +38425,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38459,9 +38459,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38493,9 +38493,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38527,9 +38527,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38561,9 +38561,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38595,9 +38595,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38629,9 +38629,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38663,9 +38663,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38697,9 +38697,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38731,9 +38731,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38765,9 +38765,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38799,9 +38799,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38833,9 +38833,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38867,9 +38867,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38901,9 +38901,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38935,9 +38935,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -38969,9 +38969,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39003,9 +39003,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39037,9 +39037,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39071,9 +39071,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39105,9 +39105,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39139,9 +39139,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39173,9 +39173,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39207,9 +39207,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39241,9 +39241,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39275,9 +39275,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39309,9 +39309,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39343,9 +39343,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39377,9 +39377,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39411,9 +39411,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39445,9 +39445,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39479,9 +39479,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39513,9 +39513,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39547,9 +39547,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39581,9 +39581,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39615,9 +39615,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39649,9 +39649,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39683,9 +39683,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39717,9 +39717,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39751,9 +39751,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39785,9 +39785,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39819,9 +39819,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39853,9 +39853,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39887,9 +39887,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39921,9 +39921,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39955,9 +39955,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -39989,9 +39989,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40023,9 +40023,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40057,9 +40057,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40091,9 +40091,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40125,9 +40125,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40159,9 +40159,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40193,9 +40193,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40227,9 +40227,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40261,9 +40261,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40295,9 +40295,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40329,9 +40329,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40363,9 +40363,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40397,9 +40397,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40431,9 +40431,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40465,9 +40465,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40499,9 +40499,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40533,9 +40533,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40567,9 +40567,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40601,9 +40601,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40635,9 +40635,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40669,9 +40669,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40703,9 +40703,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40737,9 +40737,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40771,9 +40771,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40805,9 +40805,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40839,9 +40839,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40873,9 +40873,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40907,9 +40907,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40941,9 +40941,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -40975,9 +40975,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41009,9 +41009,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41043,9 +41043,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41077,9 +41077,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41111,9 +41111,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41145,9 +41145,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41179,9 +41179,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41213,9 +41213,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41247,9 +41247,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41281,9 +41281,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41315,9 +41315,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41349,9 +41349,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41383,9 +41383,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41417,9 +41417,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41451,9 +41451,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41485,9 +41485,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41519,9 +41519,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41553,9 +41553,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41587,9 +41587,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41621,9 +41621,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41655,9 +41655,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41689,9 +41689,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41723,9 +41723,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41757,9 +41757,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41791,9 +41791,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41825,9 +41825,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41859,9 +41859,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41893,9 +41893,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41927,9 +41927,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41961,9 +41961,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -41995,9 +41995,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42029,9 +42029,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42063,9 +42063,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42097,9 +42097,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42131,9 +42131,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42165,9 +42165,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42199,9 +42199,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42233,9 +42233,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42267,9 +42267,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42301,9 +42301,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42335,9 +42335,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42369,9 +42369,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42403,9 +42403,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42437,9 +42437,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42471,9 +42471,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42505,9 +42505,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42539,9 +42539,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42573,9 +42573,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42607,9 +42607,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42641,9 +42641,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42675,9 +42675,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42709,9 +42709,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42743,9 +42743,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42777,9 +42777,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42811,9 +42811,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42845,9 +42845,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42879,9 +42879,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42913,9 +42913,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42947,9 +42947,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -42981,9 +42981,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43015,9 +43015,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43064,9 +43064,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43098,9 +43098,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43132,9 +43132,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43166,9 +43166,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43200,9 +43200,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43234,9 +43234,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43268,9 +43268,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43302,9 +43302,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43336,9 +43336,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43370,9 +43370,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43404,9 +43404,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43438,9 +43438,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43472,9 +43472,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43506,9 +43506,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43540,9 +43540,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43574,9 +43574,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43608,9 +43608,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43642,9 +43642,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43676,9 +43676,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43710,9 +43710,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43744,9 +43744,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43778,9 +43778,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43812,9 +43812,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43846,9 +43846,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43880,9 +43880,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43914,9 +43914,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43948,9 +43948,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -43982,9 +43982,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44016,9 +44016,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44050,9 +44050,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44084,9 +44084,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44118,9 +44118,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44152,9 +44152,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44186,9 +44186,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44220,9 +44220,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44254,9 +44254,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44288,9 +44288,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44322,9 +44322,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44356,9 +44356,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44390,9 +44390,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44423,9 +44423,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44456,9 +44456,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44489,9 +44489,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44549,9 +44549,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -44563,9 +44563,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -44599,9 +44599,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44633,9 +44633,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44667,9 +44667,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44701,9 +44701,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44735,9 +44735,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44769,9 +44769,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44803,9 +44803,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44837,9 +44837,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44871,9 +44871,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44905,9 +44905,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44939,9 +44939,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -44973,9 +44973,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45007,9 +45007,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45041,9 +45041,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45075,9 +45075,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45109,9 +45109,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45143,9 +45143,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45177,9 +45177,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45211,9 +45211,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45245,9 +45245,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45279,9 +45279,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45313,9 +45313,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45347,9 +45347,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45381,9 +45381,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45415,9 +45415,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45449,9 +45449,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45483,9 +45483,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45517,9 +45517,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45551,9 +45551,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45585,9 +45585,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45619,9 +45619,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45653,9 +45653,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45687,9 +45687,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45721,9 +45721,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45755,9 +45755,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45789,9 +45789,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45823,9 +45823,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45857,9 +45857,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45891,9 +45891,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45925,9 +45925,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45959,9 +45959,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -45993,9 +45993,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46027,9 +46027,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46061,9 +46061,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46095,9 +46095,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46129,9 +46129,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46163,9 +46163,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46197,9 +46197,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46231,9 +46231,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46265,9 +46265,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46299,9 +46299,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46332,9 +46332,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46365,9 +46365,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46399,9 +46399,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46448,9 +46448,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46482,9 +46482,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46516,9 +46516,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46550,9 +46550,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46584,9 +46584,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46618,9 +46618,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46652,9 +46652,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46686,9 +46686,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46720,9 +46720,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46754,9 +46754,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46788,9 +46788,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46822,9 +46822,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46856,9 +46856,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46890,9 +46890,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46924,9 +46924,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46958,9 +46958,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -46992,9 +46992,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47026,9 +47026,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47060,9 +47060,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47094,9 +47094,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47128,9 +47128,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47162,9 +47162,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47196,9 +47196,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47230,9 +47230,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47264,9 +47264,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47298,9 +47298,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47332,9 +47332,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47366,9 +47366,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47400,9 +47400,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47434,9 +47434,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47468,9 +47468,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47502,9 +47502,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47536,9 +47536,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47570,9 +47570,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47604,9 +47604,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47638,9 +47638,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47672,9 +47672,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47706,9 +47706,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47740,9 +47740,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47774,9 +47774,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47808,9 +47808,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47842,9 +47842,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47876,9 +47876,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47910,9 +47910,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47944,9 +47944,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -47978,9 +47978,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48012,9 +48012,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48046,9 +48046,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48080,9 +48080,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48114,9 +48114,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48148,9 +48148,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48181,9 +48181,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48214,9 +48214,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48248,9 +48248,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48297,9 +48297,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48331,9 +48331,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48365,9 +48365,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48399,9 +48399,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48433,9 +48433,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48467,9 +48467,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48501,9 +48501,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48535,9 +48535,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48569,9 +48569,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48603,9 +48603,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48637,9 +48637,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48671,9 +48671,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48705,9 +48705,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48739,9 +48739,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48773,9 +48773,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48807,9 +48807,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48841,9 +48841,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48875,9 +48875,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48909,9 +48909,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48943,9 +48943,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -48977,9 +48977,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49011,9 +49011,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49045,9 +49045,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49079,9 +49079,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49113,9 +49113,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49147,9 +49147,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49181,9 +49181,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49215,9 +49215,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49249,9 +49249,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49283,9 +49283,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49317,9 +49317,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49351,9 +49351,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49385,9 +49385,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49419,9 +49419,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49453,9 +49453,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49487,9 +49487,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49521,9 +49521,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49555,9 +49555,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49589,9 +49589,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49623,9 +49623,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49657,9 +49657,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49691,9 +49691,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49725,9 +49725,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49759,9 +49759,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49793,9 +49793,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49827,9 +49827,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49861,9 +49861,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49895,9 +49895,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49929,9 +49929,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49963,9 +49963,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -49997,9 +49997,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50030,9 +50030,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50063,9 +50063,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50096,9 +50096,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -50141,9 +50141,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -50155,9 +50155,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -50176,9 +50176,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50210,9 +50210,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50244,9 +50244,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50278,9 +50278,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50312,9 +50312,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50346,9 +50346,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50380,9 +50380,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50414,9 +50414,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50448,9 +50448,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50482,9 +50482,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50516,9 +50516,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50550,9 +50550,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50584,9 +50584,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50618,9 +50618,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50652,9 +50652,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50686,9 +50686,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50720,9 +50720,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50754,9 +50754,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50788,9 +50788,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50822,9 +50822,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50856,9 +50856,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50890,9 +50890,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50924,9 +50924,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50958,9 +50958,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -50992,9 +50992,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51026,9 +51026,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51060,9 +51060,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51094,9 +51094,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51128,9 +51128,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51162,9 +51162,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51196,9 +51196,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51230,9 +51230,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51264,9 +51264,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51298,9 +51298,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51332,9 +51332,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51366,9 +51366,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51400,9 +51400,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51434,9 +51434,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51468,9 +51468,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51502,9 +51502,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51536,9 +51536,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51570,9 +51570,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51604,9 +51604,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51638,9 +51638,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51672,9 +51672,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51706,9 +51706,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51740,9 +51740,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51774,9 +51774,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51808,9 +51808,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51842,9 +51842,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51876,9 +51876,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51910,9 +51910,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51944,9 +51944,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -51978,9 +51978,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52012,9 +52012,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52046,9 +52046,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52080,9 +52080,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52114,9 +52114,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52148,9 +52148,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52182,9 +52182,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52216,9 +52216,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52250,9 +52250,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52284,9 +52284,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52318,9 +52318,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52352,9 +52352,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52386,9 +52386,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52420,9 +52420,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52454,9 +52454,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52488,9 +52488,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52522,9 +52522,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52556,9 +52556,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52590,9 +52590,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52624,9 +52624,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52658,9 +52658,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52692,9 +52692,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52726,9 +52726,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52760,9 +52760,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52794,9 +52794,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52828,9 +52828,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52862,9 +52862,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52896,9 +52896,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52930,9 +52930,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52964,9 +52964,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -52998,9 +52998,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53032,9 +53032,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53066,9 +53066,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53100,9 +53100,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53134,9 +53134,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53168,9 +53168,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53202,9 +53202,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53236,9 +53236,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53270,9 +53270,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53304,9 +53304,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53338,9 +53338,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53372,9 +53372,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53406,9 +53406,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53440,9 +53440,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53474,9 +53474,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53508,9 +53508,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53542,9 +53542,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53576,9 +53576,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53610,9 +53610,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53644,9 +53644,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53678,9 +53678,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53712,9 +53712,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53746,9 +53746,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53780,9 +53780,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53814,9 +53814,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53848,9 +53848,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53882,9 +53882,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53916,9 +53916,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53950,9 +53950,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -53984,9 +53984,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54018,9 +54018,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54052,9 +54052,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54086,9 +54086,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54120,9 +54120,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54154,9 +54154,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54188,9 +54188,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54222,9 +54222,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54256,9 +54256,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54290,9 +54290,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54324,9 +54324,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54358,9 +54358,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54392,9 +54392,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54426,9 +54426,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54460,9 +54460,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54494,9 +54494,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54528,9 +54528,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54562,9 +54562,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54596,9 +54596,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54630,9 +54630,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54664,9 +54664,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54698,9 +54698,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54732,9 +54732,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54766,9 +54766,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54800,9 +54800,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54834,9 +54834,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54868,9 +54868,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54902,9 +54902,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54936,9 +54936,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -54970,9 +54970,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55004,9 +55004,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55038,9 +55038,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55072,9 +55072,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55106,9 +55106,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55140,9 +55140,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55174,9 +55174,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55208,9 +55208,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55242,9 +55242,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55276,9 +55276,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55310,9 +55310,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55344,9 +55344,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55378,9 +55378,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55412,9 +55412,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55446,9 +55446,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55480,9 +55480,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55529,9 +55529,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55563,9 +55563,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55597,9 +55597,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55631,9 +55631,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55665,9 +55665,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55699,9 +55699,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55733,9 +55733,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55767,9 +55767,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55801,9 +55801,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55835,9 +55835,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55869,9 +55869,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55903,9 +55903,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55937,9 +55937,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -55971,9 +55971,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56005,9 +56005,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56039,9 +56039,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56073,9 +56073,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56107,9 +56107,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56141,9 +56141,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56175,9 +56175,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56209,9 +56209,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56243,9 +56243,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56277,9 +56277,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56311,9 +56311,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56345,9 +56345,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56379,9 +56379,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56413,9 +56413,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56447,9 +56447,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56481,9 +56481,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56515,9 +56515,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56549,9 +56549,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56583,9 +56583,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56617,9 +56617,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56651,9 +56651,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56685,9 +56685,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56719,9 +56719,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56753,9 +56753,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56787,9 +56787,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56821,9 +56821,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56855,9 +56855,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56888,9 +56888,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56921,9 +56921,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -56954,9 +56954,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -57014,9 +57014,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -57028,9 +57028,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -57064,9 +57064,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57098,9 +57098,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57132,9 +57132,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57166,9 +57166,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57200,9 +57200,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57234,9 +57234,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57268,9 +57268,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57302,9 +57302,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57336,9 +57336,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57370,9 +57370,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57404,9 +57404,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57438,9 +57438,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57472,9 +57472,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57506,9 +57506,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57540,9 +57540,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57574,9 +57574,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57608,9 +57608,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57642,9 +57642,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57676,9 +57676,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57710,9 +57710,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57744,9 +57744,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57778,9 +57778,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57812,9 +57812,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57846,9 +57846,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57880,9 +57880,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57914,9 +57914,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57948,9 +57948,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -57982,9 +57982,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58016,9 +58016,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58050,9 +58050,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58084,9 +58084,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58118,9 +58118,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58152,9 +58152,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58186,9 +58186,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58220,9 +58220,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58254,9 +58254,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58288,9 +58288,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58322,9 +58322,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58356,9 +58356,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58390,9 +58390,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58424,9 +58424,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58458,9 +58458,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58492,9 +58492,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58526,9 +58526,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58560,9 +58560,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58594,9 +58594,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58628,9 +58628,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58662,9 +58662,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58696,9 +58696,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58730,9 +58730,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58764,9 +58764,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58797,9 +58797,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58830,9 +58830,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58864,9 +58864,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58913,9 +58913,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58947,9 +58947,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -58981,9 +58981,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59015,9 +59015,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59049,9 +59049,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59083,9 +59083,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59117,9 +59117,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59151,9 +59151,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59185,9 +59185,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59219,9 +59219,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59253,9 +59253,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59287,9 +59287,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59321,9 +59321,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59355,9 +59355,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59389,9 +59389,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59423,9 +59423,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59457,9 +59457,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59491,9 +59491,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59525,9 +59525,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59559,9 +59559,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59593,9 +59593,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59627,9 +59627,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59661,9 +59661,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59695,9 +59695,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59729,9 +59729,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59763,9 +59763,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59797,9 +59797,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59831,9 +59831,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59865,9 +59865,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59899,9 +59899,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59933,9 +59933,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -59967,9 +59967,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60001,9 +60001,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60035,9 +60035,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60069,9 +60069,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60103,9 +60103,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60137,9 +60137,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60171,9 +60171,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60205,9 +60205,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60239,9 +60239,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60273,9 +60273,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60307,9 +60307,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60341,9 +60341,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60375,9 +60375,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60409,9 +60409,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60443,9 +60443,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60477,9 +60477,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60511,9 +60511,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60545,9 +60545,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60579,9 +60579,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60613,9 +60613,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60646,9 +60646,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60679,9 +60679,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60713,9 +60713,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60762,9 +60762,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60796,9 +60796,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60830,9 +60830,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60864,9 +60864,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60898,9 +60898,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60932,9 +60932,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -60966,9 +60966,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61000,9 +61000,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61034,9 +61034,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61068,9 +61068,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61102,9 +61102,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61136,9 +61136,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61170,9 +61170,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61204,9 +61204,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61238,9 +61238,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61272,9 +61272,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61306,9 +61306,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61340,9 +61340,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61374,9 +61374,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61408,9 +61408,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61442,9 +61442,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61476,9 +61476,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61510,9 +61510,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61544,9 +61544,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61578,9 +61578,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61612,9 +61612,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61646,9 +61646,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61680,9 +61680,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61714,9 +61714,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61748,9 +61748,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61782,9 +61782,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61816,9 +61816,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61850,9 +61850,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61884,9 +61884,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61918,9 +61918,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61952,9 +61952,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -61986,9 +61986,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62020,9 +62020,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62054,9 +62054,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62088,9 +62088,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62122,9 +62122,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62156,9 +62156,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62190,9 +62190,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62224,9 +62224,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62258,9 +62258,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62292,9 +62292,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62326,9 +62326,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62360,9 +62360,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62394,9 +62394,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62428,9 +62428,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62462,9 +62462,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62495,9 +62495,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62528,9 +62528,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62561,9 +62561,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -62606,9 +62606,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -62620,9 +62620,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -62641,9 +62641,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62675,9 +62675,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62709,9 +62709,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62743,9 +62743,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62777,9 +62777,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62811,9 +62811,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62845,9 +62845,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62879,9 +62879,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62913,9 +62913,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62947,9 +62947,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -62981,9 +62981,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63015,9 +63015,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63049,9 +63049,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63083,9 +63083,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63117,9 +63117,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63151,9 +63151,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63185,9 +63185,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63219,9 +63219,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63253,9 +63253,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63287,9 +63287,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63321,9 +63321,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63355,9 +63355,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63389,9 +63389,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63423,9 +63423,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63457,9 +63457,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63491,9 +63491,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63525,9 +63525,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63559,9 +63559,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63593,9 +63593,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63627,9 +63627,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63661,9 +63661,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63695,9 +63695,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63729,9 +63729,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63763,9 +63763,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63797,9 +63797,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63831,9 +63831,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63865,9 +63865,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63899,9 +63899,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63933,9 +63933,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -63967,9 +63967,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64001,9 +64001,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64035,9 +64035,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64069,9 +64069,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64103,9 +64103,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64137,9 +64137,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64171,9 +64171,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64205,9 +64205,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64239,9 +64239,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64273,9 +64273,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64307,9 +64307,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64341,9 +64341,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64375,9 +64375,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64409,9 +64409,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64443,9 +64443,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64477,9 +64477,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64511,9 +64511,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64545,9 +64545,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64579,9 +64579,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64613,9 +64613,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64647,9 +64647,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64681,9 +64681,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64715,9 +64715,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64749,9 +64749,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64783,9 +64783,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64817,9 +64817,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64851,9 +64851,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64885,9 +64885,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64919,9 +64919,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64953,9 +64953,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -64987,9 +64987,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65021,9 +65021,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65055,9 +65055,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65089,9 +65089,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65123,9 +65123,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65157,9 +65157,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65191,9 +65191,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65225,9 +65225,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65259,9 +65259,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65293,9 +65293,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65327,9 +65327,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65361,9 +65361,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65395,9 +65395,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65429,9 +65429,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65463,9 +65463,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65497,9 +65497,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65531,9 +65531,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65565,9 +65565,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65599,9 +65599,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65633,9 +65633,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65667,9 +65667,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65701,9 +65701,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65735,9 +65735,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65784,9 +65784,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65818,9 +65818,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65852,9 +65852,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65886,9 +65886,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65920,9 +65920,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65954,9 +65954,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -65988,9 +65988,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66022,9 +66022,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66056,9 +66056,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66090,9 +66090,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66124,9 +66124,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66158,9 +66158,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66192,9 +66192,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66226,9 +66226,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66260,9 +66260,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66294,9 +66294,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66328,9 +66328,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66362,9 +66362,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66396,9 +66396,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66430,9 +66430,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66464,9 +66464,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66498,9 +66498,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66532,9 +66532,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66566,9 +66566,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66600,9 +66600,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66634,9 +66634,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66668,9 +66668,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66702,9 +66702,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66736,9 +66736,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66770,9 +66770,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66804,9 +66804,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66838,9 +66838,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66872,9 +66872,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66906,9 +66906,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66940,9 +66940,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -66974,9 +66974,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67008,9 +67008,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67042,9 +67042,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67076,9 +67076,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67110,9 +67110,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67143,9 +67143,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67176,9 +67176,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67209,9 +67209,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -67269,9 +67269,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -67283,9 +67283,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -67319,9 +67319,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67353,9 +67353,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67387,9 +67387,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67421,9 +67421,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67455,9 +67455,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67489,9 +67489,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67523,9 +67523,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67557,9 +67557,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67591,9 +67591,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67625,9 +67625,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67659,9 +67659,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67693,9 +67693,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67727,9 +67727,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67761,9 +67761,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67795,9 +67795,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67829,9 +67829,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67863,9 +67863,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67897,9 +67897,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67931,9 +67931,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67965,9 +67965,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -67999,9 +67999,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68033,9 +68033,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68067,9 +68067,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68101,9 +68101,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68135,9 +68135,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68169,9 +68169,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68203,9 +68203,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68237,9 +68237,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68271,9 +68271,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68305,9 +68305,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68339,9 +68339,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68373,9 +68373,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68407,9 +68407,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68441,9 +68441,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68475,9 +68475,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68509,9 +68509,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68543,9 +68543,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68577,9 +68577,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68611,9 +68611,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68645,9 +68645,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68679,9 +68679,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68713,9 +68713,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68747,9 +68747,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68781,9 +68781,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68815,9 +68815,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68848,9 +68848,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68881,9 +68881,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68915,9 +68915,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68964,9 +68964,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -68998,9 +68998,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69032,9 +69032,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69066,9 +69066,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69100,9 +69100,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69134,9 +69134,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69168,9 +69168,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69202,9 +69202,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69236,9 +69236,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69270,9 +69270,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69304,9 +69304,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69338,9 +69338,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69372,9 +69372,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69406,9 +69406,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69440,9 +69440,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69474,9 +69474,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69508,9 +69508,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69542,9 +69542,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69576,9 +69576,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69610,9 +69610,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69644,9 +69644,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69678,9 +69678,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69712,9 +69712,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69746,9 +69746,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69780,9 +69780,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69814,9 +69814,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69848,9 +69848,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69882,9 +69882,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69916,9 +69916,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69950,9 +69950,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -69984,9 +69984,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70018,9 +70018,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70052,9 +70052,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70086,9 +70086,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70120,9 +70120,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70154,9 +70154,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70188,9 +70188,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70222,9 +70222,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70256,9 +70256,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70290,9 +70290,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70324,9 +70324,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70358,9 +70358,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70392,9 +70392,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70426,9 +70426,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70460,9 +70460,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70493,9 +70493,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70526,9 +70526,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70559,9 +70559,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -70604,9 +70604,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -70618,9 +70618,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -70639,9 +70639,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70673,9 +70673,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70707,9 +70707,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70741,9 +70741,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70775,9 +70775,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70809,9 +70809,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70843,9 +70843,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70877,9 +70877,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70911,9 +70911,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70945,9 +70945,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -70979,9 +70979,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71013,9 +71013,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71047,9 +71047,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71081,9 +71081,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71115,9 +71115,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71164,9 +71164,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71198,9 +71198,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71232,9 +71232,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71266,9 +71266,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71300,9 +71300,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71334,9 +71334,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71368,9 +71368,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71402,9 +71402,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71436,9 +71436,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71470,9 +71470,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71504,9 +71504,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71538,9 +71538,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71572,9 +71572,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71606,9 +71606,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71640,9 +71640,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71674,9 +71674,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71708,9 +71708,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71742,9 +71742,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71776,9 +71776,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71810,9 +71810,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71844,9 +71844,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71878,9 +71878,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71912,9 +71912,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71946,9 +71946,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -71980,9 +71980,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72014,9 +72014,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72048,9 +72048,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72082,9 +72082,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72116,9 +72116,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72150,9 +72150,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72184,9 +72184,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72218,9 +72218,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72252,9 +72252,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72286,9 +72286,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72320,9 +72320,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72354,9 +72354,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72388,9 +72388,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72422,9 +72422,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72456,9 +72456,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72490,9 +72490,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72523,9 +72523,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72556,9 +72556,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72589,9 +72589,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -72632,9 +72632,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -72646,9 +72646,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 31.000000000000004, - "tipVolume": 20 + "tipVolume": 20.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -72667,9 +72667,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72701,9 +72701,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72735,9 +72735,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72769,9 +72769,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72803,9 +72803,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72837,9 +72837,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72871,9 +72871,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72905,9 +72905,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72939,9 +72939,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -72973,9 +72973,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73007,9 +73007,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73041,9 +73041,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73075,9 +73075,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73109,9 +73109,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73143,9 +73143,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73177,9 +73177,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73211,9 +73211,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73245,9 +73245,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73279,9 +73279,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73313,9 +73313,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73347,9 +73347,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73381,9 +73381,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73415,9 +73415,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73449,9 +73449,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73483,9 +73483,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73517,9 +73517,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73551,9 +73551,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73585,9 +73585,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73619,9 +73619,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73653,9 +73653,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73687,9 +73687,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73721,9 +73721,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73755,9 +73755,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73789,9 +73789,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73823,9 +73823,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73857,9 +73857,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73891,9 +73891,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73925,9 +73925,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73959,9 +73959,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -73993,9 +73993,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74027,9 +74027,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74061,9 +74061,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74095,9 +74095,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74129,9 +74129,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74163,9 +74163,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74197,9 +74197,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74231,9 +74231,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74265,9 +74265,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74299,9 +74299,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74333,9 +74333,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74367,9 +74367,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74401,9 +74401,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74435,9 +74435,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74469,9 +74469,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74503,9 +74503,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74537,9 +74537,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74571,9 +74571,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74605,9 +74605,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74639,9 +74639,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74673,9 +74673,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74707,9 +74707,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74741,9 +74741,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74775,9 +74775,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74809,9 +74809,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74843,9 +74843,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74877,9 +74877,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74911,9 +74911,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74945,9 +74945,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -74979,9 +74979,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75013,9 +75013,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75047,9 +75047,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75081,9 +75081,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75115,9 +75115,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75149,9 +75149,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75183,9 +75183,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75217,9 +75217,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75251,9 +75251,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75285,9 +75285,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75319,9 +75319,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75353,9 +75353,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75387,9 +75387,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75421,9 +75421,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75455,9 +75455,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75489,9 +75489,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75523,9 +75523,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75557,9 +75557,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75591,9 +75591,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75625,9 +75625,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75659,9 +75659,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75693,9 +75693,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75727,9 +75727,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75761,9 +75761,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75795,9 +75795,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75829,9 +75829,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75863,9 +75863,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75897,9 +75897,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75931,9 +75931,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75965,9 +75965,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -75999,9 +75999,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76033,9 +76033,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76067,9 +76067,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76101,9 +76101,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76135,9 +76135,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76169,9 +76169,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76203,9 +76203,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76237,9 +76237,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76271,9 +76271,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76305,9 +76305,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76339,9 +76339,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76373,9 +76373,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76407,9 +76407,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76441,9 +76441,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76475,9 +76475,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76509,9 +76509,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76543,9 +76543,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76577,9 +76577,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76611,9 +76611,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76645,9 +76645,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76679,9 +76679,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76713,9 +76713,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76747,9 +76747,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76781,9 +76781,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76815,9 +76815,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76849,9 +76849,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76883,9 +76883,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76917,9 +76917,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76951,9 +76951,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -76985,9 +76985,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77019,9 +77019,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77053,9 +77053,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77087,9 +77087,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77121,9 +77121,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77155,9 +77155,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77189,9 +77189,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77223,9 +77223,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77257,9 +77257,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77291,9 +77291,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77325,9 +77325,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77359,9 +77359,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77393,9 +77393,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77427,9 +77427,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77461,9 +77461,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77495,9 +77495,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77529,9 +77529,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77563,9 +77563,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77597,9 +77597,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77631,9 +77631,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77665,9 +77665,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77699,9 +77699,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77733,9 +77733,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77767,9 +77767,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77801,9 +77801,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77835,9 +77835,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77869,9 +77869,9 @@ "volume": 20.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77903,9 +77903,9 @@ "volume": 6.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77937,9 +77937,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -77971,9 +77971,9 @@ "volume": 16.666666666666657, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78020,9 +78020,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78054,9 +78054,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78088,9 +78088,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78122,9 +78122,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78156,9 +78156,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78190,9 +78190,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78224,9 +78224,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78258,9 +78258,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78292,9 +78292,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78326,9 +78326,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78360,9 +78360,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78394,9 +78394,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78428,9 +78428,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78462,9 +78462,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78496,9 +78496,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78530,9 +78530,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78564,9 +78564,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78598,9 +78598,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78632,9 +78632,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78666,9 +78666,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78700,9 +78700,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78734,9 +78734,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78768,9 +78768,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78802,9 +78802,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78836,9 +78836,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78870,9 +78870,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78904,9 +78904,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78938,9 +78938,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -78972,9 +78972,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79006,9 +79006,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79040,9 +79040,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79074,9 +79074,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79108,9 +79108,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79142,9 +79142,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79176,9 +79176,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79210,9 +79210,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79244,9 +79244,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79278,9 +79278,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79312,9 +79312,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79346,9 +79346,9 @@ "volume": 18.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79379,9 +79379,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79412,9 +79412,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79445,9 +79445,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -79518,9 +79518,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -79532,9 +79532,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -79568,9 +79568,9 @@ "volume": 166.66666666666666, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79602,9 +79602,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79636,9 +79636,9 @@ "volume": 176.66666666666666, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79669,9 +79669,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79702,9 +79702,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79736,9 +79736,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79785,9 +79785,9 @@ "volume": 166.66666666666666, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79819,9 +79819,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79853,9 +79853,9 @@ "volume": 176.66666666666666, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79886,9 +79886,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79919,9 +79919,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -79953,9 +79953,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80002,9 +80002,9 @@ "volume": 166.66666666666666, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80036,9 +80036,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80070,9 +80070,9 @@ "volume": 176.66666666666666, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80103,9 +80103,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80136,9 +80136,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80169,9 +80169,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -80214,9 +80214,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -80228,9 +80228,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -80249,9 +80249,9 @@ "volume": 50.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80298,9 +80298,9 @@ "volume": 50.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80347,9 +80347,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80381,9 +80381,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80415,9 +80415,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80449,9 +80449,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80483,9 +80483,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80517,9 +80517,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80551,9 +80551,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80585,9 +80585,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80619,9 +80619,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80653,9 +80653,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80687,9 +80687,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80721,9 +80721,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80755,9 +80755,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80789,9 +80789,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80823,9 +80823,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80857,9 +80857,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80891,9 +80891,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80925,9 +80925,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80959,9 +80959,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -80993,9 +80993,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81027,9 +81027,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81061,9 +81061,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81095,9 +81095,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81129,9 +81129,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81163,9 +81163,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81197,9 +81197,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81231,9 +81231,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81265,9 +81265,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81299,9 +81299,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81333,9 +81333,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81367,9 +81367,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81401,9 +81401,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81435,9 +81435,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81469,9 +81469,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81503,9 +81503,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81537,9 +81537,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81571,9 +81571,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81605,9 +81605,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81639,9 +81639,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81673,9 +81673,9 @@ "volume": 5.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81706,9 +81706,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81739,9 +81739,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81772,9 +81772,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -81832,9 +81832,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top" }, @@ -81846,9 +81846,9 @@ "y": 0.0, "z": 0.0 }, - "tipDiameter": 0, + "tipDiameter": 0.0, "tipLength": 51.099999999999994, - "tipVolume": 300 + "tipVolume": 300.0 }, "startedAt": "TIMESTAMP", "status": "succeeded" @@ -81867,9 +81867,9 @@ "volume": 50.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81901,9 +81901,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81935,9 +81935,9 @@ "volume": 60.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -81968,9 +81968,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -82001,9 +82001,9 @@ "volume": 10.0, "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "top", "volumeOffset": 0.0 @@ -82034,9 +82034,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1d37cbc080][pl_MagMax_RNA_Cells_Flex_multi].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1d37cbc080][pl_MagMax_RNA_Cells_Flex_multi].json index 6258b8500a1..03bb82c7b3c 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1d37cbc080][pl_MagMax_RNA_Cells_Flex_multi].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[1d37cbc080][pl_MagMax_RNA_Cells_Flex_multi].json @@ -12766,9 +12766,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -13646,9 +13646,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14526,9 +14526,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16474,9 +16474,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -17354,9 +17354,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -18234,9 +18234,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19114,9 +19114,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[238912ff51][Flex_S_v2_18_KAPA_Library_Quant].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[238912ff51][Flex_S_v2_18_KAPA_Library_Quant].json index a18cfa29797..f98ef8886da 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[238912ff51][Flex_S_v2_18_KAPA_Library_Quant].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[238912ff51][Flex_S_v2_18_KAPA_Library_Quant].json @@ -12914,9 +12914,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15070,9 +15070,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16025,9 +16025,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16980,9 +16980,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -17949,9 +17949,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -18904,9 +18904,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19859,9 +19859,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20549,9 +20549,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21145,9 +21145,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21741,9 +21741,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22777,9 +22777,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -23771,9 +23771,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -24765,9 +24765,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -26946,9 +26946,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[248a2a3107][pl_Omega_HDQ_DNA_Cells_Flex_96_channel].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[248a2a3107][pl_Omega_HDQ_DNA_Cells_Flex_96_channel].json index 6e3feab4269..427cd1c997b 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[248a2a3107][pl_Omega_HDQ_DNA_Cells_Flex_96_channel].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[248a2a3107][pl_Omega_HDQ_DNA_Cells_Flex_96_channel].json @@ -28560,9 +28560,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31094,9 +31094,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31352,9 +31352,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31532,9 +31532,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31772,9 +31772,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31952,9 +31952,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -32192,9 +32192,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -32372,9 +32372,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -32612,9 +32612,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34792,9 +34792,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35050,9 +35050,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[277961bc58][pl_NiNTA_Flex_96well_final].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[277961bc58][pl_NiNTA_Flex_96well_final].json index 6a4c99f2bbc..7602c8613f4 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[277961bc58][pl_NiNTA_Flex_96well_final].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[277961bc58][pl_NiNTA_Flex_96well_final].json @@ -20073,9 +20073,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20336,9 +20336,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20599,9 +20599,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20862,9 +20862,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21125,9 +21125,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21388,9 +21388,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21651,9 +21651,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -21914,9 +21914,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22177,9 +22177,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22440,9 +22440,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22703,9 +22703,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -22966,9 +22966,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -23317,9 +23317,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -23547,9 +23547,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -23777,9 +23777,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -24007,9 +24007,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -24237,9 +24237,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -24467,9 +24467,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -24697,9 +24697,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -24927,9 +24927,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -25157,9 +25157,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -25387,9 +25387,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -25617,9 +25617,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -25847,9 +25847,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -26158,9 +26158,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -26421,9 +26421,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -26684,9 +26684,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -26947,9 +26947,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27210,9 +27210,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27473,9 +27473,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27736,9 +27736,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -27999,9 +27999,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28262,9 +28262,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28525,9 +28525,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -28788,9 +28788,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29051,9 +29051,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29402,9 +29402,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29632,9 +29632,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -29862,9 +29862,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -30092,9 +30092,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -30322,9 +30322,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -30552,9 +30552,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -30782,9 +30782,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31012,9 +31012,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31242,9 +31242,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31472,9 +31472,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31702,9 +31702,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -31932,9 +31932,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -32243,9 +32243,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -32506,9 +32506,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -32769,9 +32769,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -33032,9 +33032,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -33295,9 +33295,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -33558,9 +33558,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -33821,9 +33821,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34084,9 +34084,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34347,9 +34347,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34610,9 +34610,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -34873,9 +34873,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35136,9 +35136,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35518,9 +35518,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35748,9 +35748,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -35978,9 +35978,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36208,9 +36208,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36438,9 +36438,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36668,9 +36668,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -36898,9 +36898,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -37128,9 +37128,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -37358,9 +37358,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -37588,9 +37588,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -37818,9 +37818,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -38048,9 +38048,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -38359,9 +38359,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -38622,9 +38622,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -38885,9 +38885,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39148,9 +39148,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39411,9 +39411,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39674,9 +39674,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -39937,9 +39937,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40200,9 +40200,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40463,9 +40463,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40726,9 +40726,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -40989,9 +40989,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -41252,9 +41252,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -41603,9 +41603,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -41833,9 +41833,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42063,9 +42063,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42293,9 +42293,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42523,9 +42523,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42753,9 +42753,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -42983,9 +42983,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -43213,9 +43213,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -43443,9 +43443,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -43673,9 +43673,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -43903,9 +43903,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44133,9 +44133,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44444,9 +44444,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44707,9 +44707,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -44970,9 +44970,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45233,9 +45233,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45496,9 +45496,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -45759,9 +45759,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46022,9 +46022,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46285,9 +46285,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46548,9 +46548,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -46811,9 +46811,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -47074,9 +47074,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -47337,9 +47337,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -47688,9 +47688,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -47918,9 +47918,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -48148,9 +48148,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -48378,9 +48378,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -48608,9 +48608,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -48838,9 +48838,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -49068,9 +49068,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -49298,9 +49298,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -49528,9 +49528,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -49758,9 +49758,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -49988,9 +49988,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -50218,9 +50218,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -50529,9 +50529,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -50792,9 +50792,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -51055,9 +51055,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -51318,9 +51318,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -51581,9 +51581,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -51844,9 +51844,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -52107,9 +52107,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -52370,9 +52370,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -52633,9 +52633,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -52896,9 +52896,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -53159,9 +53159,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -53422,9 +53422,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -53854,9 +53854,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -54117,9 +54117,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -54380,9 +54380,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -54643,9 +54643,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -54906,9 +54906,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -55169,9 +55169,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -55432,9 +55432,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -55695,9 +55695,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -55958,9 +55958,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -56221,9 +56221,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -56484,9 +56484,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -56747,9 +56747,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[28fdeebdd9][pl_Omega_HDQ_DNA_Cells_Flex_multi].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[28fdeebdd9][pl_Omega_HDQ_DNA_Cells_Flex_multi].json index d52ff5e72e4..1598272fc5c 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[28fdeebdd9][pl_Omega_HDQ_DNA_Cells_Flex_multi].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[28fdeebdd9][pl_Omega_HDQ_DNA_Cells_Flex_multi].json @@ -14220,9 +14220,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -14987,9 +14987,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -15754,9 +15754,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -16521,9 +16521,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[29bb5df52d][pl_Omega_HDQ_DNA_Bacteria_Flex_multi].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[29bb5df52d][pl_Omega_HDQ_DNA_Bacteria_Flex_multi].json index e8cb6c52713..d6a2126b774 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[29bb5df52d][pl_Omega_HDQ_DNA_Bacteria_Flex_multi].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[29bb5df52d][pl_Omega_HDQ_DNA_Bacteria_Flex_multi].json @@ -17037,9 +17037,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -17819,9 +17819,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -18601,9 +18601,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -19383,9 +19383,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, @@ -20026,9 +20026,9 @@ "pipetteId": "UUID", "wellLocation": { "offset": { - "x": 0, - "y": 0, - "z": 0 + "x": 0.0, + "y": 0.0, + "z": 0.0 }, "origin": "default" }, diff --git a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2bc6830494][pl_langone_ribo_pt1_ramp].json b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2bc6830494][pl_langone_ribo_pt1_ramp].json index 9e8f25df1f1..103852d1fad 100644 --- a/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2bc6830494][pl_langone_ribo_pt1_ramp].json +++ b/analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2bc6830494][pl_langone_ribo_pt1_ramp].json @@ -5958,25759 +5958,6 @@ }, "startedAt": "TIMESTAMP", "status": "succeeded" - }, - { - "commandType": "loadLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c06d611a9cd16f109d926a07372b0d89", - "notes": [], - "params": { - "displayName": "8", - "loadName": "nest_96_wellplate_2ml_deep", - "location": { - "moduleId": "UUID" - }, - "namespace": "opentrons", - "version": 2 - }, - "result": { - "definition": { - "allowedRoles": [], - "brand": { - "brand": "NEST", - "brandId": [ - "503001", - "503501" - ], - "links": [ - "https://www.nest-biotech.com/deep-well-plates/59253726.html" - ] - }, - "cornerOffsetFromSlot": { - "x": 0, - "y": 0, - "z": 0 - }, - "dimensions": { - "xDimension": 127.6, - "yDimension": 85.3, - "zDimension": 41 - }, - "gripForce": 15.0, - "gripHeightFromLabwareBottom": 21.9, - "gripperOffsets": {}, - "groups": [ - { - "brand": { - "brand": "NEST", - "brandId": [] - }, - "metadata": { - "displayCategory": "wellPlate", - "displayName": "NEST 96 Deep Well Plate 2mL", - "wellBottomShape": "v" - }, - "wells": [ - "A1", - "A10", - "A11", - "A12", - "A2", - "A3", - "A4", - "A5", - "A6", - "A7", - "A8", - "A9", - "B1", - "B10", - "B11", - "B12", - "B2", - "B3", - "B4", - "B5", - "B6", - "B7", - "B8", - "B9", - "C1", - "C10", - "C11", - "C12", - "C2", - "C3", - "C4", - "C5", - "C6", - "C7", - "C8", - "C9", - "D1", - "D10", - "D11", - "D12", - "D2", - "D3", - "D4", - "D5", - "D6", - "D7", - "D8", - "D9", - "E1", - "E10", - "E11", - "E12", - "E2", - "E3", - "E4", - "E5", - "E6", - "E7", - "E8", - "E9", - "F1", - "F10", - "F11", - "F12", - "F2", - "F3", - "F4", - "F5", - "F6", - "F7", - "F8", - "F9", - "G1", - "G10", - "G11", - "G12", - "G2", - "G3", - "G4", - "G5", - "G6", - "G7", - "G8", - "G9", - "H1", - "H10", - "H11", - "H12", - "H2", - "H3", - "H4", - "H5", - "H6", - "H7", - "H8", - "H9" - ] - } - ], - "metadata": { - "displayCategory": "wellPlate", - "displayName": "NEST 96 Deep Well Plate 2mL", - "displayVolumeUnits": "µL", - "tags": [] - }, - "namespace": "opentrons", - "ordering": [ - [ - "A1", - "B1", - "C1", - "D1", - "E1", - "F1", - "G1", - "H1" - ], - [ - "A10", - "B10", - "C10", - "D10", - "E10", - "F10", - "G10", - "H10" - ], - [ - "A11", - "B11", - "C11", - "D11", - "E11", - "F11", - "G11", - "H11" - ], - [ - "A12", - "B12", - "C12", - "D12", - "E12", - "F12", - "G12", - "H12" - ], - [ - "A2", - "B2", - "C2", - "D2", - "E2", - "F2", - "G2", - "H2" - ], - [ - "A3", - "B3", - "C3", - "D3", - "E3", - "F3", - "G3", - "H3" - ], - [ - "A4", - "B4", - "C4", - "D4", - "E4", - "F4", - "G4", - "H4" - ], - [ - "A5", - "B5", - "C5", - "D5", - "E5", - "F5", - "G5", - "H5" - ], - [ - "A6", - "B6", - "C6", - "D6", - "E6", - "F6", - "G6", - "H6" - ], - [ - "A7", - "B7", - "C7", - "D7", - "E7", - "F7", - "G7", - "H7" - ], - [ - "A8", - "B8", - "C8", - "D8", - "E8", - "F8", - "G8", - "H8" - ], - [ - "A9", - "B9", - "C9", - "D9", - "E9", - "F9", - "G9", - "H9" - ] - ], - "parameters": { - "format": "96Standard", - "isMagneticModuleCompatible": true, - "isTiprack": false, - "loadName": "nest_96_wellplate_2ml_deep", - "magneticModuleEngageHeight": 6.8, - "quirks": [] - }, - "schemaVersion": 2, - "stackingOffsetWithLabware": { - "opentrons_96_deep_well_adapter": { - "x": 0, - "y": 0, - "z": 16.3 - }, - "opentrons_96_deep_well_temp_mod_adapter": { - "x": 0, - "y": 0, - "z": 16.1 - } - }, - "stackingOffsetWithModule": { - "magneticBlockV1": { - "x": 0, - "y": 0, - "z": 2.66 - } - }, - "version": 2, - "wells": { - "A1": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 14.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A10": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 95.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A11": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 104.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A12": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 113.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A2": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 23.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A3": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 32.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A4": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 41.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A5": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 50.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A6": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 59.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A7": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 68.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A8": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 77.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "A9": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 86.3, - "xDimension": 8.2, - "y": 74.15, - "yDimension": 8.2, - "z": 3 - }, - "B1": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 14.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B10": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 95.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B11": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 104.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B12": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 113.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B2": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 23.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B3": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 32.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B4": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 41.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B5": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 50.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B6": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 59.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B7": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 68.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B8": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 77.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "B9": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 86.3, - "xDimension": 8.2, - "y": 65.15, - "yDimension": 8.2, - "z": 3 - }, - "C1": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 14.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C10": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 95.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C11": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 104.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C12": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 113.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C2": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 23.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C3": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 32.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C4": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 41.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C5": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 50.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C6": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 59.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C7": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 68.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C8": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 77.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "C9": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 86.3, - "xDimension": 8.2, - "y": 56.15, - "yDimension": 8.2, - "z": 3 - }, - "D1": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 14.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D10": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 95.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D11": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 104.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D12": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 113.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D2": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 23.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D3": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 32.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D4": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 41.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D5": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 50.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D6": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 59.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D7": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 68.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D8": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 77.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "D9": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 86.3, - "xDimension": 8.2, - "y": 47.15, - "yDimension": 8.2, - "z": 3 - }, - "E1": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 14.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E10": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 95.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E11": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 104.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E12": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 113.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E2": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 23.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E3": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 32.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E4": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 41.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E5": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 50.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E6": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 59.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E7": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 68.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E8": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 77.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "E9": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 86.3, - "xDimension": 8.2, - "y": 38.15, - "yDimension": 8.2, - "z": 3 - }, - "F1": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 14.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F10": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 95.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F11": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 104.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F12": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 113.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F2": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 23.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F3": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 32.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F4": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 41.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F5": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 50.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F6": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 59.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F7": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 68.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F8": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 77.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "F9": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 86.3, - "xDimension": 8.2, - "y": 29.15, - "yDimension": 8.2, - "z": 3 - }, - "G1": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 14.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G10": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 95.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G11": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 104.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G12": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 113.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G2": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 23.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G3": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 32.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G4": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 41.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G5": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 50.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G6": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 59.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G7": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 68.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G8": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 77.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "G9": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 86.3, - "xDimension": 8.2, - "y": 20.15, - "yDimension": 8.2, - "z": 3 - }, - "H1": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 14.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H10": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 95.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H11": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 104.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H12": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 113.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H2": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 23.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H3": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 32.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H4": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 41.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H5": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 50.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H6": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 59.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H7": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 68.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H8": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 77.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - }, - "H9": { - "depth": 38, - "shape": "rectangular", - "totalLiquidVolume": 2000, - "x": 86.3, - "xDimension": 8.2, - "y": 11.15, - "yDimension": 8.2, - "z": 3 - } - } - }, - "labwareId": "UUID" - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "heaterShaker/closeLabwareLatch", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7b9b3bc1ec8ef40fd43a1478e9a1896d", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "80d3c25d5533b9c4f4b83a66d4af895e", - "notes": [], - "params": { - "loadName": "nest_12_reservoir_15ml", - "location": { - "slotName": "A2" - }, - "namespace": "opentrons", - "version": 1 - }, - "result": { - "definition": { - "allowedRoles": [], - "brand": { - "brand": "NEST", - "brandId": [ - "360102" - ], - "links": [ - "https://www.nest-biotech.com/reagent-reserviors/59178414.html" - ] - }, - "cornerOffsetFromSlot": { - "x": 0, - "y": 0, - "z": 0 - }, - "dimensions": { - "xDimension": 127.76, - "yDimension": 85.48, - "zDimension": 31.4 - }, - "gripperOffsets": {}, - "groups": [ - { - "metadata": { - "wellBottomShape": "v" - }, - "wells": [ - "A1", - "A10", - "A11", - "A12", - "A2", - "A3", - "A4", - "A5", - "A6", - "A7", - "A8", - "A9" - ] - } - ], - "metadata": { - "displayCategory": "reservoir", - "displayName": "NEST 12 Well Reservoir 15 mL", - "displayVolumeUnits": "mL", - "tags": [] - }, - "namespace": "opentrons", - "ordering": [ - [ - "A1" - ], - [ - "A10" - ], - [ - "A11" - ], - [ - "A12" - ], - [ - "A2" - ], - [ - "A3" - ], - [ - "A4" - ], - [ - "A5" - ], - [ - "A6" - ], - [ - "A7" - ], - [ - "A8" - ], - [ - "A9" - ] - ], - "parameters": { - "format": "trough", - "isMagneticModuleCompatible": false, - "isTiprack": false, - "loadName": "nest_12_reservoir_15ml", - "quirks": [ - "centerMultichannelOnWells", - "touchTipDisabled" - ] - }, - "schemaVersion": 2, - "stackingOffsetWithLabware": {}, - "stackingOffsetWithModule": {}, - "version": 1, - "wells": { - "A1": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 14.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A10": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 95.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A11": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 104.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A12": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 113.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A2": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 23.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A3": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 32.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A4": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 41.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A5": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 50.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A6": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 59.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A7": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 68.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A8": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 77.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - }, - "A9": { - "depth": 26.85, - "shape": "rectangular", - "totalLiquidVolume": 15000, - "x": 86.38, - "xDimension": 8.2, - "y": 42.78, - "yDimension": 71.2, - "z": 4.55 - } - } - }, - "labwareId": "UUID" - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "39705bcf605998607e3ff52963471b4d", - "notes": [], - "params": { - "loadName": "opentrons_flex_96_tiprack_50ul", - "location": { - "slotName": "C1" - }, - "namespace": "opentrons", - "version": 1 - }, - "result": { - "definition": { - "allowedRoles": [], - "brand": { - "brand": "Opentrons", - "brandId": [] - }, - "cornerOffsetFromSlot": { - "x": 0, - "y": 0, - "z": 0 - }, - "dimensions": { - "xDimension": 127.75, - "yDimension": 85.75, - "zDimension": 99 - }, - "gripForce": 16.0, - "gripHeightFromLabwareBottom": 23.9, - "gripperOffsets": {}, - "groups": [ - { - "metadata": {}, - "wells": [ - "A1", - "A10", - "A11", - "A12", - "A2", - "A3", - "A4", - "A5", - "A6", - "A7", - "A8", - "A9", - "B1", - "B10", - "B11", - "B12", - "B2", - "B3", - "B4", - "B5", - "B6", - "B7", - "B8", - "B9", - "C1", - "C10", - "C11", - "C12", - "C2", - "C3", - "C4", - "C5", - "C6", - "C7", - "C8", - "C9", - "D1", - "D10", - "D11", - "D12", - "D2", - "D3", - "D4", - "D5", - "D6", - "D7", - "D8", - "D9", - "E1", - "E10", - "E11", - "E12", - "E2", - "E3", - "E4", - "E5", - "E6", - "E7", - "E8", - "E9", - "F1", - "F10", - "F11", - "F12", - "F2", - "F3", - "F4", - "F5", - "F6", - "F7", - "F8", - "F9", - "G1", - "G10", - "G11", - "G12", - "G2", - "G3", - "G4", - "G5", - "G6", - "G7", - "G8", - "G9", - "H1", - "H10", - "H11", - "H12", - "H2", - "H3", - "H4", - "H5", - "H6", - "H7", - "H8", - "H9" - ] - } - ], - "metadata": { - "displayCategory": "tipRack", - "displayName": "Opentrons Flex 96 Tip Rack 50 µL", - "displayVolumeUnits": "µL", - "tags": [] - }, - "namespace": "opentrons", - "ordering": [ - [ - "A1", - "B1", - "C1", - "D1", - "E1", - "F1", - "G1", - "H1" - ], - [ - "A10", - "B10", - "C10", - "D10", - "E10", - "F10", - "G10", - "H10" - ], - [ - "A11", - "B11", - "C11", - "D11", - "E11", - "F11", - "G11", - "H11" - ], - [ - "A12", - "B12", - "C12", - "D12", - "E12", - "F12", - "G12", - "H12" - ], - [ - "A2", - "B2", - "C2", - "D2", - "E2", - "F2", - "G2", - "H2" - ], - [ - "A3", - "B3", - "C3", - "D3", - "E3", - "F3", - "G3", - "H3" - ], - [ - "A4", - "B4", - "C4", - "D4", - "E4", - "F4", - "G4", - "H4" - ], - [ - "A5", - "B5", - "C5", - "D5", - "E5", - "F5", - "G5", - "H5" - ], - [ - "A6", - "B6", - "C6", - "D6", - "E6", - "F6", - "G6", - "H6" - ], - [ - "A7", - "B7", - "C7", - "D7", - "E7", - "F7", - "G7", - "H7" - ], - [ - "A8", - "B8", - "C8", - "D8", - "E8", - "F8", - "G8", - "H8" - ], - [ - "A9", - "B9", - "C9", - "D9", - "E9", - "F9", - "G9", - "H9" - ] - ], - "parameters": { - "format": "96Standard", - "isMagneticModuleCompatible": false, - "isTiprack": true, - "loadName": "opentrons_flex_96_tiprack_50ul", - "quirks": [], - "tipLength": 57.9, - "tipOverlap": 10.5 - }, - "schemaVersion": 2, - "stackingOffsetWithLabware": { - "opentrons_flex_96_tiprack_adapter": { - "x": 0, - "y": 0, - "z": 121 - } - }, - "stackingOffsetWithModule": {}, - "version": 1, - "wells": { - "A1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 74.38, - "z": 1.5 - }, - "A10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 74.38, - "z": 1.5 - }, - "A11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 74.38, - "z": 1.5 - }, - "A12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 74.38, - "z": 1.5 - }, - "A2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 74.38, - "z": 1.5 - }, - "A3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 74.38, - "z": 1.5 - }, - "A4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 74.38, - "z": 1.5 - }, - "A5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 74.38, - "z": 1.5 - }, - "A6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 74.38, - "z": 1.5 - }, - "A7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 74.38, - "z": 1.5 - }, - "A8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 74.38, - "z": 1.5 - }, - "A9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 74.38, - "z": 1.5 - }, - "B1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 65.38, - "z": 1.5 - }, - "B10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 65.38, - "z": 1.5 - }, - "B11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 65.38, - "z": 1.5 - }, - "B12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 65.38, - "z": 1.5 - }, - "B2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 65.38, - "z": 1.5 - }, - "B3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 65.38, - "z": 1.5 - }, - "B4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 65.38, - "z": 1.5 - }, - "B5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 65.38, - "z": 1.5 - }, - "B6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 65.38, - "z": 1.5 - }, - "B7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 65.38, - "z": 1.5 - }, - "B8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 65.38, - "z": 1.5 - }, - "B9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 65.38, - "z": 1.5 - }, - "C1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 56.38, - "z": 1.5 - }, - "C10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 56.38, - "z": 1.5 - }, - "C11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 56.38, - "z": 1.5 - }, - "C12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 56.38, - "z": 1.5 - }, - "C2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 56.38, - "z": 1.5 - }, - "C3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 56.38, - "z": 1.5 - }, - "C4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 56.38, - "z": 1.5 - }, - "C5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 56.38, - "z": 1.5 - }, - "C6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 56.38, - "z": 1.5 - }, - "C7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 56.38, - "z": 1.5 - }, - "C8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 56.38, - "z": 1.5 - }, - "C9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 56.38, - "z": 1.5 - }, - "D1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 47.38, - "z": 1.5 - }, - "D10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 47.38, - "z": 1.5 - }, - "D11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 47.38, - "z": 1.5 - }, - "D12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 47.38, - "z": 1.5 - }, - "D2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 47.38, - "z": 1.5 - }, - "D3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 47.38, - "z": 1.5 - }, - "D4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 47.38, - "z": 1.5 - }, - "D5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 47.38, - "z": 1.5 - }, - "D6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 47.38, - "z": 1.5 - }, - "D7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 47.38, - "z": 1.5 - }, - "D8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 47.38, - "z": 1.5 - }, - "D9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 47.38, - "z": 1.5 - }, - "E1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 38.38, - "z": 1.5 - }, - "E10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 38.38, - "z": 1.5 - }, - "E11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 38.38, - "z": 1.5 - }, - "E12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 38.38, - "z": 1.5 - }, - "E2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 38.38, - "z": 1.5 - }, - "E3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 38.38, - "z": 1.5 - }, - "E4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 38.38, - "z": 1.5 - }, - "E5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 38.38, - "z": 1.5 - }, - "E6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 38.38, - "z": 1.5 - }, - "E7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 38.38, - "z": 1.5 - }, - "E8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 38.38, - "z": 1.5 - }, - "E9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 38.38, - "z": 1.5 - }, - "F1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 29.38, - "z": 1.5 - }, - "F10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 29.38, - "z": 1.5 - }, - "F11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 29.38, - "z": 1.5 - }, - "F12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 29.38, - "z": 1.5 - }, - "F2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 29.38, - "z": 1.5 - }, - "F3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 29.38, - "z": 1.5 - }, - "F4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 29.38, - "z": 1.5 - }, - "F5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 29.38, - "z": 1.5 - }, - "F6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 29.38, - "z": 1.5 - }, - "F7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 29.38, - "z": 1.5 - }, - "F8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 29.38, - "z": 1.5 - }, - "F9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 29.38, - "z": 1.5 - }, - "G1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 20.38, - "z": 1.5 - }, - "G10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 20.38, - "z": 1.5 - }, - "G11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 20.38, - "z": 1.5 - }, - "G12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 20.38, - "z": 1.5 - }, - "G2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 20.38, - "z": 1.5 - }, - "G3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 20.38, - "z": 1.5 - }, - "G4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 20.38, - "z": 1.5 - }, - "G5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 20.38, - "z": 1.5 - }, - "G6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 20.38, - "z": 1.5 - }, - "G7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 20.38, - "z": 1.5 - }, - "G8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 20.38, - "z": 1.5 - }, - "G9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 20.38, - "z": 1.5 - }, - "H1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 11.38, - "z": 1.5 - }, - "H10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 11.38, - "z": 1.5 - }, - "H11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 11.38, - "z": 1.5 - }, - "H12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 11.38, - "z": 1.5 - }, - "H2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 11.38, - "z": 1.5 - }, - "H3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 11.38, - "z": 1.5 - }, - "H4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 11.38, - "z": 1.5 - }, - "H5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 11.38, - "z": 1.5 - }, - "H6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 11.38, - "z": 1.5 - }, - "H7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 11.38, - "z": 1.5 - }, - "H8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 11.38, - "z": 1.5 - }, - "H9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 11.38, - "z": 1.5 - } - } - }, - "labwareId": "UUID" - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "16e6802c3a90fa35fd3ad8570a56c95d", - "notes": [], - "params": { - "loadName": "opentrons_flex_96_tiprack_50ul", - "location": { - "slotName": "C2" - }, - "namespace": "opentrons", - "version": 1 - }, - "result": { - "definition": { - "allowedRoles": [], - "brand": { - "brand": "Opentrons", - "brandId": [] - }, - "cornerOffsetFromSlot": { - "x": 0, - "y": 0, - "z": 0 - }, - "dimensions": { - "xDimension": 127.75, - "yDimension": 85.75, - "zDimension": 99 - }, - "gripForce": 16.0, - "gripHeightFromLabwareBottom": 23.9, - "gripperOffsets": {}, - "groups": [ - { - "metadata": {}, - "wells": [ - "A1", - "A10", - "A11", - "A12", - "A2", - "A3", - "A4", - "A5", - "A6", - "A7", - "A8", - "A9", - "B1", - "B10", - "B11", - "B12", - "B2", - "B3", - "B4", - "B5", - "B6", - "B7", - "B8", - "B9", - "C1", - "C10", - "C11", - "C12", - "C2", - "C3", - "C4", - "C5", - "C6", - "C7", - "C8", - "C9", - "D1", - "D10", - "D11", - "D12", - "D2", - "D3", - "D4", - "D5", - "D6", - "D7", - "D8", - "D9", - "E1", - "E10", - "E11", - "E12", - "E2", - "E3", - "E4", - "E5", - "E6", - "E7", - "E8", - "E9", - "F1", - "F10", - "F11", - "F12", - "F2", - "F3", - "F4", - "F5", - "F6", - "F7", - "F8", - "F9", - "G1", - "G10", - "G11", - "G12", - "G2", - "G3", - "G4", - "G5", - "G6", - "G7", - "G8", - "G9", - "H1", - "H10", - "H11", - "H12", - "H2", - "H3", - "H4", - "H5", - "H6", - "H7", - "H8", - "H9" - ] - } - ], - "metadata": { - "displayCategory": "tipRack", - "displayName": "Opentrons Flex 96 Tip Rack 50 µL", - "displayVolumeUnits": "µL", - "tags": [] - }, - "namespace": "opentrons", - "ordering": [ - [ - "A1", - "B1", - "C1", - "D1", - "E1", - "F1", - "G1", - "H1" - ], - [ - "A10", - "B10", - "C10", - "D10", - "E10", - "F10", - "G10", - "H10" - ], - [ - "A11", - "B11", - "C11", - "D11", - "E11", - "F11", - "G11", - "H11" - ], - [ - "A12", - "B12", - "C12", - "D12", - "E12", - "F12", - "G12", - "H12" - ], - [ - "A2", - "B2", - "C2", - "D2", - "E2", - "F2", - "G2", - "H2" - ], - [ - "A3", - "B3", - "C3", - "D3", - "E3", - "F3", - "G3", - "H3" - ], - [ - "A4", - "B4", - "C4", - "D4", - "E4", - "F4", - "G4", - "H4" - ], - [ - "A5", - "B5", - "C5", - "D5", - "E5", - "F5", - "G5", - "H5" - ], - [ - "A6", - "B6", - "C6", - "D6", - "E6", - "F6", - "G6", - "H6" - ], - [ - "A7", - "B7", - "C7", - "D7", - "E7", - "F7", - "G7", - "H7" - ], - [ - "A8", - "B8", - "C8", - "D8", - "E8", - "F8", - "G8", - "H8" - ], - [ - "A9", - "B9", - "C9", - "D9", - "E9", - "F9", - "G9", - "H9" - ] - ], - "parameters": { - "format": "96Standard", - "isMagneticModuleCompatible": false, - "isTiprack": true, - "loadName": "opentrons_flex_96_tiprack_50ul", - "quirks": [], - "tipLength": 57.9, - "tipOverlap": 10.5 - }, - "schemaVersion": 2, - "stackingOffsetWithLabware": { - "opentrons_flex_96_tiprack_adapter": { - "x": 0, - "y": 0, - "z": 121 - } - }, - "stackingOffsetWithModule": {}, - "version": 1, - "wells": { - "A1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 74.38, - "z": 1.5 - }, - "A10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 74.38, - "z": 1.5 - }, - "A11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 74.38, - "z": 1.5 - }, - "A12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 74.38, - "z": 1.5 - }, - "A2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 74.38, - "z": 1.5 - }, - "A3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 74.38, - "z": 1.5 - }, - "A4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 74.38, - "z": 1.5 - }, - "A5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 74.38, - "z": 1.5 - }, - "A6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 74.38, - "z": 1.5 - }, - "A7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 74.38, - "z": 1.5 - }, - "A8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 74.38, - "z": 1.5 - }, - "A9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 74.38, - "z": 1.5 - }, - "B1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 65.38, - "z": 1.5 - }, - "B10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 65.38, - "z": 1.5 - }, - "B11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 65.38, - "z": 1.5 - }, - "B12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 65.38, - "z": 1.5 - }, - "B2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 65.38, - "z": 1.5 - }, - "B3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 65.38, - "z": 1.5 - }, - "B4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 65.38, - "z": 1.5 - }, - "B5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 65.38, - "z": 1.5 - }, - "B6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 65.38, - "z": 1.5 - }, - "B7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 65.38, - "z": 1.5 - }, - "B8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 65.38, - "z": 1.5 - }, - "B9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 65.38, - "z": 1.5 - }, - "C1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 56.38, - "z": 1.5 - }, - "C10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 56.38, - "z": 1.5 - }, - "C11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 56.38, - "z": 1.5 - }, - "C12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 56.38, - "z": 1.5 - }, - "C2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 56.38, - "z": 1.5 - }, - "C3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 56.38, - "z": 1.5 - }, - "C4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 56.38, - "z": 1.5 - }, - "C5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 56.38, - "z": 1.5 - }, - "C6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 56.38, - "z": 1.5 - }, - "C7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 56.38, - "z": 1.5 - }, - "C8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 56.38, - "z": 1.5 - }, - "C9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 56.38, - "z": 1.5 - }, - "D1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 47.38, - "z": 1.5 - }, - "D10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 47.38, - "z": 1.5 - }, - "D11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 47.38, - "z": 1.5 - }, - "D12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 47.38, - "z": 1.5 - }, - "D2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 47.38, - "z": 1.5 - }, - "D3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 47.38, - "z": 1.5 - }, - "D4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 47.38, - "z": 1.5 - }, - "D5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 47.38, - "z": 1.5 - }, - "D6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 47.38, - "z": 1.5 - }, - "D7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 47.38, - "z": 1.5 - }, - "D8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 47.38, - "z": 1.5 - }, - "D9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 47.38, - "z": 1.5 - }, - "E1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 38.38, - "z": 1.5 - }, - "E10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 38.38, - "z": 1.5 - }, - "E11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 38.38, - "z": 1.5 - }, - "E12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 38.38, - "z": 1.5 - }, - "E2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 38.38, - "z": 1.5 - }, - "E3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 38.38, - "z": 1.5 - }, - "E4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 38.38, - "z": 1.5 - }, - "E5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 38.38, - "z": 1.5 - }, - "E6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 38.38, - "z": 1.5 - }, - "E7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 38.38, - "z": 1.5 - }, - "E8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 38.38, - "z": 1.5 - }, - "E9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 38.38, - "z": 1.5 - }, - "F1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 29.38, - "z": 1.5 - }, - "F10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 29.38, - "z": 1.5 - }, - "F11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 29.38, - "z": 1.5 - }, - "F12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 29.38, - "z": 1.5 - }, - "F2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 29.38, - "z": 1.5 - }, - "F3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 29.38, - "z": 1.5 - }, - "F4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 29.38, - "z": 1.5 - }, - "F5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 29.38, - "z": 1.5 - }, - "F6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 29.38, - "z": 1.5 - }, - "F7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 29.38, - "z": 1.5 - }, - "F8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 29.38, - "z": 1.5 - }, - "F9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 29.38, - "z": 1.5 - }, - "G1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 20.38, - "z": 1.5 - }, - "G10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 20.38, - "z": 1.5 - }, - "G11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 20.38, - "z": 1.5 - }, - "G12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 20.38, - "z": 1.5 - }, - "G2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 20.38, - "z": 1.5 - }, - "G3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 20.38, - "z": 1.5 - }, - "G4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 20.38, - "z": 1.5 - }, - "G5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 20.38, - "z": 1.5 - }, - "G6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 20.38, - "z": 1.5 - }, - "G7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 20.38, - "z": 1.5 - }, - "G8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 20.38, - "z": 1.5 - }, - "G9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 20.38, - "z": 1.5 - }, - "H1": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 14.38, - "y": 11.38, - "z": 1.5 - }, - "H10": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 95.38, - "y": 11.38, - "z": 1.5 - }, - "H11": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 104.38, - "y": 11.38, - "z": 1.5 - }, - "H12": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 113.38, - "y": 11.38, - "z": 1.5 - }, - "H2": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 23.38, - "y": 11.38, - "z": 1.5 - }, - "H3": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 32.38, - "y": 11.38, - "z": 1.5 - }, - "H4": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 41.38, - "y": 11.38, - "z": 1.5 - }, - "H5": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 50.38, - "y": 11.38, - "z": 1.5 - }, - "H6": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 59.38, - "y": 11.38, - "z": 1.5 - }, - "H7": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 68.38, - "y": 11.38, - "z": 1.5 - }, - "H8": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 77.38, - "y": 11.38, - "z": 1.5 - }, - "H9": { - "depth": 97.5, - "diameter": 5.58, - "shape": "circular", - "totalLiquidVolume": 50, - "x": 86.38, - "y": 11.38, - "z": 1.5 - } - } - }, - "labwareId": "UUID" - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e293d3a3d34ffc36672d4ad8587006b8", - "notes": [], - "params": { - "loadName": "opentrons_flex_96_tiprack_200ul", - "location": { - "slotName": "B2" - }, - "namespace": "opentrons", - "version": 1 - }, - "result": { - "definition": { - "allowedRoles": [], - "brand": { - "brand": "Opentrons", - "brandId": [] - }, - "cornerOffsetFromSlot": { - "x": 0, - "y": 0, - "z": 0 - }, - "dimensions": { - "xDimension": 127.75, - "yDimension": 85.75, - "zDimension": 99 - }, - "gripForce": 16.0, - "gripHeightFromLabwareBottom": 23.9, - "gripperOffsets": {}, - "groups": [ - { - "metadata": {}, - "wells": [ - "A1", - "A10", - "A11", - "A12", - "A2", - "A3", - "A4", - "A5", - "A6", - "A7", - "A8", - "A9", - "B1", - "B10", - "B11", - "B12", - "B2", - "B3", - "B4", - "B5", - "B6", - "B7", - "B8", - "B9", - "C1", - "C10", - "C11", - "C12", - "C2", - "C3", - "C4", - "C5", - "C6", - "C7", - "C8", - "C9", - "D1", - "D10", - "D11", - "D12", - "D2", - "D3", - "D4", - "D5", - "D6", - "D7", - "D8", - "D9", - "E1", - "E10", - "E11", - "E12", - "E2", - "E3", - "E4", - "E5", - "E6", - "E7", - "E8", - "E9", - "F1", - "F10", - "F11", - "F12", - "F2", - "F3", - "F4", - "F5", - "F6", - "F7", - "F8", - "F9", - "G1", - "G10", - "G11", - "G12", - "G2", - "G3", - "G4", - "G5", - "G6", - "G7", - "G8", - "G9", - "H1", - "H10", - "H11", - "H12", - "H2", - "H3", - "H4", - "H5", - "H6", - "H7", - "H8", - "H9" - ] - } - ], - "metadata": { - "displayCategory": "tipRack", - "displayName": "Opentrons Flex 96 Tip Rack 200 µL", - "displayVolumeUnits": "µL", - "tags": [] - }, - "namespace": "opentrons", - "ordering": [ - [ - "A1", - "B1", - "C1", - "D1", - "E1", - "F1", - "G1", - "H1" - ], - [ - "A10", - "B10", - "C10", - "D10", - "E10", - "F10", - "G10", - "H10" - ], - [ - "A11", - "B11", - "C11", - "D11", - "E11", - "F11", - "G11", - "H11" - ], - [ - "A12", - "B12", - "C12", - "D12", - "E12", - "F12", - "G12", - "H12" - ], - [ - "A2", - "B2", - "C2", - "D2", - "E2", - "F2", - "G2", - "H2" - ], - [ - "A3", - "B3", - "C3", - "D3", - "E3", - "F3", - "G3", - "H3" - ], - [ - "A4", - "B4", - "C4", - "D4", - "E4", - "F4", - "G4", - "H4" - ], - [ - "A5", - "B5", - "C5", - "D5", - "E5", - "F5", - "G5", - "H5" - ], - [ - "A6", - "B6", - "C6", - "D6", - "E6", - "F6", - "G6", - "H6" - ], - [ - "A7", - "B7", - "C7", - "D7", - "E7", - "F7", - "G7", - "H7" - ], - [ - "A8", - "B8", - "C8", - "D8", - "E8", - "F8", - "G8", - "H8" - ], - [ - "A9", - "B9", - "C9", - "D9", - "E9", - "F9", - "G9", - "H9" - ] - ], - "parameters": { - "format": "96Standard", - "isMagneticModuleCompatible": false, - "isTiprack": true, - "loadName": "opentrons_flex_96_tiprack_200ul", - "quirks": [], - "tipLength": 58.35, - "tipOverlap": 10.5 - }, - "schemaVersion": 2, - "stackingOffsetWithLabware": { - "opentrons_flex_96_tiprack_adapter": { - "x": 0, - "y": 0, - "z": 121 - } - }, - "stackingOffsetWithModule": {}, - "version": 1, - "wells": { - "A1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 74.38, - "z": 1.5 - }, - "A10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 74.38, - "z": 1.5 - }, - "A11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 74.38, - "z": 1.5 - }, - "A12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 74.38, - "z": 1.5 - }, - "A2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 74.38, - "z": 1.5 - }, - "A3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 74.38, - "z": 1.5 - }, - "A4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 74.38, - "z": 1.5 - }, - "A5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 74.38, - "z": 1.5 - }, - "A6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 74.38, - "z": 1.5 - }, - "A7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 74.38, - "z": 1.5 - }, - "A8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 74.38, - "z": 1.5 - }, - "A9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 74.38, - "z": 1.5 - }, - "B1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 65.38, - "z": 1.5 - }, - "B10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 65.38, - "z": 1.5 - }, - "B11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 65.38, - "z": 1.5 - }, - "B12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 65.38, - "z": 1.5 - }, - "B2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 65.38, - "z": 1.5 - }, - "B3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 65.38, - "z": 1.5 - }, - "B4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 65.38, - "z": 1.5 - }, - "B5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 65.38, - "z": 1.5 - }, - "B6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 65.38, - "z": 1.5 - }, - "B7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 65.38, - "z": 1.5 - }, - "B8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 65.38, - "z": 1.5 - }, - "B9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 65.38, - "z": 1.5 - }, - "C1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 56.38, - "z": 1.5 - }, - "C10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 56.38, - "z": 1.5 - }, - "C11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 56.38, - "z": 1.5 - }, - "C12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 56.38, - "z": 1.5 - }, - "C2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 56.38, - "z": 1.5 - }, - "C3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 56.38, - "z": 1.5 - }, - "C4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 56.38, - "z": 1.5 - }, - "C5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 56.38, - "z": 1.5 - }, - "C6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 56.38, - "z": 1.5 - }, - "C7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 56.38, - "z": 1.5 - }, - "C8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 56.38, - "z": 1.5 - }, - "C9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 56.38, - "z": 1.5 - }, - "D1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 47.38, - "z": 1.5 - }, - "D10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 47.38, - "z": 1.5 - }, - "D11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 47.38, - "z": 1.5 - }, - "D12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 47.38, - "z": 1.5 - }, - "D2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 47.38, - "z": 1.5 - }, - "D3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 47.38, - "z": 1.5 - }, - "D4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 47.38, - "z": 1.5 - }, - "D5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 47.38, - "z": 1.5 - }, - "D6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 47.38, - "z": 1.5 - }, - "D7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 47.38, - "z": 1.5 - }, - "D8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 47.38, - "z": 1.5 - }, - "D9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 47.38, - "z": 1.5 - }, - "E1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 38.38, - "z": 1.5 - }, - "E10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 38.38, - "z": 1.5 - }, - "E11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 38.38, - "z": 1.5 - }, - "E12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 38.38, - "z": 1.5 - }, - "E2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 38.38, - "z": 1.5 - }, - "E3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 38.38, - "z": 1.5 - }, - "E4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 38.38, - "z": 1.5 - }, - "E5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 38.38, - "z": 1.5 - }, - "E6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 38.38, - "z": 1.5 - }, - "E7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 38.38, - "z": 1.5 - }, - "E8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 38.38, - "z": 1.5 - }, - "E9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 38.38, - "z": 1.5 - }, - "F1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 29.38, - "z": 1.5 - }, - "F10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 29.38, - "z": 1.5 - }, - "F11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 29.38, - "z": 1.5 - }, - "F12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 29.38, - "z": 1.5 - }, - "F2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 29.38, - "z": 1.5 - }, - "F3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 29.38, - "z": 1.5 - }, - "F4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 29.38, - "z": 1.5 - }, - "F5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 29.38, - "z": 1.5 - }, - "F6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 29.38, - "z": 1.5 - }, - "F7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 29.38, - "z": 1.5 - }, - "F8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 29.38, - "z": 1.5 - }, - "F9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 29.38, - "z": 1.5 - }, - "G1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 20.38, - "z": 1.5 - }, - "G10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 20.38, - "z": 1.5 - }, - "G11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 20.38, - "z": 1.5 - }, - "G12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 20.38, - "z": 1.5 - }, - "G2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 20.38, - "z": 1.5 - }, - "G3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 20.38, - "z": 1.5 - }, - "G4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 20.38, - "z": 1.5 - }, - "G5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 20.38, - "z": 1.5 - }, - "G6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 20.38, - "z": 1.5 - }, - "G7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 20.38, - "z": 1.5 - }, - "G8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 20.38, - "z": 1.5 - }, - "G9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 20.38, - "z": 1.5 - }, - "H1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 11.38, - "z": 1.5 - }, - "H10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 11.38, - "z": 1.5 - }, - "H11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 11.38, - "z": 1.5 - }, - "H12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 11.38, - "z": 1.5 - }, - "H2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 11.38, - "z": 1.5 - }, - "H3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 11.38, - "z": 1.5 - }, - "H4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 11.38, - "z": 1.5 - }, - "H5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 11.38, - "z": 1.5 - }, - "H6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 11.38, - "z": 1.5 - }, - "H7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 11.38, - "z": 1.5 - }, - "H8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 11.38, - "z": 1.5 - }, - "H9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 11.38, - "z": 1.5 - } - } - }, - "labwareId": "UUID" - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c9227d2a0882401c3eb6f1f2f96b8e67", - "notes": [], - "params": { - "loadName": "opentrons_flex_96_tiprack_200ul", - "location": { - "slotName": "B3" - }, - "namespace": "opentrons", - "version": 1 - }, - "result": { - "definition": { - "allowedRoles": [], - "brand": { - "brand": "Opentrons", - "brandId": [] - }, - "cornerOffsetFromSlot": { - "x": 0, - "y": 0, - "z": 0 - }, - "dimensions": { - "xDimension": 127.75, - "yDimension": 85.75, - "zDimension": 99 - }, - "gripForce": 16.0, - "gripHeightFromLabwareBottom": 23.9, - "gripperOffsets": {}, - "groups": [ - { - "metadata": {}, - "wells": [ - "A1", - "A10", - "A11", - "A12", - "A2", - "A3", - "A4", - "A5", - "A6", - "A7", - "A8", - "A9", - "B1", - "B10", - "B11", - "B12", - "B2", - "B3", - "B4", - "B5", - "B6", - "B7", - "B8", - "B9", - "C1", - "C10", - "C11", - "C12", - "C2", - "C3", - "C4", - "C5", - "C6", - "C7", - "C8", - "C9", - "D1", - "D10", - "D11", - "D12", - "D2", - "D3", - "D4", - "D5", - "D6", - "D7", - "D8", - "D9", - "E1", - "E10", - "E11", - "E12", - "E2", - "E3", - "E4", - "E5", - "E6", - "E7", - "E8", - "E9", - "F1", - "F10", - "F11", - "F12", - "F2", - "F3", - "F4", - "F5", - "F6", - "F7", - "F8", - "F9", - "G1", - "G10", - "G11", - "G12", - "G2", - "G3", - "G4", - "G5", - "G6", - "G7", - "G8", - "G9", - "H1", - "H10", - "H11", - "H12", - "H2", - "H3", - "H4", - "H5", - "H6", - "H7", - "H8", - "H9" - ] - } - ], - "metadata": { - "displayCategory": "tipRack", - "displayName": "Opentrons Flex 96 Tip Rack 200 µL", - "displayVolumeUnits": "µL", - "tags": [] - }, - "namespace": "opentrons", - "ordering": [ - [ - "A1", - "B1", - "C1", - "D1", - "E1", - "F1", - "G1", - "H1" - ], - [ - "A10", - "B10", - "C10", - "D10", - "E10", - "F10", - "G10", - "H10" - ], - [ - "A11", - "B11", - "C11", - "D11", - "E11", - "F11", - "G11", - "H11" - ], - [ - "A12", - "B12", - "C12", - "D12", - "E12", - "F12", - "G12", - "H12" - ], - [ - "A2", - "B2", - "C2", - "D2", - "E2", - "F2", - "G2", - "H2" - ], - [ - "A3", - "B3", - "C3", - "D3", - "E3", - "F3", - "G3", - "H3" - ], - [ - "A4", - "B4", - "C4", - "D4", - "E4", - "F4", - "G4", - "H4" - ], - [ - "A5", - "B5", - "C5", - "D5", - "E5", - "F5", - "G5", - "H5" - ], - [ - "A6", - "B6", - "C6", - "D6", - "E6", - "F6", - "G6", - "H6" - ], - [ - "A7", - "B7", - "C7", - "D7", - "E7", - "F7", - "G7", - "H7" - ], - [ - "A8", - "B8", - "C8", - "D8", - "E8", - "F8", - "G8", - "H8" - ], - [ - "A9", - "B9", - "C9", - "D9", - "E9", - "F9", - "G9", - "H9" - ] - ], - "parameters": { - "format": "96Standard", - "isMagneticModuleCompatible": false, - "isTiprack": true, - "loadName": "opentrons_flex_96_tiprack_200ul", - "quirks": [], - "tipLength": 58.35, - "tipOverlap": 10.5 - }, - "schemaVersion": 2, - "stackingOffsetWithLabware": { - "opentrons_flex_96_tiprack_adapter": { - "x": 0, - "y": 0, - "z": 121 - } - }, - "stackingOffsetWithModule": {}, - "version": 1, - "wells": { - "A1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 74.38, - "z": 1.5 - }, - "A10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 74.38, - "z": 1.5 - }, - "A11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 74.38, - "z": 1.5 - }, - "A12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 74.38, - "z": 1.5 - }, - "A2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 74.38, - "z": 1.5 - }, - "A3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 74.38, - "z": 1.5 - }, - "A4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 74.38, - "z": 1.5 - }, - "A5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 74.38, - "z": 1.5 - }, - "A6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 74.38, - "z": 1.5 - }, - "A7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 74.38, - "z": 1.5 - }, - "A8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 74.38, - "z": 1.5 - }, - "A9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 74.38, - "z": 1.5 - }, - "B1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 65.38, - "z": 1.5 - }, - "B10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 65.38, - "z": 1.5 - }, - "B11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 65.38, - "z": 1.5 - }, - "B12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 65.38, - "z": 1.5 - }, - "B2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 65.38, - "z": 1.5 - }, - "B3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 65.38, - "z": 1.5 - }, - "B4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 65.38, - "z": 1.5 - }, - "B5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 65.38, - "z": 1.5 - }, - "B6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 65.38, - "z": 1.5 - }, - "B7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 65.38, - "z": 1.5 - }, - "B8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 65.38, - "z": 1.5 - }, - "B9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 65.38, - "z": 1.5 - }, - "C1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 56.38, - "z": 1.5 - }, - "C10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 56.38, - "z": 1.5 - }, - "C11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 56.38, - "z": 1.5 - }, - "C12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 56.38, - "z": 1.5 - }, - "C2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 56.38, - "z": 1.5 - }, - "C3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 56.38, - "z": 1.5 - }, - "C4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 56.38, - "z": 1.5 - }, - "C5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 56.38, - "z": 1.5 - }, - "C6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 56.38, - "z": 1.5 - }, - "C7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 56.38, - "z": 1.5 - }, - "C8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 56.38, - "z": 1.5 - }, - "C9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 56.38, - "z": 1.5 - }, - "D1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 47.38, - "z": 1.5 - }, - "D10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 47.38, - "z": 1.5 - }, - "D11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 47.38, - "z": 1.5 - }, - "D12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 47.38, - "z": 1.5 - }, - "D2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 47.38, - "z": 1.5 - }, - "D3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 47.38, - "z": 1.5 - }, - "D4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 47.38, - "z": 1.5 - }, - "D5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 47.38, - "z": 1.5 - }, - "D6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 47.38, - "z": 1.5 - }, - "D7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 47.38, - "z": 1.5 - }, - "D8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 47.38, - "z": 1.5 - }, - "D9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 47.38, - "z": 1.5 - }, - "E1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 38.38, - "z": 1.5 - }, - "E10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 38.38, - "z": 1.5 - }, - "E11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 38.38, - "z": 1.5 - }, - "E12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 38.38, - "z": 1.5 - }, - "E2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 38.38, - "z": 1.5 - }, - "E3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 38.38, - "z": 1.5 - }, - "E4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 38.38, - "z": 1.5 - }, - "E5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 38.38, - "z": 1.5 - }, - "E6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 38.38, - "z": 1.5 - }, - "E7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 38.38, - "z": 1.5 - }, - "E8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 38.38, - "z": 1.5 - }, - "E9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 38.38, - "z": 1.5 - }, - "F1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 29.38, - "z": 1.5 - }, - "F10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 29.38, - "z": 1.5 - }, - "F11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 29.38, - "z": 1.5 - }, - "F12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 29.38, - "z": 1.5 - }, - "F2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 29.38, - "z": 1.5 - }, - "F3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 29.38, - "z": 1.5 - }, - "F4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 29.38, - "z": 1.5 - }, - "F5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 29.38, - "z": 1.5 - }, - "F6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 29.38, - "z": 1.5 - }, - "F7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 29.38, - "z": 1.5 - }, - "F8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 29.38, - "z": 1.5 - }, - "F9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 29.38, - "z": 1.5 - }, - "G1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 20.38, - "z": 1.5 - }, - "G10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 20.38, - "z": 1.5 - }, - "G11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 20.38, - "z": 1.5 - }, - "G12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 20.38, - "z": 1.5 - }, - "G2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 20.38, - "z": 1.5 - }, - "G3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 20.38, - "z": 1.5 - }, - "G4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 20.38, - "z": 1.5 - }, - "G5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 20.38, - "z": 1.5 - }, - "G6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 20.38, - "z": 1.5 - }, - "G7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 20.38, - "z": 1.5 - }, - "G8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 20.38, - "z": 1.5 - }, - "G9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 20.38, - "z": 1.5 - }, - "H1": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 14.38, - "y": 11.38, - "z": 1.5 - }, - "H10": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 95.38, - "y": 11.38, - "z": 1.5 - }, - "H11": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 104.38, - "y": 11.38, - "z": 1.5 - }, - "H12": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 113.38, - "y": 11.38, - "z": 1.5 - }, - "H2": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 23.38, - "y": 11.38, - "z": 1.5 - }, - "H3": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 32.38, - "y": 11.38, - "z": 1.5 - }, - "H4": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 41.38, - "y": 11.38, - "z": 1.5 - }, - "H5": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 50.38, - "y": 11.38, - "z": 1.5 - }, - "H6": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 59.38, - "y": 11.38, - "z": 1.5 - }, - "H7": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 68.38, - "y": 11.38, - "z": 1.5 - }, - "H8": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 77.38, - "y": 11.38, - "z": 1.5 - }, - "H9": { - "depth": 97.5, - "diameter": 5.59, - "shape": "circular", - "totalLiquidVolume": 200, - "x": 86.38, - "y": 11.38, - "z": 1.5 - } - } - }, - "labwareId": "UUID" - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadPipette", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "58bf66c06d4c9ef0133ceda2d3fcd0db", - "notes": [], - "params": { - "liquidPresenceDetection": false, - "mount": "left", - "pipetteName": "p50_multi_flex", - "tipOverlapNotAfterVersion": "v0" - }, - "result": { - "pipetteId": "UUID" - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadPipette", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f41b352a27113bc3a9f0595d3f285410", - "notes": [], - "params": { - "liquidPresenceDetection": false, - "mount": "right", - "pipetteName": "p1000_multi_flex", - "tipOverlapNotAfterVersion": "v0" - }, - "result": { - "pipetteId": "UUID" - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8f61100f29f0e3dae391082bcf9c4b33", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "aaa5a61f4a6a6c60e1d02ab560e8ed30", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2e9b37a4b76c8be219754992b50eac87", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ddd20b2c38d4938cfbebedc9e32a0f3a", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5c8c68d760474150e360b8ccb0614254", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f8b997229880175082181fc529ea3906", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9818ef02347426a4ef817c25ccd44b3c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d97431083e1366ed0a2b09b7195fae03", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "779b7b1760eb1092027a3b5813510a32", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5eb0bca7d2181db9880c8105f08fa8fd", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "008e8176fde7fd9caffd459f967acca1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b66801b6f296760e22f9e210a386b1b1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c8a1b0a4298bcec2592014f2bf37d576", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b693187f2934371da9b6eba9d20a3002", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e46b2068448ff0983a798cbef1a3cec0", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9f4f599a30f83a5420e5fb732ca2efc0", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4472cf6cf1f8e71cdd0be1633127c932", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "15d9492cab2583479a4f816ef17a46a1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7f4a24bd41db2e2f75e276418679944c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5e389eada4ee80880ce64d17356c0c81", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7ce70a99c5d3071241add86ec7a937a6", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2bb7f73d43103f0343484b7194519e4c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ef9fda0ac1ab209fe854ae7e7d7f4aa5", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "47674b8e252ba5e9349dd8dbc2e66a40", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "dcc8d9df462cd0c9a1b47ccd02f3eb2d", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2d70199bbce02415accfd10aa3c03c42", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "aa816105a34323703c3bb8cb06146be1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7e0516811ce65b3a61ec6786ea0219d9", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a8fd1948ecc0acbaae6cb41bb8e730de", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c6c5a01e13cc765533e432494ff7f07f", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a9c8d1c1d71ae30d2d011a21e5ffdb3e", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4bdb9bf5d751f40bb798826e2e676870", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5a73e1d4498d560d2951a9b0851d173c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4bedaaa098e7bccd2f2bfdfdd6ec0a2e", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "672d7bbe39d4d9fc5544a9075bfd1f79", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7efec2350ce946b98fbedca57bb2d546", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "96567efe8321030016f11223d404621f", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4b85144700c274e59a52004fe6667c25", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "60537125b393dbadfe94648248132f7c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "990dd741f3dcccafb23cc986b4088ad9", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d8ff455d6832a88e094daf9c9aa2eaa8", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "80fa3121956cee042c5d6dc137e3ca71", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "75bd4c7ef1588a67ea415705b2c8349c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3874e2785cec5c6baf3dff0d664d90ef", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bfdae27d79cc5953ddadab4a39c10466", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e231e7cc6a278ce42d86d6129b01453b", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9aebd50719d690f81b6385ed1533157e", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bfa8b077772ea67face5b5da8ce31feb", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9a3b06420819d013bdd36b9b2b4d2358", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5f5aa88961947babf39b10e37ee23421", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1d856d812464812151e1d5a8dd667237", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ebed53310e7b29d035f6f7a016fff0f8", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2fd7d9f9978f8381228cccb93245fc2d", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d7c19c35227a0340e67f2e0ed968d7d3", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9ac59c380e99317f35830b98fb80871d", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a443653eceb8acf82a3ff5594ec68825", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8c6d4cad0acca6032cee9ed68f3b7b77", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1947ab90ab78e871a5093646a8ad92d0", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0c22e06378cc0cd2d4064974f2fa408b", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4193cbdc63a59a75bcafbf4d97663b9d", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d1103b5929b1b7a0185f58a74468440d", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1b3f07e9f1477115b69ce6331c90d9f1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "303f4d143d7b69932fe4fd18127c81db", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "65f3615cb8230a6ddb6cf35b73c3c5aa", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d5e7c9ba1db7bf2b087d00ca46c472e9", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e01aa7bc7c31a8dffd23546b1945e675", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9247c62d2ead992011f223392961b7b2", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d293202097173e711d291e7259c65611", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "45bcdd313fe6186beac0fabca6a7dbdb", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b550a26ebfeb445caf3784c322b41089", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d1fe7ad1290317d754709b52721cf783", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2830f4f34d964ab48b8957fa864e0316", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "818860ae04707d24dc36e96743884690", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "43aa0a49ca4c34112a19b99640e21beb", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8c658cd713c0ac4f52bc9855b872130a", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "185a3fd2dbd29301fc7e92cb25f0e218", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8633954f726d96cb33537b14a848b4bc", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "620b4f47c73faf6e0265f8741d23dc8b", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2dcce3b03d36d221c7ce487bae928383", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "135d11296e5067afac8847f22d806e79", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d53f49d3f67df5cc18fb4547217e4da9", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "78298f5f0fa0e9043ab4df0549173ba2", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "84571d458086763fbd2d5e30f26a6d1b", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6fec441fec6751579035f49cb02fb651", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9774136549a3a20f61860acb85b83438", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cf2009489d7bcc50208d3cfec70bd176", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f8d7eeec9aabdf5a9e3e7e5f91046003", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5c26c2c63789e7307ea62537c48c1e42", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "466fa95ac331fbb920ab792a52078999", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "427f6c1570b503fcc1e04f26c2369aaf", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "44d529133defc8ddfe4995ce63c3f914", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "437671cc25e87718115772ec6ec66d57", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9fdddc1ec95caa85c91bab332d0af3e4", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f2d35b138c96e986cc8ca9d71bb9f6e8", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "56c24389b4ad55beb7ed6159c4ad00d3", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "120ece00f990ff4566b38892075fc007", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0bf90a90466930017162fadbea9477a6", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7d0d618d19cfbf3b537bed12e0f06f4d", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ffe0929eea2d196d78f265dd2e9926d3", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c8f5cc16090f48b1c5a31908c12e93a5", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4091b25131a60700b2e23a474cefb4f1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e908f3817c89489f66d8e675e0f10e14", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "afd0eadc7fd05676719741b36917d87c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6dfb35b2956e28a771a594758e46a714", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H1": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7ca6001ebb059fefc71631552f8af3d3", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3287ad9cac60f592ab3536068b0f40ec", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "aff52c45c3c1e93af130a41c8c80d19c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b263b9522566c1802676980e23209041", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c8516f441443ee0604fd1a2df54ad7e0", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0e0454a517248a99751a99fe1c08a2f6", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7ea7d18690ec3e20e8ed3535c1defbb9", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "511c7bf60e9439e100ce45ac967da49e", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H2": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "81a78fd07484e98039dafa397331454b", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cd8e8d0d5c7aaa5af541da01bb63f584", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b0e15451bf7b1644960e32675173dacf", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d10f1489d9c872506ebb31cd4b808540", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "43af4a7235622d22806f1be8ffd9519c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ebb3c328819a42ccc866bf0dbdfbae33", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2753b91f935a5083f07680673d6a9adc", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "33803f50aaecf245ec0655dc31be2eb8", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H3": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d982765564753512defadd4ee28cec77", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "511b2c79928728669da32ce6154bd1bc", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9a5cddb2e8602ec8fec98f7bf28918e5", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0328846c95fa6210972c3d45f8b54a74", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5ad886b1e0fdb9708d9c20c267e62220", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "927818362c3ea3c14823ccce4965f727", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1e50241cdb9fcd2bff1534b98ea618b1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8811f7a3c5a117e7f63f04b83de01698", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H4": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5760b7a67c17d86093f73c375bf61015", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f67583d0274c6ef9c3d762329f725f93", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b2ecfd404c288732c092ce69b7da8815", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2d899a259c3a91c64efdf675ed9a2d33", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fcf6ee9795c0a86091de90e908b372f4", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "73d5e752210cfb0d867e78215b598501", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9d289d19747f833f0aa03fbd1ec6a989", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8c5457ceac8bee1ce222d9547cd4d715", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H5": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4496f5d5a2f033bcfc803c3e6d47e8f7", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ca357881aa182f6f45bac132037803c2", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "26a1dc7b1ba51f89274dc8c24c519b5a", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "28533630621c4cb17a03486a46b030a5", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ad02948bafeaaa7d6fb5c0fd66d8c227", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2121bb825bc14b0ab45f167418dc4ad4", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2adb52e7ccd59d289b2a0d35cd314db9", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "40fae1aecd14139e9abcd6c26fa2c28d", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H6": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "38ea01a01df003b05f3e1cc4b045a10f", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A7": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "63beaa12dba5ecc8fad39b64c3852db1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B7": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c875efceaba3272710bc90e5a92c42f3", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C7": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7ca6e36ce75ba4acfcdb44922b69d484", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D7": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "124f2a131f81abeac9cbd0e9fcfcacb1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E7": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "da169522a77ef45b236cb685cc140252", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F7": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "386b7617e29115c4e2b6d4cb87185279", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G7": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e1d20d4c3166b98e92ab93717266d249", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H7": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "eb5db2ecd8e328db99b64db59e32ae09", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A8": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "92f6dc249826e5924338c0e1f80f5b65", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B8": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2b462a5f63f15b04d69c56da3ee88de3", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C8": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3b042a699af6be5f267567394035fe3b", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D8": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a45ffe349ff38d95744c4ef86fa0354f", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E8": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "26bd2430c3c4cc2408ad9a365f2c2891", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F8": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "df281882873d0e1445c2246c90c8a3c8", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G8": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b1ddf115c9e3f3fb1c3ef80e116749ac", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H8": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8e21511c6ffaf00d1bc58e527b893f9f", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A9": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7a8e561af25913e7702fe452cbf82920", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B9": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c5305a0cb530b54ebc46b676eb95f556", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C9": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5accdd553bd1bee18252a9ce7eea55b4", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D9": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0f61f84cea74148cf50b6f65cdf9d961", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E9": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e90e375e3c3ad3df214ffafe47b98111", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F9": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "14a072b4d5ef396d36986342c1c9bae8", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G9": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "06bfa9e5693709b0a2ac5d79a176296a", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H9": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "af95a8489936f9fef2c4925547f3ab1a", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A10": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d2664a413fb9dba6010006408dd2fa5c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B10": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "506ab0fee68596d670f6e284b19f4751", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C10": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "962a559444ed526d9dffa906045e8325", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D10": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c8cd0e9d4856a4d392b6ccab9a42d124", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E10": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f80d8d52b380ca2a2961237de98f7230", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F10": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3303870d02520796d5cab53d697ccbe1", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G10": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "203dc9ea7ded51bd40035af6e158d593", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H10": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b4f0749950b9c0a0fad2015aaac48a0c", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A11": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6f01f1aaa7ee5cc8a4c62144af4d0042", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B11": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "905c8fed115a922f2d89e2c02c27616d", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C11": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "875a960a9980be4f9dd1ba8745bc24cd", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D11": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6aeac56ee731a57aa81f33e3ff861d64", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E11": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2f4754434c34c2c99e5b1465af5c188e", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F11": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e8854207245788771690d1521b43c12f", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G11": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "811183ffe7c1a3ce615213e4e70d75e5", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H11": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2abee4fc1c9c31bd629912d9c8401687", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "A12": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7835e1234f735842daa4f61c575ce72f", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "B12": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "14ff231157b24c887d1ec0813b3c55f2", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "C12": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "858cd1cc7c5c762193c7e4fd67ebb8a9", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "D12": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a2cd720991fdbb2e317b7bada0952dc5", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "E12": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1fd4c6b672e6afe1a21e43ad7f412eb2", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "F12": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9ab95910878dd01da02ef13f3d4b1839", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "G12": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "loadLiquid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8f57d1b217457bcf0a4f4f17a828f1ab", - "notes": [], - "params": { - "labwareId": "UUID", - "liquidId": "UUID", - "volumeByWell": { - "H12": 200.0 - } - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "temperatureModule/setTargetTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d20affad4dcd31700fe9360291f7993d", - "notes": [], - "params": { - "celsius": 4.0, - "moduleId": "UUID" - }, - "result": { - "targetTemperature": 4.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "temperatureModule/waitForTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1cf4e0014a9e172647fc6f4fc603bb61", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetLidTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b6f197a28bfdd6061e504f34a2e2834f", - "notes": [], - "params": { - "celsius": 100.0, - "moduleId": "UUID" - }, - "result": { - "targetLidTemperature": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForLidTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c34383e8fe124015ee44b5838747baae", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "09d94a47b65ddbabfcf509b876834d52", - "notes": [], - "params": { - "message": "\n\n----------ADDING DB1----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "462ab3a580018a704b2f310fffed7dce", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "configureForVolume", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ab75b4de24840f7e2a2af5f5a66769d3", - "notes": [], - "params": { - "pipetteId": "UUID", - "tipOverlapNotAfterVersion": "v0", - "volume": 4.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "02e0185ec5df7ddbb734f5dc8bc35b02", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 4.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.3, - "y": 74.15, - "z": 13.0 - }, - "volume": 4.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "925dc71d217999516f0cb47e48fc7102", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 4.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 4.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bcf7ddbf19d9e5d8da7a913a3776a392", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1802acc6c262e1a9417bd185903c0d2b", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "80f028f4fd8345d9763b55de0afe11b5", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9fbb21cf3a09b3c3af84e8a5b16fd652", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "82da1bd1888beed5c0a18c149b949bde", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "08edda9fab95b7cff943521fcaf0f11b", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c4c1742d26de73b050a5b75444da155c", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "dfd81fde99a69c94a1a712d165467b14", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "da6f938d70af05383d7ca8dd87d0b3a8", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "412ed9d67777f23478129af556b697b7", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "952980a8bab99528215ea511050c1e3a", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2020916ffbf2b5ea23427b3e0becd1d0", - "notes": [], - "params": { - "seconds": 1.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "367507eeb74543f7aaf46d7edda50dd1", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "touchTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5a8cd45b5ca7727e55cb46681326b39e", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "radius": 1.0, - "speed": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -1.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 15.260000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5f25a3b0bd5eb88b36b555b4bb9ca0d5", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 412.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/closeLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8c668c6ac45ac4c3c712ac78fe8974c4", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/runProfile", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "19578f9b13c86b6cff60ae975d2d35c9", - "notes": [], - "params": { - "blockMaxVolumeUl": 15.0, - "moduleId": "UUID", - "profile": [ - { - "celsius": 95.0, - "holdSeconds": 120.0 - } - ] - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "60f6af8e29ff815ea688504e3da56a0f", - "notes": [], - "params": { - "celsius": 95.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 95.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7f3dfa13f21157d4c962c9e8764dd2c0", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cf30e2a36a38713ab8f8b79bc22024d9", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2791a45241c4974df9a0da7f6e512271", - "notes": [], - "params": { - "celsius": 94.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 94.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "709dce4723e76f0601f01d77402c1e0c", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d408d344171dddc8537fd97153ae3d18", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3b9280d9bde865fb1f120ea09947ba66", - "notes": [], - "params": { - "celsius": 93.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 93.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4fdb2a6857f213443c61970e8715e8f3", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c2afc2d7ef6f7750be6f39f23fa230cd", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "af324abb8812f6243ce4f60b561269bc", - "notes": [], - "params": { - "celsius": 92.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 92.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4750d94adf39183b246d31ae65c4bf52", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2acf9066db8f856e48d442c2fd21ceef", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f31a756630eff63d94ada46abc00afb5", - "notes": [], - "params": { - "celsius": 91.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 91.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bc0220bac8ea6f4f6e7365dc26e096d6", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "70dd2e8f92a0eb7c71a4327f9cf14331", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a643f56b9157be084f5a664b564ce763", - "notes": [], - "params": { - "celsius": 90.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 90.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "55729ac300dbb44fb5562e4a4fe52fa9", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6fb71ad687b24fafd5c7375912be7aff", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9a613c7df1dcf551715f4770504606d6", - "notes": [], - "params": { - "celsius": 89.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 89.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "956fba3d0d41f707f022af95e8ea5b10", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "87032579d8ef9f40e333340126277ddc", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "48ec80ea228a99b4b087753fa2cf9cd2", - "notes": [], - "params": { - "celsius": 88.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 88.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a3d85e4e2bc7a287cf4b1fd04f3ad0e4", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d919231fdfc16990e8288543729e68df", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d5fce482cce5eb32f39e2e6e3612193b", - "notes": [], - "params": { - "celsius": 87.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 87.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f065fc7369167d7e8e2052b1d843d1c2", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bacadfeea17404f3e979abd903a22f7a", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2219cc1e8741ba80306cc6959eb4ef80", - "notes": [], - "params": { - "celsius": 86.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 86.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e7ec1b11ffd4865a59326a2557d7f0bb", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2013780df01a388f678df3e0e1ae266b", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7209d0484d7bfa189d1732a7bbbc3a59", - "notes": [], - "params": { - "celsius": 85.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 85.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "20d2514acdab3e3448618809c358ae7a", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ae4feb9d69016017a5b909c5643ce34a", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "885f0a614feac857c796bd6c25671d77", - "notes": [], - "params": { - "celsius": 84.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 84.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8ca2e0d8f75dad16c1d6fdc572333619", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "47b118048e54aef9c5cdf7b6b5c2d74b", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "18dbb0a1a3cc2a907f7b387d6697bf90", - "notes": [], - "params": { - "celsius": 83.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 83.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "766d87e7fe8f4eaa1cff13ab7b37b364", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "08e08d2948fdf31653040e6221e6b24c", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "91cca07b39ae72da34b8a6dbe7a4483c", - "notes": [], - "params": { - "celsius": 82.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 82.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7d662560e11e84d650900d67098731b9", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9bbdfb63c0ce8de6eb75f26e9d39f9e3", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "629fa6ae8b71abab25bbe66f9f393308", - "notes": [], - "params": { - "celsius": 81.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 81.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "99322871b0b7c6f13091d0df69b5f937", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0e3572550a02b0c1337971ff7798330e", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "732b29e7066c4f0600aab01d2d99c1f0", - "notes": [], - "params": { - "celsius": 80.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 80.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c60e07b6aed1e9c1642a0cd211384b96", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "35df839e4614f877bedec488e21f1d20", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5f98a478c5a31272f942cf085b9193e0", - "notes": [], - "params": { - "celsius": 79.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 79.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d9b3ede674c7069718215c83ce60dd28", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5287c6592db2bec6f737849c5e0dec53", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5abef8b61964fccdf8486fbd9ee83f1f", - "notes": [], - "params": { - "celsius": 78.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 78.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4e3c62782ec783f1c57dbd98a8796429", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b5da778f8177c0f33003547e07ac1590", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2172d3afb18d1ae61e017c32495eee0e", - "notes": [], - "params": { - "celsius": 77.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 77.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "06484f7d18a9b6445458cd2ad2f6f507", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "011eb9735c3f22751b5286eda84e986a", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c97542e9721ff61656d06228cdab2ddf", - "notes": [], - "params": { - "celsius": 76.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 76.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2907edf068ca4a350457ad05b9fcea66", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "38cde5b0b2b6be57808d0505452776f9", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7e260670d38ee78f3bf77dbca2222256", - "notes": [], - "params": { - "celsius": 75.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 75.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d3440f65cbee4d12fd1c819b29a5a944", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "62453517166a7f2c67b88512af37d922", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "071775894f0f17b1cd0da7f9bec77426", - "notes": [], - "params": { - "celsius": 74.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 74.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "84f1fed8949a865c2f8637b308b4d9e1", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "12b9cafebf04032b6172e122bb9fe0f3", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7f876f981c311529287bda518287cda8", - "notes": [], - "params": { - "celsius": 73.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 73.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8b7f9d4867e506d964a5d5d0e4308da7", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7e94578f552f97db94b4c8b010ff31e5", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5fdae4f3bb9d2f5a48957eb26f720a2e", - "notes": [], - "params": { - "celsius": 72.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 72.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f45c9fcb71e0f54d5b93d33ae8ead783", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3f31cb260adc9c9560280a4bfb01c414", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f7b42a96363bb86230ca6f85799f5a39", - "notes": [], - "params": { - "celsius": 71.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 71.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b8bc1d21e31bf72fa658f9bee0506704", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cfda1c125e8fc2b993bc6577e7c79302", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "51a35c65b72f8facc402983cee134cf1", - "notes": [], - "params": { - "celsius": 70.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2ba43999b435e4e381c66ad10f0b6849", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1fe5265fa866a637822ecac83c532662", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7f18d9073f427de54acb37b4e35434e9", - "notes": [], - "params": { - "celsius": 69.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 69.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "831ee6cfe31051b44a94fb3d2c6d441a", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2c56d24c082ef09de8874b653f2d6c31", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0b7f04a03a08371286139a202f2712a3", - "notes": [], - "params": { - "celsius": 68.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 68.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "161c4347857dd8598ce3dded3acdfa3c", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "af099f13af8744d459dd30809edebab8", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "31c5cd8831b441e14b342dc0318ffe9f", - "notes": [], - "params": { - "celsius": 67.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 67.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1a064b599037ddf4e4e7ea30c28b0aa1", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5de020e8575d5face143bcce12a9db58", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ae11b2fcf8ff20cc4c8b27805bc3764a", - "notes": [], - "params": { - "celsius": 66.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 66.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cebd72effd7bf193c520cac7e5925291", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "32fc1942a3c2eac289ababaf76b4d9a5", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9619f00c47abba041ad3f9746b80836d", - "notes": [], - "params": { - "celsius": 65.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 65.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "175f79a2dbc8fd5bd61e87d988a74239", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e05d8703343f5f263d7ea70dc1967221", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3ba5b9a01f55e59c95711d78c47a9f36", - "notes": [], - "params": { - "celsius": 64.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 64.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d5594dd1033606855033ef1ad41542f6", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "089a2feab8db04a58ef33c9066918f17", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "75d9121a8719b566ba0f0e5d9ffcdc74", - "notes": [], - "params": { - "celsius": 63.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 63.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d73623f356eb89cfe19ecd18d90cd985", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d636b95a32388ae938c8a332243fb5da", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1dcf255b9b80e01fdbdaeb37d32564de", - "notes": [], - "params": { - "celsius": 62.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 62.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5deb8c94c4f1b96cfc805108b40eedca", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e5feb25ff7197995390beb67cca092b0", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "548caab309c6e891029022781b4f7c72", - "notes": [], - "params": { - "celsius": 61.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 61.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b051a6dd705c10c0b5171576fb461135", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7df7995ccb04711873f7c88ce4f3b22d", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "626a331d3e0a18c6b40f4a2a9470eece", - "notes": [], - "params": { - "celsius": 60.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 60.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0a15ba28b7f16a96829f07c52f3ccd5e", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "03a6b5ea04e006f00320dbb47e31dc3e", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c660e95ac85702c6c7d5e5ac8ebf8094", - "notes": [], - "params": { - "celsius": 59.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 59.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "50ccd309e74a683fa9423c800e81a336", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5cb07049db96dd11130ded42d143ce50", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "206876e176f7bab12078fe5f8ba5bf99", - "notes": [], - "params": { - "celsius": 58.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 58.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "84e968aaca6cc5104f379b6e107d1f70", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "31fde73939cca3e88201d07cdabca09b", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "365d1e4f535103137b1a83ae21285135", - "notes": [], - "params": { - "celsius": 57.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 57.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0d45a7384b5bcc64aec5fea945fbcc05", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "33372e812c8e8be65ad1f5704aa7af28", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a82d40b62d22d6db9826410daf9cfdf0", - "notes": [], - "params": { - "celsius": 56.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 56.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a7257d111ac2c8a1aaec1f29e3c067cb", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cea7b98870afd782e0bc58ef2b906000", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b1029a72e746a925225221dd43d60a6b", - "notes": [], - "params": { - "celsius": 55.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 55.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "449c44fb4436c9d1ad65957a33c7b9de", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0b796c9b826b201f121c71d768c71b05", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c385ccb14390aba280ebcffd846da996", - "notes": [], - "params": { - "celsius": 54.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 54.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "260cd32bf3faa4a107a5d67b82ea947a", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "84b6c721649b947be32a2ecb31238de2", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "450ef1c606121c85f8fd0b546bff883f", - "notes": [], - "params": { - "celsius": 53.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 53.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "76eac448d9f0b9052c76d612b4e0f58f", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8a9f3dac77b1e0c2cb093b0ca8ce64bc", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "228773bdb653246dc79389d67ccddea7", - "notes": [], - "params": { - "celsius": 52.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 52.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8ffa3f17d4d4cf4931bb465b2ab1a655", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "30692ac27903508d3e3433863079b731", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "dc1cdca16de05f0011ff5351962af844", - "notes": [], - "params": { - "celsius": 51.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 51.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f78d14c896c5238880944e3d2745bb61", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "424273bdfd37282cd7225b949612ea5c", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "730e8c948918c889e9f66b4dc3cff123", - "notes": [], - "params": { - "celsius": 50.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e6b7ecbc818b48c811b7dd10382006ec", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4414bdaf007dc3ddc5d4235a713d50e8", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "27e30028dbd12744f9b6e78f8a7df7a3", - "notes": [], - "params": { - "celsius": 49.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 49.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "71139901ce04945ce2b73ba849e88520", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8431c3dea998a92b69fb1f3cc2767753", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5bec11762ff91cdded71fa33ea46b65a", - "notes": [], - "params": { - "celsius": 48.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 48.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a17cd184838f78893d476e0186ba4029", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2b082656959e967bc5c58cf0243ce1d9", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4b2941da4add362fb9eb8eb17c942d25", - "notes": [], - "params": { - "celsius": 47.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 47.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "338175f6bcbb9df6b2afbca596d62f8d", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "770b726c2a1101d3f84ff9be0f86a273", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c9d6d2a0cecd8f1fafd7de19920e634b", - "notes": [], - "params": { - "celsius": 46.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 46.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "25224b56b31f21d331bc7fea57f06eff", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "61fca62767e1fbc35a19e50fe5215f56", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e25db724d46f78376141eff3174266ac", - "notes": [], - "params": { - "celsius": 45.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 45.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cdc239bf1cceae10be83ba50b4eb32e8", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9f340b97951e0170b7c9b12addd11353", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a194b47da99405c0b99b81cb8ac1a90f", - "notes": [], - "params": { - "celsius": 44.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 44.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "aabdde51705e8fa92e5b2483c082510a", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0d8b069bfa036f96833adb4b4fd4e41d", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ca8d6a3d407c7bbc74530c5ac1a7b8a1", - "notes": [], - "params": { - "celsius": 43.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 43.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2e4d0e45c3eab9e8aeda4508ce715edc", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3e6f2d8e3a169b73e8fee74dbd3544f1", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "41a6dd957b991a602b58dd0565687a8f", - "notes": [], - "params": { - "celsius": 42.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 42.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5dbb3aa2977749901bf83f56a47269af", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "27e410b0b7b9d49face7717b6868f7dd", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4e04eba549e4a0fd1eac894e9aa2481d", - "notes": [], - "params": { - "celsius": 41.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 41.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f29e5c61e2a5a78a573d1d7034522573", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6b07572218f8fecff3825fac91e1eaf5", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c595de43769e39fe0e8c379642befc46", - "notes": [], - "params": { - "celsius": 40.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2a530d0e062a7a71d6a219738d801fb2", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ca807ab76984b49bdd7a4aefafcfb6b3", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9f13904fee725ea59eda60ccbb02c6ca", - "notes": [], - "params": { - "celsius": 39.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 39.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6b8b49d9c6502d5a84727f58e2d030ca", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e94bdb33e2e01e20bf87d4715804876d", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e47f9391e6ee5ac78a7c679316246b71", - "notes": [], - "params": { - "celsius": 38.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 38.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "dcb4796feeb12b68fa485a69b2016da7", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7de14b27f54084e4c5eb36098b04c04c", - "notes": [], - "params": { - "seconds": 10.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/openLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6a5013ed2f97714d3cbfd897c559e6fd", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "88f890408b39bf040605a23d06f7c983", - "notes": [], - "params": { - "message": "\n\n----------ADDING RDB----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "00677224c457c69ca08840b54601fa0f", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A2" - }, - "result": { - "position": { - "x": 23.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "configureForVolume", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "830e6f207fd56e9294fba38734d9df00", - "notes": [], - "params": { - "pipetteId": "UUID", - "tipOverlapNotAfterVersion": "v0", - "volume": 5.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "32df21b33f21b23b7250e4b145e9e4cd", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 5.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A2" - }, - "result": { - "position": { - "x": 351.3, - "y": 74.15, - "z": 13.0 - }, - "volume": 5.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ed91e0a804eeaf60876f2bacf6365ee4", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 5.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 5.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ede7c09ba888bf4583780b011f910519", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "69b2b49701561ded20dd7708b73f05cd", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7361d69000d89863baebe5a648b5c0ef", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "796026df1d808f79dd281efb7d87305e", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0c69dbf3f575d6810b555c6da10136ee", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d720aded9efb2b95abddc2cbfa5a0a22", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cc4c04a66bdb066b5722e47ddf845100", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "79ff88e8014131935040cab6ea3382c1", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e011fef8002fb189f1a676f45099df69", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b4778b46da0d23b3b96ffafec1a0d265", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "75e0f72bae3c26d8516056fb2dfa748c", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "aa7d6109f7ee2df4f13fa882503b6002", - "notes": [], - "params": { - "seconds": 1.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b8ab7b6b86fb6e8e07de27a93ee82eba", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "touchTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8ceffaa91c7327a61028b344b3190f2e", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "radius": 1.0, - "speed": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -1.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 15.260000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c772791d372fbc5e453ceecc022ce28d", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/closeLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a121310b4027654d03d49fc3f61b0f16", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/runProfile", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c800fc18faaa550aa645015b51f5ed30", - "notes": [], - "params": { - "blockMaxVolumeUl": 20.0, - "moduleId": "UUID", - "profile": [ - { - "celsius": 37.0, - "holdSeconds": 900.0 - } - ] - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6be8a1b8d682bea9507b96d9687e7d50", - "notes": [], - "params": { - "celsius": 4.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 4.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c0842bfe516e54d06bf216f39e1f7feb", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/openLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5337f23583d7160c7bd3fb11be7ccdb7", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "45460103ce1c28279b31a9f9f0797a3f", - "notes": [], - "params": { - "message": "\n\n----------ADDING PRB----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4ec57e303dd4a19dab032dd2a031224a", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A3" - }, - "result": { - "position": { - "x": 32.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d240cb5e4c9aae5f378c25d2787a7dac", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A3" - }, - "result": { - "position": { - "x": 360.3, - "y": 74.15, - "z": 13.0 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8614b0539beeec0494fa8391960ddff7", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "afd5d367ed06b78b729d43f653b0d67d", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4248f46e4810d2b632eaa0b0faf9d7dc", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8c430d6585cd69d8322b9e28f7be02e1", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "58fd795fb77022bad31f9f85a2259829", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "14d00b88ebfa904eb28fdea883f7f20a", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "abccec97db2fa96f086ce79c3dcd9526", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ec8628678ddab77e94bd0db329c855d2", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1dbeb989c1418c1f1b41591f9256bd2c", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "61d6aef48803860526736dd654e18168", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b9383b900841cb7e1e8004ca09e8d98a", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 20.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 20.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "feb3786ebd0c0d587e1ef83536b20d70", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ab8317f307f5b5ae3c5dc1c2d51ad4ef", - "notes": [], - "params": { - "seconds": 1.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "812a72ab8e68ec0870c5af969fd673f2", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "touchTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6e3bad6ca7601812c99f71e758fbb4ee", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "radius": 1.0, - "speed": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -1.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 15.260000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fc683ca797abaf2da486ca70b798d012", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 412.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/closeLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4c4e5bae16743ea62b1205bacff45893", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/runProfile", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c720a403f4196e3ba8db997f36bc8996", - "notes": [], - "params": { - "blockMaxVolumeUl": 30.0, - "moduleId": "UUID", - "profile": [ - { - "celsius": 37.0, - "holdSeconds": 900.0 - }, - { - "celsius": 70.0, - "holdSeconds": 900.0 - } - ] - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "85771372837a21e64e5272f105af8da8", - "notes": [], - "params": { - "celsius": 4.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 4.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f147f7cc9ad093b5a4ac9c7cdd80a9b1", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/openLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "eec3952968005fd92c62ad251d9765fb", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "853bada9386caea7f474f43b5b9942cc", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "slotName": "D2" - }, - "strategy": "usingGripper" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1c77c5644cb42239be59cf33ba420dcd", - "notes": [], - "params": { - "celsius": 21.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 21.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b23a4cdccd3f19e07bd7ecc8c9ec139c", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "dd6de2abb6148dc2577700d5e24ff06b", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "moduleId": "UUID" - }, - "strategy": "manualMoveWithPause" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0e8ab8e504563c90b6e712c2efd40eb7", - "notes": [], - "params": { - "message": "\n\n----------ADDING BEADS----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "368d2987166a287f15376baca3fdeb39", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 288.38, - "z": 99.0 - }, - "tipDiameter": 5.59, - "tipLength": 48.25, - "tipVolume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bfdbfbfe2e68f390eab027219030bd81", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "815b95c25828650f547a6b9af13ef9cc", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e3f66cb38bcd61ee1aba73adcaa202d2", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8c558f5aa78272ec1b161696b0ad56c4", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "269d5c8565bb1c5c434a42418f67b0d7", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cbd61ce6087bf9281211ab915e439ec2", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3c66ebd9863837e70126759d45c3acab", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "032af20af9ee8a5158ec1e187cb7f6ba", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a86d2a7c488f7fee4d773c789f355911", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bcbcf4bb60b057f39b3c55634a9e6d3a", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "32b0ab44a5c49dad8b216d4b5ce0fced", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "28123d12168fdc0f3bf245f6f10d0793", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "40715bf4fccfbbe911420e7d9abeb6c5", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5f79a39dadeba2f3514ffacbd60bba56", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7f68bbee78afe7f6b884e47efa2e1072", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "13ffd96786d1ceee4edc58d93d8f2b4d", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0818d997908d21ce9cee773882c3f58f", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d15279d8a3bf0d1a076f44c3e83a4fdc", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "032aa89e17bbfd09dcacf52a15ce18e0", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "30fa39e04a180e805ccf3bde812e4416", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bb468de0a0819c83de68c459d2bc2a3b", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "65fabe75fbb76b8ae64fbe095dbd2b3f", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "573b710f381645ce3be8001d3c236981", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "25d1f00e790d48a9dce31f3e088618e6", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7c1bbb04bafc14155df034c2f15da553", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "529af4dfcaa0c5d1f571bc1be9fb3d1c", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ed452939255d89a625ff82f96c65c52c", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9c9a02898278e87d7215b1c9e392490c", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c2bb1ddf7b51c0dbc497654d4f8c4dc1", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "67497cde9ed8faaf874e00dbf11eb43e", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "47b2201b41a49fdf972b02b2c43c3a3a", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7ad69f1477f0f7065240c3bf5b33ac35", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2fe9faf60fd27b27b0f5a1011f0fd554", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d552b62cebac83b58a3410feab357780", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ea5695819d50dee07e2c9218921f7cdb", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cca1372859c26b02f6746653b6ee3fc2", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9da7d58b925ad38b592bd304000b89b0", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0e355f0015602654601b36f3fe733c7c", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9fa0f5b1ce2cc7efc1afaeac0bc92572", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "395ee0029cbb60d9f36f5a4955a252e4", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7f92051293652d7e5b633281dad5de5d", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 60.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "494376b85c763d47e43c64a86a1af8d0", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 14.3, - "y": 74.15, - "z": 56.95 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7616f9e1448ab2aaf8444a22a0e5c309", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 60.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2a75bed14de91780767a2dba2fc63659", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a33188eb7c4689cbbbc5120fbcfc7c30", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4c04d4a332881f459e5b3b64f7dd35f6", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ef7294819777242eba8889672edb5e0a", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7f903dac68a209c04b9f7807783467f3", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "746f439bb4418e6fc7dbf0baa79818f3", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8e7fb9e6e6e9498092442eb6b0de80e5", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "42e1ff8e9bb0d10f536f38547dca4217", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f6ac2c0707b3dd297cfefccf2441b654", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e62d568ad177fb6f7b765a2c1f89bbde", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3c1a1246725e07da8cbf379c35ba374f", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "17c1e8a18b96c71d15bf365a791c3a7d", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "078072d91042c3399cc75c18749eb9a5", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4ea555ffe7f4ea8ec3c16c6d88be7c03", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "002c53f35b6556a3302128bd94c7951f", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0c735b1d5cc19e89268c81062ebf8e30", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "deeaf527616058731ddf81b63a7b4261", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1b082b45d6e92bdb566b26933a376376", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "490719174a027ee2f8ed33c33c375e69", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "800841f6da189a765d8e4c20455930f1", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 70.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 70.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8d2c9a496395a9a1ded71c6b76b65915", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 13.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d4ba58f8ff62e7ae41c88f29d03938bd", - "notes": [], - "params": { - "seconds": 2.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "757be6c16526883c8a48707f09591fad", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 13.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5a1213e3a9c70ead82d418fdd2a1b549", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7ed0688b2edae638d7f07384107a0117", - "notes": [], - "params": { - "seconds": 300.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5d0b7e3a34dccde14447e701edc94117", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "moduleId": "UUID" - }, - "strategy": "usingGripper" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4d311b4eff2d0b6db3959cb80a612a86", - "notes": [], - "params": { - "seconds": 180.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "244b0c4142617762011b5821edd2e5cb", - "notes": [], - "params": { - "message": "\n\n----------REMOVE SUPER----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "11e24eda4aaea17d19fe594f29cff94c", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A2" - }, - "result": { - "position": { - "x": 187.38, - "y": 288.38, - "z": 99.0 - }, - "tipDiameter": 5.59, - "tipLength": 48.25, - "tipVolume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e03e23625068b2fe01340f043fbcf7e6", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 90.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.949999999999996 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.510000000000005 - }, - "volume": 90.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "66746d07fdd08608be1343cfc9bee132", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.149999999999999 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.31 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a9a2748035ea5336157e177af0fd1625", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 47.459999999999994 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d7c0cecf7abee7221e56bf1562305413", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "02a1ad48dc99e7d22afe884c206ad3c0", - "notes": [], - "params": { - "seconds": 2.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d86a0db1fc9ac4641d8be7762d5a4a59", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "72c6a56b1237e317ea78c68fd44d1ec9", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 509.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f453352b591a769a121caaa4d699e97a", - "notes": [], - "params": { - "message": "\n\n----------TWO WASHES----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f9004be73e3f887db59400d4fec9612e", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A3" - }, - "result": { - "position": { - "x": 196.38, - "y": 288.38, - "z": 99.0 - }, - "tipDiameter": 5.59, - "tipLength": 48.25, - "tipVolume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "73d648448c42c3fe9f59ebee82a03bcf", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A2" - }, - "result": { - "position": { - "x": 23.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8c6870f661fd245f58e78cd630530cd2", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A2" - }, - "result": { - "position": { - "x": 23.3, - "y": 74.15, - "z": 56.95 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "22886609f80251648d4df31266899f92", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 50.459999999999994 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7bd0a6dc80fe6c5b937c1c62786dccb6", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 20.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 70.46 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "40328e27da7174b4bc692d61c929fca5", - "notes": [], - "params": { - "seconds": 30.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "84a9a8cda676dfc363d11e5236a637e8", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.949999999999996 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.510000000000005 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2b9c2e0a5bdbcb142a4c9b7ec03923ce", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.149999999999999 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.31 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8e1d29cdf6a90b3fbe469e54509b5a06", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 47.459999999999994 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1a2647fa45f954247dafedf43a81334a", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 160.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - }, - "volume": 160.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fd65fece18bfe42a335b896faba7164a", - "notes": [], - "params": { - "seconds": 2.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "13b220ee6e41cc137d2aa98490b4e537", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "674eee01810aa366e74e617ed3b99f5a", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d77fb0a118401dafaec8690ea33837e5", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A4" - }, - "result": { - "position": { - "x": 205.38, - "y": 288.38, - "z": 99.0 - }, - "tipDiameter": 5.59, - "tipLength": 48.25, - "tipVolume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bac23d746d550fbebf80bd28b859e0ff", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A2" - }, - "result": { - "position": { - "x": 23.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "331a6cfd64ef8957a1d5ec1223362fb3", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A2" - }, - "result": { - "position": { - "x": 23.3, - "y": 74.15, - "z": 56.95 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5337ce78a813c809f6bb00618c4fe20a", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 50.459999999999994 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "989ca5a87edf4643238fbc9a3364efc9", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 20.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 70.46 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5364401abcf4dc920adc9ab447efdc73", - "notes": [], - "params": { - "seconds": 30.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1ee8b5cfcf4165f39a0099b7bbeea54d", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.949999999999996 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.510000000000005 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "34e58fe0b2c459fbc3f06920d0769270", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.149999999999999 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.31 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3057863e0b46bc6d14cfddcaa0aacf84", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 47.459999999999994 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "495582e5fad7bd1901c08fb6e7eb50f5", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 160.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - }, - "volume": 160.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ccd5133c5c5d866132fe063c1ae22f9c", - "notes": [], - "params": { - "seconds": 2.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ab97584f3d517de829a20f13ff5791c0", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "21c4f328e8c27e5b8eeae02aa6e69a14", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 509.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bdc01a9029d0a64275e42cdd918ff509", - "notes": [], - "params": { - "seconds": 120.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "20a08a7ac1651a88eeff1c42d5a7fd83", - "notes": [], - "params": { - "message": "\n\n----------ADDING ELUTION BUFFER----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bcd6060410179a5511d523068e7aa8ca", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "slotName": "D2" - }, - "strategy": "usingGripper" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a4af1bda7fb80f69e2384d2636266568", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A4" - }, - "result": { - "position": { - "x": 41.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f5f746debed8dcac8a497a29b31cf186", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A4" - }, - "result": { - "position": { - "x": 41.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 10.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c65c363a455fb00b832481ddec027e5a", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 10.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e2d37a0b98bad9f18001accc5d48f2ec", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c93b23c1e0fc630e0602f22bf92771f4", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "560c787161a5b7b6bc1e4789f309c7eb", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "64468195bc0d12a28b7d33dd318ae9b7", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c1b170c37265b40564968626c228e40a", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5e77139a1eda8ea5a627879417e3412e", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cd2e6a14139282eb596cbe91b211df6f", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "aa01358ba5e110ec8e0ae314196f6ab8", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9658f3926f4e574c676dcb0556367cfe", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1a78c357fe1b8ac9d4cc5b6d3ad8889c", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9b09c58201ed7d85d177642432167b83", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "122379ae04201731c95128ecc84a8c53", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e6e1bdf34d0a30334e1498f9c8cb0162", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f0b7fc1d3b9f5df73fe8fbf36b73ee2d", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "73cb056bee8d98e0d185fb641ccf0730", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "31fc0701b0138e9d517802ce3dae49e8", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0d4e94c8be945d6ebfbde9a7ce5e43a5", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a808ea340d49477099ea1a71dd6f05ba", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "580660e11127c5dfdbcda68853a49ba0", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "23fad1294e92a433838e8d5b886cbaa0", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f60561ce4427bcef72abbbe058be0186", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "aa9a60b020a6368ec75a24dc20864c5e", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bb927ea6fd625099361703d6ab193a10", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9f2659860d0651d5084898e58f18db37", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 7.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 7.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ddc9614e29345c82acaff74cacac8bbc", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 16.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "141e8300f581875761e9dc308946e092", - "notes": [], - "params": { - "seconds": 1.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b22db511c943124628d5b9e12a5214fc", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 16.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "touchTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4fc79926d971451151d9871e600b7387", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "radius": 1.0, - "speed": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -1.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 15.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cfc51d330ae4be9ed06dea079045b04c", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "642fd4b9b268619544cb12f55a88304c", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "moduleId": "UUID" - }, - "strategy": "usingGripper" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e95a1b9a129d8ccf78f7d6ffd4275c64", - "notes": [], - "params": { - "seconds": 90.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "88a37b605a7fd179714001e800e4d53a", - "notes": [], - "params": { - "message": "\n\n----------ADDING EPH3----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "128ade80924cb01a491759f7e15242cc", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "985b28047bc9bf85068fcd1cad7765a7", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 8.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A4" - }, - "result": { - "position": { - "x": 369.3, - "y": 74.15, - "z": 13.0 - }, - "volume": 8.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "773226e52da048d688c0e39ff7d205bd", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 8.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 8.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6acd32649b09ba4ee59c2e8230d1f1ba", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0bb89ce96ead87c396c38b105a1733b2", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b22f3f2d2723811944d129032a376005", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 412.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3a725551ff77626c661addc11c85e79c", - "notes": [], - "params": { - "message": "\n\n----------TRANSFERRING ELUTE----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "34064f6b319716e8d4bd456e74a85f67", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A6" - }, - "result": { - "position": { - "x": 59.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f1b5a1227acc8e0c996fac4a5b1eed5a", - "notes": [], - "params": { - "flowRate": 7.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 8.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.149999999999999 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.31 - }, - "volume": 8.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "389b353fee2a9932503c102689b31a7d", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 8.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 8.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f37014c13bbbdd7cf997d41b3e9f2555", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 50.459999999999994 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4f2926b08d8ff5716d3d388e5ff3aeda", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "199861e98b6784b606bc3a798cc21adc", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5e7ffb1e17edf90dd21bb6402f4e61cb", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": "offDeck", - "strategy": "manualMoveWithPause" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/closeLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f596cf492a4aa18c0a78962946317c9f", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/runProfile", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "13c1a4f2e91bd57161ee09096db29059", - "notes": [], - "params": { - "blockMaxVolumeUl": 17.0, - "moduleId": "UUID", - "profile": [ - { - "celsius": 94.0, - "holdSeconds": 60.0 - } - ] - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c4f468125fb7219cc751c069d3c67477", - "notes": [], - "params": { - "celsius": 4.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 4.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "da97683b7f34741bc65ff23fa048b830", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/openLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4221bd22c8a1eb22232ef8e6a7e7ff45", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0f091935a1a7399794f9581e659dcb0e", - "notes": [], - "params": { - "message": "\n\n----------ADDING FSA----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7a176bc03bbf77898f7ffa407ea492a5", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A7" - }, - "result": { - "position": { - "x": 68.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fdd4c037f72192630e23143203453fe6", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 8.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 378.3, - "y": 74.15, - "z": 13.0 - }, - "volume": 8.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c3748efc9cf7809283f676220c486f7c", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 8.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 8.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "12ff084c76776a7fc82b5bb240453304", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6b6f0aae318f1cb134fc40f53ba5c29b", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "79985b87ab70d2619333184824beb425", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d46fe87cae878bc66c847a0cbf663c5f", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d8d223bc3df2ffe1916a94b78c6f75d4", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "55236da67108ce78c1b2fd22b7f58cf5", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5013bedc495e54d9c994ca6ecb6a3304", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6a8b334c3c6dd89d77d727db4a2d0905", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d4697967048dec03f8379d91b2122941", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0e95bac1c6b7e126737543371150ebfd", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 19.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8b1193f8aad3d883df9b1236a7035a97", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8998c1ea61f8979eee45de54caa22fb7", - "notes": [], - "params": { - "seconds": 1.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5cbfa56632670eaa5d85c6dc55eeb9ea", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "touchTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0a798cd4aad04f049902c7b9f500d1c3", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "radius": 1.0, - "speed": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -1.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 15.260000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "59b2e26b7244cb274df157ebedebc0a1", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 412.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/closeLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1caf8473424e9499f11a1c12ff240937", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/runProfile", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7d0b200036ec0bd09dcf980764dbd257", - "notes": [], - "params": { - "blockMaxVolumeUl": 25.0, - "moduleId": "UUID", - "profile": [ - { - "celsius": 25.0, - "holdSeconds": 600.0 - }, - { - "celsius": 42.0, - "holdSeconds": 900.0 - }, - { - "celsius": 70.0, - "holdSeconds": 900.0 - } - ] - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ce231cd2e87c00ca926aa296519bac6a", - "notes": [], - "params": { - "celsius": 4.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 4.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "dab2ad7a7aa227d17ae84bfe3ebe67f1", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/openLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3160b183c42da9abcdfd618d6b375807", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c7ca1b17d1348ed1dd69565e0ab6d559", - "notes": [], - "params": { - "message": "\n\n----------ADDING SMM----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cbf5f3ec41a0236af7ddf7292227da40", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A8" - }, - "result": { - "position": { - "x": 77.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d59a18a21f50aec382438feabac86b5d", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 25.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A6" - }, - "result": { - "position": { - "x": 387.3, - "y": 74.15, - "z": 13.0 - }, - "volume": 25.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fa2874da436a4ce1e1b518b5929a4b7d", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 25.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 25.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ed2888e9812d6ed5b579098d5f7dcadb", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6f984f796140a36b7e4b047af2580cad", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "056bf9604a87b6b7251a0dd7ee0a0533", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2dd228ae53cef058bed3795b223cbd9c", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "401120eb90da95947308e31b43dccb8d", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2f09e6ed15dd6a9c607537f22fd56482", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "57370e705965f5648df3898f2301cd9e", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "06c1334811158883f7c36da25a22e33e", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0fca0fa0c64f8503a2837c9006667146", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "48f33362c3c7f619223ac2cc8c337c38", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 40.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8799e4f3726c598289f8bd7b23f0abf3", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4da4535a7b8dbf796c7361ed841dc830", - "notes": [], - "params": { - "seconds": 1.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "09e205b3ec47cd7f5c507ef7f6f180e1", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "touchTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "86ab24cd106991df03153585488a6bd8", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "radius": 1.0, - "speed": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -1.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 15.260000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c212e5981120f727c6decc1943c152ad", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetLidTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e842bc8a137ba1d36a17d3faf61e07b1", - "notes": [], - "params": { - "celsius": 40.0, - "moduleId": "UUID" - }, - "result": { - "targetLidTemperature": 40.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForLidTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3a91d0a6da70911cf187829fea8a1c6c", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/closeLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "60ac672f2f39611656b99757dd9bcf74", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/runProfile", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "12e135979be4e41816f8008d391ac2ff", - "notes": [], - "params": { - "blockMaxVolumeUl": 50.0, - "moduleId": "UUID", - "profile": [ - { - "celsius": 16.0, - "holdSeconds": 3600.0 - } - ] - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b719f4e921a8f757f7c4d993f3da43d9", - "notes": [], - "params": { - "celsius": 4.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 4.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8c75d278bb185b008e22212d155ef5dc", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/openLid", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b5f410ba45b213bd477900bef4951843", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b27ed28512232cb0551a5af34c2261e5", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "slotName": "D2" - }, - "strategy": "usingGripper" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/setTargetBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "094738334022c0efdb7d09feb70485a4", - "notes": [], - "params": { - "celsius": 21.0, - "holdTimeSeconds": 0.0, - "moduleId": "UUID" - }, - "result": { - "targetBlockTemperature": 21.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "thermocycler/waitForBlockTemperature", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f53679fa22cc21426034afb9166c4c09", - "notes": [], - "params": { - "moduleId": "UUID" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "16840a171c215462a28e2f4e1363f2c0", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "moduleId": "UUID" - }, - "strategy": "manualMoveWithPause" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e1a5efdc0f7ef98106e4306b5dd2e6b8", - "notes": [], - "params": { - "message": "\n\n----------ADDING BEADS----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "682b477a087dd92ac6159f900115fb35", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 214.38, - "y": 288.38, - "z": 99.0 - }, - "tipDiameter": 5.59, - "tipLength": 48.25, - "tipVolume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "43d5d251497da47db350309e9d0f34dd", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f49285a51e7d0e936653b2e5d52c3bea", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5d4d46d37afbd6ae16e079c02fb55bbc", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "65635c49da6868a4305fac7d5abf5425", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b71e5cacbf131d7daa8a13fb5dd1cf07", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fedf2d26f50dc893ce9ee6b0d7eb9fb6", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f1d932332dc990ca22ca874303e26873", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d25680d8647d4de548ebd009fab58608", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9cd005defed08a0daad0d5b1bd4d03e8", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bf755956d5e9addade84d0cef3331b6e", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d6786c7d0cd83c2509a9959f0f04a593", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a025fc0aa12c342b03016accb2973631", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b47dfd3ccbda1a5b28a165e301a5a0f9", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "216472b31cc3c3d454bbfd7d76b3b7ed", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a4a946ea27a345fbb7e85ad53a45ade1", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4e8f5cfcd829473d768d8cc759a5dac2", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3b94f21a0c88800073a4303564fc63b7", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1b4c2edaee80fe222e4a5b83472ddf1d", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8e03cc5452fe5bd73f794968e234d70b", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5cc98b1e554ad32ca80cb5951f8161d7", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c95e6f872df87081a47ea61603e9b4ca", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0ab71ffdb60d9e4794eb2a6deb883cf4", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "dc4eaa79925d9c73b6fbd25f5441dba9", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8da7cc1166fc139926f6c84baa8c70cc", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5b6f914bea0c4ffdc1f04b6de79ef05b", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4ce0076f932a86c0082742c8e5e7467c", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d928c7a3181f8a98f441585a86e9a972", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "712e51b617d0f48491ce05e0058d50c0", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "89f3ac0193b80bcd369e8ecb32ba1546", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2702670c323384ed41cdad11001069f0", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d0057ae136577bbd0ac88b74ef799412", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d83978b1ca8133a1bba1a15230260e3d", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cb4fd30824e9e428b29558d180c71b46", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3acbc3464821ecc3e7280c0b35081001", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9b8f8c48eae538d5ae472f988cd63797", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7be87f8236a14202c03d6e6252b17165", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "e9de57ce33c992b57fc0ca013dd166bf", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a78f8aa319c9395675cfdfd11d727ed2", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "48e844e531197d305f35b100f4222ec9", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "912ca45f89f86e458cb1415ae6926fe0", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 200.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "377e827673e4ed63668be12d0ccd5fd6", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 90.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 90.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f618f13c5e3cc165d6606d115ad97d83", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A5" - }, - "result": { - "position": { - "x": 50.3, - "y": 74.15, - "z": 56.95 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cd81a730a89ff4b52dcb40b50feb3ceb", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 90.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 90.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "75669bcf0b341326a1d77f0c00436a88", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9168388b37a91f3d345226ab465dc5ee", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "139f3e84c7e8105b637c0272055fe353", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1a6b6222ba580a3f05861298b380d650", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7633fe17308172238547ac37e87ed4ef", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d57db4e9aa442e5462704781bd4431bd", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ba85e92133639b1920fe10d9972440c4", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cf2afde9c174df0484ba43de9c07306b", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b496115ac86d35cea2168e5ad70548cd", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5eb6124ba5865aeede1f06c4e5744095", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1fe26400f63a02249e4c68b0d424e780", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "122fa7f2b2ac35027f935938ac50c8ab", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "05da1119ab89f4124a4b0566dee1ce0d", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0067c6aa4e4e4c88079d66322f1f414a", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8533c051c7c1c3d40baa81ce816a87ea", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "21796119f6ba48327ee82af43d70dedb", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b69b894ad8c8f464e7fa039124fdb44f", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "206d69df9193928f581d8049e9644896", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4bc1dd42516b721e81fe511e76c1bce8", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6e01eb7ee577e28580043089ccebffbf", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 100.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 100.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d70cf2f99fcfd1a78d2918c2a3640277", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 13.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b7ac87f3143a8e94167c971444ea082f", - "notes": [], - "params": { - "seconds": 2.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "74e0fde95013cef52a4b86aca1e31911", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 13.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2167c790163b647e1673430d8407acbe", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 509.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2fe1007571bb088ac9fddbe0e59b54aa", - "notes": [], - "params": { - "seconds": 300.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ef7903a646d62d50c9d0cfc39aa7c364", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "moduleId": "UUID" - }, - "strategy": "usingGripper" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d0001ee8a8bb6a3a3065465468162f92", - "notes": [], - "params": { - "seconds": 180.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1a990ae34fde9280ff36803d451ead8b", - "notes": [], - "params": { - "message": "\n\n----------REMOVE SUPER----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6d4d0312b5bfe8f4fdf1c50d16bcded9", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A6" - }, - "result": { - "position": { - "x": 223.38, - "y": 288.38, - "z": 99.0 - }, - "tipDiameter": 5.59, - "tipLength": 48.25, - "tipVolume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "35d5ea9b45c6e4fe04eaefbe07faa4ca", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 140.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.949999999999996 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.510000000000005 - }, - "volume": 140.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c83da5f07fe58aa4acf6e71a83840a1e", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.149999999999999 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.31 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "34f5ef829533277943b396863d2d3090", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 47.459999999999994 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "561ee6926eb74c52d372fe3bc0c2b891", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f4bea182967bfe77f477c8f134f6edd2", - "notes": [], - "params": { - "seconds": 2.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4d724015d6e939461f04f9b8d3bcf44a", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f7cf56fe34fc2fdea9799801b8399a0e", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fc1348cb1ac415a29f8f4f565aded186", - "notes": [], - "params": { - "message": "\n\n----------TWO WASHES----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "8017ae087c370713f8e84e54e27337da", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A7" - }, - "result": { - "position": { - "x": 232.38, - "y": 288.38, - "z": 99.0 - }, - "tipDiameter": 5.59, - "tipLength": 48.25, - "tipVolume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cda0933085243585cf946275be748ccd", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A3" - }, - "result": { - "position": { - "x": 32.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d9ca89370731eefd12eab9fd6077bc55", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A3" - }, - "result": { - "position": { - "x": 32.3, - "y": 74.15, - "z": 56.95 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d0652d79b21be209840a2a681e44ddc6", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 50.459999999999994 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "4a7e59dbe17b1adf900c5a6d9f2e7e45", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 20.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 70.46 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "36c3086a297b20a85ef8232b0c21b51a", - "notes": [], - "params": { - "seconds": 30.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7bbcfa08c751fdaba7d6d581370d2955", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.949999999999996 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.510000000000005 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b7308b04d29fd37cc9be1436c087d9b4", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.149999999999999 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.31 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "35d196d5fe5cabfd879b20a2c61cdb10", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 47.459999999999994 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5837f318dfb28b3436b919706eb936c3", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 160.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - }, - "volume": 160.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "bf173b3c58d4fb352e0372d47213119d", - "notes": [], - "params": { - "seconds": 2.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "02cd32879185c6bfb84a65d23807a880", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "15ea5c4abc794d6e7c634b6588a6e9c1", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 509.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3cf3b07d8998d888692eb223b05060df", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A8" - }, - "result": { - "position": { - "x": 241.38, - "y": 288.38, - "z": 99.0 - }, - "tipDiameter": 5.59, - "tipLength": 48.25, - "tipVolume": 200.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "04961eb23d9f6254142ee16ab14248a8", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A3" - }, - "result": { - "position": { - "x": 32.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d70bae5f325b508b99b8dd3e0138d96f", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A3" - }, - "result": { - "position": { - "x": 32.3, - "y": 74.15, - "z": 56.95 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "672debfdee77318ffff1b2cff21ccab7", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 50.459999999999994 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "66093f66f5aa96e91c39f0e3c01e46cd", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 20.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 70.46 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "347c5121278c2810e041c94cff7db2e3", - "notes": [], - "params": { - "seconds": 30.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cee3f5ea84293d86b1ba85b9e338813b", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 150.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.949999999999996 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.510000000000005 - }, - "volume": 150.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fac47a9fd8d07b04483f636217bf69d1", - "notes": [], - "params": { - "flowRate": 107.39999999999999, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 10.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.149999999999999 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.31 - }, - "volume": 10.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9d8ac359ccb52841dfb3e4df5a1f4a18", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 47.459999999999994 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "585038ade3637a88d64e4608c4bb5ee4", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 160.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - }, - "volume": 160.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ba28d9ce128a5b4b3489322a661b3651", - "notes": [], - "params": { - "seconds": 2.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3d2f711f065ca932e8230b8aa3aae811", - "notes": [], - "params": { - "flowRate": 716.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -3.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 363.78, - "z": 28.400000000000002 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9e9947e94d00cd7af2fbd93da9f0a80b", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "aceac2839f511dd95c277edef67d8fb7", - "notes": [], - "params": { - "seconds": 120.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ca67c1c88c455a5aba1959cd1caf8f2a", - "notes": [], - "params": { - "message": "\n\n----------ADDING ELUTION BUFFER----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5eca5b4fc517fd179f00584d01536884", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "slotName": "D2" - }, - "strategy": "usingGripper" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "32bbdbea3b7c9013fa48ade4fe3324fb", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A9" - }, - "result": { - "position": { - "x": 86.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "5a315c4cd14cb77926724b952a7fbead", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -37.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A6" - }, - "result": { - "position": { - "x": 59.3, - "y": 74.15, - "z": 22.95 - }, - "volume": 19.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "17ec51833aa27af96c25ee6505183966", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 19.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 2.05 - }, - "volume": 19.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "9f24e834779a37078144cf3156cd3db8", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f33cf695ecd2531478a68d2802e148f7", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c429821867b899d5c77969f6fbaacc9f", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "74b9adbac35a2a3a642e07a860b1c426", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "24733fb2d4d18cf9508abd39983f3d38", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a3ec8da86c6e0a867d6969d1297611a9", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "404694e8951bb51adca17738330711d3", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "11d60469d4437e94279c257149f593da", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fdf47b41ab2d95b5f5814f29947d2a3e", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "a7ba68c365c32dd9f9bf5efa42268ead", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7d92a9f1e96414b17520cfc4d0a39df4", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c01967532b5744a89d4ae61978d6e3f3", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ea1d41201d32add509fd9ec69cce92a4", - "notes": [], - "params": { - "flowRate": 35.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.15 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.849999999999999 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "87f5ad959bf21caa837f7be34cb2acbc", - "notes": [], - "params": { - "flowRate": 114.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 15.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -8.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 7.05 - }, - "volume": 15.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "53644c8767ea650bb4a2978c1694951f", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "3a1e1283bc33c1ffc45046a245e7deff", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "613dc6176dd81f519d7271bb1af5bc5a", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "2ea46cbed07c4b44ee476894708bc003", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c3aa22272f7134a79ddffd9136359c37", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6a81677c234f3abe6904d8aa3abab446", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "705582b79c3d98faa603533ac103d651", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f25fb64699eac468c88e4806f5bb3916", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "b9b463d95c4eefeb5814e9982affce67", - "notes": [], - "params": { - "flowRate": 52.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "7d467408fd88a0136fb6c7456c7cc94a", - "notes": [], - "params": { - "flowRate": 85.5, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 14.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.25 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 1.7499999999999993 - }, - "volume": 14.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fea7c6fb5de7e3dcd4bf4957de66f05e", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 16.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "cc92fcbd002554e2ce0855f343a3a68e", - "notes": [], - "params": { - "seconds": 1.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "017af87721e992b5157fb78be01033b2", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 16.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "touchTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "d61eb6e9d9b3183f747f70ce29828ff7", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "radius": 1.0, - "speed": 60.0, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -1.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 178.38, - "y": 74.24, - "z": 15.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "ddda43ec173555ea3b16b98ad6d2c39e", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 412.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveLabware", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "0367269519dab3d917ff70e4c2eb3ac5", - "notes": [], - "params": { - "labwareId": "UUID", - "newLocation": { - "moduleId": "UUID" - }, - "strategy": "usingGripper" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "waitForDuration", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "c92eb88b6abd64aee972dffe5e25327a", - "notes": [], - "params": { - "seconds": 120.0 - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "comment", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "fff71392918a3b8f2f00bdefa4a53134", - "notes": [], - "params": { - "message": "\n\n----------TRANSFERRING ELUTE----------\n" - }, - "result": {}, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "pickUpTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "1211dc66481f76a8e1dd9cc3a16fc306", - "notes": [], - "params": { - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top" - }, - "wellName": "A10" - }, - "result": { - "position": { - "x": 95.38, - "y": 181.38, - "z": 99.0 - }, - "tipDiameter": 5.58, - "tipLength": 47.849999999999994, - "tipVolume": 50.0 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "aspirate", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "02042c1eb009e7757551c7d6a5e2493f", - "notes": [], - "params": { - "flowRate": 7.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 17.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -14.149999999999999 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 36.31 - }, - "volume": 17.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dispense", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "f88f2a7423ac5f5070ba46bd9989126c", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "volume": 17.5, - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": -13.95 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 2.3100000000000014 - }, - "volume": 17.5 - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "moveToWell", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "03a1b309d898cee327e0b3dd131a7507", - "notes": [], - "params": { - "forceDirect": false, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 342.38, - "y": 181.24, - "z": 50.459999999999994 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "blowout", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "674540c74c2dc67ded15c1b937ef28fb", - "notes": [], - "params": { - "flowRate": 57.0, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "origin": "top", - "volumeOffset": 0.0 - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": -5.624999999999998, - "y": 356.2, - "z": 16.26 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" - }, - { - "commandType": "dropTip", - "completedAt": "TIMESTAMP", - "createdAt": "TIMESTAMP", - "id": "UUID", - "key": "6498d67c1416ebe7d0b8c12d8b8d3c87", - "notes": [], - "params": { - "alternateDropLocation": true, - "labwareId": "UUID", - "pipetteId": "UUID", - "wellLocation": { - "offset": { - "x": 0, - "y": 0, - "z": 0 - }, - "origin": "default" - }, - "wellName": "A1" - }, - "result": { - "position": { - "x": 359.25, - "y": 364.0, - "z": 40.0 - } - }, - "startedAt": "TIMESTAMP", - "status": "succeeded" } ], "config": { @@ -31721,7 +5968,34 @@ "protocolType": "python" }, "createdAt": "TIMESTAMP", - "errors": [], + "errors": [ + { + "createdAt": "TIMESTAMP", + "detail": "ValidationError [line 54]: 2 validation errors for LoadLabwareParams\ndisplayName.str\n Input should be a valid string [type=string_type, input_value=8, input_type=int]\n For further information visit https://errors.pydantic.dev/2.10/v/string_type\ndisplayName.none\n Input should be None [type=none_required, input_value=8, input_type=int]\n For further information visit https://errors.pydantic.dev/2.10/v/none_required", + "errorCode": "4000", + "errorInfo": {}, + "errorType": "ExceptionInProtocolError", + "id": "UUID", + "isDefined": false, + "wrappedErrors": [ + { + "createdAt": "TIMESTAMP", + "detail": "pydantic_core._pydantic_core.ValidationError: 2 validation errors for LoadLabwareParams\ndisplayName.str\n Input should be a valid string [type=string_type, input_value=8, input_type=int]\n For further information visit https://errors.pydantic.dev/2.10/v/string_type\ndisplayName.none\n Input should be None [type=none_required, input_value=8, input_type=int]\n For further information visit https://errors.pydantic.dev/2.10/v/none_required", + "errorCode": "4000", + "errorInfo": { + "args": "()", + "class": "ValidationError", + "title": "LoadLabwareParams", + "traceback": " File \"/usr/local/lib/python3.10/site-packages/opentrons/protocols/execution/execute_python.py\", line N, in exec_run\n exec(\"run(__context)\", new_globs)\n\n File \"\", line N, in \n\n File \"pl_langone_ribo_pt1_ramp.py\", line N, in run\n\n File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_api/module_contexts.py\", line N, in load_labware\n labware_core = self._protocol_core.load_labware(\n\n File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_api/core/engine/protocol.py\", line N, in load_labware\n cmd.LoadLabwareParams(\n\n File \"/usr/local/lib/python3.10/site-packages/pydantic/main.py\", line N, in __init__\n validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)\n" + }, + "errorType": "PythonException", + "id": "UUID", + "isDefined": false, + "wrappedErrors": [] + } + ] + } + ], "files": [ { "name": "pl_langone_ribo_pt1_ramp.py", @@ -31737,12 +6011,6 @@ "slotName": "A3" } }, - { - "definitionUri": "opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2", - "id": "UUID", - "loadName": "opentrons_96_wellplate_200ul_pcr_full_skirt", - "location": "offDeck" - }, { "definitionUri": "opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2", "id": "UUID", @@ -31755,155 +6023,25 @@ "definitionUri": "opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2", "id": "UUID", "loadName": "opentrons_96_wellplate_200ul_pcr_full_skirt", - "location": { - "moduleId": "UUID" - } + "location": "offDeck" }, { - "definitionUri": "opentrons/nest_96_wellplate_2ml_deep/2", + "definitionUri": "opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2", "id": "UUID", - "loadName": "nest_96_wellplate_2ml_deep", - "location": { - "moduleId": "UUID" - } + "loadName": "opentrons_96_wellplate_200ul_pcr_full_skirt", + "location": "offDeck" }, { "definitionUri": "opentrons/nest_96_wellplate_2ml_deep/2", - "displayName": "8", "id": "UUID", "loadName": "nest_96_wellplate_2ml_deep", "location": { "moduleId": "UUID" } - }, - { - "definitionUri": "opentrons/nest_12_reservoir_15ml/1", - "id": "UUID", - "loadName": "nest_12_reservoir_15ml", - "location": { - "slotName": "A2" - } - }, - { - "definitionUri": "opentrons/opentrons_flex_96_tiprack_50ul/1", - "id": "UUID", - "loadName": "opentrons_flex_96_tiprack_50ul", - "location": { - "slotName": "C1" - } - }, - { - "definitionUri": "opentrons/opentrons_flex_96_tiprack_50ul/1", - "id": "UUID", - "loadName": "opentrons_flex_96_tiprack_50ul", - "location": { - "slotName": "C2" - } - }, - { - "definitionUri": "opentrons/opentrons_flex_96_tiprack_200ul/1", - "id": "UUID", - "loadName": "opentrons_flex_96_tiprack_200ul", - "location": { - "slotName": "B2" - } - }, - { - "definitionUri": "opentrons/opentrons_flex_96_tiprack_200ul/1", - "id": "UUID", - "loadName": "opentrons_flex_96_tiprack_200ul", - "location": { - "slotName": "B3" - } } ], "liquidClasses": [], - "liquids": [ - { - "description": "DB1/DP1", - "displayColor": "#7EFF42", - "displayName": "DB1/DP1", - "id": "UUID" - }, - { - "description": "RDB/RDE", - "displayColor": "#50D5FF", - "displayName": "RDB/RDE", - "id": "UUID" - }, - { - "description": "PRB/PRE", - "displayColor": "#FF4F4F", - "displayName": "PRB/PRE", - "id": "UUID" - }, - { - "description": "EPH3", - "displayColor": "#B925FF", - "displayName": "EPH3", - "id": "UUID" - }, - { - "description": "FSA/RVT", - "displayColor": "#FF9900", - "displayName": "FSA/RVT", - "id": "UUID" - }, - { - "description": "SMM", - "displayColor": "#0019FF", - "displayName": "SMM", - "id": "UUID" - }, - { - "description": "BEADS", - "displayColor": "#007AFF", - "displayName": "BEADS", - "id": "UUID" - }, - { - "description": "ETHANOL", - "displayColor": "#FF0076", - "displayName": "ETHANOL", - "id": "UUID" - }, - { - "description": "ELUTION BUFFER", - "displayColor": "#00FFBC", - "displayName": "ELUTION BUFFER", - "id": "UUID" - }, - { - "description": "RSB", - "displayColor": "#00AAFF", - "displayName": "RSB", - "id": "UUID" - }, - { - "description": "Sample", - "displayColor": "#008000", - "displayName": "Sample", - "id": "UUID" - }, - { - "description": "Mastermix", - "displayColor": "#008000", - "displayName": "Mastermix", - "id": "UUID" - }, - { - "description": "Water", - "displayColor": "#A52A2A", - "displayName": "Water", - "id": "UUID" - }, - { - "description": "DNA", - "displayColor": "#A52A2A", - "displayName": "DNA", - "id": "UUID" - } - ], + "liquids": [], "metadata": { "author": "Rami Farawi = '3.8'", "version": "==0.3.1" }, + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "anyio": { "hashes": [ "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", @@ -32,11 +41,11 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "click": { "hashes": [ @@ -48,19 +57,19 @@ }, "exceptiongroup": { "hashes": [ - "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", - "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.1" + "version": "==1.2.2" }, "idna": { "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], - "markers": "python_version >= '3.5'", - "version": "==3.7" + "markers": "python_version >= '3.6'", + "version": "==3.8" }, "jsonschema": { "hashes": [ @@ -179,63 +188,129 @@ }, "packaging": { "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3", + "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3" ], "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==21.3" + "markers": "python_version >= '3.7'", + "version": "==22.0" + }, + "performance-metrics": { + "editable": true, + "file": "../performance-metrics" }, "pydantic": { "hashes": [ - "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303", - "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe", - "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47", - "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494", - "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33", - "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86", - "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d", - "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c", - "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a", - "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565", - "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb", - "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62", - "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62", - "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0", - "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523", - "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d", - "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405", - "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f", - "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b", - "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718", - "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed", - "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb", - "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5", - "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc", - "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942", - "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe", - "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246", - "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350", - "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303", - "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09", - "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33", - "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8", - "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a", - "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1", - "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6", - "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d" + "sha256:c7a8a9fdf7d100afa49647eae340e2d23efa382466a8d177efcd1381e9be5598", + "sha256:f66a7073abd93214a20c5f7b32d56843137a7a2e70d02111f3be287035c45370" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.10.12" + "markers": "python_version >= '3.8'", + "version": "==2.9.0" + }, + "pydantic-core": { + "hashes": [ + "sha256:0102e49ac7d2df3379ef8d658d3bc59d3d769b0bdb17da189b75efa861fc07b4", + "sha256:0123655fedacf035ab10c23450163c2f65a4174f2bb034b188240a6cf06bb123", + "sha256:043ef8469f72609c4c3a5e06a07a1f713d53df4d53112c6d49207c0bd3c3bd9b", + "sha256:0448b81c3dfcde439551bb04a9f41d7627f676b12701865c8a2574bcea034437", + "sha256:05b366fb8fe3d8683b11ac35fa08947d7b92be78ec64e3277d03bd7f9b7cda79", + "sha256:07049ec9306ec64e955b2e7c40c8d77dd78ea89adb97a2013d0b6e055c5ee4c5", + "sha256:084414ffe9a85a52940b49631321d636dadf3576c30259607b75516d131fecd0", + "sha256:086c5db95157dc84c63ff9d96ebb8856f47ce113c86b61065a066f8efbe80acf", + "sha256:12625e69b1199e94b0ae1c9a95d000484ce9f0182f9965a26572f054b1537e44", + "sha256:16b25a4a120a2bb7dab51b81e3d9f3cde4f9a4456566c403ed29ac81bf49744f", + "sha256:19f1352fe4b248cae22a89268720fc74e83f008057a652894f08fa931e77dced", + "sha256:1a2ab4f410f4b886de53b6bddf5dd6f337915a29dd9f22f20f3099659536b2f6", + "sha256:1c7b81beaf7c7ebde978377dc53679c6cba0e946426fc7ade54251dfe24a7604", + "sha256:1cf842265a3a820ebc6388b963ead065f5ce8f2068ac4e1c713ef77a67b71f7c", + "sha256:1eb37f7d6a8001c0f86dc8ff2ee8d08291a536d76e49e78cda8587bb54d8b329", + "sha256:23af245b8f2f4ee9e2c99cb3f93d0e22fb5c16df3f2f643f5a8da5caff12a653", + "sha256:257d6a410a0d8aeb50b4283dea39bb79b14303e0fab0f2b9d617701331ed1515", + "sha256:276ae78153a94b664e700ac362587c73b84399bd1145e135287513442e7dfbc7", + "sha256:2b1a195efd347ede8bcf723e932300292eb13a9d2a3c1f84eb8f37cbbc905b7f", + "sha256:329a721253c7e4cbd7aad4a377745fbcc0607f9d72a3cc2102dd40519be75ed2", + "sha256:358331e21a897151e54d58e08d0219acf98ebb14c567267a87e971f3d2a3be59", + "sha256:3649bd3ae6a8ebea7dc381afb7f3c6db237fc7cebd05c8ac36ca8a4187b03b30", + "sha256:3713dc093d5048bfaedbba7a8dbc53e74c44a140d45ede020dc347dda18daf3f", + "sha256:3ef71ec876fcc4d3bbf2ae81961959e8d62f8d74a83d116668409c224012e3af", + "sha256:41ae8537ad371ec018e3c5da0eb3f3e40ee1011eb9be1da7f965357c4623c501", + "sha256:4a801c5e1e13272e0909c520708122496647d1279d252c9e6e07dac216accc41", + "sha256:4c83c64d05ffbbe12d4e8498ab72bdb05bcc1026340a4a597dc647a13c1605ec", + "sha256:4cebb9794f67266d65e7e4cbe5dcf063e29fc7b81c79dc9475bd476d9534150e", + "sha256:5668b3173bb0b2e65020b60d83f5910a7224027232c9f5dc05a71a1deac9f960", + "sha256:56e6a12ec8d7679f41b3750ffa426d22b44ef97be226a9bab00a03365f217b2b", + "sha256:582871902e1902b3c8e9b2c347f32a792a07094110c1bca6c2ea89b90150caac", + "sha256:5c8aa40f6ca803f95b1c1c5aeaee6237b9e879e4dfb46ad713229a63651a95fb", + "sha256:5d813fd871b3d5c3005157622ee102e8908ad6011ec915a18bd8fde673c4360e", + "sha256:5dd0ec5f514ed40e49bf961d49cf1bc2c72e9b50f29a163b2cc9030c6742aa73", + "sha256:5f3cf3721eaf8741cffaf092487f1ca80831202ce91672776b02b875580e174a", + "sha256:6294907eaaccf71c076abdd1c7954e272efa39bb043161b4b8aa1cd76a16ce43", + "sha256:64d094ea1aa97c6ded4748d40886076a931a8bf6f61b6e43e4a1041769c39dd2", + "sha256:6650a7bbe17a2717167e3e23c186849bae5cef35d38949549f1c116031b2b3aa", + "sha256:67b6655311b00581914aba481729971b88bb8bc7996206590700a3ac85e457b8", + "sha256:6b06c5d4e8701ac2ba99a2ef835e4e1b187d41095a9c619c5b185c9068ed2a49", + "sha256:6ce883906810b4c3bd90e0ada1f9e808d9ecf1c5f0b60c6b8831d6100bcc7dd6", + "sha256:6db09153d8438425e98cdc9a289c5fade04a5d2128faff8f227c459da21b9703", + "sha256:6f80fba4af0cb1d2344869d56430e304a51396b70d46b91a55ed4959993c0589", + "sha256:743e5811b0c377eb830150d675b0847a74a44d4ad5ab8845923d5b3a756d8100", + "sha256:753294d42fb072aa1775bfe1a2ba1012427376718fa4c72de52005a3d2a22178", + "sha256:7568f682c06f10f30ef643a1e8eec4afeecdafde5c4af1b574c6df079e96f96c", + "sha256:7706e15cdbf42f8fab1e6425247dfa98f4a6f8c63746c995d6a2017f78e619ae", + "sha256:785e7f517ebb9890813d31cb5d328fa5eda825bb205065cde760b3150e4de1f7", + "sha256:7a05c0240f6c711eb381ac392de987ee974fa9336071fb697768dfdb151345ce", + "sha256:7ce7eaf9a98680b4312b7cebcdd9352531c43db00fca586115845df388f3c465", + "sha256:7ce8e26b86a91e305858e018afc7a6e932f17428b1eaa60154bd1f7ee888b5f8", + "sha256:7d0324a35ab436c9d768753cbc3c47a865a2cbc0757066cb864747baa61f6ece", + "sha256:7e9b24cca4037a561422bf5dc52b38d390fb61f7bfff64053ce1b72f6938e6b2", + "sha256:810ca06cca91de9107718dc83d9ac4d2e86efd6c02cba49a190abcaf33fb0472", + "sha256:820f6ee5c06bc868335e3b6e42d7ef41f50dfb3ea32fbd523ab679d10d8741c0", + "sha256:82764c0bd697159fe9947ad59b6db6d7329e88505c8f98990eb07e84cc0a5d81", + "sha256:8ae65fdfb8a841556b52935dfd4c3f79132dc5253b12c0061b96415208f4d622", + "sha256:8d5b0ff3218858859910295df6953d7bafac3a48d5cd18f4e3ed9999efd2245f", + "sha256:95d6bf449a1ac81de562d65d180af5d8c19672793c81877a2eda8fde5d08f2fd", + "sha256:964c7aa318da542cdcc60d4a648377ffe1a2ef0eb1e996026c7f74507b720a78", + "sha256:96ef39add33ff58cd4c112cbac076726b96b98bb8f1e7f7595288dcfb2f10b57", + "sha256:a6612c2a844043e4d10a8324c54cdff0042c558eef30bd705770793d70b224aa", + "sha256:a8031074a397a5925d06b590121f8339d34a5a74cfe6970f8a1124eb8b83f4ac", + "sha256:aab9e522efff3993a9e98ab14263d4e20211e62da088298089a03056980a3e69", + "sha256:ae579143826c6f05a361d9546446c432a165ecf1c0b720bbfd81152645cb897d", + "sha256:ae90b9e50fe1bd115b24785e962b51130340408156d34d67b5f8f3fa6540938e", + "sha256:b18cf68255a476b927910c6873d9ed00da692bb293c5b10b282bd48a0afe3ae2", + "sha256:b7efb12e5071ad8d5b547487bdad489fbd4a5a35a0fc36a1941517a6ad7f23e0", + "sha256:c4d9f15ffe68bcd3898b0ad7233af01b15c57d91cd1667f8d868e0eacbfe3f87", + "sha256:c53100c8ee5a1e102766abde2158077d8c374bee0639201f11d3032e3555dfbc", + "sha256:c57e493a0faea1e4c38f860d6862ba6832723396c884fbf938ff5e9b224200e2", + "sha256:c8319e0bd6a7b45ad76166cc3d5d6a36c97d0c82a196f478c3ee5346566eebfd", + "sha256:caffda619099cfd4f63d48462f6aadbecee3ad9603b4b88b60cb821c1b258576", + "sha256:cc0c316fba3ce72ac3ab7902a888b9dc4979162d320823679da270c2d9ad0cad", + "sha256:cdd02a08205dc90238669f082747612cb3c82bd2c717adc60f9b9ecadb540f80", + "sha256:d50ac34835c6a4a0d456b5db559b82047403c4317b3bc73b3455fefdbdc54b0a", + "sha256:d6b9dd6aa03c812017411734e496c44fef29b43dba1e3dd1fa7361bbacfc1354", + "sha256:da3131ef2b940b99106f29dfbc30d9505643f766704e14c5d5e504e6a480c35e", + "sha256:da43cbe593e3c87d07108d0ebd73771dc414488f1f91ed2e204b0370b94b37ac", + "sha256:dd59638025160056687d598b054b64a79183f8065eae0d3f5ca523cde9943940", + "sha256:e1895e949f8849bc2757c0dbac28422a04be031204df46a56ab34bcf98507342", + "sha256:e1a79ad49f346aa1a2921f31e8dbbab4d64484823e813a002679eaa46cba39e1", + "sha256:e460475719721d59cd54a350c1f71c797c763212c836bf48585478c5514d2854", + "sha256:e64ffaf8f6e17ca15eb48344d86a7a741454526f3a3fa56bc493ad9d7ec63936", + "sha256:e6e3ccebdbd6e53474b0bb7ab8b88e83c0cfe91484b25e058e581348ee5a01a5", + "sha256:e758d271ed0286d146cf7c04c539a5169a888dd0b57026be621547e756af55bc", + "sha256:f087879f1ffde024dd2788a30d55acd67959dcf6c431e9d3682d1c491a0eb474", + "sha256:f477d26183e94eaafc60b983ab25af2a809a1b48ce4debb57b343f671b7a90b6", + "sha256:fc535cb898ef88333cf317777ecdfe0faac1c2a3187ef7eb061b6f7ecf7e6bae" + ], + "markers": "python_version >= '3.8'", + "version": "==2.23.2" }, - "pyparsing": { + "pydantic-settings": { "hashes": [ - "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad", - "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742" + "sha256:bb6849dc067f1687574c12a639e231f3a6feeed0a12d710c1382045c5db1c315", + "sha256:ed81c3a0f46392b4d7c0a565c05884e6e54b3456e6f0fe4d8814981172dc9a88" ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.1.2" + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.4.0" }, "pyrsistent": { "hashes": [ @@ -290,6 +365,14 @@ "markers": "python_version >= '3.7'", "version": "==4.2.2" }, + "python-dotenv": { + "hashes": [ + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" + ], + "markers": "python_version >= '3.8'", + "version": "==1.0.1" + }, "pyusb": { "hashes": [ "sha256:2b4c7cb86dbadf044dfb9d3a4ff69fd217013dbe78a792177a3feb172449ea36", @@ -301,11 +384,11 @@ }, "setuptools": { "hashes": [ - "sha256:937a48c7cdb7a21eb53cd7f9b59e525503aa8abaf3584c730dc5f7a5bec3a650", - "sha256:a58a8fde0541dab0419750bcc521fbdf8585f6e5cb41909df3a472ef7b81ca95" + "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308", + "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6" ], "markers": "python_version >= '3.8'", - "version": "==70.1.1" + "version": "==74.1.2" }, "sniffio": { "hashes": [ @@ -320,9 +403,17 @@ "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version >= '3.8'", + "markers": "python_version < '3.13'", "version": "==4.12.2" }, + "tzdata": { + "hashes": [ + "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", + "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.1" + }, "wrapt": { "hashes": [ "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", @@ -419,19 +510,19 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "babel": { "hashes": [ - "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb", - "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413" + "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", + "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316" ], "markers": "python_version >= '3.8'", - "version": "==2.15.0" + "version": "==2.16.0" }, "backports.tarfile": { "hashes": [ @@ -473,11 +564,11 @@ }, "certifi": { "hashes": [ - "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", - "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" ], "markers": "python_version >= '3.6'", - "version": "==2024.6.2" + "version": "==2024.8.30" }, "charset-normalizer": { "hashes": [ @@ -730,11 +821,11 @@ }, "exceptiongroup": { "hashes": [ - "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", - "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.1" + "version": "==1.2.2" }, "execnet": { "hashes": [ @@ -782,51 +873,51 @@ }, "fonttools": { "hashes": [ - "sha256:099634631b9dd271d4a835d2b2a9e042ccc94ecdf7e2dd9f7f34f7daf333358d", - "sha256:0c555e039d268445172b909b1b6bdcba42ada1cf4a60e367d68702e3f87e5f64", - "sha256:1e677bfb2b4bd0e5e99e0f7283e65e47a9814b0486cb64a41adf9ef110e078f2", - "sha256:2367d47816cc9783a28645bc1dac07f8ffc93e0f015e8c9fc674a5b76a6da6e4", - "sha256:28d072169fe8275fb1a0d35e3233f6df36a7e8474e56cb790a7258ad822b6fd6", - "sha256:31f0e3147375002aae30696dd1dc596636abbd22fca09d2e730ecde0baad1d6b", - "sha256:3e0ad3c6ea4bd6a289d958a1eb922767233f00982cf0fe42b177657c86c80a8f", - "sha256:45b4afb069039f0366a43a5d454bc54eea942bfb66b3fc3e9a2c07ef4d617380", - "sha256:4a2a6ba400d386e904fd05db81f73bee0008af37799a7586deaa4aef8cd5971e", - "sha256:4f520d9ac5b938e6494f58a25c77564beca7d0199ecf726e1bd3d56872c59749", - "sha256:52a6e0a7a0bf611c19bc8ec8f7592bdae79c8296c70eb05917fd831354699b20", - "sha256:5a4788036201c908079e89ae3f5399b33bf45b9ea4514913f4dbbe4fac08efe0", - "sha256:6b4f04b1fbc01a3569d63359f2227c89ab294550de277fd09d8fca6185669fa4", - "sha256:715b41c3e231f7334cbe79dfc698213dcb7211520ec7a3bc2ba20c8515e8a3b5", - "sha256:73121a9b7ff93ada888aaee3985a88495489cc027894458cb1a736660bdfb206", - "sha256:74ae2441731a05b44d5988d3ac2cf784d3ee0a535dbed257cbfff4be8bb49eb9", - "sha256:7d6166192dcd925c78a91d599b48960e0a46fe565391c79fe6de481ac44d20ac", - "sha256:7f193f060391a455920d61684a70017ef5284ccbe6023bb056e15e5ac3de11d1", - "sha256:907fa0b662dd8fc1d7c661b90782ce81afb510fc4b7aa6ae7304d6c094b27bce", - "sha256:93156dd7f90ae0a1b0e8871032a07ef3178f553f0c70c386025a808f3a63b1f4", - "sha256:93bc9e5aaa06ff928d751dc6be889ff3e7d2aa393ab873bc7f6396a99f6fbb12", - "sha256:95db0c6581a54b47c30860d013977b8a14febc206c8b5ff562f9fe32738a8aca", - "sha256:973d030180eca8255b1bce6ffc09ef38a05dcec0e8320cc9b7bcaa65346f341d", - "sha256:9cd7a6beec6495d1dffb1033d50a3f82dfece23e9eb3c20cd3c2444d27514068", - "sha256:9fe9096a60113e1d755e9e6bda15ef7e03391ee0554d22829aa506cdf946f796", - "sha256:a209d2e624ba492df4f3bfad5996d1f76f03069c6133c60cd04f9a9e715595ec", - "sha256:a239afa1126b6a619130909c8404070e2b473dd2b7fc4aacacd2e763f8597fea", - "sha256:ba9f09ff17f947392a855e3455a846f9855f6cf6bec33e9a427d3c1d254c712f", - "sha256:bb7273789f69b565d88e97e9e1da602b4ee7ba733caf35a6c2affd4334d4f005", - "sha256:bd5bc124fae781a4422f61b98d1d7faa47985f663a64770b78f13d2c072410c2", - "sha256:bff98816cb144fb7b85e4b5ba3888a33b56ecef075b0e95b95bcd0a5fbf20f06", - "sha256:c4ee5a24e281fbd8261c6ab29faa7fd9a87a12e8c0eed485b705236c65999109", - "sha256:c93ed66d32de1559b6fc348838c7572d5c0ac1e4a258e76763a5caddd8944002", - "sha256:d1a24f51a3305362b94681120c508758a88f207fa0a681c16b5a4172e9e6c7a9", - "sha256:d8f191a17369bd53a5557a5ee4bab91d5330ca3aefcdf17fab9a497b0e7cff7a", - "sha256:daaef7390e632283051e3cf3e16aff2b68b247e99aea916f64e578c0449c9c68", - "sha256:e40013572bfb843d6794a3ce076c29ef4efd15937ab833f520117f8eccc84fd6", - "sha256:eceef49f457253000e6a2d0f7bd08ff4e9fe96ec4ffce2dbcb32e34d9c1b8161", - "sha256:ee595d7ba9bba130b2bec555a40aafa60c26ce68ed0cf509983e0f12d88674fd", - "sha256:ef50ec31649fbc3acf6afd261ed89d09eb909b97cc289d80476166df8438524d", - "sha256:fa1f3e34373aa16045484b4d9d352d4c6b5f9f77ac77a178252ccbc851e8b2ee", - "sha256:fca66d9ff2ac89b03f5aa17e0b21a97c21f3491c46b583bb131eb32c7bab33af" + "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122", + "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397", + "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f", + "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d", + "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60", + "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169", + "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8", + "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31", + "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923", + "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2", + "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb", + "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab", + "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb", + "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a", + "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670", + "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8", + "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407", + "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671", + "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88", + "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f", + "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f", + "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0", + "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb", + "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2", + "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d", + "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c", + "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3", + "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719", + "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749", + "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4", + "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f", + "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02", + "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58", + "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1", + "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41", + "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4", + "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb", + "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb", + "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3", + "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d", + "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d", + "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2" ], "markers": "python_version >= '3.8'", - "version": "==4.53.0" + "version": "==4.53.1" }, "gprof2dot": { "hashes": [ @@ -847,11 +938,11 @@ }, "idna": { "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], - "markers": "python_version >= '3.5'", - "version": "==3.7" + "markers": "python_version >= '3.6'", + "version": "==3.8" }, "imagesize": { "hashes": [ @@ -863,11 +954,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:509ecb2ab77071db5137c655e24ceb3eee66e7bbc6574165d0d114d9fc4bbe68", - "sha256:ffef94b0b66046dd8ea2d619b701fe978d9264d38f3998bc4c27ec3b146a87c8" + "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1", + "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5" ], "markers": "python_version >= '3.8'", - "version": "==7.2.1" + "version": "==8.4.0" }, "iniconfig": { "hashes": [ @@ -887,19 +978,19 @@ }, "jaraco.context": { "hashes": [ - "sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266", - "sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2" + "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", + "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4" ], "markers": "python_version >= '3.8'", - "version": "==5.3.0" + "version": "==6.0.1" }, "jaraco.functools": { "hashes": [ - "sha256:3b24ccb921d6b593bdceb56ce14799204f473976e2a9d4b15b04d0f2c2326664", - "sha256:d33fa765374c0611b52f8b3a795f8900869aa88c84769d4d1746cd68fb28c3e8" + "sha256:3460c74cd0d32bf82b9576bbb3527c4364d5b27a21f5158a62aed6c4b42e23f5", + "sha256:c9d16a3ed4ccb5a889ad8e0b7a343401ee5b2a71cee6ed192d3f68bc351e94e3" ], "markers": "python_version >= '3.8'", - "version": "==4.0.1" + "version": "==4.0.2" }, "jinja2": { "hashes": [ @@ -910,132 +1001,133 @@ "markers": "python_version >= '3.6'", "version": "==3.0.3" }, - "jsonschema": { - "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.17.3" - }, "keyring": { "hashes": [ - "sha256:2458681cdefc0dbc0b7eb6cf75d0b98e59f9ad9b2d4edd319d18f68bdca95e50", - "sha256:daaffd42dbda25ddafb1ad5fec4024e5bbcfe424597ca1ca452b299861e49f1b" + "sha256:8d85a1ea5d6db8515b59e1c5d1d1678b03cf7fc8b8dcfb1651e8c4a524eb42ef", + "sha256:8d963da00ccdf06e356acd9bf3b743208878751032d8599c6cc89eb51310ffae" ], "markers": "python_version >= '3.8'", - "version": "==25.2.1" + "version": "==25.3.0" }, "kiwisolver": { "hashes": [ - "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf", - "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", - "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", - "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", - "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046", - "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", - "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", - "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71", - "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee", - "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", - "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9", - "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", - "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985", - "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea", - "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", - "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89", - "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", - "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", - "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712", - "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342", - "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", - "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958", - "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d", - "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", - "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130", - "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", - "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898", - "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b", - "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f", - "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265", - "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93", - "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929", - "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635", - "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709", - "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", - "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb", - "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a", - "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920", - "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e", - "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544", - "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", - "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390", - "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77", - "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", - "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff", - "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", - "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", - "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", - "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c", - "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", - "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", - "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", - "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc", - "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a", - "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901", - "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", - "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", - "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", - "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad", - "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", - "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29", - "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", - "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250", - "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d", - "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3", - "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54", - "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f", - "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", - "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da", - "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", - "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", - "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523", - "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", - "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205", - "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3", - "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4", - "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", - "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", - "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb", - "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced", - "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd", - "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0", - "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", - "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18", - "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", - "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", - "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333", - "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b", - "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", - "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126", - "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", - "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09", - "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", - "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", - "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7", - "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", - "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9", - "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", - "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", - "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", - "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6", - "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", - "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892", - "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f" + "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", + "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95", + "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5", + "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0", + "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d", + "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18", + "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b", + "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258", + "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95", + "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e", + "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383", + "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02", + "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b", + "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523", + "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee", + "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88", + "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd", + "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb", + "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4", + "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e", + "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c", + "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935", + "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee", + "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e", + "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038", + "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d", + "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b", + "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5", + "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107", + "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f", + "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2", + "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17", + "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb", + "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674", + "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706", + "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327", + "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3", + "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a", + "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2", + "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f", + "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948", + "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3", + "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e", + "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545", + "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc", + "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f", + "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", + "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a", + "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8", + "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750", + "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b", + "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34", + "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225", + "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51", + "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c", + "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3", + "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde", + "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599", + "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c", + "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", + "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6", + "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39", + "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9", + "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933", + "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad", + "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520", + "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1", + "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503", + "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b", + "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36", + "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a", + "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643", + "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", + "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483", + "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf", + "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d", + "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6", + "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644", + "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2", + "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9", + "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2", + "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640", + "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade", + "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a", + "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c", + "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6", + "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00", + "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27", + "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2", + "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4", + "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379", + "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54", + "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09", + "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a", + "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c", + "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89", + "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407", + "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904", + "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376", + "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583", + "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278", + "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a", + "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d", + "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935", + "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb", + "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895", + "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b", + "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417", + "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608", + "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", + "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05", + "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a", + "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d", + "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052" ], - "markers": "python_version >= '3.7'", - "version": "==1.4.5" + "markers": "python_version >= '3.8'", + "version": "==1.4.7" }, "markdown-it-py": { "hashes": [ @@ -1172,45 +1264,45 @@ }, "more-itertools": { "hashes": [ - "sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463", - "sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320" + "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", + "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6" ], "markers": "python_version >= '3.8'", - "version": "==10.3.0" + "version": "==10.5.0" }, "mypy": { "hashes": [ - "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", - "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", - "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", - "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", - "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", - "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", - "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", - "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", - "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", - "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", - "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", - "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", - "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", - "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", - "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", - "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", - "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", - "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", - "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", - "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", - "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", - "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", - "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", - "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", - "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", - "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", - "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.8.0" + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -1222,24 +1314,24 @@ }, "nh3": { "hashes": [ - "sha256:0316c25b76289cf23be6b66c77d3608a4fdf537b35426280032f432f14291b9a", - "sha256:1a814dd7bba1cb0aba5bcb9bebcc88fd801b63e21e2450ae6c52d3b3336bc911", - "sha256:1aa52a7def528297f256de0844e8dd680ee279e79583c76d6fa73a978186ddfb", - "sha256:22c26e20acbb253a5bdd33d432a326d18508a910e4dcf9a3316179860d53345a", - "sha256:40015514022af31975c0b3bca4014634fa13cb5dc4dbcbc00570acc781316dcc", - "sha256:40d0741a19c3d645e54efba71cb0d8c475b59135c1e3c580f879ad5514cbf028", - "sha256:551672fd71d06cd828e282abdb810d1be24e1abb7ae2543a8fa36a71c1006fe9", - "sha256:66f17d78826096291bd264f260213d2b3905e3c7fae6dfc5337d49429f1dc9f3", - "sha256:85cdbcca8ef10733bd31f931956f7fbb85145a4d11ab9e6742bbf44d88b7e351", - "sha256:a3f55fabe29164ba6026b5ad5c3151c314d136fd67415a17660b4aaddacf1b10", - "sha256:b4427ef0d2dfdec10b641ed0bdaf17957eb625b2ec0ea9329b3d28806c153d71", - "sha256:ba73a2f8d3a1b966e9cdba7b211779ad8a2561d2dba9674b8a19ed817923f65f", - "sha256:c21bac1a7245cbd88c0b0e4a420221b7bfa838a2814ee5bb924e9c2f10a1120b", - "sha256:c551eb2a3876e8ff2ac63dff1585236ed5dfec5ffd82216a7a174f7c5082a78a", - "sha256:c790769152308421283679a142dbdb3d1c46c79c823008ecea8e8141db1a2062", - "sha256:d7a25fd8c86657f5d9d576268e3b3767c5cd4f42867c9383618be8517f0f022a" - ], - "version": "==0.2.17" + "sha256:0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164", + "sha256:14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86", + "sha256:19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b", + "sha256:34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad", + "sha256:36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204", + "sha256:3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a", + "sha256:42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200", + "sha256:5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189", + "sha256:6955369e4d9f48f41e3f238a9e60f9410645db7e07435e62c6a9ea6135a4907f", + "sha256:7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811", + "sha256:8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844", + "sha256:94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4", + "sha256:a7f1b5b2c15866f2db413a3649a8fe4fd7b428ae58be2c0f6bca5eefd53ca2be", + "sha256:c8b3a1cebcba9b3669ed1a84cc65bf005728d2f0bc1ed2a6594a992e817f3a50", + "sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307", + "sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe" + ], + "version": "==0.2.18" }, "numpy": { "hashes": [ @@ -1275,19 +1367,14 @@ "index": "pypi", "version": "==0.9.1" }, - "opentrons-shared-data": { - "editable": true, - "markers": "python_version >= '3.10'", - "path": "../shared-data/python" - }, "packaging": { "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3", + "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3" ], "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==21.3" + "markers": "python_version >= '3.7'", + "version": "==22.0" }, "pathspec": { "hashes": [ @@ -1297,84 +1384,91 @@ "markers": "python_version >= '3.8'", "version": "==0.12.1" }, - "performance-metrics": { - "editable": true, - "file": "../performance-metrics" - }, "pillow": { "hashes": [ - "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c", - "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2", - "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb", - "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d", - "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa", - "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3", - "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1", - "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a", - "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd", - "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8", - "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999", - "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599", - "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936", - "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375", - "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d", - "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b", - "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60", - "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572", - "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3", - "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced", - "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f", - "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b", - "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19", - "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f", - "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d", - "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383", - "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795", - "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355", - "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57", - "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09", - "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b", - "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462", - "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf", - "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f", - "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a", - "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad", - "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9", - "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d", - "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45", - "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994", - "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d", - "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338", - "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463", - "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451", - "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591", - "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c", - "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd", - "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32", - "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9", - "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf", - "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5", - "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828", - "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3", - "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5", - "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2", - "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b", - "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2", - "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475", - "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3", - "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb", - "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef", - "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015", - "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002", - "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170", - "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84", - "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57", - "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f", - "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27", - "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a" + "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", + "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", + "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df", + "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", + "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", + "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d", + "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd", + "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", + "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908", + "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", + "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", + "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", + "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b", + "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", + "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a", + "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e", + "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", + "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", + "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b", + "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", + "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", + "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab", + "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", + "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", + "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", + "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", + "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", + "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", + "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", + "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", + "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", + "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", + "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", + "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0", + "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", + "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", + "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", + "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef", + "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680", + "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b", + "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", + "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", + "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", + "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", + "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8", + "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", + "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736", + "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", + "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126", + "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd", + "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5", + "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b", + "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", + "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b", + "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", + "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", + "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2", + "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c", + "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", + "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", + "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", + "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", + "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", + "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b", + "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", + "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3", + "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84", + "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1", + "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", + "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", + "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", + "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", + "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", + "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e", + "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", + "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", + "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", + "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27", + "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", + "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1" ], "markers": "python_version >= '3.8'", - "version": "==10.3.0" + "version": "==10.4.0" }, "pkginfo": { "hashes": [ @@ -1416,49 +1510,6 @@ "markers": "python_version >= '3.8'", "version": "==2.11.1" }, - "pydantic": { - "hashes": [ - "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303", - "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe", - "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47", - "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494", - "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33", - "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86", - "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d", - "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c", - "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a", - "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565", - "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb", - "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62", - "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62", - "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0", - "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523", - "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d", - "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405", - "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f", - "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b", - "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718", - "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed", - "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb", - "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5", - "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc", - "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942", - "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe", - "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246", - "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350", - "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303", - "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09", - "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33", - "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8", - "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a", - "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1", - "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6", - "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.10.12" - }, "pydocstyle": { "hashes": [ "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019", @@ -1485,49 +1536,11 @@ }, "pyparsing": { "hashes": [ - "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad", - "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742" + "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c", + "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032" ], "markers": "python_full_version >= '3.6.8'", - "version": "==3.1.2" - }, - "pyrsistent": { - "hashes": [ - "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f", - "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e", - "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958", - "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34", - "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca", - "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d", - "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d", - "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", - "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714", - "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf", - "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee", - "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8", - "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224", - "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d", - "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054", - "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656", - "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7", - "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423", - "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce", - "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e", - "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3", - "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0", - "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f", - "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", - "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce", - "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a", - "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174", - "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86", - "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f", - "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b", - "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98", - "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022" - ], - "markers": "python_version >= '3.8'", - "version": "==0.20.0" + "version": "==3.1.4" }, "pytest": { "hashes": [ @@ -1540,12 +1553,12 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b", - "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268" + "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", + "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.23.7" + "version": "==0.23.8" }, "pytest-cov": { "hashes": [ @@ -1596,7 +1609,7 @@ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.9.0.post0" }, "readme-renderer": { @@ -1633,18 +1646,18 @@ }, "rich": { "hashes": [ - "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", - "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432" + "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc", + "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4" ], "markers": "python_full_version >= '3.7.0'", - "version": "==13.7.1" + "version": "==13.8.0" }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, "snowballstemmer": { @@ -1695,27 +1708,27 @@ }, "sphinxcontrib-applehelp": { "hashes": [ - "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619", - "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4" + "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", + "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5" ], "markers": "python_version >= '3.9'", - "version": "==1.0.8" + "version": "==2.0.0" }, "sphinxcontrib-devhelp": { "hashes": [ - "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f", - "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3" + "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", + "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2" ], "markers": "python_version >= '3.9'", - "version": "==1.0.6" + "version": "==2.0.0" }, "sphinxcontrib-htmlhelp": { "hashes": [ - "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015", - "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04" + "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", + "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9" ], "markers": "python_version >= '3.9'", - "version": "==2.0.5" + "version": "==2.1.0" }, "sphinxcontrib-jsmath": { "hashes": [ @@ -1727,19 +1740,19 @@ }, "sphinxcontrib-qthelp": { "hashes": [ - "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6", - "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182" + "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", + "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb" ], "markers": "python_version >= '3.9'", - "version": "==1.0.7" + "version": "==2.0.0" }, "sphinxcontrib-serializinghtml": { "hashes": [ - "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7", - "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f" + "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", + "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d" ], "markers": "python_version >= '3.9'", - "version": "==1.1.10" + "version": "==2.0.0" }, "sphinxext-opengraph": { "hashes": [ @@ -1798,7 +1811,7 @@ "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version >= '3.8'", + "markers": "python_version < '3.13'", "version": "==4.12.2" }, "urllib3": { @@ -1820,11 +1833,11 @@ }, "zipp": { "hashes": [ - "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19", - "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c" + "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064", + "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b" ], "markers": "python_version >= '3.8'", - "version": "==3.19.2" + "version": "==3.20.1" } } } diff --git a/api/pytest.ini b/api/pytest.ini index 61288b3f3c1..78115d41057 100644 --- a/api/pytest.ini +++ b/api/pytest.ini @@ -6,8 +6,13 @@ markers = addopts = --color=yes --strict-markers asyncio_mode = auto -# TODO this should be looked into being removed upon updating the Decoy library. The purpose of this warning is to -# catch missing attributes, but it raises for any property referenced in a test which accounts for about ~250 warnings -# which aren't serving any useful purpose and obscure other warnings. filterwarnings = + # TODO this should be looked into being removed upon updating the Decoy library. The purpose of this warning is to + # catch missing attributes, but it raises for any property referenced in a test which accounts for about ~250 warnings + # which aren't serving any useful purpose and obscure other warnings. ignore::decoy.warnings.MissingSpecAttributeWarning + # Pydantic's shims for its legacy v1 methods (e.g. `BaseModel.construct()`) + # are not type-checked properly. Forbid them, so we're forced to use their newer + # v2 replacements which are type-checked (e.g. ``BaseModel.model_construct()`) + error::pydantic.PydanticDeprecatedSince20 + diff --git a/api/release-notes-internal.md b/api/release-notes-internal.md index ac762aebec4..bc5398781e4 100644 --- a/api/release-notes-internal.md +++ b/api/release-notes-internal.md @@ -2,6 +2,10 @@ For more details about this release, please see the full [technical change log][ [technical change log]: https://github.com/Opentrons/opentrons/releases +## Internal Release 2.3.0-alpha.2 + +This internal release, pulled from the `edge` branch, contains features being developed for 8.3.0. It's for internal testing only. + ## Internal Release 2.3.0-alpha.1 This internal release, pulled from the `edge` branch, contains features being developed for 8.3.0. It's for internal testing only. diff --git a/api/setup.py b/api/setup.py index 92f06b49bef..a85fc83f114 100755 --- a/api/setup.py +++ b/api/setup.py @@ -59,9 +59,11 @@ def get_version(): f"opentrons-shared-data=={VERSION}", "aionotify==0.3.1", "anyio>=3.6.1,<4.0.0", + # todo(mm, 2024-12-14): investigate ref resolution problems caused by jsonschema>=4.18.1. "jsonschema>=3.0.1,<4.18.0", "numpy>=1.20.0,<2", - "pydantic>=1.10.9,<2.0.0", + "pydantic>=2.0.0,<3", + "pydantic-settings>=2,<3", "pyserial>=3.5", "typing-extensions>=4.0.0,<5", "click>=8.0.0,<9", diff --git a/api/src/opentrons/calibration_storage/deck_configuration.py b/api/src/opentrons/calibration_storage/deck_configuration.py index a627fce73c9..857c2c22d3f 100644 --- a/api/src/opentrons/calibration_storage/deck_configuration.py +++ b/api/src/opentrons/calibration_storage/deck_configuration.py @@ -10,7 +10,7 @@ class _CutoutFixturePlacementModel(pydantic.BaseModel): cutoutId: str cutoutFixtureId: str - opentronsModuleSerialNumber: Optional[str] + opentronsModuleSerialNumber: Optional[str] = None class _DeckConfigurationModel(pydantic.BaseModel): @@ -24,9 +24,9 @@ def serialize_deck_configuration( cutout_fixture_placements: List[CutoutFixturePlacement], last_modified: datetime ) -> bytes: """Serialize a deck configuration for storing on the filesystem.""" - data = _DeckConfigurationModel.construct( + data = _DeckConfigurationModel.model_construct( cutoutFixtures=[ - _CutoutFixturePlacementModel.construct( + _CutoutFixturePlacementModel.model_construct( cutoutId=e.cutout_id, cutoutFixtureId=e.cutout_fixture_id, opentronsModuleSerialNumber=e.opentrons_module_serial_number, diff --git a/api/src/opentrons/calibration_storage/file_operators.py b/api/src/opentrons/calibration_storage/file_operators.py index 70c16297ecd..bf80a034d54 100644 --- a/api/src/opentrons/calibration_storage/file_operators.py +++ b/api/src/opentrons/calibration_storage/file_operators.py @@ -103,7 +103,7 @@ def save_to_file( directory_path.mkdir(parents=True, exist_ok=True) file_path = directory_path / f"{file_name}.json" json_data = ( - data.json() + data.model_dump_json() if isinstance(data, pydantic.BaseModel) else json.dumps(data, cls=encoder) ) @@ -112,7 +112,7 @@ def save_to_file( def serialize_pydantic_model(data: pydantic.BaseModel) -> bytes: """Safely serialize data from a Pydantic model into a form suitable for storing on disk.""" - return data.json(by_alias=True).encode("utf-8") + return data.model_dump_json(by_alias=True).encode("utf-8") _ModelT = typing.TypeVar("_ModelT", bound=pydantic.BaseModel) @@ -133,7 +133,7 @@ def deserialize_pydantic_model( Returns `None` if the file is missing or corrupt. """ try: - return model.parse_raw(serialized) + return model.model_validate_json(serialized) except json.JSONDecodeError: _log.warning("Data is not valid JSON.", exc_info=True) return None diff --git a/api/src/opentrons/calibration_storage/helpers.py b/api/src/opentrons/calibration_storage/helpers.py index 1d271add9dd..db11dac3453 100644 --- a/api/src/opentrons/calibration_storage/helpers.py +++ b/api/src/opentrons/calibration_storage/helpers.py @@ -31,7 +31,9 @@ def convert_to_dict(obj: Any) -> Dict[str, Any]: # https://github.com/python/mypy/issues/6568 # Unfortunately, since it's not currently supported I have an # assert check instead. - assert is_dataclass(obj), "This function is intended for dataclasses only" + assert is_dataclass(obj) and not isinstance( + obj, type + ), "This function is intended for dataclasses only" return asdict(obj, dict_factory=dict_filter_none) diff --git a/api/src/opentrons/calibration_storage/ot2/models/v1.py b/api/src/opentrons/calibration_storage/ot2/models/v1.py index 922922415c8..23836e67c98 100644 --- a/api/src/opentrons/calibration_storage/ot2/models/v1.py +++ b/api/src/opentrons/calibration_storage/ot2/models/v1.py @@ -1,7 +1,7 @@ import typing from typing_extensions import Literal -from pydantic import BaseModel, Field, validator +from pydantic import field_validator, BaseModel, Field, PlainSerializer from datetime import datetime from opentrons_shared_data.pipette.types import LabwareUri @@ -9,11 +9,16 @@ from opentrons.types import Point from opentrons.calibration_storage import types +DatetimeType = typing.Annotated[ + datetime, + PlainSerializer(lambda x: x.isoformat(), when_used="json"), +] + class CalibrationStatus(BaseModel): markedBad: bool = False source: typing.Optional[types.SourceType] = None - markedAt: typing.Optional[datetime] = None + markedAt: typing.Optional[DatetimeType] = None # Schemas used to store the data types @@ -22,7 +27,7 @@ class CalibrationStatus(BaseModel): # they are currently saved in on the OT-2 to avoid a large migration. class TipLengthModel(BaseModel): tipLength: float = Field(..., description="Tip length data found from calibration.") - lastModified: datetime = Field( + lastModified: DatetimeType = Field( ..., description="The last time this tip length was calibrated." ) source: types.SourceType = Field( @@ -40,22 +45,19 @@ class TipLengthModel(BaseModel): ..., description="The tiprack hash associated with the tip length data." ) - @validator("tipLength") + @field_validator("tipLength") + @classmethod def ensure_tip_length_positive(cls, tipLength: float) -> float: if tipLength < 0.0: raise ValueError("Tip Length must be a positive number") return tipLength - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} - class DeckCalibrationModel(BaseModel): attitude: types.AttitudeMatrix = Field( ..., description="Attitude matrix for deck found from calibration." ) - last_modified: typing.Optional[datetime] = Field( + last_modified: typing.Optional[DatetimeType] = Field( default=None, description="The last time this deck was calibrated." ) source: types.SourceType = Field( @@ -72,10 +74,6 @@ class DeckCalibrationModel(BaseModel): description="The status of the calibration data.", ) - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} - class InstrumentOffsetModel(BaseModel): offset: Point = Field(..., description="Instrument offset found from calibration.") @@ -83,7 +81,7 @@ class InstrumentOffsetModel(BaseModel): uri: str = Field( ..., description="The URI of the labware used for instrument offset" ) - last_modified: datetime = Field( + last_modified: DatetimeType = Field( ..., description="The last time this instrument was calibrated." ) source: types.SourceType = Field( @@ -94,10 +92,6 @@ class InstrumentOffsetModel(BaseModel): description="The status of the calibration data.", ) - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} - # TODO(lc 09-19-2022) We need to refactor the calibration endpoints # so that we only need to use one data model schema. This model is a @@ -112,7 +106,7 @@ class PipetteOffsetCalibration(BaseModel): uri: str = Field( ..., description="The URI of the labware used for instrument offset" ) - last_modified: datetime = Field( + last_modified: DatetimeType = Field( ..., description="The last time this instrument was calibrated." ) source: types.SourceType = Field( @@ -123,10 +117,6 @@ class PipetteOffsetCalibration(BaseModel): description="The status of the calibration data.", ) - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} - # TODO(lc 09-19-2022) We need to refactor the calibration endpoints # so that we only need to use one data model schema. This model is a @@ -137,7 +127,7 @@ class TipLengthCalibration(BaseModel): ..., description="The tiprack hash associated with this tip length data." ) tipLength: float = Field(..., description="Tip length data found from calibration.") - lastModified: datetime = Field( + lastModified: DatetimeType = Field( ..., description="The last time this tip length was calibrated." ) source: types.SourceType = Field( @@ -151,12 +141,9 @@ class TipLengthCalibration(BaseModel): ..., description="The tiprack URI associated with the tip length data." ) - @validator("tipLength") + @field_validator("tipLength") + @classmethod def ensure_tip_length_positive(cls, tipLength: float) -> float: if tipLength < 0.0: raise ValueError("Tip Length must be a positive number") return tipLength - - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} diff --git a/api/src/opentrons/calibration_storage/ot2/tip_length.py b/api/src/opentrons/calibration_storage/ot2/tip_length.py index a0bcdcabf9d..979916bd85e 100644 --- a/api/src/opentrons/calibration_storage/ot2/tip_length.py +++ b/api/src/opentrons/calibration_storage/ot2/tip_length.py @@ -24,13 +24,14 @@ def _convert_tip_length_model_to_dict( - to_dict: typing.Dict[LabwareUri, v1.TipLengthModel] + to_dict: typing.Dict[LabwareUri, v1.TipLengthModel], ) -> typing.Dict[LabwareUri, typing.Any]: + # TODO[pydantic]: supported in Pydantic V2 # This is a workaround since pydantic doesn't have a nice way to # add encoders when converting to a dict. dict_of_tip_lengths = {} for key, item in to_dict.items(): - dict_of_tip_lengths[key] = json.loads(item.json()) + dict_of_tip_lengths[key] = json.loads(item.model_dump_json()) return dict_of_tip_lengths @@ -175,12 +176,14 @@ def delete_tip_length_calibration( io.save_to_file(tip_length_dir, pipette_id, dict_of_tip_lengths) else: io.delete_file(tip_length_dir / f"{pipette_id}.json") - elif tiprack_hash and any(tiprack_hash in v.dict() for v in tip_lengths.values()): + elif tiprack_hash and any( + tiprack_hash in v.model_dump() for v in tip_lengths.values() + ): # NOTE this is for backwards compatibilty only # TODO delete this check once the tip_length DELETE router # no longer depends on a tiprack hash for k, v in tip_lengths.items(): - if tiprack_hash in v.dict(): + if tiprack_hash in v.model_dump(): tip_lengths.pop(k) if tip_lengths: dict_of_tip_lengths = _convert_tip_length_model_to_dict(tip_lengths) diff --git a/api/src/opentrons/calibration_storage/ot3/models/v1.py b/api/src/opentrons/calibration_storage/ot3/models/v1.py index 55e028465c7..b895eac4c89 100644 --- a/api/src/opentrons/calibration_storage/ot3/models/v1.py +++ b/api/src/opentrons/calibration_storage/ot3/models/v1.py @@ -3,7 +3,7 @@ from typing_extensions import Literal from opentrons.hardware_control.modules.types import ModuleType from opentrons.hardware_control.types import OT3Mount -from pydantic import BaseModel, Field, validator +from pydantic import field_validator, BaseModel, Field, PlainSerializer from datetime import datetime from opentrons_shared_data.pipette.types import LabwareUri @@ -12,15 +12,21 @@ from opentrons.calibration_storage import types +DatetimeType = typing.Annotated[ + datetime, + PlainSerializer(lambda x: x.isoformat(), when_used="json"), +] + + class CalibrationStatus(BaseModel): markedBad: bool = False source: typing.Optional[types.SourceType] = None - markedAt: typing.Optional[datetime] = None + markedAt: typing.Optional[DatetimeType] = None class TipLengthModel(BaseModel): tipLength: float = Field(..., description="Tip length data found from calibration.") - lastModified: datetime = Field( + lastModified: DatetimeType = Field( ..., description="The last time this tip length was calibrated." ) uri: typing.Union[LabwareUri, Literal[""]] = Field( @@ -34,22 +40,19 @@ class TipLengthModel(BaseModel): description="The status of the calibration data.", ) - @validator("tipLength") + @field_validator("tipLength") + @classmethod def ensure_tip_length_positive(cls, tipLength: float) -> float: if tipLength < 0.0: raise ValueError("Tip Length must be a positive number") return tipLength - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} - class BeltCalibrationModel(BaseModel): attitude: types.AttitudeMatrix = Field( ..., description="Attitude matrix for belts found from calibration." ) - lastModified: datetime = Field( + lastModified: DatetimeType = Field( ..., description="The last time this deck was calibrated." ) source: types.SourceType = Field( @@ -63,14 +66,10 @@ class BeltCalibrationModel(BaseModel): description="The status of the calibration data.", ) - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} - class InstrumentOffsetModel(BaseModel): offset: Point = Field(..., description="Instrument offset found from calibration.") - lastModified: datetime = Field( + lastModified: DatetimeType = Field( ..., description="The last time this instrument was calibrated." ) source: types.SourceType = Field( @@ -81,10 +80,6 @@ class InstrumentOffsetModel(BaseModel): description="The status of the calibration data.", ) - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} - class ModuleOffsetModel(BaseModel): offset: Point = Field(..., description="Module offset found from calibration.") @@ -99,7 +94,7 @@ class ModuleOffsetModel(BaseModel): ..., description="The unique id of the instrument used to calibrate this module.", ) - lastModified: datetime = Field( + lastModified: DatetimeType = Field( ..., description="The last time this module was calibrated." ) source: types.SourceType = Field( @@ -109,7 +104,3 @@ class ModuleOffsetModel(BaseModel): default_factory=CalibrationStatus, description="The status of the calibration data.", ) - - class Config: - json_encoders = {datetime: lambda obj: obj.isoformat()} - json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)} diff --git a/api/src/opentrons/cli/analyze.py b/api/src/opentrons/cli/analyze.py index e98f3e807d7..1ce161a96a7 100644 --- a/api/src/opentrons/cli/analyze.py +++ b/api/src/opentrons/cli/analyze.py @@ -382,16 +382,20 @@ async def _analyze( else: result = AnalysisResult.OK - results = AnalyzeResults.construct( + results = AnalyzeResults.model_construct( createdAt=datetime.now(tz=timezone.utc), files=[ - ProtocolFile.construct(name=f.path.name, role=f.role) + ProtocolFile.model_construct(name=f.path.name, role=f.role) for f in protocol_source.files ], config=( - JsonConfig.construct(schemaVersion=protocol_source.config.schema_version) + JsonConfig.model_construct( + schemaVersion=protocol_source.config.schema_version + ) if isinstance(protocol_source.config, JsonProtocolConfig) - else PythonConfig.construct(apiVersion=protocol_source.config.api_version) + else PythonConfig.model_construct( + apiVersion=protocol_source.config.api_version + ) ), result=result, metadata=protocol_source.metadata, @@ -411,14 +415,14 @@ async def _analyze( "json", outputs, lambda to_file: to_file.write( - results.json(exclude_none=True).encode("utf-8"), + results.model_dump_json(exclude_none=True).encode("utf-8"), ), ) _call_for_output_of_kind( "human-json", outputs, lambda to_file: to_file.write( - results.json(exclude_none=True, indent=2).encode("utf-8") + results.model_dump_json(exclude_none=True, indent=2).encode("utf-8") ), ) if check: diff --git a/api/src/opentrons/drivers/asyncio/communication/serial_connection.py b/api/src/opentrons/drivers/asyncio/communication/serial_connection.py index 294e5779a7b..f925cfe8680 100644 --- a/api/src/opentrons/drivers/asyncio/communication/serial_connection.py +++ b/api/src/opentrons/drivers/asyncio/communication/serial_connection.py @@ -298,6 +298,7 @@ async def create( alarm_keyword: Optional[str] = None, reset_buffer_before_write: bool = False, async_error_ack: Optional[str] = None, + number_of_retries: int = 0, ) -> AsyncResponseSerialConnection: """ Create a connection. @@ -340,6 +341,7 @@ async def create( error_keyword=error_keyword or "err", alarm_keyword=alarm_keyword or "alarm", async_error_ack=async_error_ack or "async", + number_of_retries=number_of_retries, ) def __init__( @@ -352,6 +354,7 @@ def __init__( error_keyword: str, alarm_keyword: str, async_error_ack: str, + number_of_retries: int = 0, ) -> None: """ Constructor @@ -383,6 +386,7 @@ def __init__( self._name = name self._ack = ack.encode() self._retry_wait_time_seconds = retry_wait_time_seconds + self._number_of_retries = number_of_retries self._error_keyword = error_keyword.lower() self._alarm_keyword = alarm_keyword.lower() self._async_error_ack = async_error_ack.lower() @@ -403,7 +407,9 @@ async def send_command( Raises: SerialException """ return await self.send_data( - data=command.build(), retries=retries, timeout=timeout + data=command.build(), + retries=retries or self._number_of_retries, + timeout=timeout, ) async def send_data( @@ -424,7 +430,9 @@ async def send_data( async with super().send_data_lock, self._serial.timeout_override( "timeout", timeout ): - return await self._send_data(data=data, retries=retries) + return await self._send_data( + data=data, retries=retries or self._number_of_retries + ) async def _send_data(self, data: str, retries: int = 0) -> str: """ @@ -439,6 +447,7 @@ async def _send_data(self, data: str, retries: int = 0) -> str: Raises: SerialException """ data_encode = data.encode() + retries = retries or self._number_of_retries for retry in range(retries + 1): log.debug(f"{self._name}: Write -> {data_encode!r}") diff --git a/api/src/opentrons/drivers/command_builder.py b/api/src/opentrons/drivers/command_builder.py index 99ac5c7890c..ea90a12b946 100644 --- a/api/src/opentrons/drivers/command_builder.py +++ b/api/src/opentrons/drivers/command_builder.py @@ -6,7 +6,7 @@ class CommandBuilder: """Class used to build GCODE commands.""" - def __init__(self, terminator: str) -> None: + def __init__(self, terminator: str = "\n") -> None: """ Construct a command builder. @@ -17,7 +17,7 @@ def __init__(self, terminator: str) -> None: self._elements: List[str] = [] def add_float( - self, prefix: str, value: float, precision: Optional[int] + self, prefix: str, value: float, precision: Optional[int] = None ) -> CommandBuilder: """ Add a float value. diff --git a/api/src/opentrons/drivers/flex_stacker/__init__.py b/api/src/opentrons/drivers/flex_stacker/__init__.py new file mode 100644 index 00000000000..cd4866c179a --- /dev/null +++ b/api/src/opentrons/drivers/flex_stacker/__init__.py @@ -0,0 +1,9 @@ +from .abstract import AbstractStackerDriver +from .driver import FlexStackerDriver +from .simulator import SimulatingDriver + +__all__ = [ + "AbstractStackerDriver", + "FlexStackerDriver", + "SimulatingDriver", +] diff --git a/api/src/opentrons/drivers/flex_stacker/abstract.py b/api/src/opentrons/drivers/flex_stacker/abstract.py new file mode 100644 index 00000000000..5ba3cdcb026 --- /dev/null +++ b/api/src/opentrons/drivers/flex_stacker/abstract.py @@ -0,0 +1,89 @@ +from typing import Protocol + +from .types import ( + StackerAxis, + PlatformStatus, + Direction, + MoveParams, + StackerInfo, + LEDColor, +) + + +class AbstractStackerDriver(Protocol): + """Protocol for the Stacker driver.""" + + async def connect(self) -> None: + """Connect to stacker.""" + ... + + async def disconnect(self) -> None: + """Disconnect from stacker.""" + ... + + async def is_connected(self) -> bool: + """Check connection to stacker.""" + ... + + async def update_firmware(self, firmware_file_path: str) -> None: + """Updates the firmware on the device.""" + ... + + async def get_device_info(self) -> StackerInfo: + """Get Device Info.""" + ... + + async def set_serial_number(self, sn: str) -> bool: + """Set Serial Number.""" + ... + + async def stop_motors(self) -> bool: + """Stop all motor movement.""" + ... + + async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool: + """Get limit switch status. + + :return: True if limit switch is triggered, False otherwise + """ + ... + + async def get_platform_sensor(self, direction: Direction) -> bool: + """Get platform sensor status. + + :return: True if platform is present, False otherwise + """ + ... + + async def get_platform_status(self) -> PlatformStatus: + """Get platform status.""" + ... + + async def get_hopper_door_closed(self) -> bool: + """Get whether or not door is closed. + + :return: True if door is closed, False otherwise + """ + ... + + async def move_in_mm( + self, axis: StackerAxis, distance: float, params: MoveParams | None = None + ) -> bool: + """Move axis.""" + ... + + async def move_to_limit_switch( + self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None + ) -> bool: + """Move until limit switch is triggered.""" + ... + + async def home_axis(self, axis: StackerAxis, direction: Direction) -> bool: + """Home axis.""" + ... + + async def set_led( + self, power: float, color: LEDColor | None = None, external: bool | None = None + ) -> bool: + """Set LED color of status bar.""" + ... diff --git a/api/src/opentrons/drivers/flex_stacker/driver.py b/api/src/opentrons/drivers/flex_stacker/driver.py new file mode 100644 index 00000000000..83671023772 --- /dev/null +++ b/api/src/opentrons/drivers/flex_stacker/driver.py @@ -0,0 +1,260 @@ +import asyncio +import re +from typing import Optional + +from opentrons.drivers.command_builder import CommandBuilder +from opentrons.drivers.asyncio.communication import AsyncResponseSerialConnection + +from .abstract import AbstractStackerDriver +from .types import ( + GCODE, + StackerAxis, + PlatformStatus, + Direction, + StackerInfo, + HardwareRevision, + MoveParams, + LimitSwitchStatus, + LEDColor, +) + + +FS_BAUDRATE = 115200 +DEFAULT_FS_TIMEOUT = 40 +FS_ACK = "OK\n" +FS_ERROR_KEYWORD = "err" +FS_ASYNC_ERROR_ACK = "async" +DEFAULT_COMMAND_RETRIES = 0 +GCODE_ROUNDING_PRECISION = 2 + + +class FlexStackerDriver(AbstractStackerDriver): + """FLEX Stacker driver.""" + + @classmethod + def parse_device_info(cls, response: str) -> StackerInfo: + """Parse stacker info.""" + # TODO: Validate serial number format once established + _RE = re.compile( + f"^{GCODE.DEVICE_INFO} FW:(?P\\S+) HW:Opentrons-flex-stacker-(?P\\S+) SerialNo:(?P\\S+)$" + ) + m = _RE.match(response) + if not m: + raise ValueError(f"Incorrect Response for device info: {response}") + return StackerInfo( + m.group("fw"), HardwareRevision(m.group("hw")), m.group("sn") + ) + + @classmethod + def parse_limit_switch_status(cls, response: str) -> LimitSwitchStatus: + """Parse limit switch statuses.""" + field_names = LimitSwitchStatus.get_fields() + pattern = r"\s".join([rf"{name}:(?P<{name}>\d)" for name in field_names]) + _RE = re.compile(f"^{GCODE.GET_LIMIT_SWITCH} {pattern}$") + m = _RE.match(response) + if not m: + raise ValueError(f"Incorrect Response for limit switch status: {response}") + return LimitSwitchStatus(*(bool(int(m.group(name))) for name in field_names)) + + @classmethod + def parse_platform_sensor_status(cls, response: str) -> PlatformStatus: + """Parse platform statuses.""" + field_names = PlatformStatus.get_fields() + pattern = r"\s".join([rf"{name}:(?P<{name}>\d)" for name in field_names]) + _RE = re.compile(f"^{GCODE.GET_PLATFORM_SENSOR} {pattern}$") + m = _RE.match(response) + if not m: + raise ValueError(f"Incorrect Response for platform status: {response}") + return PlatformStatus(*(bool(int(m.group(name))) for name in field_names)) + + @classmethod + def parse_door_closed(cls, response: str) -> bool: + """Parse door closed.""" + _RE = re.compile(r"^M122 D:(\d)$") + match = _RE.match(response) + if not match: + raise ValueError(f"Incorrect Response for door closed: {response}") + return bool(int(match.group(1))) + + @classmethod + def append_move_params( + cls, command: CommandBuilder, params: MoveParams | None + ) -> CommandBuilder: + """Append move params.""" + if params is not None: + if params.max_speed is not None: + command.add_float("V", params.max_speed, GCODE_ROUNDING_PRECISION) + if params.acceleration is not None: + command.add_float("A", params.acceleration, GCODE_ROUNDING_PRECISION) + if params.max_speed_discont is not None: + command.add_float( + "D", params.max_speed_discont, GCODE_ROUNDING_PRECISION + ) + return command + + @classmethod + async def create( + cls, port: str, loop: Optional[asyncio.AbstractEventLoop] + ) -> "FlexStackerDriver": + """Create a FLEX Stacker driver.""" + connection = await AsyncResponseSerialConnection.create( + port=port, + baud_rate=FS_BAUDRATE, + timeout=DEFAULT_FS_TIMEOUT, + number_of_retries=DEFAULT_COMMAND_RETRIES, + ack=FS_ACK, + loop=loop, + error_keyword=FS_ERROR_KEYWORD, + async_error_ack=FS_ASYNC_ERROR_ACK, + ) + return cls(connection) + + def __init__(self, connection: AsyncResponseSerialConnection) -> None: + """ + Constructor + + Args: + connection: Connection to the FLEX Stacker + """ + self._connection = connection + + async def connect(self) -> None: + """Connect to stacker.""" + await self._connection.open() + + async def disconnect(self) -> None: + """Disconnect from stacker.""" + await self._connection.close() + + async def is_connected(self) -> bool: + """Check connection to stacker.""" + return await self._connection.is_open() + + async def get_device_info(self) -> StackerInfo: + """Get Device Info.""" + response = await self._connection.send_command( + GCODE.DEVICE_INFO.build_command() + ) + await self._connection.send_command(GCODE.GET_RESET_REASON.build_command()) + return self.parse_device_info(response) + + async def set_serial_number(self, sn: str) -> bool: + """Set Serial Number.""" + # TODO: validate the serial number format + resp = await self._connection.send_command( + GCODE.SET_SERIAL_NUMBER.build_command().add_element(sn) + ) + if not re.match(rf"^{GCODE.SET_SERIAL_NUMBER}$", resp): + raise ValueError(f"Incorrect Response for set serial number: {resp}") + return True + + async def stop_motors(self) -> bool: + """Stop all motor movement.""" + resp = await self._connection.send_command(GCODE.STOP_MOTORS.build_command()) + if not re.match(rf"^{GCODE.STOP_MOTORS}$", resp): + raise ValueError(f"Incorrect Response for stop motors: {resp}") + return True + + async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool: + """Get limit switch status. + + :return: True if limit switch is triggered, False otherwise + """ + response = await self.get_limit_switches_status() + return response.get(axis, direction) + + async def get_limit_switches_status(self) -> LimitSwitchStatus: + """Get limit switch statuses for all axes.""" + response = await self._connection.send_command( + GCODE.GET_LIMIT_SWITCH.build_command() + ) + return self.parse_limit_switch_status(response) + + async def get_platform_sensor(self, direction: Direction) -> bool: + """Get platform sensor at one direction.""" + response = await self.get_platform_status() + return response.get(direction) + + async def get_platform_status(self) -> PlatformStatus: + """Get platform sensor status. + + :return: True if platform is detected, False otherwise + """ + response = await self._connection.send_command( + GCODE.GET_PLATFORM_SENSOR.build_command() + ) + return self.parse_platform_sensor_status(response) + + async def get_hopper_door_closed(self) -> bool: + """Get whether or not door is closed. + + :return: True if door is closed, False otherwise + """ + response = await self._connection.send_command( + GCODE.GET_DOOR_SWITCH.build_command() + ) + return self.parse_door_closed(response) + + async def move_in_mm( + self, axis: StackerAxis, distance: float, params: MoveParams | None = None + ) -> bool: + """Move axis.""" + command = self.append_move_params( + GCODE.MOVE_TO.build_command().add_float( + axis.name, distance, GCODE_ROUNDING_PRECISION + ), + params, + ) + resp = await self._connection.send_command(command) + if not re.match(rf"^{GCODE.MOVE_TO}$", resp): + raise ValueError(f"Incorrect Response for move to: {resp}") + return True + + async def move_to_limit_switch( + self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None + ) -> bool: + """Move until limit switch is triggered.""" + command = self.append_move_params( + GCODE.MOVE_TO_SWITCH.build_command().add_int(axis.name, direction.value), + params, + ) + resp = await self._connection.send_command(command) + if not re.match(rf"^{GCODE.MOVE_TO_SWITCH}$", resp): + raise ValueError(f"Incorrect Response for move to switch: {resp}") + return True + + async def home_axis(self, axis: StackerAxis, direction: Direction) -> bool: + """Home axis.""" + resp = await self._connection.send_command( + GCODE.HOME_AXIS.build_command().add_int(axis.name, direction.value) + ) + if not re.match(rf"^{GCODE.HOME_AXIS}$", resp): + raise ValueError(f"Incorrect Response for home axis: {resp}") + return True + + async def set_led( + self, power: float, color: LEDColor | None = None, external: bool | None = None + ) -> bool: + """Set LED color. + + :param power: Power of the LED (0-1.0), 0 is off, 1 is full power + :param color: Color of the LED + :param external: True if external LED, False if internal LED + """ + power = max(0, min(power, 1.0)) + command = GCODE.SET_LED.build_command().add_float( + "P", power, GCODE_ROUNDING_PRECISION + ) + if color is not None: + command.add_int("C", color.value) + if external is not None: + command.add_int("E", external) + resp = await self._connection.send_command(command) + if not re.match(rf"^{GCODE.SET_LED}$", resp): + raise ValueError(f"Incorrect Response for set led: {resp}") + return True + + async def update_firmware(self, firmware_file_path: str) -> None: + """Updates the firmware on the device.""" + # TODO: Implement firmware update + pass diff --git a/api/src/opentrons/drivers/flex_stacker/simulator.py b/api/src/opentrons/drivers/flex_stacker/simulator.py new file mode 100644 index 00000000000..1e0b59b19de --- /dev/null +++ b/api/src/opentrons/drivers/flex_stacker/simulator.py @@ -0,0 +1,109 @@ +from typing import Optional + +from opentrons.util.async_helpers import ensure_yield + +from .abstract import AbstractStackerDriver +from .types import ( + StackerAxis, + PlatformStatus, + Direction, + StackerInfo, + HardwareRevision, + MoveParams, + LimitSwitchStatus, +) + + +class SimulatingDriver(AbstractStackerDriver): + """FLEX Stacker driver simulator.""" + + def __init__(self, serial_number: Optional[str] = None) -> None: + self._sn = serial_number or "dummySerialFS" + self._limit_switch_status = LimitSwitchStatus(False, False, False, False, False) + self._platform_sensor_status = PlatformStatus(False, False) + self._door_closed = True + + def set_limit_switch(self, status: LimitSwitchStatus) -> bool: + self._limit_switch_status = status + return True + + def set_platform_sensor(self, status: PlatformStatus) -> bool: + self._platform_sensor_status = status + return True + + def set_door_closed(self, door_closed: bool) -> bool: + self._door_closed = door_closed + return True + + @ensure_yield + async def connect(self) -> None: + """Connect to stacker.""" + pass + + @ensure_yield + async def disconnect(self) -> None: + """Disconnect from stacker.""" + pass + + @ensure_yield + async def is_connected(self) -> bool: + """Check connection to stacker.""" + return True + + @ensure_yield + async def get_device_info(self) -> StackerInfo: + """Get Device Info.""" + return StackerInfo(fw="stacker-fw", hw=HardwareRevision.EVT, sn=self._sn) + + @ensure_yield + async def set_serial_number(self, sn: str) -> bool: + """Set Serial Number.""" + return True + + @ensure_yield + async def stop_motor(self) -> bool: + """Stop motor movement.""" + return True + + @ensure_yield + async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool: + """Get limit switch status. + + :return: True if limit switch is triggered, False otherwise + """ + return self._limit_switch_status.get(axis, direction) + + @ensure_yield + async def get_limit_switches_status(self) -> LimitSwitchStatus: + """Get limit switch statuses for all axes.""" + return self._limit_switch_status + + @ensure_yield + async def get_platform_sensor_status(self) -> PlatformStatus: + """Get platform sensor status. + + :return: True if platform is detected, False otherwise + """ + return self._platform_sensor_status + + @ensure_yield + async def get_hopper_door_closed(self) -> bool: + """Get whether or not door is closed. + + :return: True if door is closed, False otherwise + """ + return self._door_closed + + @ensure_yield + async def move_in_mm( + self, axis: StackerAxis, distance: float, params: MoveParams | None = None + ) -> bool: + """Move axis.""" + return True + + @ensure_yield + async def move_to_limit_switch( + self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None + ) -> bool: + """Move until limit switch is triggered.""" + return True diff --git a/api/src/opentrons/drivers/flex_stacker/types.py b/api/src/opentrons/drivers/flex_stacker/types.py new file mode 100644 index 00000000000..4035aaaa755 --- /dev/null +++ b/api/src/opentrons/drivers/flex_stacker/types.py @@ -0,0 +1,138 @@ +from enum import Enum +from dataclasses import dataclass, fields +from typing import List + +from opentrons.drivers.command_builder import CommandBuilder + + +class GCODE(str, Enum): + + MOVE_TO = "G0" + MOVE_TO_SWITCH = "G5" + HOME_AXIS = "G28" + STOP_MOTORS = "M0" + GET_RESET_REASON = "M114" + DEVICE_INFO = "M115" + GET_LIMIT_SWITCH = "M119" + SET_LED = "M200" + GET_PLATFORM_SENSOR = "M121" + GET_DOOR_SWITCH = "M122" + SET_SERIAL_NUMBER = "M996" + ENTER_BOOTLOADER = "dfu" + + def build_command(self) -> CommandBuilder: + """Build command.""" + return CommandBuilder().add_gcode(self) + + +STACKER_VID = 0x483 +STACKER_PID = 0xEF24 +STACKER_FREQ = 115200 + + +class HardwareRevision(Enum): + """Hardware Revision.""" + + NFF = "nff" + EVT = "a1" + + +@dataclass +class StackerInfo: + """Stacker Info.""" + + fw: str + hw: HardwareRevision + sn: str + + +class StackerAxis(Enum): + """Stacker Axis.""" + + X = "X" + Z = "Z" + L = "L" + + def __str__(self) -> str: + """Name.""" + return self.name + + +class LEDColor(Enum): + """Stacker LED Color.""" + + WHITE = 0 + RED = 1 + GREEN = 2 + BLUE = 3 + + +class Direction(Enum): + """Direction.""" + + RETRACT = 0 # negative + EXTENT = 1 # positive + + def __str__(self) -> str: + """Convert to tag for clear logging.""" + return "negative" if self == Direction.RETRACT else "positive" + + def opposite(self) -> "Direction": + """Get opposite direction.""" + return Direction.EXTENT if self == Direction.RETRACT else Direction.RETRACT + + def distance(self, distance: float) -> float: + """Get signed distance, where retract direction is negative.""" + return distance * -1 if self == Direction.RETRACT else distance + + +@dataclass +class LimitSwitchStatus: + """Stacker Limit Switch Statuses.""" + + XE: bool + XR: bool + ZE: bool + ZR: bool + LR: bool + + @classmethod + def get_fields(cls) -> List[str]: + """Get fields.""" + return [f.name for f in fields(cls)] + + def get(self, axis: StackerAxis, direction: Direction) -> bool: + """Get limit switch status.""" + if axis == StackerAxis.X: + return self.XE if direction == Direction.EXTENT else self.XR + if axis == StackerAxis.Z: + return self.ZE if direction == Direction.EXTENT else self.ZR + if direction == Direction.EXTENT: + raise ValueError("Latch does not have extent limit switch") + return self.LR + + +@dataclass +class PlatformStatus: + """Stacker Platform Statuses.""" + + E: bool + R: bool + + @classmethod + def get_fields(cls) -> List[str]: + """Get fields.""" + return [f.name for f in fields(cls)] + + def get(self, direction: Direction) -> bool: + """Get platform status.""" + return self.E if direction == Direction.EXTENT else self.R + + +@dataclass +class MoveParams: + """Move Parameters.""" + + max_speed: float | None = None + acceleration: float | None = None + max_speed_discont: float | None = None diff --git a/api/src/opentrons/execute.py b/api/src/opentrons/execute.py index a9b3562d82b..998d6bc6597 100644 --- a/api/src/opentrons/execute.py +++ b/api/src/opentrons/execute.py @@ -560,7 +560,9 @@ def _create_live_context_pe( # Non-async would use call_soon_threadsafe(), which makes the waiting harder. async def add_all_extra_labware() -> None: for labware_definition_dict in extra_labware.values(): - labware_definition = LabwareDefinition.parse_obj(labware_definition_dict) + labware_definition = LabwareDefinition.model_validate( + labware_definition_dict + ) pe.add_labware_definition(labware_definition) # Add extra_labware to ProtocolEngine, being careful not to modify ProtocolEngine from this diff --git a/api/src/opentrons/hardware_control/__init__.py b/api/src/opentrons/hardware_control/__init__.py index d575a2eada5..b49f1462249 100644 --- a/api/src/opentrons/hardware_control/__init__.py +++ b/api/src/opentrons/hardware_control/__init__.py @@ -38,8 +38,7 @@ ] HardwareControlAPI = Union[OT2HardwareControlAPI, OT3HardwareControlAPI] -# this type ignore is because of https://github.com/python/mypy/issues/13437 -ThreadManagedHardware = ThreadManager[HardwareControlAPI] # type: ignore[misc] +ThreadManagedHardware = ThreadManager[HardwareControlAPI] SyncHardwareAPI = SynchronousAdapter[HardwareControlAPI] __all__ = [ diff --git a/api/src/opentrons/hardware_control/backends/flex_protocol.py b/api/src/opentrons/hardware_control/backends/flex_protocol.py index 64f82a751ef..ef38af631e7 100644 --- a/api/src/opentrons/hardware_control/backends/flex_protocol.py +++ b/api/src/opentrons/hardware_control/backends/flex_protocol.py @@ -165,6 +165,7 @@ async def liquid_probe( threshold_pascals: float, plunger_impulse_time: float, num_baseline_reads: int, + z_offset_for_plunger_prep: float, probe: InstrumentProbeType = InstrumentProbeType.PRIMARY, force_both_sensors: bool = False, response_queue: Optional[PipetteSensorResponseQueue] = None, diff --git a/api/src/opentrons/hardware_control/backends/ot3controller.py b/api/src/opentrons/hardware_control/backends/ot3controller.py index f710eb405ac..84ffbffd8da 100644 --- a/api/src/opentrons/hardware_control/backends/ot3controller.py +++ b/api/src/opentrons/hardware_control/backends/ot3controller.py @@ -1072,6 +1072,7 @@ def _build_attached_pip( converted_name.pipette_type, converted_name.pipette_channels, converted_name.pipette_version, + converted_name.oem_type, ), "id": OT3Controller._combine_serial_number(attached), } @@ -1493,6 +1494,7 @@ async def liquid_probe( threshold_pascals: float, plunger_impulse_time: float, num_baseline_reads: int, + z_offset_for_plunger_prep: float, probe: InstrumentProbeType = InstrumentProbeType.PRIMARY, force_both_sensors: bool = False, response_queue: Optional[PipetteSensorResponseQueue] = None, @@ -1535,6 +1537,7 @@ def response_capture(data: Dict[SensorId, List[SensorDataType]]) -> None: threshold_pascals=threshold_pascals, plunger_impulse_time=plunger_impulse_time, num_baseline_reads=num_baseline_reads, + z_offset_for_plunger_prep=z_offset_for_plunger_prep, sensor_id=sensor_id_for_instrument(probe), force_both_sensors=force_both_sensors, emplace_data=response_capture, diff --git a/api/src/opentrons/hardware_control/backends/ot3simulator.py b/api/src/opentrons/hardware_control/backends/ot3simulator.py index e85b51ad53f..b7466386be6 100644 --- a/api/src/opentrons/hardware_control/backends/ot3simulator.py +++ b/api/src/opentrons/hardware_control/backends/ot3simulator.py @@ -353,6 +353,7 @@ async def liquid_probe( threshold_pascals: float, plunger_impulse_time: float, num_baseline_reads: int, + z_offset_for_plunger_prep: float, probe: InstrumentProbeType = InstrumentProbeType.PRIMARY, force_both_sensors: bool = False, response_queue: Optional[PipetteSensorResponseQueue] = None, @@ -510,6 +511,7 @@ def _attached_pipette_to_mount( converted_name.pipette_type, converted_name.pipette_channels, converted_name.pipette_version, + converted_name.oem_type, ), "id": None, } @@ -532,6 +534,7 @@ def _attached_pipette_to_mount( converted_name.pipette_type, converted_name.pipette_channels, converted_name.pipette_version, + converted_name.oem_type, ), "id": init_instr["id"], } @@ -543,6 +546,7 @@ def _attached_pipette_to_mount( converted_name.pipette_type, converted_name.pipette_channels, converted_name.pipette_version, + converted_name.oem_type, ), "id": None, } diff --git a/api/src/opentrons/hardware_control/emulation/module_server/client.py b/api/src/opentrons/hardware_control/emulation/module_server/client.py index 4108fe76069..5adcde0f267 100644 --- a/api/src/opentrons/hardware_control/emulation/module_server/client.py +++ b/api/src/opentrons/hardware_control/emulation/module_server/client.py @@ -66,7 +66,7 @@ async def read(self) -> Message: """Read a message from the module server.""" try: b = await self._reader.readuntil(MessageDelimiter) - m: Message = Message.parse_raw(b) + m: Message = Message.model_validate_json(b) return m except LimitOverrunError as e: raise ModuleServerClientError(str(e)) diff --git a/api/src/opentrons/hardware_control/emulation/module_server/server.py b/api/src/opentrons/hardware_control/emulation/module_server/server.py index 5a3d696eb7b..36878c342e3 100644 --- a/api/src/opentrons/hardware_control/emulation/module_server/server.py +++ b/api/src/opentrons/hardware_control/emulation/module_server/server.py @@ -53,7 +53,9 @@ def on_server_connected( self._connections[identifier] = connection for c in self._clients: c.write( - Message(status="connected", connections=[connection]).json().encode() + Message(status="connected", connections=[connection]) + .model_dump_json() + .encode() ) c.write(b"\n") @@ -72,7 +74,7 @@ def on_server_disconnected(self, identifier: str) -> None: for c in self._clients: c.write( Message(status="disconnected", connections=[connection]) - .json() + .model_dump_json() .encode() ) c.write(MessageDelimiter) @@ -95,7 +97,7 @@ async def _handle_connection( # A client connected. Send a dump of all connected modules. m = Message(status="dump", connections=list(self._connections.values())) - writer.write(m.json().encode()) + writer.write(m.model_dump_json().encode()) writer.write(MessageDelimiter) self._clients.add(writer) diff --git a/api/src/opentrons/hardware_control/emulation/settings.py b/api/src/opentrons/hardware_control/emulation/settings.py index 538b0281808..dd4da4dfc54 100644 --- a/api/src/opentrons/hardware_control/emulation/settings.py +++ b/api/src/opentrons/hardware_control/emulation/settings.py @@ -1,7 +1,8 @@ from typing import List from opentrons.hardware_control.emulation.types import ModuleType from opentrons.hardware_control.emulation.util import TEMPERATURE_ROOM -from pydantic import BaseSettings, BaseModel +from pydantic import BaseModel +from pydantic_settings import BaseSettings, SettingsConfigDict class PipetteSettings(BaseModel): @@ -113,8 +114,6 @@ class Settings(BaseSettings): emulator_port=9003, driver_port=9998 ) magdeck_proxy: ProxySettings = ProxySettings(emulator_port=9004, driver_port=9999) - - class Config: - env_prefix = "OT_EMULATOR_" + model_config = SettingsConfigDict(env_prefix="OT_EMULATOR_") module_server: ModuleServerSettings = ModuleServerSettings() diff --git a/api/src/opentrons/hardware_control/instruments/ot2/instrument_calibration.py b/api/src/opentrons/hardware_control/instruments/ot2/instrument_calibration.py index e093763dcd1..b3b82b22421 100644 --- a/api/src/opentrons/hardware_control/instruments/ot2/instrument_calibration.py +++ b/api/src/opentrons/hardware_control/instruments/ot2/instrument_calibration.py @@ -123,7 +123,8 @@ def load_tip_length_for_pipette( ) -> TipLengthCalibration: if isinstance(tiprack, LabwareDefinition): tiprack = typing.cast( - "TypeDictLabwareDef", tiprack.dict(exclude_none=True, exclude_unset=True) + "TypeDictLabwareDef", + tiprack.model_dump(exclude_none=True, exclude_unset=True), ) tip_length_data = calibration_storage.load_tip_length_calibration( diff --git a/api/src/opentrons/hardware_control/instruments/ot2/pipette.py b/api/src/opentrons/hardware_control/instruments/ot2/pipette.py index 2d63342cf19..2205da161f1 100644 --- a/api/src/opentrons/hardware_control/instruments/ot2/pipette.py +++ b/api/src/opentrons/hardware_control/instruments/ot2/pipette.py @@ -56,6 +56,7 @@ UlPerMmAction, PipetteName, PipetteModel, + PipetteOEMType, ) from opentrons.hardware_control.dev_types import InstrumentHardwareConfigs @@ -96,7 +97,7 @@ def __init__( use_old_aspiration_functions: bool = False, ) -> None: self._config = config - self._config_as_dict = config.dict() + self._config_as_dict = config.model_dump() self._pipette_offset = pipette_offset_cal self._pipette_type = self._config.pipette_type self._pipette_version = self._config.version @@ -112,17 +113,20 @@ def __init__( pipette_type=config.pipette_type, pipette_channels=config.channels, pipette_generation=config.display_category, + oem_type=PipetteOEMType.OT, ) self._acting_as = self._pipette_name self._pipette_model = PipetteModelVersionType( pipette_type=config.pipette_type, pipette_channels=config.channels, pipette_version=config.version, + oem_type=PipetteOEMType.OT, ) self._valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps( self._pipette_model.pipette_type, self._pipette_model.pipette_channels, self._pipette_model.pipette_version, + PipetteOEMType.OT, ) self._nozzle_offset = self._config.nozzle_offset self._nozzle_manager = ( @@ -189,7 +193,7 @@ def act_as(self, name: PipetteNameType) -> None: ], f"{self.name} is not back-compatible with {name}" liquid_model = load_pipette_data.load_liquid_model( - name.pipette_type, name.pipette_channels, name.get_version() + name.pipette_type, name.pipette_channels, name.get_version(), name.oem_type ) # TODO need to grab name config here to deal with act as test self._liquid_class.max_volume = liquid_model["default"].max_volume @@ -273,15 +277,16 @@ def update_config_item( self._config, elements, liquid_class ) # Update the cached dict representation - self._config_as_dict = self._config.dict() + self._config_as_dict = self._config.model_dump() def reload_configurations(self) -> None: self._config = load_pipette_data.load_definition( self._pipette_model.pipette_type, self._pipette_model.pipette_channels, self._pipette_model.pipette_version, + self._pipette_model.oem_type, ) - self._config_as_dict = self._config.dict() + self._config_as_dict = self._config.model_dump() def reset_state(self) -> None: self._current_volume = 0.0 @@ -656,8 +661,8 @@ def _reload_and_check_skip( # Same config, good enough return attached_instr, True else: - newdict = new_config.dict() - olddict = attached_instr.config.dict() + newdict = new_config.model_dump() + olddict = attached_instr.config.model_dump() changed: Set[str] = set() for k in newdict.keys(): if newdict[k] != olddict[k]: diff --git a/api/src/opentrons/hardware_control/instruments/ot2/pipette_handler.py b/api/src/opentrons/hardware_control/instruments/ot2/pipette_handler.py index 7bd41e02e74..9da14196d52 100644 --- a/api/src/opentrons/hardware_control/instruments/ot2/pipette_handler.py +++ b/api/src/opentrons/hardware_control/instruments/ot2/pipette_handler.py @@ -230,7 +230,7 @@ def get_attached_instrument(self, mount: MountType) -> PipetteDict: result["current_nozzle_map"] = instr.nozzle_manager.current_configuration result["min_volume"] = instr.liquid_class.min_volume result["max_volume"] = instr.liquid_class.max_volume - result["channels"] = instr.channels + result["channels"] = instr._max_channels.value result["has_tip"] = instr.has_tip result["tip_length"] = instr.current_tip_length result["aspirate_speed"] = self.plunger_speed( diff --git a/api/src/opentrons/hardware_control/instruments/ot3/gripper.py b/api/src/opentrons/hardware_control/instruments/ot3/gripper.py index ba49ea7d5e7..bd70547ee45 100644 --- a/api/src/opentrons/hardware_control/instruments/ot3/gripper.py +++ b/api/src/opentrons/hardware_control/instruments/ot3/gripper.py @@ -318,8 +318,8 @@ def _reload_gripper( # Same config, good enough return attached_instr, True else: - newdict = new_config.dict() - olddict = attached_instr.config.dict() + newdict = new_config.model_dump() + olddict = attached_instr.config.model_dump() changed: Set[str] = set() for k in newdict.keys(): if newdict[k] != olddict[k]: diff --git a/api/src/opentrons/hardware_control/instruments/ot3/pipette.py b/api/src/opentrons/hardware_control/instruments/ot3/pipette.py index b9355874906..bd7671d745d 100644 --- a/api/src/opentrons/hardware_control/instruments/ot3/pipette.py +++ b/api/src/opentrons/hardware_control/instruments/ot3/pipette.py @@ -42,6 +42,7 @@ PipetteName, PipetteModel, Quirks, + PipetteOEMType, ) from opentrons_shared_data.pipette import ( load_data as load_pipette_data, @@ -79,7 +80,7 @@ def __init__( use_old_aspiration_functions: bool = False, ) -> None: self._config = config - self._config_as_dict = config.dict() + self._config_as_dict = config.model_dump() self._plunger_motor_current = config.plunger_motor_configurations self._pick_up_configurations = config.pick_up_tip_configurations self._plunger_homing_configurations = config.plunger_homing_configurations @@ -93,22 +94,26 @@ def __init__( self._liquid_class_name = pip_types.LiquidClasses.default self._liquid_class = self._config.liquid_properties[self._liquid_class_name] + oem = PipetteOEMType.get_oem_from_quirks(config.quirks) # TODO (lc 12-05-2022) figure out how we can safely deprecate "name" and "model" self._pipette_name = PipetteNameType( pipette_type=config.pipette_type, pipette_channels=config.channels, pipette_generation=config.display_category, + oem_type=oem, ) self._acting_as = self._pipette_name self._pipette_model = PipetteModelVersionType( pipette_type=config.pipette_type, pipette_channels=config.channels, pipette_version=config.version, + oem_type=oem, ) self._valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps( self._pipette_model.pipette_type, self._pipette_model.pipette_channels, self._pipette_model.pipette_version, + self._pipette_model.oem_type, ) self._nozzle_offset = self._config.nozzle_offset self._nozzle_manager = ( @@ -250,8 +255,9 @@ def reload_configurations(self) -> None: self._pipette_model.pipette_type, self._pipette_model.pipette_channels, self._pipette_model.pipette_version, + self._pipette_model.oem_type, ) - self._config_as_dict = self._config.dict() + self._config_as_dict = self._config.model_dump() def reset_state(self) -> None: self._current_volume = 0.0 @@ -770,8 +776,8 @@ def _reload_and_check_skip( # Same config, good enough return attached_instr, True else: - newdict = new_config.dict() - olddict = attached_instr.config.dict() + newdict = new_config.model_dump() + olddict = attached_instr.config.model_dump() changed: Set[str] = set() for k in newdict.keys(): if newdict[k] != olddict[k]: diff --git a/api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py b/api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py index dda5031a8a3..ef081b95a62 100644 --- a/api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py +++ b/api/src/opentrons/hardware_control/instruments/ot3/pipette_handler.py @@ -237,6 +237,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict: "back_compat_names", "supported_tips", "lld_settings", + "available_sensors", ] instr_dict = instr.as_dict() @@ -248,7 +249,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict: result["current_nozzle_map"] = instr.nozzle_manager.current_configuration result["min_volume"] = instr.liquid_class.min_volume result["max_volume"] = instr.liquid_class.max_volume - result["channels"] = instr._max_channels + result["channels"] = instr._max_channels.value result["has_tip"] = instr.has_tip result["tip_length"] = instr.current_tip_length result["aspirate_speed"] = self.plunger_speed( @@ -289,6 +290,7 @@ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict: "drop_tip": instr.plunger_positions.drop_tip, } result["shaft_ul_per_mm"] = instr.config.shaft_ul_per_mm + result["available_sensors"] = instr.config.available_sensors return cast(PipetteDict, result) @property diff --git a/api/src/opentrons/hardware_control/modules/mod_abc.py b/api/src/opentrons/hardware_control/modules/mod_abc.py index b07c6156a88..ebc0da2fa13 100644 --- a/api/src/opentrons/hardware_control/modules/mod_abc.py +++ b/api/src/opentrons/hardware_control/modules/mod_abc.py @@ -2,7 +2,7 @@ import asyncio import logging import re -from typing import ClassVar, Mapping, Optional, TypeVar, cast +from typing import ClassVar, Mapping, Optional, TypeVar from packaging.version import InvalidVersion, parse, Version from opentrons.config import IS_ROBOT, ROBOT_FIRMWARE_DIR from opentrons.drivers.rpi_drivers.types import USBPort @@ -31,7 +31,7 @@ def parse_fw_version(version: str) -> Version: raise InvalidVersion() except InvalidVersion: device_version = parse("v0.0.0") - return cast(Version, device_version) + return device_version class AbstractModule(abc.ABC): diff --git a/api/src/opentrons/hardware_control/ot3_calibration.py b/api/src/opentrons/hardware_control/ot3_calibration.py index b0ebcd027ce..9303add23d6 100644 --- a/api/src/opentrons/hardware_control/ot3_calibration.py +++ b/api/src/opentrons/hardware_control/ot3_calibration.py @@ -968,7 +968,7 @@ def load_attitude_matrix(to_default: bool = True) -> DeckCalibration: return DeckCalibration( attitude=apply_machine_transform(calibration_data.attitude), source=calibration_data.source, - status=types.CalibrationStatus(**calibration_data.status.dict()), + status=types.CalibrationStatus(**calibration_data.status.model_dump()), belt_attitude=calibration_data.attitude, last_modified=calibration_data.lastModified, pipette_calibrated_with=calibration_data.pipetteCalibratedWith, diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index 7bb5e05f47b..6295757e7ab 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -2677,6 +2677,7 @@ async def _liquid_probe_pass( probe_settings: LiquidProbeSettings, probe: InstrumentProbeType, p_travel: float, + z_offset_for_plunger_prep: float, force_both_sensors: bool = False, response_queue: Optional[PipetteSensorResponseQueue] = None, ) -> float: @@ -2689,6 +2690,7 @@ async def _liquid_probe_pass( probe_settings.sensor_threshold_pascals, probe_settings.plunger_impulse_time, probe_settings.samples_for_baselining, + z_offset_for_plunger_prep, probe=probe, force_both_sensors=force_both_sensors, response_queue=response_queue, @@ -2838,6 +2840,7 @@ async def prep_plunger_for_probe_move( probe_settings, checked_probe, plunger_travel_mm + sensor_baseline_plunger_move_mm, + z_offset_for_plunger_prep, force_both_sensors, response_queue, ) diff --git a/api/src/opentrons/hardware_control/robot_calibration.py b/api/src/opentrons/hardware_control/robot_calibration.py index 270344fff2f..8ecf6b67be6 100644 --- a/api/src/opentrons/hardware_control/robot_calibration.py +++ b/api/src/opentrons/hardware_control/robot_calibration.py @@ -154,7 +154,7 @@ def load_attitude_matrix() -> DeckCalibration: return DeckCalibration( attitude=calibration_data.attitude, source=calibration_data.source, - status=types.CalibrationStatus(**calibration_data.status.dict()), + status=types.CalibrationStatus(**calibration_data.status.model_dump()), last_modified=calibration_data.last_modified, pipette_calibrated_with=calibration_data.pipette_calibrated_with, tiprack=calibration_data.tiprack, diff --git a/api/src/opentrons/protocol_api/core/engine/labware.py b/api/src/opentrons/protocol_api/core/engine/labware.py index 4d868bd30ac..d462401927f 100644 --- a/api/src/opentrons/protocol_api/core/engine/labware.py +++ b/api/src/opentrons/protocol_api/core/engine/labware.py @@ -92,12 +92,14 @@ def get_name(self) -> str: def get_definition(self) -> LabwareDefinitionDict: """Get the labware's definition as a plain dictionary.""" - return cast(LabwareDefinitionDict, self._definition.dict(exclude_none=True)) + return cast( + LabwareDefinitionDict, self._definition.model_dump(exclude_none=True) + ) def get_parameters(self) -> LabwareParametersDict: return cast( LabwareParametersDict, - self._definition.parameters.dict(exclude_none=True), + self._definition.parameters.model_dump(exclude_none=True), ) def get_quirks(self) -> List[str]: @@ -118,7 +120,7 @@ def set_calibration(self, delta: Point) -> None: details={"kind": "labware-not-in-slot"}, ) - request = LabwareOffsetCreate.construct( + request = LabwareOffsetCreate.model_construct( definitionUri=self.get_uri(), location=offset_location, vector=LabwareOffsetVector(x=delta.x, y=delta.y, z=delta.z), diff --git a/api/src/opentrons/protocol_api/core/engine/protocol.py b/api/src/opentrons/protocol_api/core/engine/protocol.py index d43fc9a2058..bfc808c3091 100644 --- a/api/src/opentrons/protocol_api/core/engine/protocol.py +++ b/api/src/opentrons/protocol_api/core/engine/protocol.py @@ -6,6 +6,7 @@ from opentrons.protocol_engine import commands as cmd from opentrons.protocol_engine.commands import LoadModuleResult + from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3 from opentrons_shared_data.labware.labware_definition import LabwareDefinition from opentrons_shared_data.labware.types import LabwareDefinition as LabwareDefDict @@ -192,7 +193,7 @@ def add_labware_definition( ) -> LabwareLoadParams: """Add a labware definition to the set of loadable definitions.""" uri = self._engine_client.add_labware_definition( - LabwareDefinition.parse_obj(definition) + LabwareDefinition.model_validate(definition) ) return LabwareLoadParams.from_uri(uri) @@ -736,9 +737,7 @@ def define_liquid( _id=liquid.id, name=liquid.displayName, description=liquid.description, - display_color=( - liquid.displayColor.__root__ if liquid.displayColor else None - ), + display_color=(liquid.displayColor.root if liquid.displayColor else None), ) def define_liquid_class(self, name: str) -> LiquidClass: diff --git a/api/src/opentrons/protocol_api/validation.py b/api/src/opentrons/protocol_api/validation.py index f0db8a71e5e..cd1e5112718 100644 --- a/api/src/opentrons/protocol_api/validation.py +++ b/api/src/opentrons/protocol_api/validation.py @@ -72,6 +72,7 @@ "flex_8channel_50": PipetteNameType.P50_MULTI_FLEX, "flex_1channel_1000": PipetteNameType.P1000_SINGLE_FLEX, "flex_8channel_1000": PipetteNameType.P1000_MULTI_FLEX, + "flex_8channel_1000_em": PipetteNameType.P1000_MULTI_EM, "flex_96channel_1000": PipetteNameType.P1000_96, "flex_96channel_200": PipetteNameType.P200_96, } diff --git a/api/src/opentrons/protocol_engine/commands/__init__.py b/api/src/opentrons/protocol_engine/commands/__init__.py index 4774a45c475..b5edda52397 100644 --- a/api/src/opentrons/protocol_engine/commands/__init__.py +++ b/api/src/opentrons/protocol_engine/commands/__init__.py @@ -35,8 +35,10 @@ from .command_unions import ( Command, + CommandAdapter, CommandParams, CommandCreate, + CommandCreateAdapter, CommandResult, CommandType, CommandDefinedErrorData, @@ -365,8 +367,10 @@ __all__ = [ # command type unions "Command", + "CommandAdapter", "CommandParams", "CommandCreate", + "CommandCreateAdapter", "CommandResult", "CommandType", "CommandPrivateResult", diff --git a/api/src/opentrons/protocol_engine/commands/absorbance_reader/initialize.py b/api/src/opentrons/protocol_engine/commands/absorbance_reader/initialize.py index 3c1fd8bcd56..bcb2639d0a5 100644 --- a/api/src/opentrons/protocol_engine/commands/absorbance_reader/initialize.py +++ b/api/src/opentrons/protocol_engine/commands/absorbance_reader/initialize.py @@ -1,9 +1,10 @@ """Command models to initialize an Absorbance Reader.""" from __future__ import annotations -from typing import List, Optional, Literal, TYPE_CHECKING +from typing import List, Optional, Literal, TYPE_CHECKING, Any from typing_extensions import Type from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from opentrons.drivers.types import ABSMeasurementMode from opentrons.protocol_engine.types import ABSMeasureMode @@ -21,6 +22,10 @@ InitializeCommandType = Literal["absorbanceReader/initialize"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class InitializeParams(BaseModel): """Input parameters to initialize an absorbance reading.""" @@ -29,8 +34,10 @@ class InitializeParams(BaseModel): ..., description="Initialize single or multi measurement mode." ) sampleWavelengths: List[int] = Field(..., description="Sample wavelengths in nm.") - referenceWavelength: Optional[int] = Field( - None, description="Optional reference wavelength in nm." + referenceWavelength: int | SkipJsonSchema[None] = Field( + None, + description="Optional reference wavelength in nm.", + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/absorbance_reader/read.py b/api/src/opentrons/protocol_engine/commands/absorbance_reader/read.py index 8d6e0fb91f0..c8f7dca8706 100644 --- a/api/src/opentrons/protocol_engine/commands/absorbance_reader/read.py +++ b/api/src/opentrons/protocol_engine/commands/absorbance_reader/read.py @@ -1,10 +1,11 @@ """Command models to read absorbance.""" from __future__ import annotations from datetime import datetime -from typing import Optional, Dict, TYPE_CHECKING, List -from typing_extensions import Literal, Type +from typing import Optional, Dict, TYPE_CHECKING, List, Any +from typing_extensions import Literal, Type from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ...errors import CannotPerformModuleAction, StorageLimitReachedError @@ -23,6 +24,10 @@ from opentrons.protocol_engine.execution import EquipmentHandler +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + ReadAbsorbanceCommandType = Literal["absorbanceReader/read"] @@ -30,9 +35,10 @@ class ReadAbsorbanceParams(BaseModel): """Input parameters for an absorbance reading.""" moduleId: str = Field(..., description="Unique ID of the Absorbance Reader.") - fileName: Optional[str] = Field( + fileName: str | SkipJsonSchema[None] = Field( None, description="Optional file name to use when storing the results of a measurement.", + json_schema_extra=_remove_default, ) @@ -119,7 +125,9 @@ async def execute( # noqa: C901 ) asbsorbance_result[wavelength] = converted_values transform_results.append( - ReadData.construct(wavelength=wavelength, data=converted_values) + ReadData.model_construct( + wavelength=wavelength, data=converted_values + ) ) # Handle the virtual module case for data creation (all zeroes) elif self._state_view.config.use_virtual_modules: @@ -133,7 +141,9 @@ async def execute( # noqa: C901 ) asbsorbance_result[wavelength] = converted_values transform_results.append( - ReadData.construct(wavelength=wavelength, data=converted_values) + ReadData.model_construct( + wavelength=wavelength, data=converted_values + ) ) else: raise CannotPerformModuleAction( @@ -151,7 +161,7 @@ async def execute( # noqa: C901 file_ids: list[str] = [] if params.fileName is not None: # Create the Plate Reader Transform - plate_read_result = PlateReaderData.construct( + plate_read_result = PlateReaderData.model_construct( read_results=transform_results, reference_wavelength=abs_reader_substate.reference_wavelength, start_time=start_time, diff --git a/api/src/opentrons/protocol_engine/commands/aspirate.py b/api/src/opentrons/protocol_engine/commands/aspirate.py index fa84afbde8c..9664d733b8a 100644 --- a/api/src/opentrons/protocol_engine/commands/aspirate.py +++ b/api/src/opentrons/protocol_engine/commands/aspirate.py @@ -226,7 +226,7 @@ class Aspirate( commandType: AspirateCommandType = "aspirate" params: AspirateParams - result: Optional[AspirateResult] + result: Optional[AspirateResult] = None _ImplementationCls: Type[AspirateImplementation] = AspirateImplementation diff --git a/api/src/opentrons/protocol_engine/commands/aspirate_in_place.py b/api/src/opentrons/protocol_engine/commands/aspirate_in_place.py index 1f89c9c5d74..434924928d7 100644 --- a/api/src/opentrons/protocol_engine/commands/aspirate_in_place.py +++ b/api/src/opentrons/protocol_engine/commands/aspirate_in_place.py @@ -165,7 +165,7 @@ class AspirateInPlace( commandType: AspirateInPlaceCommandType = "aspirateInPlace" params: AspirateInPlaceParams - result: Optional[AspirateInPlaceResult] + result: Optional[AspirateInPlaceResult] = None _ImplementationCls: Type[ AspirateInPlaceImplementation diff --git a/api/src/opentrons/protocol_engine/commands/blow_out.py b/api/src/opentrons/protocol_engine/commands/blow_out.py index b2e8765b4a1..07582781a3a 100644 --- a/api/src/opentrons/protocol_engine/commands/blow_out.py +++ b/api/src/opentrons/protocol_engine/commands/blow_out.py @@ -130,7 +130,7 @@ class BlowOut( commandType: BlowOutCommandType = "blowout" params: BlowOutParams - result: Optional[BlowOutResult] + result: Optional[BlowOutResult] = None _ImplementationCls: Type[BlowOutImplementation] = BlowOutImplementation diff --git a/api/src/opentrons/protocol_engine/commands/blow_out_in_place.py b/api/src/opentrons/protocol_engine/commands/blow_out_in_place.py index f5f648bcec8..527c921e499 100644 --- a/api/src/opentrons/protocol_engine/commands/blow_out_in_place.py +++ b/api/src/opentrons/protocol_engine/commands/blow_out_in_place.py @@ -100,7 +100,7 @@ class BlowOutInPlace( commandType: BlowOutInPlaceCommandType = "blowOutInPlace" params: BlowOutInPlaceParams - result: Optional[BlowOutInPlaceResult] + result: Optional[BlowOutInPlaceResult] = None _ImplementationCls: Type[ BlowOutInPlaceImplementation diff --git a/api/src/opentrons/protocol_engine/commands/calibration/calibrate_gripper.py b/api/src/opentrons/protocol_engine/commands/calibration/calibrate_gripper.py index 0333a171077..25ab19e2cd4 100644 --- a/api/src/opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +++ b/api/src/opentrons/protocol_engine/commands/calibration/calibrate_gripper.py @@ -1,10 +1,11 @@ """Models and implementation for the calibrateGripper command.""" from enum import Enum -from typing import Optional, Type +from typing import Optional, Type, Any from typing_extensions import Literal from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from opentrons.types import Point from opentrons.hardware_control import HardwareControlAPI @@ -22,6 +23,10 @@ CalibrateGripperCommandType = Literal["calibration/calibrateGripper"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class CalibrateGripperParamsJaw(Enum): # noqa: D101 FRONT = "front" REAR = "rear" @@ -39,7 +44,7 @@ class CalibrateGripperParams(BaseModel): ), ) - otherJawOffset: Optional[Vec3f] = Field( + otherJawOffset: Vec3f | SkipJsonSchema[None] = Field( None, description=( "If an offset for the other probe is already found, then specifying it here" @@ -48,6 +53,7 @@ class CalibrateGripperParams(BaseModel): " If this param is not specified then the command will only find and return" " the offset for the specified probe." ), + json_schema_extra=_remove_default, ) @@ -62,11 +68,12 @@ class CalibrateGripperResult(BaseModel): ), ) - savedCalibration: Optional[GripperCalibrationOffset] = Field( + savedCalibration: GripperCalibrationOffset | SkipJsonSchema[None] = Field( None, description=( "Gripper calibration result data, when `otherJawOffset` is provided." ), + json_schema_extra=_remove_default, ) @@ -118,8 +125,8 @@ async def execute( calibration_data = result return SuccessData( - public=CalibrateGripperResult.construct( - jawOffset=Vec3f.construct( + public=CalibrateGripperResult.model_construct( + jawOffset=Vec3f.model_construct( x=probe_offset.x, y=probe_offset.y, z=probe_offset.z ), savedCalibration=calibration_data, @@ -143,7 +150,7 @@ class CalibrateGripper( commandType: CalibrateGripperCommandType = "calibration/calibrateGripper" params: CalibrateGripperParams - result: Optional[CalibrateGripperResult] + result: Optional[CalibrateGripperResult] = None _ImplementationCls: Type[ CalibrateGripperImplementation diff --git a/api/src/opentrons/protocol_engine/commands/calibration/calibrate_module.py b/api/src/opentrons/protocol_engine/commands/calibration/calibrate_module.py index f488e8eab97..e203dcc19be 100644 --- a/api/src/opentrons/protocol_engine/commands/calibration/calibrate_module.py +++ b/api/src/opentrons/protocol_engine/commands/calibration/calibrate_module.py @@ -101,7 +101,7 @@ class CalibrateModule( commandType: CalibrateModuleCommandType = "calibration/calibrateModule" params: CalibrateModuleParams - result: Optional[CalibrateModuleResult] + result: Optional[CalibrateModuleResult] = None _ImplementationCls: Type[ CalibrateModuleImplementation diff --git a/api/src/opentrons/protocol_engine/commands/calibration/calibrate_pipette.py b/api/src/opentrons/protocol_engine/commands/calibration/calibrate_pipette.py index fbe754f6389..cb0eb93876c 100644 --- a/api/src/opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +++ b/api/src/opentrons/protocol_engine/commands/calibration/calibrate_pipette.py @@ -65,8 +65,8 @@ async def execute( await ot3_api.save_instrument_offset(mount=ot3_mount, delta=pipette_offset) return SuccessData( - public=CalibratePipetteResult.construct( - pipetteOffset=InstrumentOffsetVector.construct( + public=CalibratePipetteResult.model_construct( + pipetteOffset=InstrumentOffsetVector.model_construct( x=pipette_offset.x, y=pipette_offset.y, z=pipette_offset.z ) ), @@ -80,7 +80,7 @@ class CalibratePipette( commandType: CalibratePipetteCommandType = "calibration/calibratePipette" params: CalibratePipetteParams - result: Optional[CalibratePipetteResult] + result: Optional[CalibratePipetteResult] = None _ImplementationCls: Type[ CalibratePipetteImplementation diff --git a/api/src/opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py b/api/src/opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py index afb178cae99..ca18d70a265 100644 --- a/api/src/opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +++ b/api/src/opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py @@ -136,7 +136,7 @@ class MoveToMaintenancePosition( "calibration/moveToMaintenancePosition" ) params: MoveToMaintenancePositionParams - result: Optional[MoveToMaintenancePositionResult] + result: Optional[MoveToMaintenancePositionResult] = None _ImplementationCls: Type[ MoveToMaintenancePositionImplementation diff --git a/api/src/opentrons/protocol_engine/commands/command.py b/api/src/opentrons/protocol_engine/commands/command.py index c009f314afb..38d1512905e 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -14,10 +14,12 @@ List, Type, Union, + Any, + Dict, ) from pydantic import BaseModel, Field -from pydantic.generics import GenericModel +from pydantic.json_schema import SkipJsonSchema from opentrons.hardware_control import HardwareControlAPI from opentrons.protocol_engine.state.update_types import StateUpdate @@ -62,8 +64,12 @@ class CommandIntent(str, enum.Enum): FIXIT = "fixit" +def _pop_default(s: Dict[str, Any]) -> None: + s.pop("default", None) + + class BaseCommandCreate( - GenericModel, + BaseModel, # These type parameters need to be invariant because our fields are mutable. Generic[_ParamsT], ): @@ -81,7 +87,7 @@ class BaseCommandCreate( ), ) params: _ParamsT = Field(..., description="Command execution data payload") - intent: Optional[CommandIntent] = Field( + intent: CommandIntent | SkipJsonSchema[None] = Field( None, description=( "The reason the command was added. If not specified or `protocol`," @@ -94,14 +100,16 @@ class BaseCommandCreate( "Use setup commands for activities like pre-run calibration checks" " and module setup, like pre-heating." ), + json_schema_extra=_pop_default, ) - key: Optional[str] = Field( + key: str | SkipJsonSchema[None] = Field( None, description=( "A key value, unique in this run, that can be used to track" " the same logical command across multiple runs of the same protocol." " If a value is not provided, one will be generated." ), + json_schema_extra=_pop_default, ) @@ -143,8 +151,65 @@ class DefinedErrorData(Generic[_ErrorT_co]): ) +_ExecuteReturnT_co = TypeVar( + "_ExecuteReturnT_co", + bound=Union[ + SuccessData[BaseModel], + DefinedErrorData[ErrorOccurrence], + ], + covariant=True, +) + + +class AbstractCommandImpl( + ABC, + Generic[_ParamsT_contra, _ExecuteReturnT_co], +): + """Abstract command creation and execution implementation. + + A given command request should map to a specific command implementation, + which defines how to execute the command and map data from execution into the + result model. + """ + + def __init__( + self, + state_view: StateView, + hardware_api: HardwareControlAPI, + equipment: execution.EquipmentHandler, + file_provider: execution.FileProvider, + movement: execution.MovementHandler, + gantry_mover: execution.GantryMover, + labware_movement: execution.LabwareMovementHandler, + pipetting: execution.PipettingHandler, + tip_handler: execution.TipHandler, + run_control: execution.RunControlHandler, + rail_lights: execution.RailLightsHandler, + model_utils: ModelUtils, + status_bar: execution.StatusBarHandler, + command_note_adder: CommandNoteAdder, + ) -> None: + """Initialize the command implementation with execution handlers.""" + pass + + @abstractmethod + async def execute(self, params: _ParamsT_contra) -> _ExecuteReturnT_co: + """Execute the command, mapping data from execution into a response model. + + This should either: + + - Return a `SuccessData`, if the command completed normally. + - Return a `DefinedErrorData`, if the command failed with a "defined error." + Defined errors are errors that are documented as part of the robot's public + API. + - Raise an exception, if the command failed with any other error + (in other words, an undefined error). + """ + ... + + class BaseCommand( - GenericModel, + BaseModel, # These type parameters need to be invariant because our fields are mutable. Generic[_ParamsT, _ResultT, _ErrorT], ): @@ -241,60 +306,3 @@ class BaseCommand( ], ] ] - - -_ExecuteReturnT_co = TypeVar( - "_ExecuteReturnT_co", - bound=Union[ - SuccessData[BaseModel], - DefinedErrorData[ErrorOccurrence], - ], - covariant=True, -) - - -class AbstractCommandImpl( - ABC, - Generic[_ParamsT_contra, _ExecuteReturnT_co], -): - """Abstract command creation and execution implementation. - - A given command request should map to a specific command implementation, - which defines how to execute the command and map data from execution into the - result model. - """ - - def __init__( - self, - state_view: StateView, - hardware_api: HardwareControlAPI, - equipment: execution.EquipmentHandler, - file_provider: execution.FileProvider, - movement: execution.MovementHandler, - gantry_mover: execution.GantryMover, - labware_movement: execution.LabwareMovementHandler, - pipetting: execution.PipettingHandler, - tip_handler: execution.TipHandler, - run_control: execution.RunControlHandler, - rail_lights: execution.RailLightsHandler, - model_utils: ModelUtils, - status_bar: execution.StatusBarHandler, - command_note_adder: CommandNoteAdder, - ) -> None: - """Initialize the command implementation with execution handlers.""" - pass - - @abstractmethod - async def execute(self, params: _ParamsT_contra) -> _ExecuteReturnT_co: - """Execute the command, mapping data from execution into a response model. - - This should either: - - - Return a `SuccessData`, if the command completed normally. - - Return a `DefinedErrorData`, if the command failed with a "defined error." - Defined errors are errors that are documented as part of the robot's public - API. - - Raise an exception, if the command failed with any other error - (in other words, an undefined error). - """ - ... diff --git a/api/src/opentrons/protocol_engine/commands/command_unions.py b/api/src/opentrons/protocol_engine/commands/command_unions.py index 16663bf6df6..3f5bb09e510 100644 --- a/api/src/opentrons/protocol_engine/commands/command_unions.py +++ b/api/src/opentrons/protocol_engine/commands/command_unions.py @@ -3,7 +3,7 @@ from collections.abc import Collection from typing import Annotated, Type, Union, get_type_hints -from pydantic import Field +from pydantic import Field, TypeAdapter from opentrons.util.get_union_elements import get_union_elements @@ -686,6 +686,13 @@ Field(discriminator="commandType"), ] +# Each time a TypeAdapter is instantiated, it will construct a new validator and +# serializer. To improve performance, TypeAdapters are instantiated once. +# See https://docs.pydantic.dev/latest/concepts/performance/#typeadapter-instantiated-once +CommandCreateAdapter: TypeAdapter[CommandCreate] = TypeAdapter(CommandCreate) + +CommandAdapter: TypeAdapter[Command] = TypeAdapter(Command) + CommandResult = Union[ AirGapInPlaceResult, AspirateResult, diff --git a/api/src/opentrons/protocol_engine/commands/comment.py b/api/src/opentrons/protocol_engine/commands/comment.py index 5cd0b0c3113..f14a9a9992c 100644 --- a/api/src/opentrons/protocol_engine/commands/comment.py +++ b/api/src/opentrons/protocol_engine/commands/comment.py @@ -43,7 +43,7 @@ class Comment(BaseCommand[CommentParams, CommentResult, ErrorOccurrence]): commandType: CommentCommandType = "comment" params: CommentParams - result: Optional[CommentResult] + result: Optional[CommentResult] = None _ImplementationCls: Type[CommentImplementation] = CommentImplementation diff --git a/api/src/opentrons/protocol_engine/commands/configure_for_volume.py b/api/src/opentrons/protocol_engine/commands/configure_for_volume.py index 1c8aa21f491..50e1e7546bc 100644 --- a/api/src/opentrons/protocol_engine/commands/configure_for_volume.py +++ b/api/src/opentrons/protocol_engine/commands/configure_for_volume.py @@ -1,7 +1,9 @@ """Configure for volume command request, result, and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any + from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from .pipetting_common import PipetteIdMixin @@ -16,6 +18,10 @@ ConfigureForVolumeCommandType = Literal["configureForVolume"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class ConfigureForVolumeParams(PipetteIdMixin): """Parameters required to configure volume for a specific pipette.""" @@ -25,12 +31,13 @@ class ConfigureForVolumeParams(PipetteIdMixin): "than a pipette-specific maximum volume.", ge=0, ) - tipOverlapNotAfterVersion: Optional[str] = Field( + tipOverlapNotAfterVersion: str | SkipJsonSchema[None] = Field( None, description="A version of tip overlap data to not exceed. The highest-versioned " "tip overlap data that does not exceed this version will be used. Versions are " "expressed as vN where N is an integer, counting up from v0. If None, the current " "highest version will be used.", + json_schema_extra=_remove_default, ) @@ -81,7 +88,7 @@ class ConfigureForVolume( commandType: ConfigureForVolumeCommandType = "configureForVolume" params: ConfigureForVolumeParams - result: Optional[ConfigureForVolumeResult] + result: Optional[ConfigureForVolumeResult] = None _ImplementationCls: Type[ ConfigureForVolumeImplementation diff --git a/api/src/opentrons/protocol_engine/commands/configure_nozzle_layout.py b/api/src/opentrons/protocol_engine/commands/configure_nozzle_layout.py index f78839773ec..072307a0609 100644 --- a/api/src/opentrons/protocol_engine/commands/configure_nozzle_layout.py +++ b/api/src/opentrons/protocol_engine/commands/configure_nozzle_layout.py @@ -61,9 +61,11 @@ async def execute( self, params: ConfigureNozzleLayoutParams ) -> SuccessData[ConfigureNozzleLayoutResult]: """Check that requested pipette can support the requested nozzle layout.""" - primary_nozzle = params.configurationParams.dict().get("primaryNozzle") - front_right_nozzle = params.configurationParams.dict().get("frontRightNozzle") - back_left_nozzle = params.configurationParams.dict().get("backLeftNozzle") + primary_nozzle = params.configurationParams.model_dump().get("primaryNozzle") + front_right_nozzle = params.configurationParams.model_dump().get( + "frontRightNozzle" + ) + back_left_nozzle = params.configurationParams.model_dump().get("backLeftNozzle") nozzle_params = await self._tip_handler.available_for_nozzle_layout( pipette_id=params.pipetteId, style=params.configurationParams.style, @@ -97,7 +99,7 @@ class ConfigureNozzleLayout( commandType: ConfigureNozzleLayoutCommandType = "configureNozzleLayout" params: ConfigureNozzleLayoutParams - result: Optional[ConfigureNozzleLayoutResult] + result: Optional[ConfigureNozzleLayoutResult] = None _ImplementationCls: Type[ ConfigureNozzleLayoutImplementation diff --git a/api/src/opentrons/protocol_engine/commands/custom.py b/api/src/opentrons/protocol_engine/commands/custom.py index 1bdf07084be..b15b5cdb8d3 100644 --- a/api/src/opentrons/protocol_engine/commands/custom.py +++ b/api/src/opentrons/protocol_engine/commands/custom.py @@ -10,7 +10,7 @@ If you are implementing a custom command, you should probably put your own disambiguation identifier in the payload. """ -from pydantic import BaseModel, Extra +from pydantic import ConfigDict, BaseModel, SerializeAsAny from typing import Optional, Type from typing_extensions import Literal @@ -24,19 +24,13 @@ class CustomParams(BaseModel): """Payload used by a custom command.""" - class Config: - """Allow arbitrary fields.""" - - extra = Extra.allow + model_config = ConfigDict(extra="allow") class CustomResult(BaseModel): """Result data from a custom command.""" - class Config: - """Allow arbitrary fields.""" - - extra = Extra.allow + model_config = ConfigDict(extra="allow") class CustomImplementation( @@ -50,7 +44,7 @@ class CustomImplementation( async def execute(self, params: CustomParams) -> SuccessData[CustomResult]: """A custom command does nothing when executed directly.""" return SuccessData( - public=CustomResult.construct(), + public=CustomResult.model_construct(), ) @@ -58,8 +52,8 @@ class Custom(BaseCommand[CustomParams, CustomResult, ErrorOccurrence]): """Custom command model.""" commandType: CustomCommandType = "custom" - params: CustomParams - result: Optional[CustomResult] + params: SerializeAsAny[CustomParams] + result: Optional[CustomResult] = None _ImplementationCls: Type[CustomImplementation] = CustomImplementation diff --git a/api/src/opentrons/protocol_engine/commands/dispense.py b/api/src/opentrons/protocol_engine/commands/dispense.py index 18f157934d4..8ad2365ccb5 100644 --- a/api/src/opentrons/protocol_engine/commands/dispense.py +++ b/api/src/opentrons/protocol_engine/commands/dispense.py @@ -1,11 +1,12 @@ """Dispense command request, result, and implementation models.""" from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Type, Union +from typing import TYPE_CHECKING, Optional, Type, Union, Any from typing_extensions import Literal from pydantic import Field +from pydantic.json_schema import SkipJsonSchema from ..state.update_types import StateUpdate, CLEAR from .pipetting_common import ( @@ -39,14 +40,19 @@ DispenseCommandType = Literal["dispense"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class DispenseParams( PipetteIdMixin, DispenseVolumeMixin, FlowRateMixin, LiquidHandlingWellLocationMixin ): """Payload required to dispense to a specific well.""" - pushOut: Optional[float] = Field( + pushOut: float | SkipJsonSchema[None] = Field( None, description="push the plunger a small amount farther than necessary for accurate low-volume dispensing", + json_schema_extra=_remove_default, ) @@ -172,7 +178,7 @@ class Dispense( commandType: DispenseCommandType = "dispense" params: DispenseParams - result: Optional[DispenseResult] + result: Optional[DispenseResult] = None _ImplementationCls: Type[DispenseImplementation] = DispenseImplementation diff --git a/api/src/opentrons/protocol_engine/commands/dispense_in_place.py b/api/src/opentrons/protocol_engine/commands/dispense_in_place.py index fc1f9e19610..117aa011a84 100644 --- a/api/src/opentrons/protocol_engine/commands/dispense_in_place.py +++ b/api/src/opentrons/protocol_engine/commands/dispense_in_place.py @@ -1,9 +1,10 @@ """Dispense-in-place command request, result, and implementation models.""" from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Type, Union +from typing import TYPE_CHECKING, Optional, Type, Union, Any from typing_extensions import Literal from pydantic import Field +from pydantic.json_schema import SkipJsonSchema from .pipetting_common import ( PipetteIdMixin, @@ -32,12 +33,17 @@ DispenseInPlaceCommandType = Literal["dispenseInPlace"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class DispenseInPlaceParams(PipetteIdMixin, DispenseVolumeMixin, FlowRateMixin): """Payload required to dispense in place.""" - pushOut: Optional[float] = Field( + pushOut: float | SkipJsonSchema[None] = Field( None, description="push the plunger a small amount farther than necessary for accurate low-volume dispensing", + json_schema_extra=_remove_default, ) @@ -154,7 +160,7 @@ class DispenseInPlace( commandType: DispenseInPlaceCommandType = "dispenseInPlace" params: DispenseInPlaceParams - result: Optional[DispenseInPlaceResult] + result: Optional[DispenseInPlaceResult] = None _ImplementationCls: Type[ DispenseInPlaceImplementation diff --git a/api/src/opentrons/protocol_engine/commands/drop_tip.py b/api/src/opentrons/protocol_engine/commands/drop_tip.py index 4faee3d5e2f..2c393064eb6 100644 --- a/api/src/opentrons/protocol_engine/commands/drop_tip.py +++ b/api/src/opentrons/protocol_engine/commands/drop_tip.py @@ -1,9 +1,11 @@ """Drop tip command request, result, and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any from pydantic import Field -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema + from typing_extensions import Literal from opentrons.protocol_engine.errors.exceptions import TipAttachedError @@ -37,6 +39,10 @@ DropTipCommandType = Literal["dropTip"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class DropTipParams(PipetteIdMixin): """Payload required to drop a tip in a specific well.""" @@ -46,15 +52,16 @@ class DropTipParams(PipetteIdMixin): default_factory=DropTipWellLocation, description="Relative well location at which to drop the tip.", ) - homeAfter: Optional[bool] = Field( + homeAfter: bool | SkipJsonSchema[None] = Field( None, description=( "Whether to home this pipette's plunger after dropping the tip." " You should normally leave this unspecified to let the robot choose" " a safe default depending on its hardware." ), + json_schema_extra=_remove_default, ) - alternateDropLocation: Optional[bool] = Field( + alternateDropLocation: bool | SkipJsonSchema[None] = Field( False, description=( "Whether to alternate location where tip is dropped within the labware." @@ -63,6 +70,7 @@ class DropTipParams(PipetteIdMixin): " labware well." " If False, the tip will be dropped at the top center of the well." ), + json_schema_extra=_remove_default, ) @@ -184,7 +192,7 @@ class DropTip( commandType: DropTipCommandType = "dropTip" params: DropTipParams - result: Optional[DropTipResult] + result: Optional[DropTipResult] = None _ImplementationCls: Type[DropTipImplementation] = DropTipImplementation diff --git a/api/src/opentrons/protocol_engine/commands/drop_tip_in_place.py b/api/src/opentrons/protocol_engine/commands/drop_tip_in_place.py index 8687382b53f..09bd73b8bb1 100644 --- a/api/src/opentrons/protocol_engine/commands/drop_tip_in_place.py +++ b/api/src/opentrons/protocol_engine/commands/drop_tip_in_place.py @@ -1,7 +1,10 @@ """Drop tip in place command request, result, and implementation models.""" from __future__ import annotations + +from typing import TYPE_CHECKING, Optional, Type, Any + from pydantic import Field, BaseModel -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from .command import ( @@ -24,16 +27,21 @@ DropTipInPlaceCommandType = Literal["dropTipInPlace"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class DropTipInPlaceParams(PipetteIdMixin): """Payload required to drop a tip in place.""" - homeAfter: Optional[bool] = Field( + homeAfter: bool | SkipJsonSchema[None] = Field( None, description=( "Whether to home this pipette's plunger after dropping the tip." " You should normally leave this unspecified to let the robot choose" " a safe default depending on its hardware." ), + json_schema_extra=_remove_default, ) @@ -112,7 +120,7 @@ class DropTipInPlace( commandType: DropTipInPlaceCommandType = "dropTipInPlace" params: DropTipInPlaceParams - result: Optional[DropTipInPlaceResult] + result: Optional[DropTipInPlaceResult] = None _ImplementationCls: Type[ DropTipInPlaceImplementation diff --git a/api/src/opentrons/protocol_engine/commands/generate_command_schema.py b/api/src/opentrons/protocol_engine/commands/generate_command_schema.py index acc0923bcdf..938244b74e8 100644 --- a/api/src/opentrons/protocol_engine/commands/generate_command_schema.py +++ b/api/src/opentrons/protocol_engine/commands/generate_command_schema.py @@ -1,24 +1,17 @@ """Generate a JSON schema against which all create commands statically validate.""" + import json -import pydantic import argparse import sys -from opentrons.protocol_engine.commands.command_unions import CommandCreate - - -class CreateCommandUnion(pydantic.BaseModel): - """Model that validates a union of all CommandCreate models.""" - - __root__: CommandCreate +from opentrons.protocol_engine.commands.command_unions import CommandCreateAdapter def generate_command_schema(version: str) -> str: """Generate a JSON Schema that all valid create commands can validate against.""" - raw_json_schema = CreateCommandUnion.schema_json() - schema_as_dict = json.loads(raw_json_schema) + schema_as_dict = CommandCreateAdapter.json_schema(mode="validation") schema_as_dict["$id"] = f"opentronsCommandSchemaV{version}" schema_as_dict["$schema"] = "http://json-schema.org/draft-07/schema#" - return json.dumps(schema_as_dict, indent=2) + return json.dumps(schema_as_dict, indent=2, sort_keys=True) if __name__ == "__main__": diff --git a/api/src/opentrons/protocol_engine/commands/get_next_tip.py b/api/src/opentrons/protocol_engine/commands/get_next_tip.py index 7ff10681bfb..f9f2cb8ba61 100644 --- a/api/src/opentrons/protocol_engine/commands/get_next_tip.py +++ b/api/src/opentrons/protocol_engine/commands/get_next_tip.py @@ -2,7 +2,9 @@ from __future__ import annotations from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type, List, Literal, Union +from typing import TYPE_CHECKING, Any, Optional, Type, List, Literal, Union + +from pydantic.json_schema import SkipJsonSchema from opentrons.types import NozzleConfigurationType @@ -21,6 +23,10 @@ from ..state.state import StateView +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + GetNextTipCommandType = Literal["getNextTip"] @@ -32,10 +38,11 @@ class GetNextTipParams(PipetteIdMixin): description="Labware ID(s) of tip racks to resolve next available tip(s) from" " Labware IDs will be resolved sequentially", ) - startingTipWell: Optional[str] = Field( + startingTipWell: str | SkipJsonSchema[None] = Field( None, description="Name of starting tip rack 'well'." " This only applies to the first tip rack in the list provided in labwareIDs", + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/get_tip_presence.py b/api/src/opentrons/protocol_engine/commands/get_tip_presence.py index 6bbe5fa2fe3..05f9e1052c1 100644 --- a/api/src/opentrons/protocol_engine/commands/get_tip_presence.py +++ b/api/src/opentrons/protocol_engine/commands/get_tip_presence.py @@ -71,7 +71,7 @@ class GetTipPresence( commandType: GetTipPresenceCommandType = "getTipPresence" params: GetTipPresenceParams - result: Optional[GetTipPresenceResult] + result: Optional[GetTipPresenceResult] = None _ImplementationCls: Type[ GetTipPresenceImplementation diff --git a/api/src/opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py b/api/src/opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py index 2151fb05877..211b374be7e 100644 --- a/api/src/opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +++ b/api/src/opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py @@ -69,7 +69,7 @@ class CloseLabwareLatch( commandType: CloseLabwareLatchCommandType = "heaterShaker/closeLabwareLatch" params: CloseLabwareLatchParams - result: Optional[CloseLabwareLatchResult] + result: Optional[CloseLabwareLatchResult] = None _ImplementationCls: Type[CloseLabwareLatchImpl] = CloseLabwareLatchImpl diff --git a/api/src/opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py b/api/src/opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py index 3932f1d6490..6ac76f020d3 100644 --- a/api/src/opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +++ b/api/src/opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py @@ -68,7 +68,7 @@ class DeactivateHeater( commandType: DeactivateHeaterCommandType = "heaterShaker/deactivateHeater" params: DeactivateHeaterParams - result: Optional[DeactivateHeaterResult] + result: Optional[DeactivateHeaterResult] = None _ImplementationCls: Type[DeactivateHeaterImpl] = DeactivateHeaterImpl diff --git a/api/src/opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py b/api/src/opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py index b259745ea3d..e7e50b07fee 100644 --- a/api/src/opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +++ b/api/src/opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py @@ -70,7 +70,7 @@ class DeactivateShaker( commandType: DeactivateShakerCommandType = "heaterShaker/deactivateShaker" params: DeactivateShakerParams - result: Optional[DeactivateShakerResult] + result: Optional[DeactivateShakerResult] = None _ImplementationCls: Type[DeactivateShakerImpl] = DeactivateShakerImpl diff --git a/api/src/opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py b/api/src/opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py index 9c3a9d8ae7d..8bb869c47ee 100644 --- a/api/src/opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +++ b/api/src/opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py @@ -96,7 +96,7 @@ class OpenLabwareLatch( commandType: OpenLabwareLatchCommandType = "heaterShaker/openLabwareLatch" params: OpenLabwareLatchParams - result: Optional[OpenLabwareLatchResult] + result: Optional[OpenLabwareLatchResult] = None _ImplementationCls: Type[OpenLabwareLatchImpl] = OpenLabwareLatchImpl diff --git a/api/src/opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py b/api/src/opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py index 8828195c658..2bcf1d48ba3 100644 --- a/api/src/opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +++ b/api/src/opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py @@ -109,7 +109,7 @@ class SetAndWaitForShakeSpeed( "heaterShaker/setAndWaitForShakeSpeed" ) params: SetAndWaitForShakeSpeedParams - result: Optional[SetAndWaitForShakeSpeedResult] + result: Optional[SetAndWaitForShakeSpeedResult] = None _ImplementationCls: Type[SetAndWaitForShakeSpeedImpl] = SetAndWaitForShakeSpeedImpl diff --git a/api/src/opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py b/api/src/opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py index fa29390b910..f0b0533aca3 100644 --- a/api/src/opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +++ b/api/src/opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py @@ -76,7 +76,7 @@ class SetTargetTemperature( commandType: SetTargetTemperatureCommandType = "heaterShaker/setTargetTemperature" params: SetTargetTemperatureParams - result: Optional[SetTargetTemperatureResult] + result: Optional[SetTargetTemperatureResult] = None _ImplementationCls: Type[SetTargetTemperatureImpl] = SetTargetTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py b/api/src/opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py index bb440a2674c..676fbcd4bfb 100644 --- a/api/src/opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +++ b/api/src/opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py @@ -1,9 +1,10 @@ """Command models to wait for a Heater-Shaker Module's target temperature.""" from __future__ import annotations -from typing import Optional, TYPE_CHECKING -from typing_extensions import Literal, Type +from typing import Optional, TYPE_CHECKING, Any +from typing_extensions import Literal, Type from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ...errors.error_occurrence import ErrorOccurrence @@ -16,11 +17,15 @@ WaitForTemperatureCommandType = Literal["heaterShaker/waitForTemperature"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class WaitForTemperatureParams(BaseModel): """Input parameters to wait for a Heater-Shaker's target temperature.""" moduleId: str = Field(..., description="Unique ID of the Heater-Shaker Module.") - celsius: Optional[float] = Field( + celsius: float | SkipJsonSchema[None] = Field( None, description="Target temperature in °C. If not specified, will " "default to the module's target temperature. " @@ -28,6 +33,7 @@ class WaitForTemperatureParams(BaseModel): "could lead to unpredictable behavior and hence is not " "recommended for use. This parameter can be removed in a " "future version without prior notice.", + json_schema_extra=_remove_default, ) @@ -82,7 +88,7 @@ class WaitForTemperature( commandType: WaitForTemperatureCommandType = "heaterShaker/waitForTemperature" params: WaitForTemperatureParams - result: Optional[WaitForTemperatureResult] + result: Optional[WaitForTemperatureResult] = None _ImplementationCls: Type[WaitForTemperatureImpl] = WaitForTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/home.py b/api/src/opentrons/protocol_engine/commands/home.py index 7b82f90e1fd..f096740958f 100644 --- a/api/src/opentrons/protocol_engine/commands/home.py +++ b/api/src/opentrons/protocol_engine/commands/home.py @@ -1,7 +1,10 @@ """Home command payload, result, and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, List, Type, Any + from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, List, Type +from pydantic.json_schema import SkipJsonSchema + from typing_extensions import Literal from opentrons.types import MountType @@ -17,24 +20,30 @@ HomeCommandType = Literal["home"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class HomeParams(BaseModel): """Payload required for a Home command.""" - axes: Optional[List[MotorAxis]] = Field( + axes: List[MotorAxis] | SkipJsonSchema[None] = Field( None, description=( "Axes to return to their home positions. If omitted," " will home all motors. Extra axes may be implicitly homed" " to ensure accurate homing of the explicitly specified axes." ), + json_schema_extra=_remove_default, ) - skipIfMountPositionOk: Optional[MountType] = Field( + skipIfMountPositionOk: MountType | SkipJsonSchema[None] = Field( None, description=( "If this parameter is provided, the gantry will only be homed if the" " specified mount has an invalid position. If omitted, the homing action" " will be executed unconditionally." ), + json_schema_extra=_remove_default, ) @@ -77,7 +86,7 @@ class Home(BaseCommand[HomeParams, HomeResult, ErrorOccurrence]): commandType: HomeCommandType = "home" params: HomeParams - result: Optional[HomeResult] + result: Optional[HomeResult] = None _ImplementationCls: Type[HomeImplementation] = HomeImplementation diff --git a/api/src/opentrons/protocol_engine/commands/liquid_probe.py b/api/src/opentrons/protocol_engine/commands/liquid_probe.py index 1bf58e8be26..a0418d53c9a 100644 --- a/api/src/opentrons/protocol_engine/commands/liquid_probe.py +++ b/api/src/opentrons/protocol_engine/commands/liquid_probe.py @@ -1,10 +1,11 @@ """The liquidProbe and tryLiquidProbe commands.""" from __future__ import annotations -from typing import TYPE_CHECKING, NamedTuple, Optional, Type, Union -from typing_extensions import Literal +from typing import TYPE_CHECKING, NamedTuple, Optional, Type, Union, Any +from typing_extensions import Literal from pydantic import Field +from pydantic.json_schema import SkipJsonSchema from opentrons.protocol_engine.state import update_types from opentrons.protocol_engine.errors.exceptions import ( @@ -47,6 +48,10 @@ from ..state.state import StateView +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + LiquidProbeCommandType = Literal["liquidProbe"] TryLiquidProbeCommandType = Literal["tryLiquidProbe"] @@ -82,12 +87,13 @@ class LiquidProbeResult(DestinationPositionResult): class TryLiquidProbeResult(DestinationPositionResult): """Result data from the execution of a `tryLiquidProbe` command.""" - z_position: Optional[float] = Field( + z_position: float | SkipJsonSchema[None] = Field( ..., description=( "The Z coordinate, in mm, of the found liquid in deck space." " If no liquid was found, `null` or omitted." ), + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/load_labware.py b/api/src/opentrons/protocol_engine/commands/load_labware.py index 2d8b8c3df78..6b65fe239e4 100644 --- a/api/src/opentrons/protocol_engine/commands/load_labware.py +++ b/api/src/opentrons/protocol_engine/commands/load_labware.py @@ -1,7 +1,9 @@ """Load labware command request, result, and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any + from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from opentrons_shared_data.labware.labware_definition import LabwareDefinition @@ -29,6 +31,10 @@ LoadLabwareCommandType = Literal["loadLabware"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class LoadLabwareParams(BaseModel): """Payload required to load a labware into a slot.""" @@ -48,18 +54,20 @@ class LoadLabwareParams(BaseModel): ..., description="The labware definition version.", ) - labwareId: Optional[str] = Field( + labwareId: str | SkipJsonSchema[None] = Field( None, description="An optional ID to assign to this labware. If None, an ID " "will be generated.", + json_schema_extra=_remove_default, ) - displayName: Optional[str] = Field( + displayName: str | SkipJsonSchema[None] = Field( None, description="An optional user-specified display name " "or label for this labware.", # NOTE: v4/5 JSON protocols will always have a displayName which will be the # user-specified label OR the displayName property of the labware's definition. # TODO: Make sure v6 JSON protocols don't do that. + json_schema_extra=_remove_default, ) @@ -187,7 +195,7 @@ class LoadLabware(BaseCommand[LoadLabwareParams, LoadLabwareResult, ErrorOccurre commandType: LoadLabwareCommandType = "loadLabware" params: LoadLabwareParams - result: Optional[LoadLabwareResult] + result: Optional[LoadLabwareResult] = None _ImplementationCls: Type[LoadLabwareImplementation] = LoadLabwareImplementation diff --git a/api/src/opentrons/protocol_engine/commands/load_liquid.py b/api/src/opentrons/protocol_engine/commands/load_liquid.py index f6aa037fa01..a1ed1e8401c 100644 --- a/api/src/opentrons/protocol_engine/commands/load_liquid.py +++ b/api/src/opentrons/protocol_engine/commands/load_liquid.py @@ -81,7 +81,7 @@ class LoadLiquid(BaseCommand[LoadLiquidParams, LoadLiquidResult, ErrorOccurrence commandType: LoadLiquidCommandType = "loadLiquid" params: LoadLiquidParams - result: Optional[LoadLiquidResult] + result: Optional[LoadLiquidResult] = None _ImplementationCls: Type[LoadLiquidImplementation] = LoadLiquidImplementation diff --git a/api/src/opentrons/protocol_engine/commands/load_liquid_class.py b/api/src/opentrons/protocol_engine/commands/load_liquid_class.py index bd267abe567..8cb3d4a06fb 100644 --- a/api/src/opentrons/protocol_engine/commands/load_liquid_class.py +++ b/api/src/opentrons/protocol_engine/commands/load_liquid_class.py @@ -1,9 +1,11 @@ """LoadLiquidClass stores the liquid class settings used for a transfer into the Protocol Engine.""" from __future__ import annotations -from typing import Optional, Type, TYPE_CHECKING +from typing import Optional, Type, TYPE_CHECKING, Any + from typing_extensions import Literal from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ..errors import LiquidClassDoesNotExistError @@ -19,13 +21,18 @@ LoadLiquidClassCommandType = Literal["loadLiquidClass"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class LoadLiquidClassParams(BaseModel): """The liquid class transfer properties to store.""" - liquidClassId: Optional[str] = Field( + liquidClassId: str | SkipJsonSchema[None] = Field( None, description="Unique identifier for the liquid class to store. " "If you do not supply a liquidClassId, we will generate one.", + json_schema_extra=_remove_default, ) liquidClassRecord: LiquidClassRecord = Field( ..., diff --git a/api/src/opentrons/protocol_engine/commands/load_module.py b/api/src/opentrons/protocol_engine/commands/load_module.py index f8b88e08814..a0f4736a3a4 100644 --- a/api/src/opentrons/protocol_engine/commands/load_module.py +++ b/api/src/opentrons/protocol_engine/commands/load_module.py @@ -1,8 +1,9 @@ """Implementation, request models, and response models for the load module command.""" from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Type +from typing import TYPE_CHECKING, Optional, Type, Any from typing_extensions import Literal from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from opentrons.protocol_engine.state.update_types import StateUpdate @@ -27,6 +28,10 @@ LoadModuleCommandType = Literal["loadModule"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class LoadModuleParams(BaseModel): """Payload required to load a module.""" @@ -59,12 +64,13 @@ class LoadModuleParams(BaseModel): ), ) - moduleId: Optional[str] = Field( + moduleId: str | SkipJsonSchema[None] = Field( None, description=( "An optional ID to assign to this module." " If None, an ID will be generated." ), + json_schema_extra=_remove_default, ) @@ -77,7 +83,7 @@ class LoadModuleResult(BaseModel): # TODO(mm, 2023-04-13): Remove this field. Jira RSS-221. definition: ModuleDefinition = Field( - deprecated=True, + json_schema_extra={"deprecated": True}, description=( "The definition of the connected module." " This field is an implementation detail. We might change or remove it without warning." @@ -204,7 +210,7 @@ class LoadModule(BaseCommand[LoadModuleParams, LoadModuleResult, ErrorOccurrence commandType: LoadModuleCommandType = "loadModule" params: LoadModuleParams - result: Optional[LoadModuleResult] + result: Optional[LoadModuleResult] = None _ImplementationCls: Type[LoadModuleImplementation] = LoadModuleImplementation diff --git a/api/src/opentrons/protocol_engine/commands/load_pipette.py b/api/src/opentrons/protocol_engine/commands/load_pipette.py index 6d8d74b4fa2..812299a6da1 100644 --- a/api/src/opentrons/protocol_engine/commands/load_pipette.py +++ b/api/src/opentrons/protocol_engine/commands/load_pipette.py @@ -1,20 +1,23 @@ """Load pipette command request, result, and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any + +from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema +from typing_extensions import Literal -from opentrons.protocol_engine.state.update_types import StateUpdate from opentrons_shared_data.pipette.pipette_load_name_conversions import ( convert_to_pipette_name_type, ) from opentrons_shared_data.pipette.types import PipetteGenerationType from opentrons_shared_data.robot import user_facing_robot_type from opentrons_shared_data.robot.types import RobotTypeEnum -from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type -from typing_extensions import Literal + from opentrons_shared_data.pipette.types import PipetteNameType from opentrons.types import MountType +from opentrons.protocol_engine.state.update_types import StateUpdate from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ..errors.error_occurrence import ErrorOccurrence from ..errors import InvalidSpecificationForRobotTypeError, InvalidLoadPipetteSpecsError @@ -27,6 +30,10 @@ LoadPipetteCommandType = Literal["loadPipette"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class LoadPipetteParams(BaseModel): """Payload needed to load a pipette on to a mount.""" @@ -38,21 +45,24 @@ class LoadPipetteParams(BaseModel): ..., description="The mount the pipette should be present on.", ) - pipetteId: Optional[str] = Field( + pipetteId: str | SkipJsonSchema[None] = Field( None, description="An optional ID to assign to this pipette. If None, an ID " "will be generated.", + json_schema_extra=_remove_default, ) - tipOverlapNotAfterVersion: Optional[str] = Field( + tipOverlapNotAfterVersion: str | SkipJsonSchema[None] = Field( None, description="A version of tip overlap data to not exceed. The highest-versioned " "tip overlap data that does not exceed this version will be used. Versions are " "expressed as vN where N is an integer, counting up from v0. If None, the current " "highest version will be used.", + json_schema_extra=_remove_default, ) - liquidPresenceDetection: Optional[bool] = Field( + liquidPresenceDetection: bool | SkipJsonSchema[None] = Field( None, description="Enable liquid presence detection for this pipette. Defaults to False.", + json_schema_extra=_remove_default, ) @@ -140,7 +150,7 @@ class LoadPipette(BaseCommand[LoadPipetteParams, LoadPipetteResult, ErrorOccurre commandType: LoadPipetteCommandType = "loadPipette" params: LoadPipetteParams - result: Optional[LoadPipetteResult] + result: Optional[LoadPipetteResult] = None _ImplementationCls: Type[LoadPipetteImplementation] = LoadPipetteImplementation diff --git a/api/src/opentrons/protocol_engine/commands/magnetic_module/disengage.py b/api/src/opentrons/protocol_engine/commands/magnetic_module/disengage.py index c20b18e481d..d0c7abef5a4 100644 --- a/api/src/opentrons/protocol_engine/commands/magnetic_module/disengage.py +++ b/api/src/opentrons/protocol_engine/commands/magnetic_module/disengage.py @@ -83,7 +83,7 @@ class Disengage(BaseCommand[DisengageParams, DisengageResult, ErrorOccurrence]): commandType: DisengageCommandType = "magneticModule/disengage" params: DisengageParams - result: Optional[DisengageResult] + result: Optional[DisengageResult] = None _ImplementationCls: Type[DisengageImplementation] = DisengageImplementation diff --git a/api/src/opentrons/protocol_engine/commands/magnetic_module/engage.py b/api/src/opentrons/protocol_engine/commands/magnetic_module/engage.py index 62f4e24eef4..6686d88edba 100644 --- a/api/src/opentrons/protocol_engine/commands/magnetic_module/engage.py +++ b/api/src/opentrons/protocol_engine/commands/magnetic_module/engage.py @@ -105,7 +105,7 @@ class Engage(BaseCommand[EngageParams, EngageResult, ErrorOccurrence]): commandType: EngageCommandType = "magneticModule/engage" params: EngageParams - result: Optional[EngageResult] + result: Optional[EngageResult] = None _ImplementationCls: Type[EngageImplementation] = EngageImplementation diff --git a/api/src/opentrons/protocol_engine/commands/move_labware.py b/api/src/opentrons/protocol_engine/commands/move_labware.py index b64491f5192..8eb93ce9217 100644 --- a/api/src/opentrons/protocol_engine/commands/move_labware.py +++ b/api/src/opentrons/protocol_engine/commands/move_labware.py @@ -1,14 +1,17 @@ """Models and implementation for the ``moveLabware`` command.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any + +from pydantic.json_schema import SkipJsonSchema +from pydantic import BaseModel, Field +from typing_extensions import Literal + from opentrons_shared_data.errors.exceptions import ( FailedGripperPickupError, LabwareDroppedError, StallOrCollisionDetectedError, ) -from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type -from typing_extensions import Literal from opentrons.protocol_engine.resources.model_utils import ModelUtils from opentrons.types import Point @@ -49,6 +52,10 @@ MoveLabwareCommandType = Literal["moveLabware"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + # Extra buffer on top of minimum distance to move to the right _TRASH_CHUTE_DROP_BUFFER_MM = 8 @@ -63,15 +70,17 @@ class MoveLabwareParams(BaseModel): description="Whether to use the gripper to perform the labware movement" " or to perform a manual movement with an option to pause.", ) - pickUpOffset: Optional[LabwareOffsetVector] = Field( + pickUpOffset: LabwareOffsetVector | SkipJsonSchema[None] = Field( None, description="Offset to use when picking up labware. " "Experimental param, subject to change", + json_schema_extra=_remove_default, ) - dropOffset: Optional[LabwareOffsetVector] = Field( + dropOffset: LabwareOffsetVector | SkipJsonSchema[None] = Field( None, description="Offset to use when dropping off labware. " "Experimental param, subject to change", + json_schema_extra=_remove_default, ) @@ -359,7 +368,7 @@ class MoveLabware( commandType: MoveLabwareCommandType = "moveLabware" params: MoveLabwareParams - result: Optional[MoveLabwareResult] + result: Optional[MoveLabwareResult] = None _ImplementationCls: Type[MoveLabwareImplementation] = MoveLabwareImplementation diff --git a/api/src/opentrons/protocol_engine/commands/move_relative.py b/api/src/opentrons/protocol_engine/commands/move_relative.py index 54c877a3693..d54f9470184 100644 --- a/api/src/opentrons/protocol_engine/commands/move_relative.py +++ b/api/src/opentrons/protocol_engine/commands/move_relative.py @@ -88,7 +88,7 @@ class MoveRelative( commandType: MoveRelativeCommandType = "moveRelative" params: MoveRelativeParams - result: Optional[MoveRelativeResult] + result: Optional[MoveRelativeResult] = None _ImplementationCls: Type[MoveRelativeImplementation] = MoveRelativeImplementation diff --git a/api/src/opentrons/protocol_engine/commands/move_to_addressable_area.py b/api/src/opentrons/protocol_engine/commands/move_to_addressable_area.py index 7380a01951a..2ac788b33cf 100644 --- a/api/src/opentrons/protocol_engine/commands/move_to_addressable_area.py +++ b/api/src/opentrons/protocol_engine/commands/move_to_addressable_area.py @@ -160,7 +160,7 @@ class MoveToAddressableArea( commandType: MoveToAddressableAreaCommandType = "moveToAddressableArea" params: MoveToAddressableAreaParams - result: Optional[MoveToAddressableAreaResult] + result: Optional[MoveToAddressableAreaResult] = None _ImplementationCls: Type[ MoveToAddressableAreaImplementation diff --git a/api/src/opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py b/api/src/opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py index 679e769cc2e..f4afcd5d1ff 100644 --- a/api/src/opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +++ b/api/src/opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py @@ -1,9 +1,11 @@ """Move to addressable area for drop tip command request, result, and implementation models.""" from __future__ import annotations -from pydantic import Field -from typing import TYPE_CHECKING, Optional, Type +from typing import TYPE_CHECKING, Optional, Type, Any from typing_extensions import Literal +from pydantic import Field +from pydantic.json_schema import SkipJsonSchema + from ..errors import LocationNotAccessibleByPipetteError from ..types import AddressableOffsetVector from ..resources import fixture_validation @@ -32,6 +34,10 @@ MoveToAddressableAreaForDropTipCommandType = Literal["moveToAddressableAreaForDropTip"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class MoveToAddressableAreaForDropTipParams(PipetteIdMixin, MovementMixin): """Payload required to move a pipette to a specific addressable area. @@ -65,7 +71,7 @@ class MoveToAddressableAreaForDropTipParams(PipetteIdMixin, MovementMixin): AddressableOffsetVector(x=0, y=0, z=0), description="Relative offset of addressable area to move pipette's critical point.", ) - alternateDropLocation: Optional[bool] = Field( + alternateDropLocation: bool | SkipJsonSchema[None] = Field( False, description=( "Whether to alternate location where tip is dropped within the addressable area." @@ -74,8 +80,9 @@ class MoveToAddressableAreaForDropTipParams(PipetteIdMixin, MovementMixin): " labware well." " If False, the tip will be dropped at the top center of the area." ), + json_schema_extra=_remove_default, ) - ignoreTipConfiguration: Optional[bool] = Field( + ignoreTipConfiguration: bool | SkipJsonSchema[None] = Field( True, description=( "Whether to utilize the critical point of the tip configuraiton when moving to an addressable area." @@ -83,6 +90,7 @@ class MoveToAddressableAreaForDropTipParams(PipetteIdMixin, MovementMixin): " as the critical point for movement." " If False, this command will use the critical point provided by the current tip configuration." ), + json_schema_extra=_remove_default, ) @@ -170,7 +178,7 @@ class MoveToAddressableAreaForDropTip( "moveToAddressableAreaForDropTip" ) params: MoveToAddressableAreaForDropTipParams - result: Optional[MoveToAddressableAreaForDropTipResult] + result: Optional[MoveToAddressableAreaForDropTipResult] = None _ImplementationCls: Type[ MoveToAddressableAreaForDropTipImplementation diff --git a/api/src/opentrons/protocol_engine/commands/move_to_coordinates.py b/api/src/opentrons/protocol_engine/commands/move_to_coordinates.py index 36b7ff64ed0..3493f5d6ea6 100644 --- a/api/src/opentrons/protocol_engine/commands/move_to_coordinates.py +++ b/api/src/opentrons/protocol_engine/commands/move_to_coordinates.py @@ -91,7 +91,7 @@ class MoveToCoordinates( commandType: MoveToCoordinatesCommandType = "moveToCoordinates" params: MoveToCoordinatesParams - result: Optional[MoveToCoordinatesResult] + result: Optional[MoveToCoordinatesResult] = None _ImplementationCls: Type[ MoveToCoordinatesImplementation diff --git a/api/src/opentrons/protocol_engine/commands/move_to_well.py b/api/src/opentrons/protocol_engine/commands/move_to_well.py index 6aaf398650f..73ebfbff638 100644 --- a/api/src/opentrons/protocol_engine/commands/move_to_well.py +++ b/api/src/opentrons/protocol_engine/commands/move_to_well.py @@ -106,7 +106,7 @@ class MoveToWell( commandType: MoveToWellCommandType = "moveToWell" params: MoveToWellParams - result: Optional[MoveToWellResult] + result: Optional[MoveToWellResult] = None _ImplementationCls: Type[MoveToWellImplementation] = MoveToWellImplementation diff --git a/api/src/opentrons/protocol_engine/commands/movement_common.py b/api/src/opentrons/protocol_engine/commands/movement_common.py index ca12d2d1ad8..babf70b29d9 100644 --- a/api/src/opentrons/protocol_engine/commands/movement_common.py +++ b/api/src/opentrons/protocol_engine/commands/movement_common.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Optional, Union, TYPE_CHECKING, Literal +from typing import Optional, Union, TYPE_CHECKING, Literal, Any from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from opentrons_shared_data.errors import ErrorCodes from opentrons_shared_data.errors.exceptions import StallOrCollisionDetectedError @@ -26,6 +27,10 @@ from ..resources.model_utils import ModelUtils +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class WellLocationMixin(BaseModel): """Mixin for command requests that take a location that's somewhere in a well.""" @@ -63,13 +68,14 @@ class LiquidHandlingWellLocationMixin(BaseModel): class MovementMixin(BaseModel): """Mixin for command requests that move a pipette.""" - minimumZHeight: Optional[float] = Field( + minimumZHeight: float | SkipJsonSchema[None] = Field( None, description=( "Optional minimal Z margin in mm." " If this is larger than the API's default safe Z margin," " it will make the arc higher. If it's smaller, it will have no effect." ), + json_schema_extra=_remove_default, ) forceDirect: bool = Field( @@ -83,12 +89,13 @@ class MovementMixin(BaseModel): ), ) - speed: Optional[float] = Field( + speed: float | SkipJsonSchema[None] = Field( None, description=( "Override the travel speed in mm/s." " This controls the straight linear speed of motion." ), + json_schema_extra=_remove_default, ) @@ -175,7 +182,7 @@ async def move_to_well( state_update=StateUpdate().clear_all_pipette_locations(), ) else: - deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z) + deck_point = DeckPoint.model_construct(x=position.x, y=position.y, z=position.z) return SuccessData( public=DestinationPositionResult( position=deck_point, @@ -215,7 +222,7 @@ async def move_relative( state_update=StateUpdate().clear_all_pipette_locations(), ) else: - deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z) + deck_point = DeckPoint.model_construct(x=position.x, y=position.y, z=position.z) return SuccessData( public=DestinationPositionResult( position=deck_point, @@ -270,7 +277,7 @@ async def move_to_addressable_area( .set_addressable_area_used(addressable_area_name=addressable_area_name), ) else: - deck_point = DeckPoint.construct(x=x, y=y, z=z) + deck_point = DeckPoint.model_construct(x=x, y=y, z=z) return SuccessData( public=DestinationPositionResult(position=deck_point), state_update=StateUpdate() @@ -317,7 +324,7 @@ async def move_to_coordinates( state_update=StateUpdate().clear_all_pipette_locations(), ) else: - deck_point = DeckPoint.construct(x=x, y=y, z=z) + deck_point = DeckPoint.model_construct(x=x, y=y, z=z) return SuccessData( public=DestinationPositionResult(position=DeckPoint(x=x, y=y, z=z)), diff --git a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py index af8723a5bba..e5612bb3cdc 100644 --- a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py +++ b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py @@ -212,7 +212,7 @@ class PickUpTip( commandType: PickUpTipCommandType = "pickUpTip" params: PickUpTipParams - result: Optional[PickUpTipResult] + result: Optional[PickUpTipResult] = None _ImplementationCls: Type[PickUpTipImplementation] = PickUpTipImplementation diff --git a/api/src/opentrons/protocol_engine/commands/pipetting_common.py b/api/src/opentrons/protocol_engine/commands/pipetting_common.py index 0292b51eee1..c373642a02e 100644 --- a/api/src/opentrons/protocol_engine/commands/pipetting_common.py +++ b/api/src/opentrons/protocol_engine/commands/pipetting_common.py @@ -1,10 +1,12 @@ """Common pipetting command base models.""" from __future__ import annotations -from opentrons_shared_data.errors import ErrorCodes +from typing import Literal, Tuple, TYPE_CHECKING + +from typing_extensions import TypedDict from pydantic import BaseModel, Field -from typing import Literal, Tuple, TypedDict, TYPE_CHECKING +from opentrons_shared_data.errors import ErrorCodes from opentrons.protocol_engine.errors.error_occurrence import ErrorOccurrence from opentrons.protocol_engine.types import AspiratedFluid, FluidKind from opentrons_shared_data.errors.exceptions import PipetteOverpressureError @@ -71,6 +73,10 @@ class BaseLiquidHandlingResult(BaseModel): ) +class EmptyResult(BaseModel): + """A result with no data.""" + + class ErrorLocationInfo(TypedDict): """Holds a retry location for in-place error recovery. @@ -139,7 +145,7 @@ async def prepare_for_aspirate( pipetting: PipettingHandler, model_utils: ModelUtils, location_if_error: ErrorLocationInfo, -) -> SuccessData[BaseModel] | DefinedErrorData[OverpressureError]: +) -> SuccessData[EmptyResult] | DefinedErrorData[OverpressureError]: """Execute pipetting.prepare_for_aspirate, handle errors, and marshal success.""" try: await pipetting.prepare_for_aspirate(pipette_id) @@ -161,7 +167,7 @@ async def prepare_for_aspirate( ) else: return SuccessData( - public=BaseModel(), + public=EmptyResult(), state_update=StateUpdate().set_fluid_empty(pipette_id=pipette_id), ) @@ -259,7 +265,7 @@ async def blow_out_in_place( location_if_error: ErrorLocationInfo, pipetting: PipettingHandler, model_utils: ModelUtils, -) -> SuccessData[BaseModel] | DefinedErrorData[OverpressureError]: +) -> SuccessData[EmptyResult] | DefinedErrorData[OverpressureError]: """Execute a blow-out-in-place micro-operation.""" try: await pipetting.blow_out_in_place(pipette_id=pipette_id, flow_rate=flow_rate) @@ -281,6 +287,6 @@ async def blow_out_in_place( ) else: return SuccessData( - public=BaseModel(), + public=EmptyResult(), state_update=StateUpdate().set_fluid_empty(pipette_id=pipette_id), ) diff --git a/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py b/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py index cabcb2039eb..beaf6e1ca0c 100644 --- a/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py +++ b/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py @@ -95,7 +95,7 @@ class PrepareToAspirate( commandType: PrepareToAspirateCommandType = "prepareToAspirate" params: PrepareToAspirateParams - result: Optional[PrepareToAspirateResult] + result: Optional[PrepareToAspirateResult] = None _ImplementationCls: Type[ PrepareToAspirateImplementation diff --git a/api/src/opentrons/protocol_engine/commands/reload_labware.py b/api/src/opentrons/protocol_engine/commands/reload_labware.py index 60230a1c6dd..07d70957cdb 100644 --- a/api/src/opentrons/protocol_engine/commands/reload_labware.py +++ b/api/src/opentrons/protocol_engine/commands/reload_labware.py @@ -89,7 +89,7 @@ class ReloadLabware( commandType: ReloadLabwareCommandType = "reloadLabware" params: ReloadLabwareParams - result: Optional[ReloadLabwareResult] + result: Optional[ReloadLabwareResult] = None _ImplementationCls: Type[ReloadLabwareImplementation] = ReloadLabwareImplementation diff --git a/api/src/opentrons/protocol_engine/commands/retract_axis.py b/api/src/opentrons/protocol_engine/commands/retract_axis.py index 49020eb517e..19c7653793f 100644 --- a/api/src/opentrons/protocol_engine/commands/retract_axis.py +++ b/api/src/opentrons/protocol_engine/commands/retract_axis.py @@ -61,7 +61,7 @@ class RetractAxis(BaseCommand[RetractAxisParams, RetractAxisResult, ErrorOccurre commandType: RetractAxisCommandType = "retractAxis" params: RetractAxisParams - result: Optional[RetractAxisResult] + result: Optional[RetractAxisResult] = None _ImplementationCls: Type[RetractAxisImplementation] = RetractAxisImplementation diff --git a/api/src/opentrons/protocol_engine/commands/robot/close_gripper_jaw.py b/api/src/opentrons/protocol_engine/commands/robot/close_gripper_jaw.py index 965c6d2ec72..36b027c351a 100644 --- a/api/src/opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +++ b/api/src/opentrons/protocol_engine/commands/robot/close_gripper_jaw.py @@ -1,10 +1,12 @@ """Command models for opening a gripper jaw.""" from __future__ import annotations -from typing import Literal, Type, Optional -from opentrons.hardware_control import HardwareControlAPI -from opentrons.protocol_engine.resources import ensure_ot3_hardware +from typing import Literal, Type, Optional, Any from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema + +from opentrons.hardware_control import HardwareControlAPI +from opentrons.protocol_engine.resources import ensure_ot3_hardware from ..command import ( AbstractCommandImpl, @@ -18,12 +20,17 @@ closeGripperJawCommandType = Literal["robot/closeGripperJaw"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class closeGripperJawParams(BaseModel): """Payload required to close a gripper.""" - force: Optional[float] = Field( + force: float | SkipJsonSchema[None] = Field( default=None, description="The force the gripper should use to hold the jaws, falls to default if none is provided.", + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/robot/move_axes_relative.py b/api/src/opentrons/protocol_engine/commands/robot/move_axes_relative.py index 238229ebce6..99b30e9595a 100644 --- a/api/src/opentrons/protocol_engine/commands/robot/move_axes_relative.py +++ b/api/src/opentrons/protocol_engine/commands/robot/move_axes_relative.py @@ -1,8 +1,10 @@ """Command models for moving any robot axis relative.""" + from __future__ import annotations -from typing import Literal, Type, Optional, TYPE_CHECKING +from typing import Literal, Type, Optional, TYPE_CHECKING, Any from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from opentrons.hardware_control import HardwareControlAPI from opentrons.protocol_engine.resources import ensure_ot3_hardware @@ -23,15 +25,20 @@ MoveAxesRelativeCommandType = Literal["robot/moveAxesRelative"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class MoveAxesRelativeParams(BaseModel): """Payload required to move axes relative to position.""" axis_map: MotorAxisMapType = Field( ..., description="A dictionary mapping axes to relative movements in mm." ) - speed: Optional[float] = Field( + speed: float | SkipJsonSchema[None] = Field( default=None, description="The max velocity to move the axes at. Will fall to hardware defaults if none provided.", + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/robot/move_axes_to.py b/api/src/opentrons/protocol_engine/commands/robot/move_axes_to.py index 0d17d5f171f..93fc2746c84 100644 --- a/api/src/opentrons/protocol_engine/commands/robot/move_axes_to.py +++ b/api/src/opentrons/protocol_engine/commands/robot/move_axes_to.py @@ -1,7 +1,9 @@ """Command models for moving any robot axis to an absolute position.""" from __future__ import annotations -from typing import Literal, Optional, Type, TYPE_CHECKING +from typing import Literal, Optional, Type, TYPE_CHECKING, Any + from pydantic import Field, BaseModel +from pydantic.json_schema import SkipJsonSchema from opentrons.hardware_control import HardwareControlAPI from opentrons.protocol_engine.resources import ensure_ot3_hardware @@ -22,18 +24,25 @@ MoveAxesToCommandType = Literal["robot/moveAxesTo"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class MoveAxesToParams(BaseModel): """Payload required to move axes to absolute position.""" axis_map: MotorAxisMapType = Field( ..., description="The specified axes to move to an absolute deck position with." ) - critical_point: Optional[MotorAxisMapType] = Field( - default=None, description="The critical point to move the mount with." + critical_point: MotorAxisMapType | SkipJsonSchema[None] = Field( + default=None, + description="The critical point to move the mount with.", + json_schema_extra=_remove_default, ) - speed: Optional[float] = Field( + speed: float | SkipJsonSchema[None] = Field( default=None, description="The max velocity to move the axes at. Will fall to hardware defaults if none provided.", + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/robot/move_to.py b/api/src/opentrons/protocol_engine/commands/robot/move_to.py index 199d5be5079..e2a3af07767 100644 --- a/api/src/opentrons/protocol_engine/commands/robot/move_to.py +++ b/api/src/opentrons/protocol_engine/commands/robot/move_to.py @@ -1,8 +1,10 @@ """Command models for moving any robot mount to a destination point.""" from __future__ import annotations -from typing import Literal, Type, Optional, TYPE_CHECKING +from typing import Literal, Type, Optional, TYPE_CHECKING, Any from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema + from opentrons.types import MountType from ..movement_common import DestinationPositionResult @@ -23,6 +25,10 @@ MoveToCommandType = Literal["robot/moveTo"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class MoveToParams(BaseModel): """Payload required to move to a destination position.""" @@ -34,9 +40,10 @@ class MoveToParams(BaseModel): ..., description="X, Y and Z coordinates in mm from deck's origin location (left-front-bottom corner of work space)", ) - speed: Optional[float] = Field( + speed: float | SkipJsonSchema[None] = Field( default=None, description="The max velocity to move the axes at. Will fall to hardware defaults if none provided.", + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/save_position.py b/api/src/opentrons/protocol_engine/commands/save_position.py index 4bc208d1421..6a1d22c4687 100644 --- a/api/src/opentrons/protocol_engine/commands/save_position.py +++ b/api/src/opentrons/protocol_engine/commands/save_position.py @@ -1,8 +1,10 @@ """Save pipette position command request, result, and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any + from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from ..types import DeckPoint @@ -16,19 +18,26 @@ SavePositionCommandType = Literal["savePosition"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class SavePositionParams(BaseModel): """Payload needed to save a pipette's current position.""" pipetteId: str = Field( ..., description="Unique identifier of the pipette in question." ) - positionId: Optional[str] = Field( + positionId: str | SkipJsonSchema[None] = Field( None, description="An optional ID to assign to this command instance. " "Auto-assigned if not defined.", + json_schema_extra=_remove_default, ) - failOnNotHomed: Optional[bool] = Field( - True, descrption="Require all axes to be homed before saving position." + failOnNotHomed: bool | SkipJsonSchema[None] = Field( + True, + description="Require all axes to be homed before saving position.", + json_schema_extra=_remove_default, ) @@ -86,7 +95,7 @@ class SavePosition( commandType: SavePositionCommandType = "savePosition" params: SavePositionParams - result: Optional[SavePositionResult] + result: Optional[SavePositionResult] = None _ImplementationCls: Type[SavePositionImplementation] = SavePositionImplementation diff --git a/api/src/opentrons/protocol_engine/commands/set_rail_lights.py b/api/src/opentrons/protocol_engine/commands/set_rail_lights.py index 09254dbe966..7ca3929695b 100644 --- a/api/src/opentrons/protocol_engine/commands/set_rail_lights.py +++ b/api/src/opentrons/protocol_engine/commands/set_rail_lights.py @@ -53,7 +53,7 @@ class SetRailLights( commandType: SetRailLightsCommandType = "setRailLights" params: SetRailLightsParams - result: Optional[SetRailLightsResult] + result: Optional[SetRailLightsResult] = None _ImplementationCls: Type[SetRailLightsImplementation] = SetRailLightsImplementation diff --git a/api/src/opentrons/protocol_engine/commands/set_status_bar.py b/api/src/opentrons/protocol_engine/commands/set_status_bar.py index 2e1483f6d93..62ca9b7682a 100644 --- a/api/src/opentrons/protocol_engine/commands/set_status_bar.py +++ b/api/src/opentrons/protocol_engine/commands/set_status_bar.py @@ -75,7 +75,7 @@ class SetStatusBar( commandType: SetStatusBarCommandType = "setStatusBar" params: SetStatusBarParams - result: Optional[SetStatusBarResult] + result: Optional[SetStatusBarResult] = None _ImplementationCls: Type[SetStatusBarImplementation] = SetStatusBarImplementation diff --git a/api/src/opentrons/protocol_engine/commands/temperature_module/deactivate.py b/api/src/opentrons/protocol_engine/commands/temperature_module/deactivate.py index e56c98e6e30..e1514c91f30 100644 --- a/api/src/opentrons/protocol_engine/commands/temperature_module/deactivate.py +++ b/api/src/opentrons/protocol_engine/commands/temperature_module/deactivate.py @@ -72,7 +72,7 @@ class DeactivateTemperature( commandType: DeactivateTemperatureCommandType = "temperatureModule/deactivate" params: DeactivateTemperatureParams - result: Optional[DeactivateTemperatureResult] + result: Optional[DeactivateTemperatureResult] = None _ImplementationCls: Type[DeactivateTemperatureImpl] = DeactivateTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py b/api/src/opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py index 6d65bf47bb0..b0aa7bd5cd0 100644 --- a/api/src/opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +++ b/api/src/opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py @@ -81,7 +81,7 @@ class SetTargetTemperature( "temperatureModule/setTargetTemperature" ) params: SetTargetTemperatureParams - result: Optional[SetTargetTemperatureResult] + result: Optional[SetTargetTemperatureResult] = None _ImplementationCls: Type[SetTargetTemperatureImpl] = SetTargetTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py b/api/src/opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py index fa7784de141..5f3f052d91b 100644 --- a/api/src/opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +++ b/api/src/opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py @@ -1,9 +1,10 @@ """Command models to wait for target temperature of a Temperature Module.""" from __future__ import annotations -from typing import Optional, TYPE_CHECKING -from typing_extensions import Literal, Type +from typing import Optional, TYPE_CHECKING, Any +from typing_extensions import Literal, Type from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ...errors.error_occurrence import ErrorOccurrence @@ -15,11 +16,15 @@ WaitForTemperatureCommandType = Literal["temperatureModule/waitForTemperature"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class WaitForTemperatureParams(BaseModel): """Input parameters to wait for a Temperature Module's target temperature.""" moduleId: str = Field(..., description="Unique ID of the Temperature Module.") - celsius: Optional[float] = Field( + celsius: float | SkipJsonSchema[None] = Field( None, description="Target temperature in °C. If not specified, will " "default to the module's target temperature. " @@ -27,6 +32,7 @@ class WaitForTemperatureParams(BaseModel): "could lead to unpredictable behavior and hence is not " "recommended for use. This parameter can be removed in a " "future version without prior notice.", + json_schema_extra=_remove_default, ) @@ -84,7 +90,7 @@ class WaitForTemperature( commandType: WaitForTemperatureCommandType = "temperatureModule/waitForTemperature" params: WaitForTemperatureParams - result: Optional[WaitForTemperatureResult] + result: Optional[WaitForTemperatureResult] = None _ImplementationCls: Type[WaitForTemperatureImpl] = WaitForTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/close_lid.py b/api/src/opentrons/protocol_engine/commands/thermocycler/close_lid.py index 578a5d6b7a7..c5171c82da8 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/close_lid.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/close_lid.py @@ -73,7 +73,7 @@ class CloseLid(BaseCommand[CloseLidParams, CloseLidResult, ErrorOccurrence]): commandType: CloseLidCommandType = "thermocycler/closeLid" params: CloseLidParams - result: Optional[CloseLidResult] + result: Optional[CloseLidResult] = None _ImplementationCls: Type[CloseLidImpl] = CloseLidImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/deactivate_block.py b/api/src/opentrons/protocol_engine/commands/thermocycler/deactivate_block.py index 67199577d53..fb49f031ac4 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/deactivate_block.py @@ -66,7 +66,7 @@ class DeactivateBlock( commandType: DeactivateBlockCommandType = "thermocycler/deactivateBlock" params: DeactivateBlockParams - result: Optional[DeactivateBlockResult] + result: Optional[DeactivateBlockResult] = None _ImplementationCls: Type[DeactivateBlockImpl] = DeactivateBlockImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py b/api/src/opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py index 9c3541efb12..f92065c88e9 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py @@ -66,7 +66,7 @@ class DeactivateLid( commandType: DeactivateLidCommandType = "thermocycler/deactivateLid" params: DeactivateLidParams - result: Optional[DeactivateLidResult] + result: Optional[DeactivateLidResult] = None _ImplementationCls: Type[DeactivateLidImpl] = DeactivateLidImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/open_lid.py b/api/src/opentrons/protocol_engine/commands/thermocycler/open_lid.py index 3df32d1ec99..6eedb9f4c60 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/open_lid.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/open_lid.py @@ -73,7 +73,7 @@ class OpenLid(BaseCommand[OpenLidParams, OpenLidResult, ErrorOccurrence]): commandType: OpenLidCommandType = "thermocycler/openLid" params: OpenLidParams - result: Optional[OpenLidResult] + result: Optional[OpenLidResult] = None _ImplementationCls: Type[OpenLidImpl] = OpenLidImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py b/api/src/opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py index 6f63aed8fe3..40f87af7772 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py @@ -1,9 +1,10 @@ """Command models to execute a Thermocycler profile.""" from __future__ import annotations -from typing import List, Optional, TYPE_CHECKING, overload, Union +from typing import List, Optional, TYPE_CHECKING, overload, Union, Any from typing_extensions import Literal, Type from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from opentrons.hardware_control.modules.types import ThermocyclerStep, ThermocyclerCycle @@ -21,6 +22,10 @@ RunExtendedProfileCommandType = Literal["thermocycler/runExtendedProfile"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class ProfileStep(BaseModel): """An individual step in a Thermocycler extended profile.""" @@ -45,10 +50,11 @@ class RunExtendedProfileParams(BaseModel): ..., description="Elements of the profile. Each can be either a step or a cycle.", ) - blockMaxVolumeUl: Optional[float] = Field( + blockMaxVolumeUl: float | SkipJsonSchema[None] = Field( None, description="Amount of liquid in uL of the most-full well" " in labware loaded onto the thermocycler.", + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/run_profile.py b/api/src/opentrons/protocol_engine/commands/thermocycler/run_profile.py index 02aa7ad93e2..fee6ed82bb3 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/run_profile.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/run_profile.py @@ -1,9 +1,10 @@ """Command models to execute a Thermocycler profile.""" from __future__ import annotations -from typing import List, Optional, TYPE_CHECKING +from typing import List, Optional, TYPE_CHECKING, Any from typing_extensions import Literal, Type from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from opentrons.hardware_control.modules.types import ThermocyclerStep @@ -18,6 +19,10 @@ RunProfileCommandType = Literal["thermocycler/runProfile"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class RunProfileStepParams(BaseModel): """Input parameters for an individual Thermocycler profile step.""" @@ -35,10 +40,11 @@ class RunProfileParams(BaseModel): ..., description="Array of profile steps with target temperature and temperature hold time.", ) - blockMaxVolumeUl: Optional[float] = Field( + blockMaxVolumeUl: float | SkipJsonSchema[None] = Field( None, description="Amount of liquid in uL of the most-full well" " in labware loaded onto the thermocycler.", + json_schema_extra=_remove_default, ) @@ -104,7 +110,7 @@ class RunProfile(BaseCommand[RunProfileParams, RunProfileResult, ErrorOccurrence commandType: RunProfileCommandType = "thermocycler/runProfile" params: RunProfileParams - result: Optional[RunProfileResult] + result: Optional[RunProfileResult] = None _ImplementationCls: Type[RunProfileImpl] = RunProfileImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py b/api/src/opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py index b69bb15ea90..f1884e8ee9e 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py @@ -1,9 +1,10 @@ """Command models to start heating a Thermocycler's block.""" from __future__ import annotations -from typing import Optional, TYPE_CHECKING +from typing import Optional, TYPE_CHECKING, Any from typing_extensions import Literal, Type from pydantic import BaseModel, Field +from pydantic.json_schema import SkipJsonSchema from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ...errors.error_occurrence import ErrorOccurrence @@ -16,21 +17,27 @@ SetTargetBlockTemperatureCommandType = Literal["thermocycler/setTargetBlockTemperature"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class SetTargetBlockTemperatureParams(BaseModel): """Input parameters to set a Thermocycler's target block temperature.""" moduleId: str = Field(..., description="Unique ID of the Thermocycler Module.") celsius: float = Field(..., description="Target temperature in °C.") - blockMaxVolumeUl: Optional[float] = Field( + blockMaxVolumeUl: float | SkipJsonSchema[None] = Field( None, description="Amount of liquid in uL of the most-full well" " in labware loaded onto the thermocycler.", + json_schema_extra=_remove_default, ) - holdTimeSeconds: Optional[float] = Field( + holdTimeSeconds: float | SkipJsonSchema[None] = Field( None, description="Amount of time, in seconds, to hold the temperature for." " If specified, a waitForBlockTemperature command will block until" " the given hold time has elapsed.", + json_schema_extra=_remove_default, ) @@ -113,7 +120,7 @@ class SetTargetBlockTemperature( "thermocycler/setTargetBlockTemperature" ) params: SetTargetBlockTemperatureParams - result: Optional[SetTargetBlockTemperatureResult] + result: Optional[SetTargetBlockTemperatureResult] = None _ImplementationCls: Type[ SetTargetBlockTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py b/api/src/opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py index 37217e047ae..a1af8941ee9 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py @@ -84,7 +84,7 @@ class SetTargetLidTemperature( "thermocycler/setTargetLidTemperature" ) params: SetTargetLidTemperatureParams - result: Optional[SetTargetLidTemperatureResult] + result: Optional[SetTargetLidTemperatureResult] = None _ImplementationCls: Type[SetTargetLidTemperatureImpl] = SetTargetLidTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py b/api/src/opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py index 8e8c9b1a4ec..388d1af13d9 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py @@ -77,7 +77,7 @@ class WaitForBlockTemperature( "thermocycler/waitForBlockTemperature" ) params: WaitForBlockTemperatureParams - result: Optional[WaitForBlockTemperatureResult] + result: Optional[WaitForBlockTemperatureResult] = None _ImplementationCls: Type[WaitForBlockTemperatureImpl] = WaitForBlockTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py b/api/src/opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py index 95e5fbc4f0a..233e4ff5a8c 100644 --- a/api/src/opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +++ b/api/src/opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py @@ -75,7 +75,7 @@ class WaitForLidTemperature( commandType: WaitForLidTemperatureCommandType = "thermocycler/waitForLidTemperature" params: WaitForLidTemperatureParams - result: Optional[WaitForLidTemperatureResult] + result: Optional[WaitForLidTemperatureResult] = None _ImplementationCls: Type[WaitForLidTemperatureImpl] = WaitForLidTemperatureImpl diff --git a/api/src/opentrons/protocol_engine/commands/touch_tip.py b/api/src/opentrons/protocol_engine/commands/touch_tip.py index 2d7c507d321..d4591bf1d27 100644 --- a/api/src/opentrons/protocol_engine/commands/touch_tip.py +++ b/api/src/opentrons/protocol_engine/commands/touch_tip.py @@ -1,13 +1,19 @@ """Touch tip command request, result, and implementation models.""" from __future__ import annotations -from pydantic import Field -from typing import TYPE_CHECKING, Optional, Type +from typing import TYPE_CHECKING, Optional, Type, Any + from typing_extensions import Literal +from pydantic import Field +from pydantic.json_schema import SkipJsonSchema from opentrons.types import Point -from ..errors import TouchTipDisabledError, LabwareIsTipRackError +from ..errors import ( + TouchTipDisabledError, + TouchTipIncompatibleArgumentsError, + LabwareIsTipRackError, +) from ..types import DeckPoint from .command import ( AbstractCommandImpl, @@ -35,6 +41,10 @@ TouchTipCommandType = Literal["touchTip"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class TouchTipParams(PipetteIdMixin, WellLocationMixin): """Payload needed to touch a pipette tip the sides of a specific well.""" @@ -45,12 +55,20 @@ class TouchTipParams(PipetteIdMixin, WellLocationMixin): ), ) - speed: Optional[float] = Field( + mmFromEdge: float | SkipJsonSchema[None] = Field( + None, + description="Offset away from the the well edge, in millimeters." + "Incompatible when a radius is included as a non 1.0 value.", + json_schema_extra=_remove_default, + ) + + speed: float | SkipJsonSchema[None] = Field( None, description=( "Override the travel speed in mm/s." " This controls the straight linear speed of motion." ), + json_schema_extra=_remove_default, ) @@ -89,6 +107,11 @@ async def execute( labware_id = params.labwareId well_name = params.wellName + if params.radius != 1.0 and params.mmFromEdge is not None: + raise TouchTipIncompatibleArgumentsError( + "Cannot use mmFromEdge with a radius that is not 1.0" + ) + if self._state_view.labware.get_has_quirk(labware_id, "touchTipDisabled"): raise TouchTipDisabledError( f"Touch tip not allowed on labware {labware_id}" @@ -112,11 +135,13 @@ async def execute( pipette_id, params.speed ) + mm_from_edge = params.mmFromEdge if params.mmFromEdge is not None else 0 touch_waypoints = self._state_view.motion.get_touch_tip_waypoints( pipette_id=pipette_id, labware_id=labware_id, well_name=well_name, radius=params.radius, + mm_from_edge=mm_from_edge, center_point=Point( center_result.public.position.x, center_result.public.position.y, @@ -129,7 +154,7 @@ async def execute( waypoints=touch_waypoints, speed=touch_speed, ) - final_deck_point = DeckPoint.construct( + final_deck_point = DeckPoint.model_construct( x=final_point.x, y=final_point.y, z=final_point.z ) state_update = center_result.state_update.set_pipette_location( @@ -150,7 +175,7 @@ class TouchTip(BaseCommand[TouchTipParams, TouchTipResult, StallOrCollisionError commandType: TouchTipCommandType = "touchTip" params: TouchTipParams - result: Optional[TouchTipResult] + result: Optional[TouchTipResult] = None _ImplementationCls: Type[TouchTipImplementation] = TouchTipImplementation diff --git a/api/src/opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py b/api/src/opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py index 5aa4e292f63..56a87a468dd 100644 --- a/api/src/opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +++ b/api/src/opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py @@ -1,13 +1,16 @@ """Command models to drop tip in place while plunger positions are unknown.""" + from __future__ import annotations -from opentrons.protocol_engine.state.update_types import StateUpdate +from typing import TYPE_CHECKING, Optional, Type, Any + from pydantic import Field, BaseModel -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from opentrons.hardware_control import HardwareControlAPI from opentrons.hardware_control.types import Axis +from opentrons.protocol_engine.state.update_types import StateUpdate from ..pipetting_common import PipetteIdMixin from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData from ...errors.error_occurrence import ErrorOccurrence @@ -21,16 +24,21 @@ UnsafeDropTipInPlaceCommandType = Literal["unsafe/dropTipInPlace"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class UnsafeDropTipInPlaceParams(PipetteIdMixin): """Payload required to drop a tip in place even if the plunger position is not known.""" - homeAfter: Optional[bool] = Field( + homeAfter: bool | SkipJsonSchema[None] = Field( None, description=( "Whether to home this pipette's plunger after dropping the tip." " You should normally leave this unspecified to let the robot choose" " a safe default depending on its hardware." ), + json_schema_extra=_remove_default, ) diff --git a/api/src/opentrons/protocol_engine/commands/verify_tip_presence.py b/api/src/opentrons/protocol_engine/commands/verify_tip_presence.py index e0412022e85..dc6f451e3e4 100644 --- a/api/src/opentrons/protocol_engine/commands/verify_tip_presence.py +++ b/api/src/opentrons/protocol_engine/commands/verify_tip_presence.py @@ -1,8 +1,9 @@ """Verify tip presence command request, result and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any from pydantic import Field, BaseModel -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from .pipetting_common import PipetteIdMixin @@ -18,14 +19,20 @@ VerifyTipPresenceCommandType = Literal["verifyTipPresence"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class VerifyTipPresenceParams(PipetteIdMixin): """Payload required for a VerifyTipPresence command.""" expectedState: TipPresenceStatus = Field( ..., description="The expected tip presence status on the pipette." ) - followSingularSensor: Optional[InstrumentSensorId] = Field( - default=None, description="The sensor id to follow if the other can be ignored." + followSingularSensor: InstrumentSensorId | SkipJsonSchema[None] = Field( + default=None, + description="The sensor id to follow if the other can be ignored.", + json_schema_extra=_remove_default, ) @@ -77,7 +84,7 @@ class VerifyTipPresence( commandType: VerifyTipPresenceCommandType = "verifyTipPresence" params: VerifyTipPresenceParams - result: Optional[VerifyTipPresenceResult] + result: Optional[VerifyTipPresenceResult] = None _ImplementationCls: Type[ VerifyTipPresenceImplementation diff --git a/api/src/opentrons/protocol_engine/commands/wait_for_duration.py b/api/src/opentrons/protocol_engine/commands/wait_for_duration.py index 04f8693386e..26a4372c8ca 100644 --- a/api/src/opentrons/protocol_engine/commands/wait_for_duration.py +++ b/api/src/opentrons/protocol_engine/commands/wait_for_duration.py @@ -1,7 +1,9 @@ """Wait for duration command request, result, and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any + from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData @@ -14,13 +16,18 @@ WaitForDurationCommandType = Literal["waitForDuration"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class WaitForDurationParams(BaseModel): """Payload required to pause the protocol.""" seconds: float = Field(..., description="Duration, in seconds, to wait for.") - message: Optional[str] = Field( + message: str | SkipJsonSchema[None] = Field( None, description="A user-facing message associated with the pause", + json_schema_extra=_remove_default, ) @@ -53,7 +60,7 @@ class WaitForDuration( commandType: WaitForDurationCommandType = "waitForDuration" params: WaitForDurationParams - result: Optional[WaitForDurationResult] + result: Optional[WaitForDurationResult] = None _ImplementationCls: Type[ WaitForDurationImplementation diff --git a/api/src/opentrons/protocol_engine/commands/wait_for_resume.py b/api/src/opentrons/protocol_engine/commands/wait_for_resume.py index f5066d52521..28458aa3721 100644 --- a/api/src/opentrons/protocol_engine/commands/wait_for_resume.py +++ b/api/src/opentrons/protocol_engine/commands/wait_for_resume.py @@ -1,7 +1,9 @@ """Wait for resume command request, result, and implementation models.""" from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Type, Any + from pydantic import BaseModel, Field -from typing import TYPE_CHECKING, Optional, Type +from pydantic.json_schema import SkipJsonSchema from typing_extensions import Literal from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData @@ -16,12 +18,17 @@ WaitForResumeCommandType = Literal["waitForResume", "pause"] +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default", None) + + class WaitForResumeParams(BaseModel): """Payload required to pause the protocol.""" - message: Optional[str] = Field( + message: str | SkipJsonSchema[None] = Field( None, description="A user-facing message associated with the pause", + json_schema_extra=_remove_default, ) @@ -54,7 +61,7 @@ class WaitForResume( commandType: WaitForResumeCommandType = "waitForResume" params: WaitForResumeParams - result: Optional[WaitForResumeResult] + result: Optional[WaitForResumeResult] = None _ImplementationCls: Type[WaitForResumeImplementation] = WaitForResumeImplementation diff --git a/api/src/opentrons/protocol_engine/errors/__init__.py b/api/src/opentrons/protocol_engine/errors/__init__.py index 8148ce132e6..2b0fb6a6060 100644 --- a/api/src/opentrons/protocol_engine/errors/__init__.py +++ b/api/src/opentrons/protocol_engine/errors/__init__.py @@ -24,6 +24,7 @@ LabwareIsTipRackError, LabwareIsAdapterError, TouchTipDisabledError, + TouchTipIncompatibleArgumentsError, WellDoesNotExistError, PipetteNotLoadedError, ModuleNotLoadedError, @@ -110,6 +111,7 @@ "LabwareIsTipRackError", "LabwareIsAdapterError", "TouchTipDisabledError", + "TouchTipIncompatibleArgumentsError", "WellDoesNotExistError", "PipetteNotLoadedError", "ModuleNotLoadedError", diff --git a/api/src/opentrons/protocol_engine/errors/error_occurrence.py b/api/src/opentrons/protocol_engine/errors/error_occurrence.py index 4141befe9b8..002596d0172 100644 --- a/api/src/opentrons/protocol_engine/errors/error_occurrence.py +++ b/api/src/opentrons/protocol_engine/errors/error_occurrence.py @@ -4,7 +4,7 @@ from datetime import datetime from textwrap import dedent from typing import Any, Dict, Mapping, List, Type, Union, Optional, Sequence -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, ConfigDict from opentrons_shared_data.errors.codes import ErrorCodes from .exceptions import ProtocolEngineError from opentrons_shared_data.errors.exceptions import EnumeratedError @@ -29,7 +29,7 @@ def from_failed( wrappedErrors = [ cls.from_failed(id, createdAt, err) for err in error.wrapping ] - return cls.construct( + return cls.model_construct( id=id, createdAt=createdAt, errorType=type(error).__name__, @@ -39,6 +39,21 @@ def from_failed( wrappedErrors=wrappedErrors, ) + @staticmethod + def schema_extra(schema: Dict[str, Any], model: object) -> None: + """Append the schema to make the errorCode appear required. + + `errorCode`, `wrappedErrors`, and `errorInfo` have defaults because they are not included in earlier + versions of this model, _and_ this model is loaded directly from + the on-robot store. That means that, without a default, it will + fail to parse. Once a default is defined, the automated schema will + mark this as a non-required field, which is misleading as this is + a response from the server to the client and it will always have an + errorCode defined. This hack is required because it informs the client + that it does not, in fact, have to account for a missing errorCode, wrappedError, or errorInfo. + """ + schema["required"].extend(["errorCode", "wrappedErrors", "errorInfo"]) + id: str = Field(..., description="Unique identifier of this error occurrence.") createdAt: datetime = Field(..., description="When the error occurred.") @@ -145,23 +160,7 @@ def from_failed( default=[], description="Errors that may have caused this one." ) - class Config: - """Customize configuration for this model.""" - - @staticmethod - def schema_extra(schema: Dict[str, Any], model: object) -> None: - """Append the schema to make the errorCode appear required. - - `errorCode`, `wrappedErrors`, and `errorInfo` have defaults because they are not included in earlier - versions of this model, _and_ this model is loaded directly from - the on-robot store. That means that, without a default, it will - fail to parse. Once a default is defined, the automated schema will - mark this as a non-required field, which is misleading as this is - a response from the server to the client and it will always have an - errorCode defined. This hack is required because it informs the client - that it does not, in fact, have to account for a missing errorCode, wrappedError, or errorInfo. - """ - schema["required"].extend(["errorCode", "wrappedErrors", "errorInfo"]) + model_config = ConfigDict(json_schema_extra=schema_extra) # TODO (tz, 7-12-23): move this to exceptions.py when we stop relaying on ErrorOccurrence. @@ -180,4 +179,4 @@ def __init__( self.original_error = original_error -ErrorOccurrence.update_forward_refs() +ErrorOccurrence.model_rebuild() diff --git a/api/src/opentrons/protocol_engine/errors/exceptions.py b/api/src/opentrons/protocol_engine/errors/exceptions.py index 563a1fb816d..c3fddf99a61 100644 --- a/api/src/opentrons/protocol_engine/errors/exceptions.py +++ b/api/src/opentrons/protocol_engine/errors/exceptions.py @@ -361,6 +361,19 @@ def __init__( super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) +class TouchTipIncompatibleArgumentsError(ProtocolEngineError): + """Raised when touch tip is used with both a custom radius and a mmFromEdge argument.""" + + def __init__( + self, + message: Optional[str] = None, + details: Optional[Dict[str, Any]] = None, + wrapping: Optional[Sequence[EnumeratedError]] = None, + ) -> None: + """Build a TouchTipIncompatibleArgumentsError.""" + super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) + + class WellDoesNotExistError(ProtocolEngineError): """Raised when referencing a well that does not exist.""" diff --git a/api/src/opentrons/protocol_engine/execution/command_executor.py b/api/src/opentrons/protocol_engine/execution/command_executor.py index b6c686e0b11..47184d94ef2 100644 --- a/api/src/opentrons/protocol_engine/execution/command_executor.py +++ b/api/src/opentrons/protocol_engine/execution/command_executor.py @@ -188,7 +188,7 @@ async def execute(self, command_id: str) -> None: "completedAt": self._model_utils.get_timestamp(), "notes": note_tracker.get_notes(), } - succeeded_command = running_command.copy(update=update) + succeeded_command = running_command.model_copy(update=update) self._action_dispatcher.dispatch( SucceedCommandAction( command=succeeded_command, diff --git a/api/src/opentrons/protocol_engine/notes/notes.py b/api/src/opentrons/protocol_engine/notes/notes.py index 8c349d167cd..2ec71d90b55 100644 --- a/api/src/opentrons/protocol_engine/notes/notes.py +++ b/api/src/opentrons/protocol_engine/notes/notes.py @@ -35,7 +35,7 @@ def make_error_recovery_debug_note(type: "ErrorRecoveryType") -> CommandNote: This is intended to be read by developers and support people, not computers. """ message = f"Handling this command failure with {type.name}." - return CommandNote.construct( + return CommandNote.model_construct( noteKind="debugErrorRecovery", shortMessage=message, longMessage=message, diff --git a/api/src/opentrons/protocol_engine/resources/labware_data_provider.py b/api/src/opentrons/protocol_engine/resources/labware_data_provider.py index 0b08720d4e9..8d5cdfc7899 100644 --- a/api/src/opentrons/protocol_engine/resources/labware_data_provider.py +++ b/api/src/opentrons/protocol_engine/resources/labware_data_provider.py @@ -44,7 +44,7 @@ async def get_labware_definition( def _get_labware_definition_sync( load_name: str, namespace: str, version: int ) -> LabwareDefinition: - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( get_labware_definition(load_name, namespace, version) ) diff --git a/api/src/opentrons/protocol_engine/resources/module_data_provider.py b/api/src/opentrons/protocol_engine/resources/module_data_provider.py index a12b85ee5b3..3ee7b5d6bd9 100644 --- a/api/src/opentrons/protocol_engine/resources/module_data_provider.py +++ b/api/src/opentrons/protocol_engine/resources/module_data_provider.py @@ -22,7 +22,7 @@ class ModuleDataProvider: def get_definition(model: ModuleModel) -> ModuleDefinition: """Get the module definition.""" data = load_definition(model_or_loadname=model.value, version="3") - return ModuleDefinition.parse_obj(data) + return ModuleDefinition.model_validate(data) @staticmethod def load_module_calibrations() -> Dict[str, ModuleOffsetData]: diff --git a/api/src/opentrons/protocol_engine/resources/pipette_data_provider.py b/api/src/opentrons/protocol_engine/resources/pipette_data_provider.py index 4df6b0d4d77..ee721c88f2c 100644 --- a/api/src/opentrons/protocol_engine/resources/pipette_data_provider.py +++ b/api/src/opentrons/protocol_engine/resources/pipette_data_provider.py @@ -98,6 +98,7 @@ def configure_virtual_pipette_nozzle_layout( config.pipette_type, config.channels, config.version, + pip_types.PipetteOEMType.OT, ) new_nozzle_manager = NozzleConfigurationManager.build_from_config( config, valid_nozzle_maps @@ -130,6 +131,7 @@ def configure_virtual_pipette_for_volume( pipette_model.pipette_type, pipette_model.pipette_channels, pipette_model.pipette_version, + pipette_model.oem_type, ) liquid_class = pipette_definition.liquid_class_for_volume_between_default_and_defaultlowvolume( @@ -163,6 +165,7 @@ def _get_virtual_pipette_full_config_by_model_string( pipette_model.pipette_type, pipette_model.pipette_channels, pipette_model.pipette_version, + pipette_model.oem_type, ) def _get_virtual_pipette_static_config_by_model( # noqa: C901 @@ -179,6 +182,7 @@ def _get_virtual_pipette_static_config_by_model( # noqa: C901 pipette_model.pipette_type, pipette_model.pipette_channels, pipette_model.pipette_version, + pipette_model.oem_type, ) try: tip_type = pip_types.PipetteTipType( @@ -195,6 +199,7 @@ def _get_virtual_pipette_static_config_by_model( # noqa: C901 pipette_model.pipette_type, pipette_model.pipette_channels, pipette_model.pipette_version, + pipette_model.oem_type, ) if pipette_id not in self._nozzle_manager_layout_by_id: nozzle_manager = NozzleConfigurationManager.build_from_config( diff --git a/api/src/opentrons/protocol_engine/slot_standardization.py b/api/src/opentrons/protocol_engine/slot_standardization.py index b600258bbf0..d940517eebe 100644 --- a/api/src/opentrons/protocol_engine/slot_standardization.py +++ b/api/src/opentrons/protocol_engine/slot_standardization.py @@ -35,9 +35,9 @@ def standardize_labware_offset( original: LabwareOffsetCreate, robot_type: RobotType ) -> LabwareOffsetCreate: """Convert the deck slot in the given `LabwareOffsetCreate` to match the given robot type.""" - return original.copy( + return original.model_copy( update={ - "location": original.location.copy( + "location": original.location.model_copy( update={ "slotName": original.location.slotName.to_equivalent_for_robot_type( robot_type @@ -70,40 +70,40 @@ def standardize_command( def _standardize_load_labware( original: commands.LoadLabwareCreate, robot_type: RobotType ) -> commands.LoadLabwareCreate: - params = original.params.copy( + params = original.params.model_copy( update={ "location": _standardize_labware_location( original.params.location, robot_type ) } ) - return original.copy(update={"params": params}) + return original.model_copy(update={"params": params}) def _standardize_load_module( original: commands.LoadModuleCreate, robot_type: RobotType ) -> commands.LoadModuleCreate: - params = original.params.copy( + params = original.params.model_copy( update={ "location": _standardize_deck_slot_location( original.params.location, robot_type ) } ) - return original.copy(update={"params": params}) + return original.model_copy(update={"params": params}) def _standardize_move_labware( original: commands.MoveLabwareCreate, robot_type: RobotType ) -> commands.MoveLabwareCreate: - params = original.params.copy( + params = original.params.model_copy( update={ "newLocation": _standardize_labware_location( original.params.newLocation, robot_type ) } ) - return original.copy(update={"params": params}) + return original.model_copy(update={"params": params}) _standardize_command_functions: Dict[ @@ -135,6 +135,6 @@ def _standardize_labware_location( def _standardize_deck_slot_location( original: DeckSlotLocation, robot_type: RobotType ) -> DeckSlotLocation: - return original.copy( + return original.model_copy( update={"slotName": original.slotName.to_equivalent_for_robot_type(robot_type)} ) diff --git a/api/src/opentrons/protocol_engine/state/_move_types.py b/api/src/opentrons/protocol_engine/state/_move_types.py index b8dcb28bd8d..94201ffead9 100644 --- a/api/src/opentrons/protocol_engine/state/_move_types.py +++ b/api/src/opentrons/protocol_engine/state/_move_types.py @@ -53,15 +53,19 @@ def get_move_type_to_well( def get_edge_point_list( - center: Point, x_radius: float, y_radius: float, edge_path_type: EdgePathType + center: Point, + x_radius: float, + y_radius: float, + mm_from_edge: float, + edge_path_type: EdgePathType, ) -> List[Point]: """Get list of edge points dependent on edge path type.""" edges = EdgeList( - right=center + Point(x=x_radius, y=0, z=0), - left=center + Point(x=-x_radius, y=0, z=0), + right=center + Point(x=x_radius - mm_from_edge, y=0, z=0), + left=center + Point(x=-x_radius + mm_from_edge, y=0, z=0), center=center, - forward=center + Point(x=0, y=y_radius, z=0), - back=center + Point(x=0, y=-y_radius, z=0), + forward=center + Point(x=0, y=y_radius - mm_from_edge, z=0), + back=center + Point(x=0, y=-y_radius + mm_from_edge, z=0), ) if edge_path_type == EdgePathType.LEFT: diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index 8aa71c9c1f8..e059212e73f 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -304,7 +304,7 @@ def _handle_queue_command_action(self, action: QueueCommandAction) -> None: # TODO(mc, 2021-06-22): mypy has trouble with this automatic # request > command mapping, figure out how to type precisely # (or wait for a future mypy version that can figure it out). - queued_command = action.request._CommandCls.construct( + queued_command = action.request._CommandCls.model_construct( # type: ignore[call-arg] id=action.command_id, key=( action.request.key @@ -326,7 +326,7 @@ def _handle_queue_command_action(self, action: QueueCommandAction) -> None: def _handle_run_command_action(self, action: RunCommandAction) -> None: prev_entry = self._state.command_history.get(action.command_id) - running_command = prev_entry.command.copy( + running_command = prev_entry.command.model_copy( update={ "status": CommandStatus.RUNNING, "startedAt": action.started_at, @@ -525,7 +525,7 @@ def _update_to_failed( notes: Optional[List[CommandNote]], ) -> None: prev_entry = self._state.command_history.get(command_id) - failed_command = prev_entry.command.copy( + failed_command = prev_entry.command.model_copy( update={ "completedAt": failed_at, "status": CommandStatus.FAILED, @@ -679,7 +679,7 @@ def get_error(self) -> Optional[ErrorOccurrence]: finish_error = self._state.finish_error if run_error and finish_error: - combined_error = ErrorOccurrence.construct( + combined_error = ErrorOccurrence( id=finish_error.id, createdAt=finish_error.createdAt, errorType="RunAndFinishFailed", diff --git a/api/src/opentrons/protocol_engine/state/geometry.py b/api/src/opentrons/protocol_engine/state/geometry.py index ed915530b90..e0d9cb1afa1 100644 --- a/api/src/opentrons/protocol_engine/state/geometry.py +++ b/api/src/opentrons/protocol_engine/state/geometry.py @@ -489,7 +489,7 @@ def get_well_position( well_depth=well_depth, operation_volume=operation_volume, ) - offset = offset.copy(update={"z": offset.z + offset_adjustment}) + offset = offset.model_copy(update={"z": offset.z + offset_adjustment}) self.validate_well_position( well_location=well_location, z_offset=offset.z, pipette_id=pipette_id ) diff --git a/api/src/opentrons/protocol_engine/state/labware.py b/api/src/opentrons/protocol_engine/state/labware.py index 95b2baa1974..3f00ad14de7 100644 --- a/api/src/opentrons/protocol_engine/state/labware.py +++ b/api/src/opentrons/protocol_engine/state/labware.py @@ -131,7 +131,7 @@ def __init__( for fixed_labware in deck_fixed_labware } labware_by_id = { - fixed_labware.labware_id: LoadedLabware.construct( + fixed_labware.labware_id: LoadedLabware.model_construct( id=fixed_labware.labware_id, location=fixed_labware.location, loadName=fixed_labware.definition.parameters.loadName, @@ -159,7 +159,7 @@ def handle_action(self, action: Action) -> None: self._set_labware_location(state_update) if isinstance(action, AddLabwareOffsetAction): - labware_offset = LabwareOffset.construct( + labware_offset = LabwareOffset.model_construct( id=action.labware_offset_id, createdAt=action.created_at, definitionUri=action.request.definitionUri, @@ -212,7 +212,7 @@ def _add_loaded_labware(self, state_update: update_types.StateUpdate) -> None: self._state.labware_by_id[ loaded_labware_update.labware_id - ] = LoadedLabware.construct( + ] = LoadedLabware.model_construct( id=loaded_labware_update.labware_id, location=location, loadName=loaded_labware_update.definition.parameters.loadName, @@ -998,11 +998,15 @@ def get_child_gripper_offsets( return None else: return LabwareMovementOffsetData( - pickUpOffset=cast( - LabwareOffsetVector, parsed_offsets[offset_key].pickUpOffset + pickUpOffset=LabwareOffsetVector.model_construct( + x=parsed_offsets[offset_key].pickUpOffset.x, + y=parsed_offsets[offset_key].pickUpOffset.y, + z=parsed_offsets[offset_key].pickUpOffset.z, ), - dropOffset=cast( - LabwareOffsetVector, parsed_offsets[offset_key].dropOffset + dropOffset=LabwareOffsetVector.model_construct( + x=parsed_offsets[offset_key].dropOffset.x, + y=parsed_offsets[offset_key].dropOffset.y, + z=parsed_offsets[offset_key].dropOffset.z, ), ) diff --git a/api/src/opentrons/protocol_engine/state/modules.py b/api/src/opentrons/protocol_engine/state/modules.py index 0f2eea35aa0..cc079768a98 100644 --- a/api/src/opentrons/protocol_engine/state/modules.py +++ b/api/src/opentrons/protocol_engine/state/modules.py @@ -640,7 +640,7 @@ def get(self, module_id: str) -> LoadedModule: DeckSlotLocation(slotName=slot_name) if slot_name is not None else None ) - return LoadedModule.construct( + return LoadedModule.model_construct( id=module_id, location=location, model=attached_module.definition.model, diff --git a/api/src/opentrons/protocol_engine/state/motion.py b/api/src/opentrons/protocol_engine/state/motion.py index 0863c42a0c1..855025d01b6 100644 --- a/api/src/opentrons/protocol_engine/state/motion.py +++ b/api/src/opentrons/protocol_engine/state/motion.py @@ -327,6 +327,7 @@ def get_touch_tip_waypoints( labware_id: str, well_name: str, center_point: Point, + mm_from_edge: float = 0, radius: float = 1.0, ) -> List[motion_planning.Waypoint]: """Get a list of touch points for a touch tip operation.""" @@ -346,7 +347,11 @@ def get_touch_tip_waypoints( ) positions = _move_types.get_edge_point_list( - center_point, x_offset, y_offset, edge_path_type + center=center_point, + x_radius=x_offset, + y_radius=y_offset, + mm_from_edge=mm_from_edge, + edge_path_type=edge_path_type, ) critical_point: Optional[CriticalPoint] = None diff --git a/api/src/opentrons/protocol_engine/state/state.py b/api/src/opentrons/protocol_engine/state/state.py index 58e977cc2f4..5ff12b739f3 100644 --- a/api/src/opentrons/protocol_engine/state/state.py +++ b/api/src/opentrons/protocol_engine/state/state.py @@ -143,7 +143,7 @@ def get_summary(self) -> StateSummary: """Get protocol run data.""" error = self._commands.get_error() # TODO maybe add summary here for AA - return StateSummary.construct( + return StateSummary.model_construct( status=self._commands.get_status(), errors=[] if error is None else [error], pipettes=self._pipettes.get_all(), diff --git a/api/src/opentrons/protocol_engine/state/state_summary.py b/api/src/opentrons/protocol_engine/state/state_summary.py index d6b18613071..64f8e2b2737 100644 --- a/api/src/opentrons/protocol_engine/state/state_summary.py +++ b/api/src/opentrons/protocol_engine/state/state_summary.py @@ -28,8 +28,8 @@ class StateSummary(BaseModel): pipettes: List[LoadedPipette] modules: List[LoadedModule] labwareOffsets: List[LabwareOffset] - startedAt: Optional[datetime] - completedAt: Optional[datetime] + startedAt: Optional[datetime] = None + completedAt: Optional[datetime] = None liquids: List[Liquid] = Field(default_factory=list) wells: List[WellInfoSummary] = Field(default_factory=list) files: List[str] = Field(default_factory=list) diff --git a/api/src/opentrons/protocol_engine/types.py b/api/src/opentrons/protocol_engine/types.py index 11f1972e105..b1388d58212 100644 --- a/api/src/opentrons/protocol_engine/types.py +++ b/api/src/opentrons/protocol_engine/types.py @@ -1,30 +1,30 @@ """Public protocol engine value types and models.""" from __future__ import annotations -import re from datetime import datetime from enum import Enum from dataclasses import dataclass from pathlib import Path +from typing import ( + Any, + Dict, + FrozenSet, + List, + Mapping, + NamedTuple, + Optional, + Tuple, + Union, +) + from pydantic import ( + ConfigDict, BaseModel, Field, + RootModel, StrictBool, StrictFloat, StrictInt, StrictStr, - validator, - Extra, -) -from typing import ( - Optional, - Union, - List, - Dict, - Any, - NamedTuple, - Tuple, - FrozenSet, - Mapping, ) from typing_extensions import Literal, TypeGuard @@ -554,7 +554,7 @@ class ModuleDimensions(BaseModel): bareOverallHeight: float overLabwareHeight: float - lidHeight: Optional[float] + lidHeight: Optional[float] = None class Vec3f(BaseModel): @@ -709,8 +709,8 @@ class LoadedModule(BaseModel): id: str model: ModuleModel - location: Optional[DeckSlotLocation] - serialNumber: Optional[str] + location: Optional[DeckSlotLocation] = None + serialNumber: Optional[str] = None class LabwareOffsetLocation(BaseModel): @@ -819,17 +819,10 @@ class LoadedLabware(BaseModel): ) -class HexColor(BaseModel): +class HexColor(RootModel[str]): """Hex color representation.""" - __root__: str - - @validator("__root__") - def _color_is_a_valid_hex(cls, v: str) -> str: - match = re.search(r"^#(?:[0-9a-fA-F]{3,4}){1,2}$", v) - if not match: - raise ValueError("Color is not a valid hex color.") - return v + root: str = Field(pattern=r"^#(?:[0-9a-fA-F]{3,4}){1,2}$") EmptyLiquidId = Literal["EMPTY"] @@ -842,7 +835,7 @@ class Liquid(BaseModel): id: str displayName: str description: str - displayColor: Optional[HexColor] + displayColor: Optional[HexColor] = None class LiquidClassRecord(ByTipTypeSetting, frozen=True): @@ -885,7 +878,7 @@ def dict_to_tuple(d: dict[str, Any]) -> tuple[tuple[str, Any], ...]: for field_name, value in d.items() ) - return hash(dict_to_tuple(self.dict())) + return hash(dict_to_tuple(self.model_dump())) class LiquidClassRecordWithId(LiquidClassRecord, frozen=True): @@ -1051,12 +1044,12 @@ class QuadrantNozzleLayoutConfiguration(BaseModel): ) frontRightNozzle: str = Field( ..., - regex=NOZZLE_NAME_REGEX, + pattern=NOZZLE_NAME_REGEX, description="The front right nozzle in your configuration.", ) backLeftNozzle: str = Field( ..., - regex=NOZZLE_NAME_REGEX, + pattern=NOZZLE_NAME_REGEX, description="The back left nozzle in your configuration.", ) @@ -1186,11 +1179,7 @@ class CustomCommandAnnotation(BaseCommandAnnotation): """Annotates a group of atomic commands in some manner that Opentrons software does not anticipate or originate.""" annotationType: Literal["custom"] = "custom" - - class Config: - """Config to allow extra, non-defined properties.""" - - extra = Extra.allow + model_config = ConfigDict(extra="allow") CommandAnnotation = Union[SecondOrderCommandAnnotation, CustomCommandAnnotation] diff --git a/api/src/opentrons/protocol_reader/extract_labware_definitions.py b/api/src/opentrons/protocol_reader/extract_labware_definitions.py index 2ecb64a8a39..88d7e256a07 100644 --- a/api/src/opentrons/protocol_reader/extract_labware_definitions.py +++ b/api/src/opentrons/protocol_reader/extract_labware_definitions.py @@ -41,7 +41,10 @@ async def extract_labware_definitions( async def _extract_from_labware_file(path: Path) -> LabwareDefinition: - return await anyio.to_thread.run_sync(LabwareDefinition.parse_file, path) + def _do_parse() -> LabwareDefinition: + return LabwareDefinition.model_validate_json(path.read_bytes()) + + return await anyio.to_thread.run_sync(_do_parse) async def _extract_from_json_protocol_file(path: Path) -> List[LabwareDefinition]: @@ -52,7 +55,7 @@ def extract_sync(path: Path) -> List[LabwareDefinition]: # which require this labwareDefinitions key. unvalidated_definitions = json_contents["labwareDefinitions"].values() validated_definitions = [ - LabwareDefinition.parse_obj(u) for u in unvalidated_definitions + LabwareDefinition.model_validate(u) for u in unvalidated_definitions ] return validated_definitions diff --git a/api/src/opentrons/protocol_reader/file_format_validator.py b/api/src/opentrons/protocol_reader/file_format_validator.py index df119ac3ffa..17969fc70fe 100644 --- a/api/src/opentrons/protocol_reader/file_format_validator.py +++ b/api/src/opentrons/protocol_reader/file_format_validator.py @@ -60,7 +60,7 @@ async def validate(files: Iterable[IdentifiedFile]) -> None: async def _validate_labware_definition(info: IdentifiedLabwareDefinition) -> None: def validate_sync() -> None: try: - LabwareDefinition.parse_obj(info.unvalidated_json) + LabwareDefinition.model_validate(info.unvalidated_json) except PydanticValidationError as e: raise FileFormatValidationError( message=f"{info.original_file.name} could not be read as a labware definition.", @@ -133,17 +133,17 @@ async def _validate_json_protocol(info: IdentifiedJsonMain) -> None: def validate_sync() -> None: if info.schema_version == 8: try: - JsonProtocolV8.parse_obj(info.unvalidated_json) + JsonProtocolV8.model_validate(info.unvalidated_json) except PydanticValidationError as pve: _handle_v8_json_protocol_validation_error(info, pve) else: try: if info.schema_version == 7: - JsonProtocolV7.parse_obj(info.unvalidated_json) + JsonProtocolV7.model_validate(info.unvalidated_json) elif info.schema_version == 6: - JsonProtocolV6.parse_obj(info.unvalidated_json) + JsonProtocolV6.model_validate(info.unvalidated_json) else: - JsonProtocolUpToV5.parse_obj(info.unvalidated_json) + JsonProtocolUpToV5.model_validate(info.unvalidated_json) except PydanticValidationError as e: raise FileFormatValidationError._generic_json_failure(info, e) from e diff --git a/api/src/opentrons/protocol_runner/json_file_reader.py b/api/src/opentrons/protocol_runner/json_file_reader.py index 488c28d273b..f318be1db5d 100644 --- a/api/src/opentrons/protocol_runner/json_file_reader.py +++ b/api/src/opentrons/protocol_runner/json_file_reader.py @@ -30,11 +30,17 @@ def read( }, ) if protocol_source.config.schema_version == 6: - return ProtocolSchemaV6.parse_file(protocol_source.main_file) + return ProtocolSchemaV6.model_validate_json( + protocol_source.main_file.read_bytes() + ) elif protocol_source.config.schema_version == 7: - return ProtocolSchemaV7.parse_file(protocol_source.main_file) + return ProtocolSchemaV7.model_validate_json( + protocol_source.main_file.read_bytes() + ) elif protocol_source.config.schema_version == 8: - return ProtocolSchemaV8.parse_file(protocol_source.main_file) + return ProtocolSchemaV8.model_validate_json( + protocol_source.main_file.read_bytes() + ) else: raise ProtocolFilesInvalidError( message=f"{name} is a JSON protocol v{protocol_source.config.schema_version} which this robot cannot execute", diff --git a/api/src/opentrons/protocol_runner/json_translator.py b/api/src/opentrons/protocol_runner/json_translator.py index 767bf058730..f20bb3464d8 100644 --- a/api/src/opentrons/protocol_runner/json_translator.py +++ b/api/src/opentrons/protocol_runner/json_translator.py @@ -1,6 +1,7 @@ """Translation of JSON protocol commands into ProtocolEngine commands.""" -from typing import cast, List, Union, Iterator -from pydantic import parse_obj_as, ValidationError as PydanticValidationError + +from typing import List, Union, Iterator +from pydantic import ValidationError as PydanticValidationError, TypeAdapter from opentrons_shared_data.pipette.types import PipetteNameType from opentrons_shared_data.protocol.models import ( @@ -10,6 +11,8 @@ protocol_schema_v7, ProtocolSchemaV8, protocol_schema_v8, + Location, + # CommandSchemaId, ) from opentrons_shared_data import command as command_schema from opentrons_shared_data.errors.exceptions import InvalidProtocolData, PythonException @@ -31,6 +34,15 @@ class CommandTranslatorError(Exception): pass +# Each time a TypeAdapter is instantiated, it will construct a new validator and +# serializer. To improve performance, TypeAdapters are instantiated once. +# See https://docs.pydantic.dev/latest/concepts/performance/#typeadapter-instantiated-once +LabwareLocationAdapter: TypeAdapter[LabwareLocation] = TypeAdapter(LabwareLocation) +CommandAnnotationAdapter: TypeAdapter[CommandAnnotation] = TypeAdapter( + CommandAnnotation +) + + def _translate_labware_command( protocol: ProtocolSchemaV6, command: protocol_schema_v6.Command, @@ -41,6 +53,8 @@ def _translate_labware_command( assert labware_id is not None definition_id = protocol.labware[labware_id].definitionId assert definition_id is not None + + location = command.params.location labware_command = pe_commands.LoadLabwareCreate( params=pe_commands.LoadLabwareParams( labwareId=command.params.labwareId, @@ -48,10 +62,8 @@ def _translate_labware_command( version=protocol.labwareDefinitions[definition_id].version, namespace=protocol.labwareDefinitions[definition_id].namespace, loadName=protocol.labwareDefinitions[definition_id].parameters.loadName, - location=parse_obj_as( - # https://github.com/samuelcolvin/pydantic/issues/1847 - LabwareLocation, # type: ignore[arg-type] - command.params.location, + location=LabwareLocationAdapter.validate_python( + location.model_dump() if isinstance(location, Location) else location ), ), key=command.key, @@ -70,6 +82,7 @@ def _translate_v7_labware_command( assert command.params.namespace is not None assert command.params.loadName is not None + location = command.params.location labware_command = pe_commands.LoadLabwareCreate( params=pe_commands.LoadLabwareParams( labwareId=command.params.labwareId, @@ -77,10 +90,8 @@ def _translate_v7_labware_command( version=command.params.version, namespace=command.params.namespace, loadName=command.params.loadName, - location=parse_obj_as( - # https://github.com/samuelcolvin/pydantic/issues/1847 - LabwareLocation, # type: ignore[arg-type] - command.params.location, + location=LabwareLocationAdapter.validate_python( + location.model_dump() if isinstance(location, Location) else location ), ), key=command.key, @@ -98,10 +109,14 @@ def _translate_module_command( # load module command must contain module_id. modules cannot be None. assert module_id is not None assert modules is not None + + location = command.params.location translated_obj = pe_commands.LoadModuleCreate( params=pe_commands.LoadModuleParams( model=ModuleModel(modules[module_id].model), - location=DeckSlotLocation.parse_obj(command.params.location), + location=DeckSlotLocation.model_validate( + location.model_dump() if isinstance(location, Location) else location + ), moduleId=command.params.moduleId, ), key=command.key, @@ -117,10 +132,13 @@ def _translate_v7_module_command( # load module command must contain module_id. modules cannot be None. assert module_id is not None assert command.params.model is not None + location = command.params.location translated_obj = pe_commands.LoadModuleCreate( params=pe_commands.LoadModuleParams( model=ModuleModel(command.params.model), - location=DeckSlotLocation.parse_obj(command.params.location), + location=DeckSlotLocation.model_validate( + location.model_dump() if isinstance(location, Location) else location + ), moduleId=command.params.moduleId, ), key=command.key, @@ -171,9 +189,9 @@ def _translate_simple_command( protocol_schema_v6.Command, protocol_schema_v7.Command, protocol_schema_v8.Command, - ] + ], ) -> pe_commands.CommandCreate: - dict_command = command.dict(exclude_none=True) + dict_command = command.model_dump(exclude_none=True) # map deprecated `delay` commands to `waitForResume` / `waitForDuration` if dict_command["commandType"] == "delay": @@ -182,15 +200,7 @@ def _translate_simple_command( else: dict_command["commandType"] = "waitForDuration" - translated_obj = cast( - pe_commands.CommandCreate, - parse_obj_as( - # https://github.com/samuelcolvin/pydantic/issues/1847 - pe_commands.CommandCreate, # type: ignore[arg-type] - dict_command, - ), - ) - return translated_obj + return pe_commands.CommandCreateAdapter.validate_python(dict_command) class JsonTranslator: @@ -207,7 +217,7 @@ def translate_liquids( id=liquid_id, displayName=liquid.displayName, description=liquid.description, - displayColor=HexColor(__root__=liquid.displayColor) + displayColor=HexColor(liquid.displayColor) if liquid.displayColor is not None else None, ) @@ -294,9 +304,8 @@ def translate_command_annotations( return [] else: command_annotations: List[CommandAnnotation] = [ - parse_obj_as( - CommandAnnotation, # type: ignore[arg-type] - command_annotation.dict(), + CommandAnnotationAdapter.validate_python( + command_annotation.model_dump(), ) for command_annotation in protocol.commandAnnotations ] diff --git a/api/src/opentrons/protocol_runner/legacy_command_mapper.py b/api/src/opentrons/protocol_runner/legacy_command_mapper.py index 27b1c7ea331..f7d50e539d8 100644 --- a/api/src/opentrons/protocol_runner/legacy_command_mapper.py +++ b/api/src/opentrons/protocol_runner/legacy_command_mapper.py @@ -177,11 +177,13 @@ def map_command( # noqa: C901 completed_command: pe_commands.Command if command_error is None: if isinstance(running_command, pe_commands.PickUpTip): - completed_command = running_command.copy( + completed_command = running_command.model_copy( update={ - "result": pe_commands.PickUpTipResult.construct( + "result": pe_commands.PickUpTipResult.model_construct( tipVolume=command["payload"]["location"].max_volume, # type: ignore[typeddict-item] - tipLength=command["payload"]["instrument"].hw_pipette["tip_length"], # type: ignore[typeddict-item] + tipLength=command["payload"]["instrument"].hw_pipette[ # type: ignore[typeddict-item] + "tip_length" + ], position=pe_types.DeckPoint(x=0, y=0, z=0), ), "status": pe_commands.CommandStatus.SUCCEEDED, @@ -190,9 +192,9 @@ def map_command( # noqa: C901 } ) elif isinstance(running_command, pe_commands.DropTip): - completed_command = running_command.copy( + completed_command = running_command.model_copy( update={ - "result": pe_commands.DropTipResult.construct( + "result": pe_commands.DropTipResult.model_construct( position=pe_types.DeckPoint(x=0, y=0, z=0) ), "status": pe_commands.CommandStatus.SUCCEEDED, @@ -201,9 +203,9 @@ def map_command( # noqa: C901 } ) elif isinstance(running_command, pe_commands.Aspirate): - completed_command = running_command.copy( + completed_command = running_command.model_copy( update={ - # Don't .construct() result, because we want to validate + # Don't .model_construct() result, because we want to validate # volume. "result": pe_commands.AspirateResult( volume=running_command.params.volume, @@ -215,9 +217,9 @@ def map_command( # noqa: C901 } ) elif isinstance(running_command, pe_commands.Dispense): - completed_command = running_command.copy( + completed_command = running_command.model_copy( update={ - # Don't .construct() result, because we want to validate + # Don't .model_construct() result, because we want to validate # volume. "result": pe_commands.DispenseResult( volume=running_command.params.volume, @@ -229,9 +231,9 @@ def map_command( # noqa: C901 } ) elif isinstance(running_command, pe_commands.BlowOut): - completed_command = running_command.copy( + completed_command = running_command.model_copy( update={ - "result": pe_commands.BlowOutResult.construct( + "result": pe_commands.BlowOutResult.model_construct( position=pe_types.DeckPoint(x=0, y=0, z=0) ), "status": pe_commands.CommandStatus.SUCCEEDED, @@ -240,18 +242,18 @@ def map_command( # noqa: C901 } ) elif isinstance(running_command, pe_commands.Comment): - completed_command = running_command.copy( + completed_command = running_command.model_copy( update={ - "result": pe_commands.CommentResult.construct(), + "result": pe_commands.CommentResult.model_construct(), "status": pe_commands.CommandStatus.SUCCEEDED, "completedAt": now, "notes": [], } ) elif isinstance(running_command, pe_commands.Custom): - completed_command = running_command.copy( + completed_command = running_command.model_copy( update={ - "result": pe_commands.CustomResult.construct(), + "result": pe_commands.CustomResult.model_construct(), "status": pe_commands.CommandStatus.SUCCEEDED, "completedAt": now, "notes": [], @@ -261,7 +263,7 @@ def map_command( # noqa: C901 # TODO(mm, 2024-06-13): This looks potentially wrong. # We're creating a `SUCCEEDED` command that does not have a `result`, # which is not normally possible. - completed_command = running_command.copy( + completed_command = running_command.model_copy( update={ "status": pe_commands.CommandStatus.SUCCEEDED, "completedAt": now, @@ -331,51 +333,51 @@ def _build_initial_command( elif command["name"] == legacy_command_types.BLOW_OUT: return self._build_blow_out(command=command, command_id=command_id, now=now) elif command["name"] == legacy_command_types.PAUSE: - wait_for_resume_running = pe_commands.WaitForResume.construct( + wait_for_resume_running = pe_commands.WaitForResume.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - params=pe_commands.WaitForResumeParams.construct( + params=pe_commands.WaitForResumeParams.model_construct( message=command["payload"]["userMessage"], ), ) wait_for_resume_create: pe_commands.CommandCreate = ( - pe_commands.WaitForResumeCreate.construct( + pe_commands.WaitForResumeCreate.model_construct( key=wait_for_resume_running.key, params=wait_for_resume_running.params, ) ) return wait_for_resume_create, wait_for_resume_running elif command["name"] == legacy_command_types.COMMENT: - comment_running = pe_commands.Comment.construct( + comment_running = pe_commands.Comment.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - params=pe_commands.CommentParams.construct( + params=pe_commands.CommentParams.model_construct( message=command["payload"]["text"], ), ) - comment_create = pe_commands.CommentCreate.construct( + comment_create = pe_commands.CommentCreate.model_construct( key=comment_running.key, params=comment_running.params ) return comment_create, comment_running else: - custom_running = pe_commands.Custom.construct( + custom_running = pe_commands.Custom.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - params=LegacyCommandParams.construct( + params=LegacyCommandParams.model_construct( legacyCommandType=command["name"], legacyCommandText=command["payload"]["text"], ), ) - custom_create = pe_commands.CustomCreate.construct( + custom_create = pe_commands.CustomCreate.model_construct( key=custom_running.key, params=custom_running.params, ) @@ -396,19 +398,19 @@ def _build_drop_tip( labware_id = self._labware_id_by_slot[slot] pipette_id = self._pipette_id_by_mount[mount] - running = pe_commands.DropTip.construct( + running = pe_commands.DropTip.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - params=pe_commands.DropTipParams.construct( + params=pe_commands.DropTipParams.model_construct( pipetteId=pipette_id, labwareId=labware_id, wellName=well_name, ), ) - create = pe_commands.DropTipCreate.construct( + create = pe_commands.DropTipCreate.model_construct( key=running.key, params=running.params, ) @@ -430,19 +432,19 @@ def _build_pick_up_tip( labware_id = self._labware_id_by_slot[slot] pipette_id = self._pipette_id_by_mount[mount] - running = pe_commands.PickUpTip.construct( + running = pe_commands.PickUpTip.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - params=pe_commands.PickUpTipParams.construct( + params=pe_commands.PickUpTipParams.model_construct( pipetteId=pipette_id, labwareId=labware_id, wellName=well_name, ), ) - create = pe_commands.PickUpTipCreate.construct( + create = pe_commands.PickUpTipCreate.model_construct( key=running.key, params=running.params ) return create, running @@ -482,31 +484,31 @@ def _build_liquid_handling( # TODO(mm, 2024-03-22): I don't think this has been true since # https://github.com/Opentrons/opentrons/pull/14211. Can we just use # aspirate and dispense commands now? - move_to_well_running = pe_commands.MoveToWell.construct( + move_to_well_running = pe_commands.MoveToWell.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - params=pe_commands.MoveToWellParams.construct( + params=pe_commands.MoveToWellParams.model_construct( pipetteId=pipette_id, labwareId=labware_id, wellName=well_name, ), ) - move_to_well_create = pe_commands.MoveToWellCreate.construct( + move_to_well_create = pe_commands.MoveToWellCreate.model_construct( key=move_to_well_running.key, params=move_to_well_running.params ) return move_to_well_create, move_to_well_running elif command["name"] == legacy_command_types.ASPIRATE: flow_rate = command["payload"]["rate"] * pipette.flow_rate.aspirate - aspirate_running = pe_commands.Aspirate.construct( + aspirate_running = pe_commands.Aspirate.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - # Don't .construct() params, because we want to validate + # Don't .model_construct() params, because we want to validate # volume and flowRate. params=pe_commands.AspirateParams( pipetteId=pipette_id, @@ -516,19 +518,19 @@ def _build_liquid_handling( flowRate=flow_rate, ), ) - aspirate_create = pe_commands.AspirateCreate.construct( + aspirate_create = pe_commands.AspirateCreate.model_construct( key=aspirate_running.key, params=aspirate_running.params ) return aspirate_create, aspirate_running else: flow_rate = command["payload"]["rate"] * pipette.flow_rate.dispense - dispense_running = pe_commands.Dispense.construct( + dispense_running = pe_commands.Dispense.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - # Don't .construct params, because we want to validate + # Don't .model_construct params, because we want to validate # volume and flowRate. params=pe_commands.DispenseParams( pipetteId=pipette_id, @@ -538,24 +540,24 @@ def _build_liquid_handling( flowRate=flow_rate, ), ) - dispense_create = pe_commands.DispenseCreate.construct( + dispense_create = pe_commands.DispenseCreate.model_construct( key=dispense_running.key, params=dispense_running.params ) return dispense_create, dispense_running else: - running = pe_commands.Custom.construct( + running = pe_commands.Custom.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - params=LegacyCommandParams.construct( + params=LegacyCommandParams.model_construct( legacyCommandType=command["name"], legacyCommandText=command["payload"]["text"], ), ) - create = pe_commands.CustomCreate.construct( + create = pe_commands.CustomCreate.model_construct( key=running.key, params=running.params ) return create, running @@ -584,13 +586,13 @@ def _build_blow_out( well_name = well.well_name pipette_id = self._pipette_id_by_mount[mount] - blow_out_running = pe_commands.BlowOut.construct( + blow_out_running = pe_commands.BlowOut.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - # Don't .construct() params, because we want to validate flowRate. + # Don't .model_construct() params, because we want to validate flowRate. params=pe_commands.BlowOutParams( pipetteId=pipette_id, labwareId=labware_id, @@ -598,7 +600,7 @@ def _build_blow_out( flowRate=flow_rate, ), ) - blow_out_create = pe_commands.BlowOutCreate.construct( + blow_out_create = pe_commands.BlowOutCreate.model_construct( key=blow_out_running.key, params=blow_out_running.params ) return blow_out_create, blow_out_running @@ -606,18 +608,18 @@ def _build_blow_out( # TODO:(jr, 15.08.2022): blow_out commands with no specified labware get filtered # into custom. Refactor this in followup legacy command mapping else: - custom_running = pe_commands.Custom.construct( + custom_running = pe_commands.Custom.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.RUNNING, createdAt=now, startedAt=now, - params=LegacyCommandParams.construct( + params=LegacyCommandParams.model_construct( legacyCommandType=command["name"], legacyCommandText=command["payload"]["text"], ), ) - custom_create = pe_commands.CustomCreate.construct( + custom_create = pe_commands.CustomCreate.model_construct( key=custom_running.key, params=custom_running.params ) return custom_create, custom_running @@ -631,23 +633,23 @@ def _map_labware_load( slot = labware_load_info.deck_slot location: pe_types.LabwareLocation if labware_load_info.on_module: - location = pe_types.ModuleLocation.construct( + location = pe_types.ModuleLocation.model_construct( moduleId=self._module_id_by_slot[slot] ) else: - location = pe_types.DeckSlotLocation.construct(slotName=slot) + location = pe_types.DeckSlotLocation.model_construct(slotName=slot) command_id = f"commands.LOAD_LABWARE-{count}" labware_id = f"labware-{count}" - succeeded_command = pe_commands.LoadLabware.construct( + succeeded_command = pe_commands.LoadLabware.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.SUCCEEDED, createdAt=now, startedAt=now, completedAt=now, - params=pe_commands.LoadLabwareParams.construct( + params=pe_commands.LoadLabwareParams.model_construct( location=location, loadName=labware_load_info.labware_load_name, namespace=labware_load_info.labware_namespace, @@ -655,9 +657,9 @@ def _map_labware_load( displayName=labware_load_info.labware_display_name, ), notes=[], - result=pe_commands.LoadLabwareResult.construct( + result=pe_commands.LoadLabwareResult.model_construct( labwareId=labware_id, - definition=LabwareDefinition.parse_obj( + definition=LabwareDefinition.model_validate( labware_load_info.labware_definition ), offsetId=labware_load_info.offset_id, @@ -666,7 +668,7 @@ def _map_labware_load( queue_action = pe_actions.QueueCommandAction( command_id=succeeded_command.id, created_at=succeeded_command.createdAt, - request=pe_commands.LoadLabwareCreate.construct( + request=pe_commands.LoadLabwareCreate.model_construct( key=succeeded_command.key, params=succeeded_command.params ), request_hash=None, @@ -714,19 +716,19 @@ def _map_instrument_load( pipette_id = f"pipette-{count}" mount = MountType(str(instrument_load_info.mount).lower()) - succeeded_command = pe_commands.LoadPipette.construct( + succeeded_command = pe_commands.LoadPipette.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.SUCCEEDED, createdAt=now, startedAt=now, completedAt=now, - params=pe_commands.LoadPipetteParams.construct( + params=pe_commands.LoadPipetteParams.model_construct( pipetteName=PipetteNameType(instrument_load_info.instrument_load_name), mount=mount, ), notes=[], - result=pe_commands.LoadPipetteResult.construct(pipetteId=pipette_id), + result=pe_commands.LoadPipetteResult.model_construct(pipetteId=pipette_id), ) serial = instrument_load_info.pipette_dict.get("pipette_id", None) or "" state_update = StateUpdate() @@ -749,7 +751,7 @@ def _map_instrument_load( queue_action = pe_actions.QueueCommandAction( command_id=succeeded_command.id, created_at=succeeded_command.createdAt, - request=pe_commands.LoadPipetteCreate.construct( + request=pe_commands.LoadPipetteCreate.model_construct( key=succeeded_command.key, params=succeeded_command.params ), request_hash=None, @@ -791,14 +793,14 @@ def _map_module_load( loaded_model ) or self._module_data_provider.get_definition(loaded_model) - succeeded_command = pe_commands.LoadModule.construct( + succeeded_command = pe_commands.LoadModule.model_construct( id=command_id, key=command_id, status=pe_commands.CommandStatus.SUCCEEDED, createdAt=now, startedAt=now, completedAt=now, - params=pe_commands.LoadModuleParams.construct( + params=pe_commands.LoadModuleParams.model_construct( model=requested_model, location=pe_types.DeckSlotLocation( slotName=module_load_info.deck_slot, @@ -806,7 +808,7 @@ def _map_module_load( moduleId=module_id, ), notes=[], - result=pe_commands.LoadModuleResult.construct( + result=pe_commands.LoadModuleResult.model_construct( moduleId=module_id, serialNumber=module_load_info.module_serial, definition=loaded_definition, @@ -816,7 +818,7 @@ def _map_module_load( queue_action = pe_actions.QueueCommandAction( command_id=succeeded_command.id, created_at=succeeded_command.createdAt, - request=pe_commands.LoadModuleCreate.construct( + request=pe_commands.LoadModuleCreate.model_construct( key=succeeded_command.key, params=succeeded_command.params ), request_hash=None, diff --git a/api/src/opentrons/protocol_runner/python_protocol_wrappers.py b/api/src/opentrons/protocol_runner/python_protocol_wrappers.py index f20012f1dfe..ce063013878 100644 --- a/api/src/opentrons/protocol_runner/python_protocol_wrappers.py +++ b/api/src/opentrons/protocol_runner/python_protocol_wrappers.py @@ -66,7 +66,7 @@ def read( namespace=lw.namespace, load_name=lw.parameters.loadName, version=lw.version, - ): cast(LabwareDefinitionTypedDict, lw.dict(exclude_none=True)) + ): cast(LabwareDefinitionTypedDict, lw.model_dump(exclude_none=True)) for lw in labware_definitions } data_file_paths = [ diff --git a/api/src/opentrons/protocols/labware.py b/api/src/opentrons/protocols/labware.py index 2fcffaaf2d4..d3159ee5d9f 100644 --- a/api/src/opentrons/protocols/labware.py +++ b/api/src/opentrons/protocols/labware.py @@ -3,7 +3,6 @@ import logging import json import os - from pathlib import Path from typing import Any, AnyStr, Dict, Optional, Union, List @@ -139,14 +138,24 @@ def verify_definition( :raises jsonschema.ValidationError: If the definition is not valid. :returns: The parsed definition """ - schema_body = load_shared_data("labware/schemas/2.json").decode("utf-8") - labware_schema_v2 = json.loads(schema_body) + schemata_by_version = { + 2: json.loads(load_shared_data("labware/schemas/2.json").decode("utf-8")), + 3: json.loads(load_shared_data("labware/schemas/3.json").decode("utf-8")), + } if isinstance(contents, dict): to_return = contents else: to_return = json.loads(contents) - jsonschema.validate(to_return, labware_schema_v2) + try: + schema_version = to_return["schemaVersion"] + schema = schemata_by_version[schema_version] + except KeyError: + raise RuntimeError( + f'Invalid or unknown labware schema version {to_return.get("schemaVersion", None)}' + ) + jsonschema.validate(to_return, schema) + # we can type ignore this because if it passes the jsonschema it has # the correct structure return to_return # type: ignore[return-value] @@ -201,7 +210,6 @@ def _get_labware_definition_from_bundle( def _get_standard_labware_definition( load_name: str, namespace: Optional[str] = None, version: Optional[int] = None ) -> LabwareDefinition: - if version is None: checked_version = 1 else: diff --git a/api/src/opentrons/protocols/models/json_protocol.py b/api/src/opentrons/protocols/models/json_protocol.py index 979d0192f62..ef2fec8823d 100644 --- a/api/src/opentrons/protocols/models/json_protocol.py +++ b/api/src/opentrons/protocols/models/json_protocol.py @@ -9,7 +9,7 @@ from typing import Any, Dict, List, Optional, Union -from pydantic import BaseModel, Extra, Field +from pydantic import ConfigDict, BaseModel, Field from typing_extensions import Literal from opentrons_shared_data.labware.labware_definition import LabwareDefinition @@ -75,8 +75,7 @@ class Metadata(BaseModel): Optional metadata about the protocol """ - class Config: - extra = Extra.allow + model_config = ConfigDict(extra="allow") protocolName: Optional[str] = Field( None, description="A short, human-readable name for the protocol" @@ -574,8 +573,7 @@ class Pipettes(BaseModel): Fields describing an individual pipette """ - class Config: - extra = Extra.allow + model_config = ConfigDict(extra="allow") mount: Literal["left", "right"] = Field( ..., description="Where the pipette is mounted" @@ -592,8 +590,7 @@ class Labware(BaseModel): Fields describing a single labware on the deck """ - class Config: - extra = Extra.allow + model_config = ConfigDict(extra="allow") slot: str = Field( ..., @@ -616,8 +613,7 @@ class Modules(BaseModel): Fields describing a single module on the deck """ - class Config: - extra = Extra.allow + model_config = ConfigDict(extra="allow") slot: str = Field( ..., diff --git a/api/src/opentrons/simulate.py b/api/src/opentrons/simulate.py index e565bab83e0..bed24c68731 100644 --- a/api/src/opentrons/simulate.py +++ b/api/src/opentrons/simulate.py @@ -829,7 +829,9 @@ def _create_live_context_pe( # Non-async would use call_soon_threadsafe(), which makes the waiting harder. async def add_all_extra_labware() -> None: for labware_definition_dict in extra_labware.values(): - labware_definition = LabwareDefinition.parse_obj(labware_definition_dict) + labware_definition = LabwareDefinition.model_validate( + labware_definition_dict + ) pe.add_labware_definition(labware_definition) # Add extra_labware to ProtocolEngine, being careful not to modify ProtocolEngine from this diff --git a/api/src/opentrons/types.py b/api/src/opentrons/types.py index 1f73d63c8c6..09e138513c1 100644 --- a/api/src/opentrons/types.py +++ b/api/src/opentrons/types.py @@ -160,7 +160,7 @@ def __iter__(self) -> Iterator[Union[Point, LabwareLike]]: point, labware = location some_function_taking_both(*location) """ - return iter((self._point, self._labware)) # type: ignore [arg-type] + return iter((self._point, self._labware)) def __eq__(self, other: object) -> bool: return ( diff --git a/api/tests/opentrons/calibration_storage/test_deck_attitude.py b/api/tests/opentrons/calibration_storage/test_deck_attitude.py index bbb832651d1..bce3ae02809 100644 --- a/api/tests/opentrons/calibration_storage/test_deck_attitude.py +++ b/api/tests/opentrons/calibration_storage/test_deck_attitude.py @@ -57,7 +57,7 @@ def test_save_ot2_deck_attitude(ot_config_tempdir: Any) -> None: "pip1", "mytiprack", ) - assert get_robot_deck_attitude() != {} + assert get_robot_deck_attitude() is not None def test_save_ot3_deck_attitude(ot_config_tempdir: Any) -> None: diff --git a/api/tests/opentrons/calibration_storage/test_file_operators.py b/api/tests/opentrons/calibration_storage/test_file_operators.py index 5a95f225fe3..ec25a2279c1 100644 --- a/api/tests/opentrons/calibration_storage/test_file_operators.py +++ b/api/tests/opentrons/calibration_storage/test_file_operators.py @@ -84,7 +84,7 @@ def test_deserialize_pydantic_model_valid() -> None: serialized = b'{"integer_field": 123, "! aliased field !": "abc"}' assert io.deserialize_pydantic_model( serialized, DummyModel - ) == DummyModel.construct(integer_field=123, aliased_field="abc") + ) == DummyModel.model_construct(integer_field=123, aliased_field="abc") def test_deserialize_pydantic_model_invalid_as_json() -> None: diff --git a/api/tests/opentrons/calibration_storage/test_tip_length_ot2.py b/api/tests/opentrons/calibration_storage/test_tip_length_ot2.py index df503241d75..51096866b5d 100644 --- a/api/tests/opentrons/calibration_storage/test_tip_length_ot2.py +++ b/api/tests/opentrons/calibration_storage/test_tip_length_ot2.py @@ -46,7 +46,7 @@ def starting_calibration_data( "tipLength": 27, "lastModified": inside_data.lastModified.isoformat(), "source": inside_data.source, - "status": inside_data.status.dict(), + "status": inside_data.status.model_dump(), "uri": "dummy_namespace/minimal_labware_def/1", } } diff --git a/api/tests/opentrons/drivers/flex_stacker/__init__.py b/api/tests/opentrons/drivers/flex_stacker/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/api/tests/opentrons/drivers/flex_stacker/test_driver.py b/api/tests/opentrons/drivers/flex_stacker/test_driver.py new file mode 100644 index 00000000000..aea2492cf9e --- /dev/null +++ b/api/tests/opentrons/drivers/flex_stacker/test_driver.py @@ -0,0 +1,257 @@ +import pytest +from mock import AsyncMock +from opentrons.drivers.asyncio.communication.serial_connection import ( + AsyncResponseSerialConnection, +) +from opentrons.drivers.flex_stacker.driver import FlexStackerDriver +from opentrons.drivers.flex_stacker import types + + +@pytest.fixture +def connection() -> AsyncMock: + return AsyncMock(spec=AsyncResponseSerialConnection) + + +@pytest.fixture +def subject(connection: AsyncMock) -> FlexStackerDriver: + connection.send_command.return_value = "" + return FlexStackerDriver(connection) + + +async def test_get_device_info( + subject: FlexStackerDriver, connection: AsyncMock +) -> None: + """It should send a get device info command""" + connection.send_command.return_value = ( + "M115 FW:0.0.1 HW:Opentrons-flex-stacker-a1 SerialNo:STCA120230605001" + ) + response = await subject.get_device_info() + assert response == types.StackerInfo( + fw="0.0.1", + hw=types.HardwareRevision.EVT, + sn="STCA120230605001", + ) + + device_info = types.GCODE.DEVICE_INFO.build_command() + reset_reason = types.GCODE.GET_RESET_REASON.build_command() + connection.send_command.assert_any_call(device_info) + connection.send_command.assert_called_with(reset_reason) + connection.reset_mock() + + # Test invalid response + connection.send_command.return_value = "M115 FW:0.0.1 SerialNo:STCA120230605001" + + # This should raise ValueError + with pytest.raises(ValueError): + response = await subject.get_device_info() + + device_info = types.GCODE.DEVICE_INFO.build_command() + reset_reason = types.GCODE.GET_RESET_REASON.build_command() + connection.send_command.assert_any_call(device_info) + connection.send_command.assert_called_with(reset_reason) + + +async def test_stop_motors(subject: FlexStackerDriver, connection: AsyncMock) -> None: + """It should send a stop motors command""" + connection.send_command.return_value = "M0" + response = await subject.stop_motors() + assert response + + stop_motors = types.GCODE.STOP_MOTORS.build_command() + connection.send_command.assert_any_call(stop_motors) + connection.reset_mock() + + # This should raise ValueError + with pytest.raises(ValueError): + await subject.get_device_info() + + +async def test_set_serial_number( + subject: FlexStackerDriver, connection: AsyncMock +) -> None: + """It should send a set serial number command""" + connection.send_command.return_value = "M996" + + serial_number = "Something" + response = await subject.set_serial_number(serial_number) + assert response + + set_serial_number = types.GCODE.SET_SERIAL_NUMBER.build_command().add_element( + serial_number + ) + connection.send_command.assert_any_call(set_serial_number) + connection.reset_mock() + + # Test invalid response + connection.send_command.return_value = "M9nn" + with pytest.raises(ValueError): + response = await subject.set_serial_number(serial_number) + + set_serial_number = types.GCODE.SET_SERIAL_NUMBER.build_command().add_element( + serial_number + ) + connection.send_command.assert_any_call(set_serial_number) + connection.reset_mock() + + +async def test_get_limit_switch( + subject: FlexStackerDriver, connection: AsyncMock +) -> None: + """It should send a get limit switch command and return the boolean of one.""" + connection.send_command.return_value = "M119 XE:1 XR:0 ZE:0 ZR:1 LR:1" + response = await subject.get_limit_switch( + types.StackerAxis.X, types.Direction.EXTENT + ) + assert response + + limit_switch_status = types.GCODE.GET_LIMIT_SWITCH.build_command() + connection.send_command.assert_any_call(limit_switch_status) + connection.reset_mock() + + +async def test_get_limit_switches_status( + subject: FlexStackerDriver, connection: AsyncMock +) -> None: + """It should send a get limit switch status and return LimitSwitchStatus.""" + connection.send_command.return_value = "M119 XE:1 XR:0 ZE:0 ZR:1 LR:1" + response = await subject.get_limit_switches_status() + assert response == types.LimitSwitchStatus( + XE=True, + XR=False, + ZE=False, + ZR=True, + LR=True, + ) + + limit_switch_status = types.GCODE.GET_LIMIT_SWITCH.build_command() + connection.send_command.assert_any_call(limit_switch_status) + connection.reset_mock() + + # Test invalid response + connection.send_command.return_value = "M119 XE:b XR:0 ZE:a ZR:1 LR:n" + with pytest.raises(ValueError): + response = await subject.get_limit_switches_status() + + limit_switch_status = types.GCODE.GET_LIMIT_SWITCH.build_command() + connection.send_command.assert_any_call(limit_switch_status) + + +async def test_get_platform_sensor( + subject: FlexStackerDriver, connection: AsyncMock +) -> None: + """It should send a get platform sensor command return status of specified sensor.""" + connection.send_command.return_value = "M121 E:1 R:1" + response = await subject.get_platform_sensor(types.Direction.EXTENT) + assert response + + platform_sensor = types.GCODE.GET_PLATFORM_SENSOR.build_command() + connection.send_command.assert_any_call(platform_sensor) + connection.reset_mock() + + +async def test_get_platform_status( + subject: FlexStackerDriver, connection: AsyncMock +) -> None: + """it should send a get platform sensors status.""" + connection.send_command.return_value = "M121 E:0 R:1" + response = await subject.get_platform_status() + assert response == types.PlatformStatus( + E=False, + R=True, + ) + + platform_status = types.GCODE.GET_PLATFORM_SENSOR.build_command() + connection.send_command.assert_any_call(platform_status) + connection.reset_mock() + + # Test invalid response + connection.send_command.return_value = "M121 E:0 R:1 something" + with pytest.raises(ValueError): + response = await subject.get_platform_status() + + platform_status = types.GCODE.GET_PLATFORM_SENSOR.build_command() + connection.send_command.assert_any_call(platform_status) + + +async def test_get_hopper_door_closed( + subject: FlexStackerDriver, connection: AsyncMock +) -> None: + """It should send a get door closed command.""" + connection.send_command.return_value = "M122 D:1" + response = await subject.get_hopper_door_closed() + assert response + + door_closed = types.GCODE.GET_DOOR_SWITCH.build_command() + connection.send_command.assert_any_call(door_closed) + connection.reset_mock() + + # Test door open + connection.send_command.return_value = "M122 D:0" + response = await subject.get_hopper_door_closed() + assert not response + + door_closed = types.GCODE.GET_DOOR_SWITCH.build_command() + connection.send_command.assert_any_call(door_closed) + connection.reset_mock() + + # Test invalid response + connection.send_command.return_value = "M122 78gybhjk" + + with pytest.raises(ValueError): + response = await subject.get_hopper_door_closed() + + door_closed = types.GCODE.GET_DOOR_SWITCH.build_command() + connection.send_command.assert_any_call(door_closed) + connection.reset_mock() + + +async def test_move_in_mm(subject: FlexStackerDriver, connection: AsyncMock) -> None: + """It should send a move to command""" + connection.send_command.return_value = "G0" + response = await subject.move_in_mm(types.StackerAxis.X, 10) + assert response + + move_to = types.GCODE.MOVE_TO.build_command().add_float("X", 10) + connection.send_command.assert_any_call(move_to) + connection.reset_mock() + + +async def test_move_to_switch( + subject: FlexStackerDriver, connection: AsyncMock +) -> None: + """It should send a move to switch command""" + connection.send_command.return_value = "G5" + axis = types.StackerAxis.X + direction = types.Direction.EXTENT + response = await subject.move_to_limit_switch(axis, direction) + assert response + + move_to = types.GCODE.MOVE_TO_SWITCH.build_command().add_int( + axis.name, direction.value + ) + connection.send_command.assert_any_call(move_to) + connection.reset_mock() + + +async def test_home_axis(subject: FlexStackerDriver, connection: AsyncMock) -> None: + """It should send a home axis command""" + connection.send_command.return_value = "G28" + axis = types.StackerAxis.X + direction = types.Direction.EXTENT + response = await subject.home_axis(axis, direction) + assert response + + move_to = types.GCODE.HOME_AXIS.build_command().add_int(axis.name, direction.value) + connection.send_command.assert_any_call(move_to) + connection.reset_mock() + + +async def test_set_led(subject: FlexStackerDriver, connection: AsyncMock) -> None: + """It should send a set led command""" + connection.send_command.return_value = "M200" + response = await subject.set_led(1, types.LEDColor.RED) + assert response + + set_led = types.GCODE.SET_LED.build_command().add_float("P", 1).add_int("C", 1) + connection.send_command.assert_any_call(set_led) + connection.reset_mock() diff --git a/api/tests/opentrons/hardware_control/backends/test_ot3_controller.py b/api/tests/opentrons/hardware_control/backends/test_ot3_controller.py index 9c03bed68b2..9bd87fe62ec 100644 --- a/api/tests/opentrons/hardware_control/backends/test_ot3_controller.py +++ b/api/tests/opentrons/hardware_control/backends/test_ot3_controller.py @@ -745,6 +745,7 @@ async def test_liquid_probe( threshold_pascals=fake_liquid_settings.sensor_threshold_pascals, plunger_impulse_time=fake_liquid_settings.plunger_impulse_time, num_baseline_reads=fake_liquid_settings.samples_for_baselining, + z_offset_for_plunger_prep=2.0, ) except PipetteLiquidNotFoundError: # the move raises a liquid not found now since we don't call the move group and it doesn't diff --git a/api/tests/opentrons/hardware_control/instruments/test_instrument_calibration.py b/api/tests/opentrons/hardware_control/instruments/test_instrument_calibration.py index 6f9ad72c460..fd746ed9743 100644 --- a/api/tests/opentrons/hardware_control/instruments/test_instrument_calibration.py +++ b/api/tests/opentrons/hardware_control/instruments/test_instrument_calibration.py @@ -55,10 +55,10 @@ def tip_rack_dict() -> LabwareDefDict: @pytest.fixture def tip_rack_model() -> LabwareDefinition: """Get a tip rack Pydantic model definition value object.""" - return LabwareDefinition.construct( # type: ignore[call-arg] + return LabwareDefinition.model_construct( # type: ignore[call-arg] namespace="test", version=1, - parameters=Parameters.construct( # type: ignore[call-arg] + parameters=Parameters.model_construct( # type: ignore[call-arg] loadName="cool-labware", tipOverlap=None, # add a None value to validate serialization to dictionary ), diff --git a/api/tests/opentrons/hardware_control/instruments/test_nozzle_manager.py b/api/tests/opentrons/hardware_control/instruments/test_nozzle_manager.py index ba1f10aaaef..066cdbc3caf 100644 --- a/api/tests/opentrons/hardware_control/instruments/test_nozzle_manager.py +++ b/api/tests/opentrons/hardware_control/instruments/test_nozzle_manager.py @@ -10,6 +10,7 @@ PipetteModelType, PipetteChannelType, PipetteVersionType, + PipetteOEMType, ) from opentrons_shared_data.pipette.pipette_definition import ( PipetteConfigurations, @@ -261,7 +262,10 @@ def test_single_pipettes_always_full( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.SINGLE_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.SINGLE_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, ValidNozzleMaps(maps=A1) @@ -289,7 +293,10 @@ def test_single_pipette_map_entries( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.SINGLE_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.SINGLE_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, ValidNozzleMaps(maps=A1) @@ -326,7 +333,10 @@ def test_single_pipette_map_geometry( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.SINGLE_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.SINGLE_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, ValidNozzleMaps(maps=A1) @@ -359,7 +369,10 @@ def test_multi_config_identification( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.EIGHT_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.EIGHT_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, @@ -419,7 +432,10 @@ def test_multi_config_map_entries( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.EIGHT_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.EIGHT_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, @@ -485,7 +501,10 @@ def test_multi_config_geometry( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.EIGHT_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.EIGHT_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, @@ -536,7 +555,10 @@ def test_96_config_identification( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.NINETY_SIX_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.NINETY_SIX_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, @@ -651,7 +673,10 @@ def test_96_config_map_entries( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.NINETY_SIX_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.NINETY_SIX_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, @@ -988,7 +1013,10 @@ def test_96_config_geometry( pipette_details: Tuple[PipetteModelType, PipetteVersionType], ) -> None: config = load_definition( - pipette_details[0], PipetteChannelType.NINETY_SIX_CHANNEL, pipette_details[1] + pipette_details[0], + PipetteChannelType.NINETY_SIX_CHANNEL, + pipette_details[1], + PipetteOEMType.OT, ) subject = nozzle_manager.NozzleConfigurationManager.build_from_config( config, diff --git a/api/tests/opentrons/hardware_control/test_gripper.py b/api/tests/opentrons/hardware_control/test_gripper.py index f084a11df89..fdce4f27d3d 100644 --- a/api/tests/opentrons/hardware_control/test_gripper.py +++ b/api/tests/opentrons/hardware_control/test_gripper.py @@ -104,8 +104,13 @@ def test_reload_instrument_cal_ot3_conf_changed( "fakeid123", jaw_max_offset=15, ) - new_conf = fake_gripper_conf.copy( - update={"grip_force_profile": {"default_grip_force": 1}} + new_conf = fake_gripper_conf.model_copy( + update={ + "grip_force_profile": fake_gripper_conf.grip_force_profile.model_copy( + update={"default_grip_force": 1} + ) + }, + deep=True, ) assert new_conf != old_gripper.config diff --git a/api/tests/opentrons/hardware_control/test_ot3_api.py b/api/tests/opentrons/hardware_control/test_ot3_api.py index 2fd3fb4377c..200549108db 100644 --- a/api/tests/opentrons/hardware_control/test_ot3_api.py +++ b/api/tests/opentrons/hardware_control/test_ot3_api.py @@ -82,6 +82,7 @@ PipetteChannelType, PipetteVersionType, LiquidClasses, + PipetteOEMType, ) from opentrons_shared_data.pipette import ( load_data as load_pipette_data, @@ -381,6 +382,7 @@ class PipetteLoadConfig(TypedDict): channels: Literal[1, 8, 96] version: Tuple[Literal[1, 2, 3], Literal[0, 1, 2, 3, 4, 5, 6]] model: PipetteModel + oem_type: PipetteOEMType class GripperLoadConfig(TypedDict): @@ -402,8 +404,24 @@ class GripperLoadConfig(TypedDict): ( ( [ - (OT3Mount.RIGHT, {"channels": 8, "version": (3, 3), "model": "p50"}), - (OT3Mount.LEFT, {"channels": 1, "version": (3, 3), "model": "p1000"}), + ( + OT3Mount.RIGHT, + { + "channels": 8, + "version": (3, 3), + "model": "p50", + "oem_type": PipetteOEMType.OT, + }, + ), + ( + OT3Mount.LEFT, + { + "channels": 1, + "version": (3, 3), + "model": "p1000", + "oem_type": PipetteOEMType.OT, + }, + ), ], GantryLoad.LOW_THROUGHPUT, ), @@ -413,34 +431,88 @@ class GripperLoadConfig(TypedDict): GantryLoad.LOW_THROUGHPUT, ), ( - [(OT3Mount.LEFT, {"channels": 8, "version": (3, 3), "model": "p1000"})], + [ + ( + OT3Mount.LEFT, + { + "channels": 8, + "version": (3, 3), + "model": "p1000", + "oem_type": "ot", + }, + ) + ], GantryLoad.LOW_THROUGHPUT, ), ( - [(OT3Mount.RIGHT, {"channels": 8, "version": (3, 3), "model": "p1000"})], + [ + ( + OT3Mount.RIGHT, + { + "channels": 8, + "version": (3, 3), + "model": "p1000", + "oem_type": "ot", + }, + ) + ], GantryLoad.LOW_THROUGHPUT, ), ( - [(OT3Mount.LEFT, {"channels": 96, "model": "p1000", "version": (3, 3)})], + [ + ( + OT3Mount.LEFT, + { + "channels": 96, + "model": "p1000", + "version": (3, 3), + "oem_type": "ot", + }, + ) + ], GantryLoad.HIGH_THROUGHPUT, ), ( [ - (OT3Mount.LEFT, {"channels": 1, "version": (3, 3), "model": "p1000"}), + ( + OT3Mount.LEFT, + { + "channels": 1, + "version": (3, 3), + "model": "p1000", + "oem_type": "ot", + }, + ), (OT3Mount.GRIPPER, {"model": GripperModel.v1, "id": "g12345"}), ], GantryLoad.LOW_THROUGHPUT, ), ( [ - (OT3Mount.RIGHT, {"channels": 8, "version": (3, 3), "model": "p1000"}), + ( + OT3Mount.RIGHT, + { + "channels": 8, + "version": (3, 3), + "model": "p1000", + "oem_type": "ot", + }, + ), (OT3Mount.GRIPPER, {"model": GripperModel.v1, "id": "g12345"}), ], GantryLoad.LOW_THROUGHPUT, ), ( [ - (OT3Mount.LEFT, {"channels": 96, "model": "p1000", "version": (3, 3)}), + ( + OT3Mount.LEFT, + { + "channels": 96, + "model": "p1000", + "version": (3, 3), + "oem_type": "ot", + }, + ), (OT3Mount.GRIPPER, {"model": GripperModel.v1, "id": "g12345"}), ], GantryLoad.HIGH_THROUGHPUT, @@ -463,6 +535,7 @@ async def test_gantry_load_transform( PipetteModelType(pair[1]["model"]), PipetteChannelType(pair[1]["channels"]), PipetteVersionType(*pair[1]["version"]), + PipetteOEMType(pair[1]["oem_type"]), ) instr_data = AttachedPipette(config=pipette_config, id="fakepip") await ot3_hardware.cache_pipette(pair[0], instr_data, None) @@ -557,9 +630,30 @@ def mock_verify_tip_presence( load_pipette_configs = [ - {OT3Mount.LEFT: {"channels": 1, "version": (3, 3), "model": "p1000"}}, - {OT3Mount.RIGHT: {"channels": 8, "version": (3, 3), "model": "p50"}}, - {OT3Mount.LEFT: {"channels": 96, "model": "p1000", "version": (3, 3)}}, + { + OT3Mount.LEFT: { + "channels": 1, + "version": (3, 3), + "model": "p1000", + "oem_type": PipetteOEMType.OT, + } + }, + { + OT3Mount.RIGHT: { + "channels": 8, + "version": (3, 3), + "model": "p50", + "oem_type": PipetteOEMType.OT, + } + }, + { + OT3Mount.LEFT: { + "channels": 96, + "model": "p1000", + "version": (3, 3), + "oem_type": PipetteOEMType.OT, + } + }, ] @@ -573,6 +667,7 @@ async def prepare_for_mock_blowout( PipetteModelType(configs["model"]), PipetteChannelType(configs["channels"]), PipetteVersionType(*configs["version"]), + PipetteOEMType(configs["oem_type"]), ) instr_data = AttachedPipette(config=pipette_config, id="fakepip") await ot3_hardware.cache_pipette(mount, instr_data, None) @@ -804,7 +899,10 @@ async def test_liquid_probe( ) -> None: instr_data = AttachedPipette( config=load_pipette_data.load_definition( - PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4) + PipetteModelType("p1000"), + PipetteChannelType(1), + PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -858,6 +956,7 @@ async def test_liquid_probe( fake_settings_aspirate.sensor_threshold_pascals, fake_settings_aspirate.plunger_impulse_time, fake_settings_aspirate.samples_for_baselining, + probe_safe_reset_mm, probe=InstrumentProbeType.PRIMARY, force_both_sensors=False, response_queue=None, @@ -894,7 +993,10 @@ async def test_liquid_probe_plunger_moves( # when approaching its max z distance instr_data = AttachedPipette( config=load_pipette_data.load_definition( - PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4) + PipetteModelType("p1000"), + PipetteChannelType(1), + PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -1001,7 +1103,10 @@ async def test_liquid_probe_mount_moves( """Verify move targets for one singular liquid pass probe.""" instr_data = AttachedPipette( config=load_pipette_data.load_definition( - PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4) + PipetteModelType("p1000"), + PipetteChannelType(1), + PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -1063,7 +1168,10 @@ async def test_multi_liquid_probe( ) -> None: instr_data = AttachedPipette( config=load_pipette_data.load_definition( - PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4) + PipetteModelType("p1000"), + PipetteChannelType(1), + PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -1114,6 +1222,7 @@ async def test_multi_liquid_probe( fake_settings_aspirate.sensor_threshold_pascals, fake_settings_aspirate.plunger_impulse_time, fake_settings_aspirate.samples_for_baselining, + 2.0, probe=InstrumentProbeType.PRIMARY, force_both_sensors=False, response_queue=None, @@ -1129,7 +1238,10 @@ async def test_liquid_not_found( ) -> None: instr_data = AttachedPipette( config=load_pipette_data.load_definition( - PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4) + PipetteModelType("p1000"), + PipetteChannelType(1), + PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -1149,6 +1261,7 @@ async def _fake_pos_update_and_raise( threshold_pascals: float, plunger_impulse_time: float, num_baseline_reads: int, + z_offset_for_plunger_prep: float, probe: InstrumentProbeType = InstrumentProbeType.PRIMARY, force_both_sensors: bool = False, response_queue: Optional[ @@ -1596,7 +1709,10 @@ async def test_home_plunger( mount = OT3Mount.LEFT instr_data = AttachedPipette( config=load_pipette_data.load_definition( - PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4) + PipetteModelType("p1000"), + PipetteChannelType(1), + PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -1618,6 +1734,7 @@ async def test_prepare_for_aspirate( PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -1652,6 +1769,7 @@ async def test_plunger_ready_to_aspirate_after_dispense( PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -1677,7 +1795,10 @@ async def test_move_to_plunger_bottom( mount = OT3Mount.LEFT instr_data = AttachedPipette( config=load_pipette_data.load_definition( - PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 4) + PipetteModelType("p1000"), + PipetteChannelType(1), + PipetteVersionType(3, 4), + PipetteOEMType.OT, ), id="fakepip", ) @@ -2127,6 +2248,7 @@ async def test_home_axis( PipetteModelType("p1000"), PipetteChannelType(1), PipetteVersionType(3, 3), + PipetteOEMType.OT, ) instr_data = AttachedPipette(config=pipette_config, id="fakepip") await ot3_hardware.cache_pipette(Axis.to_ot3_mount(axis), instr_data, None) diff --git a/api/tests/opentrons/hardware_control/test_pipette.py b/api/tests/opentrons/hardware_control/test_pipette.py index 25ac7b0298a..610fcc2a022 100644 --- a/api/tests/opentrons/hardware_control/test_pipette.py +++ b/api/tests/opentrons/hardware_control/test_pipette.py @@ -73,7 +73,10 @@ def _create_pipette( ) -> ot3_pipette.Pipette: return ot3_pipette.Pipette( load_pipette_data.load_definition( - model.pipette_type, model.pipette_channels, model.pipette_version + model.pipette_type, + model.pipette_channels, + model.pipette_version, + model.oem_type, ), calibration, id, diff --git a/api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py b/api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py index 352dcb35c58..73f39006299 100644 --- a/api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py +++ b/api/tests/opentrons/protocol_api/core/engine/test_instrument_core.py @@ -1,4 +1,5 @@ """Test for the ProtocolEngine-based instrument API core.""" + from typing import cast, Optional from opentrons_shared_data.errors.exceptions import PipetteLiquidNotFoundError @@ -99,7 +100,7 @@ def subject( ) -> InstrumentCore: """Get a InstrumentCore test subject with its dependencies mocked out.""" decoy.when(mock_engine_client.state.pipettes.get("abc123")).then_return( - LoadedPipette.construct(mount=MountType.LEFT) # type: ignore[call-arg] + LoadedPipette.model_construct(mount=MountType.LEFT) # type: ignore[call-arg] ) decoy.when(mock_engine_client.state.pipettes.get_flow_rates("abc123")).then_return( @@ -130,7 +131,7 @@ def test_get_pipette_name( ) -> None: """It should get the pipette's load name.""" decoy.when(mock_engine_client.state.pipettes.get("abc123")).then_return( - LoadedPipette.construct(pipetteName=PipetteNameType.P300_SINGLE) # type: ignore[call-arg] + LoadedPipette.model_construct(pipetteName=PipetteNameType.P300_SINGLE) # type: ignore[call-arg] ) result = subject.get_pipette_name() @@ -143,7 +144,7 @@ def test_get_mount( ) -> None: """It should get the pipette's mount.""" decoy.when(mock_engine_client.state.pipettes.get("abc123")).then_return( - LoadedPipette.construct(mount=MountType.LEFT) # type: ignore[call-arg] + LoadedPipette.model_construct(mount=MountType.LEFT) # type: ignore[call-arg] ) result = subject.get_mount() @@ -161,7 +162,7 @@ def test_get_hardware_state( pipette_dict = cast(PipetteDict, {"display_name": "Cool Pipette", "has_tip": True}) decoy.when(mock_engine_client.state.pipettes.get("abc123")).then_return( - LoadedPipette.construct(mount=MountType.LEFT) # type: ignore[call-arg] + LoadedPipette.model_construct(mount=MountType.LEFT) # type: ignore[call-arg] ) decoy.when(mock_sync_hardware.get_attached_instrument(Mount.LEFT)).then_return( pipette_dict @@ -530,7 +531,7 @@ def test_aspirate_from_well( pipette_id="abc123", labware_id="123abc", well_name="my cool well", - well_location=WellLocation( + well_location=LiquidHandlingWellLocation( origin=WellOrigin.TOP, offset=WellOffset(x=3, y=2, z=1) ), ), @@ -828,7 +829,7 @@ def test_dispense_to_well( pipette_id="abc123", labware_id="123abc", well_name="my cool well", - well_location=WellLocation( + well_location=LiquidHandlingWellLocation( origin=WellOrigin.TOP, offset=WellOffset(x=3, y=2, z=1) ), ), @@ -1438,7 +1439,7 @@ def test_detect_liquid_presence( ) ) ).then_return( - cmd.TryLiquidProbeResult.construct( + cmd.TryLiquidProbeResult.model_construct( z_position=returned_from_engine, position=object(), # type: ignore[arg-type] ) diff --git a/api/tests/opentrons/protocol_api/core/engine/test_labware_core.py b/api/tests/opentrons/protocol_api/core/engine/test_labware_core.py index 6f4458f87ff..beca8fe99d1 100644 --- a/api/tests/opentrons/protocol_api/core/engine/test_labware_core.py +++ b/api/tests/opentrons/protocol_api/core/engine/test_labware_core.py @@ -35,7 +35,7 @@ @pytest.fixture def labware_definition() -> LabwareDefinition: """Get a LabwareDefinition value object to use in tests.""" - return LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + return LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] @pytest.fixture @@ -59,10 +59,10 @@ def subject(mock_engine_client: EngineClient) -> LabwareCore: @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] namespace="hello", version=42, - parameters=LabwareDefinitionParameters.construct(loadName="world"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="world"), # type: ignore[call-arg] ordering=[], ) ], @@ -76,14 +76,14 @@ def test_get_load_params(subject: LabwareCore) -> None: @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] namespace="hello", version=42, - parameters=LabwareDefinitionParameters.construct(loadName="world"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="world"), # type: ignore[call-arg] ordering=[], - metadata=LabwareDefinitionMetadata.construct( + metadata=LabwareDefinitionMetadata.model_construct( # type: ignore[call-arg] displayName="what a cool labware" - ), # type: ignore[call-arg] + ), ) ], ) @@ -130,10 +130,10 @@ def test_set_calibration_succeeds_in_ok_location( @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] namespace="hello", version=42, - parameters=LabwareDefinitionParameters.construct(loadName="world"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="world"), # type: ignore[call-arg] ordering=[], ) ], @@ -164,9 +164,9 @@ def test_set_calibration_fails_in_bad_location( @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] namespace="hello", - parameters=LabwareDefinitionParameters.construct(loadName="world"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="world"), # type: ignore[call-arg] ordering=[], allowedRoles=[], stackingOffsetWithLabware={}, @@ -208,9 +208,9 @@ def test_get_user_display_name(decoy: Decoy, mock_engine_client: EngineClient) - @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[], - metadata=LabwareDefinitionMetadata.construct( # type: ignore[call-arg] + metadata=LabwareDefinitionMetadata.model_construct( # type: ignore[call-arg] displayName="Cool Display Name" ), ) @@ -226,8 +226,10 @@ def test_get_display_name(subject: LabwareCore) -> None: @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] - parameters=LabwareDefinitionParameters.construct(loadName="load-name"), # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct( # type: ignore[call-arg] + loadName="load-name" + ), ), ], ) @@ -254,9 +256,9 @@ def test_get_name_display_name(decoy: Decoy, mock_engine_client: EngineClient) - @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[], - parameters=LabwareDefinitionParameters.construct(isTiprack=True), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(isTiprack=True), # type: ignore[call-arg] ) ], ) @@ -271,13 +273,13 @@ def test_is_tip_rack(subject: LabwareCore) -> None: argnames=["labware_definition", "expected_result"], argvalues=[ ( - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[], allowedRoles=[LabwareRole.adapter] ), True, ), ( - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[], allowedRoles=[LabwareRole.labware] ), False, @@ -294,7 +296,7 @@ def test_is_adapter(expected_result: bool, subject: LabwareCore) -> None: @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[["A1", "B1"], ["A2", "B2"]], ) ], @@ -354,9 +356,9 @@ def test_get_next_tip( @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[], - parameters=LabwareDefinitionParameters.construct(isTiprack=True), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(isTiprack=True), # type: ignore[call-arg] ) ], ) @@ -371,10 +373,10 @@ def test_reset_tips( @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[], - parameters=LabwareDefinitionParameters.construct(isTiprack=False), # type: ignore[call-arg] - metadata=LabwareDefinitionMetadata.construct( # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(isTiprack=False), # type: ignore[call-arg] + metadata=LabwareDefinitionMetadata.model_construct( # type: ignore[call-arg] displayName="Cool Display Name" ), ) @@ -430,9 +432,9 @@ def test_get_calibrated_offset( @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[], - parameters=LabwareDefinitionParameters.construct(quirks=["quirk"]), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(quirks=["quirk"]), # type: ignore[call-arg] ) ], ) diff --git a/api/tests/opentrons/protocol_api/core/engine/test_module_core.py b/api/tests/opentrons/protocol_api/core/engine/test_module_core.py index f18a672afb8..a1310999d72 100644 --- a/api/tests/opentrons/protocol_api/core/engine/test_module_core.py +++ b/api/tests/opentrons/protocol_api/core/engine/test_module_core.py @@ -1,4 +1,5 @@ """Tests for opentrons.protocol_api.core.engine.ModuleCore.""" + import pytest import inspect from decoy import Decoy @@ -107,7 +108,7 @@ def test_get_display_name( decoy: Decoy, subject: ModuleCore, mock_engine_client: EngineClient ) -> None: """It should return the module display name.""" - module_definition = ModuleDefinition.construct( # type: ignore[call-arg] + module_definition = ModuleDefinition.model_construct( # type: ignore[call-arg] displayName="abra kadabra", ) decoy.when(mock_engine_client.state.modules.get_definition("1234")).then_return( diff --git a/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py b/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py index 1cf6bc57049..6b5065f98c9 100644 --- a/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py +++ b/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py @@ -1,4 +1,5 @@ """Test for the ProtocolEngine-based protocol API core.""" + import inspect from typing import Optional, Type, cast, Tuple @@ -179,7 +180,7 @@ def subject( decoy.when( mock_engine_client.state.labware.get_definition("fixed-trash-123") ).then_return( - LabwareDefinition.construct(ordering=[["A1"]]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[["A1"]]) # type: ignore[call-arg] ) return ProtocolCore( @@ -358,13 +359,13 @@ def test_load_labware( ).then_return( commands.LoadLabwareResult( labwareId="abc123", - definition=LabwareDefinition.construct(), # type: ignore[call-arg] + definition=LabwareDefinition.model_construct(), # type: ignore[call-arg] offsetId=None, ) ) decoy.when(mock_engine_client.state.labware.get_definition("abc123")).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) result = subject.load_labware( @@ -394,7 +395,7 @@ def test_load_labware( slot_name=DeckSlotName.SLOT_5, ) ).then_return( - LoadedLabware.construct(id="abc123") # type: ignore[call-arg] + LoadedLabware.model_construct(id="abc123") # type: ignore[call-arg] ) assert subject.get_slot_item(DeckSlotName.SLOT_5) is result @@ -432,13 +433,13 @@ def test_load_labware_on_staging_slot( ).then_return( commands.LoadLabwareResult( labwareId="abc123", - definition=LabwareDefinition.construct(), # type: ignore[call-arg] + definition=LabwareDefinition.model_construct(), # type: ignore[call-arg] offsetId=None, ) ) decoy.when(mock_engine_client.state.labware.get_definition("abc123")).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) result = subject.load_labware( @@ -468,7 +469,7 @@ def test_load_labware_on_staging_slot( slot_name=StagingSlotName.SLOT_B4, ) ).then_return( - LoadedLabware.construct(id="abc123") # type: ignore[call-arg] + LoadedLabware.model_construct(id="abc123") # type: ignore[call-arg] ) assert subject.get_slot_item(StagingSlotName.SLOT_B4) is result @@ -509,13 +510,13 @@ def test_load_labware_on_labware( ).then_return( commands.LoadLabwareResult( labwareId="abc123", - definition=LabwareDefinition.construct(), # type: ignore[call-arg] + definition=LabwareDefinition.model_construct(), # type: ignore[call-arg] offsetId=None, ) ) decoy.when(mock_engine_client.state.labware.get_definition("abc123")).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) decoy.when( @@ -579,13 +580,13 @@ def test_load_labware_off_deck( ).then_return( commands.LoadLabwareResult( labwareId="abc123", - definition=LabwareDefinition.construct(), # type: ignore[call-arg] + definition=LabwareDefinition.model_construct(), # type: ignore[call-arg] offsetId=None, ) ) decoy.when(mock_engine_client.state.labware.get_definition("abc123")).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) result = subject.load_labware( @@ -642,13 +643,13 @@ def test_load_adapter( ).then_return( commands.LoadLabwareResult( labwareId="abc123", - definition=LabwareDefinition.construct(), # type: ignore[call-arg] + definition=LabwareDefinition.model_construct(), # type: ignore[call-arg] offsetId=None, ) ) decoy.when(mock_engine_client.state.labware.get_definition("abc123")).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) result = subject.load_adapter( @@ -677,7 +678,7 @@ def test_load_adapter( slot_name=DeckSlotName.SLOT_5, ) ).then_return( - LoadedLabware.construct(id="abc123") # type: ignore[call-arg] + LoadedLabware.model_construct(id="abc123") # type: ignore[call-arg] ) assert subject.get_slot_item(DeckSlotName.SLOT_5) is result @@ -714,13 +715,13 @@ def test_load_adapter_on_staging_slot( ).then_return( commands.LoadLabwareResult( labwareId="abc123", - definition=LabwareDefinition.construct(), # type: ignore[call-arg] + definition=LabwareDefinition.model_construct(), # type: ignore[call-arg] offsetId=None, ) ) decoy.when(mock_engine_client.state.labware.get_definition("abc123")).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) result = subject.load_adapter( @@ -749,7 +750,7 @@ def test_load_adapter_on_staging_slot( slot_name=StagingSlotName.SLOT_B4, ) ).then_return( - LoadedLabware.construct(id="abc123") # type: ignore[call-arg] + LoadedLabware.model_construct(id="abc123") # type: ignore[call-arg] ) assert subject.get_slot_item(StagingSlotName.SLOT_B4) is result @@ -860,7 +861,7 @@ def test_move_labware( decoy.when( mock_engine_client.state.labware.get_definition("labware-id") ).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) labware = LabwareCore(labware_id="labware-id", engine_client=mock_engine_client) subject.move_labware( @@ -903,7 +904,7 @@ def test_move_labware_on_staging_slot( decoy.when( mock_engine_client.state.labware.get_definition("labware-id") ).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) labware = LabwareCore(labware_id="labware-id", engine_client=mock_engine_client) subject.move_labware( @@ -944,7 +945,7 @@ def test_move_labware_on_non_connected_module( decoy.when( mock_engine_client.state.labware.get_definition("labware-id") ).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) labware = LabwareCore(labware_id="labware-id", engine_client=mock_engine_client) non_connected_module_core = NonConnectedModuleCore( @@ -990,7 +991,7 @@ def test_move_labware_off_deck( decoy.when( mock_engine_client.state.labware.get_definition("labware-id") ).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) labware = LabwareCore(labware_id="labware-id", engine_client=mock_engine_client) @@ -1056,13 +1057,13 @@ def test_load_labware_on_module( ).then_return( commands.LoadLabwareResult( labwareId="abc123", - definition=LabwareDefinition.construct(), # type: ignore[call-arg] + definition=LabwareDefinition.model_construct(), # type: ignore[call-arg] offsetId=None, ) ) decoy.when(mock_engine_client.state.labware.get_definition("abc123")).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) module_core = ModuleCore( @@ -1133,13 +1134,13 @@ def test_load_labware_on_non_connected_module( ).then_return( commands.LoadLabwareResult( labwareId="abc123", - definition=LabwareDefinition.construct(), # type: ignore[call-arg] + definition=LabwareDefinition.model_construct(), # type: ignore[call-arg] offsetId=None, ) ) decoy.when(mock_engine_client.state.labware.get_definition("abc123")).then_return( - LabwareDefinition.construct(ordering=[]) # type: ignore[call-arg] + LabwareDefinition.model_construct(ordering=[]) # type: ignore[call-arg] ) non_connected_module_core = NonConnectedModuleCore( @@ -1185,7 +1186,7 @@ def test_add_labware_definition( """It should add a labware definition to the engine.""" decoy.when( mock_engine_client.add_labware_definition( - definition=LabwareDefinition.parse_obj(minimal_labware_def) + definition=LabwareDefinition.model_validate(minimal_labware_def) ) ).then_return(LabwareUri("hello/world/123")) @@ -1261,7 +1262,7 @@ def test_load_module( robot_type: RobotType, ) -> None: """It should issue a load module engine command.""" - definition = ModuleDefinition.construct() # type: ignore[call-arg] + definition = ModuleDefinition.model_construct() # type: ignore[call-arg] mock_hw_mod_1 = decoy.mock(cls=AbstractModule) mock_hw_mod_2 = decoy.mock(cls=AbstractModule) @@ -1315,7 +1316,7 @@ def test_load_module( slot_name=slot_name, ) ).then_return( - LoadedModule.construct(id="abc123") # type: ignore[call-arg] + LoadedModule.model_construct(id="abc123") # type: ignore[call-arg] ) decoy.when(mock_engine_client.state.labware.get_id_by_module("abc123")).then_raise( LabwareNotLoadedOnModuleError("oh no") @@ -1333,7 +1334,7 @@ def test_load_mag_block( subject: ProtocolCore, ) -> None: """It should issue a load module engine command.""" - definition = ModuleDefinition.construct() # type: ignore[call-arg] + definition = ModuleDefinition.model_construct() # type: ignore[call-arg] decoy.when(mock_engine_client.state.config.robot_type).then_return("OT-3 Standard") @@ -1378,7 +1379,7 @@ def test_load_mag_block( slot_name=DeckSlotName.SLOT_1, ) ).then_return( - LoadedModule.construct(id="abc123") # type: ignore[call-arg] + LoadedModule.model_construct(id="abc123") # type: ignore[call-arg] ) decoy.when(mock_engine_client.state.labware.get_id_by_module("abc123")).then_raise( LabwareNotLoadedOnModuleError("oh no") @@ -1413,7 +1414,7 @@ def test_load_module_thermocycler_with_no_location( expected_slot: DeckSlotName, ) -> None: """It should issue a load module engine command with location at 7.""" - definition = ModuleDefinition.construct() # type: ignore[call-arg] + definition = ModuleDefinition.model_construct() # type: ignore[call-arg] mock_hw_mod = decoy.mock(cls=AbstractModule) decoy.when(mock_hw_mod.device_info).then_return({"serial": "xyz789"}) @@ -1670,11 +1671,11 @@ def test_add_liquid( subject: ProtocolCore, ) -> None: """It should return the created liquid.""" - liquid = PE_Liquid.construct( + liquid = PE_Liquid.model_construct( id="water-id", displayName="water", description="water desc", - displayColor=HexColor(__root__="#fff"), + displayColor=HexColor("#fff"), ) expected_result = Liquid( diff --git a/api/tests/opentrons/protocol_api/core/engine/test_well_core.py b/api/tests/opentrons/protocol_api/core/engine/test_well_core.py index 31b562f7e81..6e1912f0aec 100644 --- a/api/tests/opentrons/protocol_api/core/engine/test_well_core.py +++ b/api/tests/opentrons/protocol_api/core/engine/test_well_core.py @@ -1,4 +1,5 @@ """Test for the ProtocolEngine-based well API core.""" + import inspect import pytest @@ -49,7 +50,7 @@ def api_version() -> APIVersion: @pytest.fixture def well_definition() -> WellDefinition: """Get a partial WellDefinition value object.""" - return WellDefinition.construct() # type: ignore[call-arg] + return WellDefinition.model_construct() # type: ignore[call-arg] @pytest.fixture @@ -93,7 +94,7 @@ def test_display_name( @pytest.mark.parametrize( "well_definition", - [WellDefinition.construct(totalLiquidVolume=101)], # type: ignore[call-arg] + [WellDefinition.model_construct(totalLiquidVolume=101)], # type: ignore[call-arg] ) def test_max_volume(subject: WellCore) -> None: """It should have a max volume.""" @@ -192,7 +193,7 @@ def test_load_liquid( @pytest.mark.parametrize( "well_definition", - [WellDefinition.construct(diameter=123.4)], # type: ignore[call-arg] + [WellDefinition.model_construct(diameter=123.4)], # type: ignore[call-arg] ) def test_diameter(subject: WellCore) -> None: """It should get the diameter.""" @@ -201,7 +202,7 @@ def test_diameter(subject: WellCore) -> None: @pytest.mark.parametrize( "well_definition", - [WellDefinition.construct(xDimension=567.8)], # type: ignore[call-arg] + [WellDefinition.model_construct(xDimension=567.8)], # type: ignore[call-arg] ) def test_length(subject: WellCore) -> None: """It should get the length.""" @@ -210,7 +211,7 @@ def test_length(subject: WellCore) -> None: @pytest.mark.parametrize( "well_definition", - [WellDefinition.construct(yDimension=987.6)], # type: ignore[call-arg] + [WellDefinition.model_construct(yDimension=987.6)], # type: ignore[call-arg] ) def test_width(subject: WellCore) -> None: """It should get the width.""" @@ -219,7 +220,7 @@ def test_width(subject: WellCore) -> None: @pytest.mark.parametrize( "well_definition", - [WellDefinition.construct(depth=42.0)], # type: ignore[call-arg] + [WellDefinition.model_construct(depth=42.0)], # type: ignore[call-arg] ) def test_depth(subject: WellCore) -> None: """It should get the depth.""" diff --git a/api/tests/opentrons/protocol_api/test_liquid_class_properties.py b/api/tests/opentrons/protocol_api/test_liquid_class_properties.py index f335cb385bc..94e6dd49205 100644 --- a/api/tests/opentrons/protocol_api/test_liquid_class_properties.py +++ b/api/tests/opentrons/protocol_api/test_liquid_class_properties.py @@ -17,7 +17,7 @@ def test_build_aspirate_settings() -> None: """It should convert the shared data aspirate settings to the PAPI type.""" fixture_data = load_shared_data("liquid-class/fixtures/1/fixture_glycerol50.json") - liquid_class_model = LiquidClassSchemaV1.parse_raw(fixture_data) + liquid_class_model = LiquidClassSchemaV1.model_validate_json(fixture_data) aspirate_data = liquid_class_model.byPipette[0].byTipType[0].aspirate aspirate_properties = build_aspirate_properties(aspirate_data) @@ -61,7 +61,7 @@ def test_build_aspirate_settings() -> None: def test_build_single_dispense_settings() -> None: """It should convert the shared data single dispense settings to the PAPI type.""" fixture_data = load_shared_data("liquid-class/fixtures/1/fixture_glycerol50.json") - liquid_class_model = LiquidClassSchemaV1.parse_raw(fixture_data) + liquid_class_model = LiquidClassSchemaV1.model_validate_json(fixture_data) single_dispense_data = liquid_class_model.byPipette[0].byTipType[0].singleDispense single_dispense_properties = build_single_dispense_properties(single_dispense_data) @@ -118,7 +118,7 @@ def test_build_single_dispense_settings() -> None: def test_build_multi_dispense_settings() -> None: """It should convert the shared data multi dispense settings to the PAPI type.""" fixture_data = load_shared_data("liquid-class/fixtures/1/fixture_glycerol50.json") - liquid_class_model = LiquidClassSchemaV1.parse_raw(fixture_data) + liquid_class_model = LiquidClassSchemaV1.model_validate_json(fixture_data) multi_dispense_data = liquid_class_model.byPipette[0].byTipType[0].multiDispense assert multi_dispense_data is not None diff --git a/api/tests/opentrons/protocol_api/test_validation.py b/api/tests/opentrons/protocol_api/test_validation.py index 342e197535b..ce12d1a8f53 100644 --- a/api/tests/opentrons/protocol_api/test_validation.py +++ b/api/tests/opentrons/protocol_api/test_validation.py @@ -1,4 +1,5 @@ """Tests for Protocol API input validation.""" + from typing import ContextManager, List, Type, Union, Optional, Dict, Sequence, Any from contextlib import nullcontext as do_not_raise @@ -223,7 +224,9 @@ def test_ensure_deck_slot_invalid( """It should raise an exception if given an invalid name.""" with pytest.raises(expected_error_type, match=expected_error_match): subject.ensure_and_convert_deck_slot( - input_value, input_api_version, input_robot_type # type: ignore[arg-type] + input_value, # type: ignore[arg-type] + input_api_version, + input_robot_type, ) @@ -243,23 +246,23 @@ def test_ensure_lowercase_name_invalid() -> None: ("definition", "expected_raise"), [ ( - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[LabwareRole.labware], - parameters=LabwareDefinitionParameters.construct(loadName="Foo"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="Foo"), # type: ignore[call-arg] ), do_not_raise(), ), ( - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[], - parameters=LabwareDefinitionParameters.construct(loadName="Foo"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="Foo"), # type: ignore[call-arg] ), do_not_raise(), ), ( - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[LabwareRole.adapter], - parameters=LabwareDefinitionParameters.construct(loadName="Foo"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="Foo"), # type: ignore[call-arg] ), pytest.raises(subject.LabwareDefinitionIsNotLabwareError), ), @@ -277,23 +280,23 @@ def test_ensure_definition_is_labware( ("definition", "expected_raise"), [ ( - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[LabwareRole.adapter], - parameters=LabwareDefinitionParameters.construct(loadName="Foo"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="Foo"), # type: ignore[call-arg] ), do_not_raise(), ), ( - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[], - parameters=LabwareDefinitionParameters.construct(loadName="Foo"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="Foo"), # type: ignore[call-arg] ), pytest.raises(subject.LabwareDefinitionIsNotAdapterError), ), ( - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[LabwareRole.labware], - parameters=LabwareDefinitionParameters.construct(loadName="Foo"), # type: ignore[call-arg] + parameters=LabwareDefinitionParameters.model_construct(loadName="Foo"), # type: ignore[call-arg] ), pytest.raises(subject.LabwareDefinitionIsNotAdapterError), ), @@ -547,7 +550,8 @@ def test_validate_with_wrong_location() -> None: """Should raise a LocationTypeError.""" with pytest.raises(subject.LocationTypeError): subject.validate_location( - location=42, last_location=None # type: ignore[arg-type] + location=42, # type: ignore[arg-type] + last_location=None, ) diff --git a/api/tests/opentrons/protocol_engine/clients/test_child_thread_transport.py b/api/tests/opentrons/protocol_engine/clients/test_child_thread_transport.py index 9cbd03c3ec8..700f11ff190 100644 --- a/api/tests/opentrons/protocol_engine/clients/test_child_thread_transport.py +++ b/api/tests/opentrons/protocol_engine/clients/test_child_thread_transport.py @@ -1,4 +1,5 @@ """Tests for am ChildThreadTransport.""" + import threading from asyncio import get_running_loop from datetime import datetime @@ -104,7 +105,7 @@ async def test_call_method( subject: ChildThreadTransport, ) -> None: """It should call a synchronous method in a thread-safe manner.""" - labware_def = LabwareDefinition.construct(namespace="hello") # type: ignore[call-arg] + labware_def = LabwareDefinition.model_construct(namespace="hello") # type: ignore[call-arg] labware_uri = LabwareUri("hello/world/123") calling_thread_id = None diff --git a/api/tests/opentrons/protocol_engine/clients/test_sync_client.py b/api/tests/opentrons/protocol_engine/clients/test_sync_client.py index 03d6912371c..628e23cc052 100644 --- a/api/tests/opentrons/protocol_engine/clients/test_sync_client.py +++ b/api/tests/opentrons/protocol_engine/clients/test_sync_client.py @@ -12,6 +12,7 @@ import pytest from decoy import Decoy + from opentrons_shared_data.labware.types import LabwareUri from opentrons_shared_data.labware.labware_definition import LabwareDefinition @@ -61,7 +62,7 @@ def test_execute_command_without_recovery( result_from_transport ) result_from_subject = subject.execute_command_without_recovery(params) - assert result_from_subject == result_from_transport + assert result_from_subject == result_from_transport # type: ignore[comparison-overlap] def test_add_labware_definition( @@ -70,7 +71,7 @@ def test_add_labware_definition( subject: SyncClient, ) -> None: """It should add a labware definition.""" - labware_definition = LabwareDefinition.construct(namespace="hello") # type: ignore[call-arg] + labware_definition = LabwareDefinition.model_construct(namespace="hello") # type: ignore[call-arg] expected_labware_uri = LabwareUri("hello/world/123") decoy.when( @@ -108,7 +109,7 @@ def test_add_liquid( subject: SyncClient, ) -> None: """It should add a liquid to engine state.""" - liquid = Liquid.construct(displayName="water") # type: ignore[call-arg] + liquid = Liquid.model_construct(displayName="water") # type: ignore[call-arg] decoy.when( transport.call_method( diff --git a/api/tests/opentrons/protocol_engine/commands/test_air_gap_in_place.py b/api/tests/opentrons/protocol_engine/commands/test_air_gap_in_place.py index 5d66a845dcc..b9d110fd9c2 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_air_gap_in_place.py +++ b/api/tests/opentrons/protocol_engine/commands/test_air_gap_in_place.py @@ -265,7 +265,7 @@ async def test_overpressure_error( if isinstance(location, CurrentWell): assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], @@ -275,7 +275,7 @@ async def test_overpressure_error( ) else: assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_aspirate.py b/api/tests/opentrons/protocol_engine/commands/test_aspirate.py index 8e50d1825ae..4a8adbcdc76 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_aspirate.py +++ b/api/tests/opentrons/protocol_engine/commands/test_aspirate.py @@ -411,7 +411,7 @@ async def test_overpressure_error( result = await subject.execute(params) assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], @@ -581,7 +581,7 @@ async def test_stall_during_final_movement( result = await subject.execute(params) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], @@ -639,7 +639,7 @@ async def test_stall_during_preparation( result = await subject.execute(params) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate( @@ -715,7 +715,7 @@ async def test_overpressure_during_preparation( result = await subject.execute(params) assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_aspirate_in_place.py b/api/tests/opentrons/protocol_engine/commands/test_aspirate_in_place.py index 48dba2e0c3e..5a7ca3ee940 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_aspirate_in_place.py +++ b/api/tests/opentrons/protocol_engine/commands/test_aspirate_in_place.py @@ -304,7 +304,7 @@ async def test_overpressure_error( if isinstance(location, CurrentWell): assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], @@ -323,7 +323,7 @@ async def test_overpressure_error( ) else: assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_blow_out.py b/api/tests/opentrons/protocol_engine/commands/test_blow_out.py index c06b62ace97..7549141be5b 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_blow_out.py +++ b/api/tests/opentrons/protocol_engine/commands/test_blow_out.py @@ -158,7 +158,7 @@ async def test_overpressure_error( result = await subject.execute(data) assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], @@ -233,7 +233,7 @@ async def test_stall_error( result = await subject.execute(data) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_blow_out_in_place.py b/api/tests/opentrons/protocol_engine/commands/test_blow_out_in_place.py index 97e8e8c0851..50bee696c5a 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_blow_out_in_place.py +++ b/api/tests/opentrons/protocol_engine/commands/test_blow_out_in_place.py @@ -106,7 +106,7 @@ async def test_overpressure_error( result = await subject.execute(data) assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_dispense.py b/api/tests/opentrons/protocol_engine/commands/test_dispense.py index e0e18307b69..5b60b61d4df 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_dispense.py +++ b/api/tests/opentrons/protocol_engine/commands/test_dispense.py @@ -119,7 +119,7 @@ async def test_dispense_implementation( labware_id="labware-id-abc123", well_name="A3", ), - new_deck_point=DeckPoint.construct(x=1, y=2, z=3), + new_deck_point=DeckPoint.model_construct(x=1, y=2, z=3), ), liquid_operated=update_types.LiquidOperatedUpdate( labware_id="labware-id-abc123", @@ -203,7 +203,7 @@ async def test_overpressure_error( result = await subject.execute(data) assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], @@ -216,7 +216,7 @@ async def test_overpressure_error( labware_id="labware-id", well_name="well-name", ), - new_deck_point=DeckPoint.construct(x=1, y=2, z=3), + new_deck_point=DeckPoint.model_construct(x=1, y=2, z=3), ), liquid_operated=update_types.LiquidOperatedUpdate( labware_id="labware-id", @@ -234,7 +234,7 @@ async def test_overpressure_error( labware_id="labware-id", well_name="well-name", ), - new_deck_point=DeckPoint.construct(x=1, y=2, z=3), + new_deck_point=DeckPoint.model_construct(x=1, y=2, z=3), ), ), ) @@ -288,7 +288,7 @@ async def test_stall_error( result = await subject.execute(data) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_dispense_in_place.py b/api/tests/opentrons/protocol_engine/commands/test_dispense_in_place.py index bc39fba4a00..e9c715223de 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_dispense_in_place.py +++ b/api/tests/opentrons/protocol_engine/commands/test_dispense_in_place.py @@ -207,7 +207,7 @@ async def test_overpressure_error( if isinstance(location, CurrentWell): assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], @@ -226,7 +226,7 @@ async def test_overpressure_error( ) else: assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_drop_tip.py b/api/tests/opentrons/protocol_engine/commands/test_drop_tip.py index 038ea12255b..430fa8dff32 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_drop_tip.py +++ b/api/tests/opentrons/protocol_engine/commands/test_drop_tip.py @@ -60,7 +60,7 @@ def mock_model_utils(decoy: Decoy) -> ModelUtils: def test_drop_tip_params_defaults() -> None: """A drop tip should use a `WellOrigin.DROP_TIP` by default.""" - default_params = DropTipParams.parse_obj( + default_params = DropTipParams.model_validate( {"pipetteId": "abc", "labwareId": "def", "wellName": "ghj"} ) @@ -71,7 +71,7 @@ def test_drop_tip_params_defaults() -> None: def test_drop_tip_params_default_origin() -> None: """A drop tip should drop a `WellOrigin.DROP_TIP` by default even if an offset is given.""" - default_params = DropTipParams.parse_obj( + default_params = DropTipParams.model_validate( { "pipetteId": "abc", "labwareId": "def", @@ -303,7 +303,7 @@ async def test_tip_attached_error( result = await subject.execute(params) assert result == DefinedErrorData( - public=TipPhysicallyAttachedError.construct( + public=TipPhysicallyAttachedError.model_construct( id="error-id", createdAt=datetime(year=1, month=2, day=3), wrappedErrors=[matchers.Anything()], @@ -396,7 +396,7 @@ async def test_stall_error( result = await subject.execute(params) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id="error-id", createdAt=datetime(year=1, month=2, day=3), wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_drop_tip_in_place.py b/api/tests/opentrons/protocol_engine/commands/test_drop_tip_in_place.py index 5565ffea88c..8c4716cf380 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_drop_tip_in_place.py +++ b/api/tests/opentrons/protocol_engine/commands/test_drop_tip_in_place.py @@ -104,7 +104,7 @@ async def test_tip_attached_error( result = await subject.execute(params) assert result == DefinedErrorData( - public=TipPhysicallyAttachedError.construct( + public=TipPhysicallyAttachedError.model_construct( id="error-id", createdAt=datetime(year=1, month=2, day=3), wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_liquid_probe.py b/api/tests/opentrons/protocol_engine/commands/test_liquid_probe.py index 34b979901aa..c9661512aaa 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_liquid_probe.py +++ b/api/tests/opentrons/protocol_engine/commands/test_liquid_probe.py @@ -350,7 +350,7 @@ async def test_liquid_not_found_error( ) if isinstance(subject, LiquidProbeImplementation): assert result == DefinedErrorData( - public=LiquidNotFoundError.construct( + public=LiquidNotFoundError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], @@ -726,7 +726,7 @@ async def test_liquid_probe_stall( result = await subject.execute(data) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=error_id, createdAt=timestamp, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate(pipette_location=update_types.CLEAR), diff --git a/api/tests/opentrons/protocol_engine/commands/test_load_liquid_class.py b/api/tests/opentrons/protocol_engine/commands/test_load_liquid_class.py index 041a7b2f8ca..54de10f3bc2 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_load_liquid_class.py +++ b/api/tests/opentrons/protocol_engine/commands/test_load_liquid_class.py @@ -152,7 +152,7 @@ async def test_load_liquid_class_conflicting_definition_for_id( liquid_class_record ) - new_liquid_class_record = liquid_class_record.copy(deep=True) + new_liquid_class_record = liquid_class_record.model_copy(deep=True) new_liquid_class_record.aspirate.offset.x += 123 # make it different params = LoadLiquidClassParams( liquidClassId="liquid-class-1", liquidClassRecord=new_liquid_class_record diff --git a/api/tests/opentrons/protocol_engine/commands/test_move_labware.py b/api/tests/opentrons/protocol_engine/commands/test_move_labware.py index 49e3c4f5471..2036bda558a 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_move_labware.py +++ b/api/tests/opentrons/protocol_engine/commands/test_move_labware.py @@ -1,4 +1,5 @@ """Test the ``moveLabware`` command.""" + from datetime import datetime import inspect from unittest.mock import sentinel @@ -6,13 +7,13 @@ import pytest from decoy import Decoy, matchers +from opentrons_shared_data.labware.labware_definition import Parameters, Dimensions from opentrons_shared_data.errors.exceptions import ( EnumeratedError, FailedGripperPickupError, LabwareDroppedError, StallOrCollisionDetectedError, ) -from opentrons_shared_data.labware.labware_definition import Parameters, Dimensions from opentrons_shared_data.gripper.constants import GRIPPER_PADDLE_WIDTH from opentrons.protocol_engine.state import update_types @@ -168,7 +169,7 @@ async def test_move_labware_implementation_on_labware( decoy.when( state_view.labware.get_definition(labware_id="my-cool-labware-id") ).then_return( - LabwareDefinition.construct(namespace="spacename") # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="spacename") # type: ignore[call-arg] ) decoy.when( state_view.geometry.ensure_location_not_occupied( @@ -189,7 +190,7 @@ async def test_move_labware_implementation_on_labware( "my-even-cooler-labware-id" ), state_view.labware.raise_if_labware_cannot_be_stacked( - LabwareDefinition.construct(namespace="spacename"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="spacename"), # type: ignore[call-arg] "my-even-cooler-labware-id", ), ) @@ -314,7 +315,7 @@ async def test_gripper_error( labware_namespace = "labware-namespace" labware_load_name = "load-name" labware_definition_uri = "opentrons-test/load-name/1" - labware_def = LabwareDefinition.construct( # type: ignore[call-arg] + labware_def = LabwareDefinition.model_construct( # type: ignore[call-arg] namespace=labware_namespace, ) original_location = DeckSlotLocation(slotName=DeckSlotName.SLOT_A1) @@ -324,7 +325,7 @@ async def test_gripper_error( # Common MoveLabwareImplementation boilerplate: decoy.when(state_view.labware.get_definition(labware_id=labware_id)).then_return( - LabwareDefinition.construct(namespace=labware_namespace) # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace=labware_namespace) # type: ignore[call-arg] ) decoy.when(state_view.labware.get(labware_id=labware_id)).then_return( LoadedLabware( @@ -374,7 +375,7 @@ async def test_gripper_error( result = await subject.execute(params) assert result == DefinedErrorData( - public=GripperMovementError.construct( + public=GripperMovementError.model_construct( id=error_id, createdAt=error_created_at, errorCode=underlying_exception.code.value.code, @@ -462,7 +463,7 @@ async def test_gripper_move_to_waste_chute_implementation( pickUpOffset=LabwareOffsetVector(x=1, y=2, z=3), dropOffset=None, ) - labware_def = LabwareDefinition.construct( # type: ignore[call-arg] + labware_def = LabwareDefinition.model_construct( # type: ignore[call-arg] namespace="my-cool-namespace", dimensions=Dimensions( yDimension=labware_width, zDimension=labware_width, xDimension=labware_width @@ -670,8 +671,8 @@ async def test_move_labware_raises_when_moving_adapter_with_gripper( strategy=LabwareMovementStrategy.USING_GRIPPER, ) - definition = LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct(loadName="My cool adapter"), # type: ignore[call-arg] + definition = LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(loadName="My cool adapter"), # type: ignore[call-arg] ) decoy.when(state_view.labware.get(labware_id="my-cool-labware-id")).then_return( @@ -711,8 +712,8 @@ async def test_move_labware_raises_when_moving_labware_with_gripper_incompatible strategy=LabwareMovementStrategy.USING_GRIPPER, ) - definition = LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct(loadName="My cool labware"), # type: ignore[call-arg] + definition = LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(loadName="My cool labware"), # type: ignore[call-arg] ) decoy.when(state_view.labware.get(labware_id="my-cool-labware-id")).then_return( @@ -761,7 +762,7 @@ async def test_move_labware_with_gripper_raises_on_ot2( decoy.when( state_view.labware.get_definition(labware_id="my-cool-labware-id") ).then_return( - LabwareDefinition.construct(namespace="spacename") # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="spacename") # type: ignore[call-arg] ) decoy.when(state_view.config).then_return( @@ -783,8 +784,10 @@ async def test_move_labware_raises_when_moving_fixed_trash_labware( strategy=LabwareMovementStrategy.USING_GRIPPER, ) - definition = LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct(loadName="My cool labware", quirks=["fixedTrash"]), # type: ignore[call-arg] + definition = LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct( # type: ignore[call-arg] + loadName="My cool labware", quirks=["fixedTrash"] + ), ) decoy.when(state_view.labware.get(labware_id="my-cool-labware-id")).then_return( diff --git a/api/tests/opentrons/protocol_engine/commands/test_move_relative.py b/api/tests/opentrons/protocol_engine/commands/test_move_relative.py index 1e2d98ebf21..7a993c16d35 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_move_relative.py +++ b/api/tests/opentrons/protocol_engine/commands/test_move_relative.py @@ -85,7 +85,7 @@ async def test_move_relative_stalls( result = await subject.execute(data) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=test_id, createdAt=timestamp, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate(pipette_location=update_types.CLEAR), diff --git a/api/tests/opentrons/protocol_engine/commands/test_move_to_addressable_area.py b/api/tests/opentrons/protocol_engine/commands/test_move_to_addressable_area.py index 9142f792252..0570d91c8bc 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_move_to_addressable_area.py +++ b/api/tests/opentrons/protocol_engine/commands/test_move_to_addressable_area.py @@ -209,7 +209,7 @@ async def test_move_to_addressable_area_implementation_handles_stalls( result = await subject.execute(data) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=test_id, createdAt=timestamp, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate( diff --git a/api/tests/opentrons/protocol_engine/commands/test_move_to_addressable_area_for_drop_tip.py b/api/tests/opentrons/protocol_engine/commands/test_move_to_addressable_area_for_drop_tip.py index b6ee2097458..e90bb7271f7 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_move_to_addressable_area_for_drop_tip.py +++ b/api/tests/opentrons/protocol_engine/commands/test_move_to_addressable_area_for_drop_tip.py @@ -133,7 +133,7 @@ async def test_move_to_addressable_area_for_drop_tip_handles_stalls( result = await subject.execute(data) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=test_id, createdAt=timestamp, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate( diff --git a/api/tests/opentrons/protocol_engine/commands/test_move_to_coordinates.py b/api/tests/opentrons/protocol_engine/commands/test_move_to_coordinates.py index 85afb189988..3c9fc10bb1c 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_move_to_coordinates.py +++ b/api/tests/opentrons/protocol_engine/commands/test_move_to_coordinates.py @@ -97,7 +97,7 @@ async def test_move_to_coordinates_stall( result = await subject.execute(params=params) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=test_id, createdAt=timestamp, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate(pipette_location=update_types.CLEAR), diff --git a/api/tests/opentrons/protocol_engine/commands/test_move_to_well.py b/api/tests/opentrons/protocol_engine/commands/test_move_to_well.py index db91abd5a41..56a2691bbee 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_move_to_well.py +++ b/api/tests/opentrons/protocol_engine/commands/test_move_to_well.py @@ -158,7 +158,7 @@ async def test_move_to_well_stall_defined_error( result = await subject.execute(data) assert isinstance(result, DefinedErrorData) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate(pipette_location=update_types.CLEAR), diff --git a/api/tests/opentrons/protocol_engine/commands/test_pick_up_tip.py b/api/tests/opentrons/protocol_engine/commands/test_pick_up_tip.py index 07170e08288..d4c53ea5992 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_pick_up_tip.py +++ b/api/tests/opentrons/protocol_engine/commands/test_pick_up_tip.py @@ -167,7 +167,7 @@ async def test_tip_physically_missing_error( ) assert result == DefinedErrorData( - public=TipPhysicallyMissingError.construct( + public=TipPhysicallyMissingError.model_construct( id=error_id, createdAt=error_created_at, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate( @@ -255,7 +255,7 @@ async def test_stall_error( ) assert result == DefinedErrorData( - public=StallOrCollisionError.construct( + public=StallOrCollisionError.model_construct( id=error_id, createdAt=error_created_at, wrappedErrors=[matchers.Anything()] ), state_update=update_types.StateUpdate( diff --git a/api/tests/opentrons/protocol_engine/commands/test_prepare_to_aspirate.py b/api/tests/opentrons/protocol_engine/commands/test_prepare_to_aspirate.py index f9eded1ffa0..5e77529f646 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_prepare_to_aspirate.py +++ b/api/tests/opentrons/protocol_engine/commands/test_prepare_to_aspirate.py @@ -91,7 +91,7 @@ async def test_overpressure_error( result = await subject.execute(data) assert result == DefinedErrorData( - public=OverpressureError.construct( + public=OverpressureError.model_construct( id=error_id, createdAt=error_timestamp, wrappedErrors=[matchers.Anything()], diff --git a/api/tests/opentrons/protocol_engine/commands/test_touch_tip.py b/api/tests/opentrons/protocol_engine/commands/test_touch_tip.py index 0d4071efd6c..5756810c9ee 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_touch_tip.py +++ b/api/tests/opentrons/protocol_engine/commands/test_touch_tip.py @@ -102,8 +102,99 @@ async def test_touch_tip_implementation( pipette_id="abc", labware_id="123", well_name="A3", - center_point=Point(x=1, y=2, z=3), radius=0.456, + mm_from_edge=0, + center_point=Point(x=1, y=2, z=3), + ) + ).then_return( + [ + Waypoint( + position=Point(x=11, y=22, z=33), + critical_point=CriticalPoint.XY_CENTER, + ), + Waypoint( + position=Point(x=44, y=55, z=66), + critical_point=CriticalPoint.XY_CENTER, + ), + ] + ) + + decoy.when( + await mock_gantry_mover.move_to( + pipette_id="abc", + waypoints=[ + Waypoint( + position=Point(x=11, y=22, z=33), + critical_point=CriticalPoint.XY_CENTER, + ), + Waypoint( + position=Point(x=44, y=55, z=66), + critical_point=CriticalPoint.XY_CENTER, + ), + ], + speed=9001, + ) + ).then_return(Point(x=4, y=5, z=6)) + + result = await subject.execute(params) + + assert result == SuccessData( + public=TouchTipResult(position=DeckPoint(x=4, y=5, z=6)), + state_update=update_types.StateUpdate( + pipette_location=update_types.PipetteLocationUpdate( + pipette_id="abc", + new_location=update_types.Well(labware_id="123", well_name="A3"), + new_deck_point=DeckPoint(x=4, y=5, z=6), + ) + ), + ) + + +async def test_touch_tip_implementation_with_mm_to_edge( + decoy: Decoy, + mock_state_view: StateView, + mock_movement_handler: MovementHandler, + mock_gantry_mover: GantryMover, + subject: TouchTipImplementation, +) -> None: + """A TouchTip command should use mmFromEdge if provided.""" + params = TouchTipParams( + pipetteId="abc", + labwareId="123", + wellName="A3", + wellLocation=WellLocation(offset=WellOffset(x=1, y=2, z=3)), + mmFromEdge=0.789, + speed=42.0, + ) + + decoy.when( + await mock_movement_handler.move_to_well( + pipette_id="abc", + labware_id="123", + well_name="A3", + well_location=WellLocation(offset=WellOffset(x=1, y=2, z=3)), + current_well=None, + force_direct=False, + minimum_z_height=None, + speed=None, + operation_volume=None, + ) + ).then_return(Point(x=1, y=2, z=3)) + + decoy.when( + mock_state_view.pipettes.get_movement_speed( + pipette_id="abc", requested_speed=42.0 + ) + ).then_return(9001) + + decoy.when( + mock_state_view.motion.get_touch_tip_waypoints( + pipette_id="abc", + labware_id="123", + well_name="A3", + radius=1.0, + mm_from_edge=0.789, + center_point=Point(x=1, y=2, z=3), ) ).then_return( [ @@ -183,3 +274,20 @@ async def test_touch_tip_no_tip_racks( with pytest.raises(errors.LabwareIsTipRackError): await subject.execute(params) + + +async def test_touch_tip_incompatible_arguments( + decoy: Decoy, mock_state_view: StateView, subject: TouchTipImplementation +) -> None: + """It should disallow touch tip if radius and mmFromEdge is provided.""" + params = TouchTipParams( + pipetteId="abc", + labwareId="123", + wellName="A3", + wellLocation=WellLocation(), + radius=1.23, + mmFromEdge=4.56, + ) + + with pytest.raises(errors.TouchTipIncompatibleArgumentsError): + await subject.execute(params) diff --git a/api/tests/opentrons/protocol_engine/conftest.py b/api/tests/opentrons/protocol_engine/conftest.py index 76c5d754f3e..48ce28e7a98 100644 --- a/api/tests/opentrons/protocol_engine/conftest.py +++ b/api/tests/opentrons/protocol_engine/conftest.py @@ -78,7 +78,7 @@ def ot3_standard_deck_def() -> DeckDefinitionV5: @pytest.fixture(scope="session") def ot2_fixed_trash_def() -> LabwareDefinition: """Get the definition of the OT-2 standard fixed trash.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("opentrons_1_trash_1100ml_fixed", 1) ) @@ -86,7 +86,7 @@ def ot2_fixed_trash_def() -> LabwareDefinition: @pytest.fixture(scope="session") def ot2_short_fixed_trash_def() -> LabwareDefinition: """Get the definition of the OT-2 short fixed trash.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("opentrons_1_trash_850ml_fixed", 1) ) @@ -94,7 +94,7 @@ def ot2_short_fixed_trash_def() -> LabwareDefinition: @pytest.fixture(scope="session") def ot3_fixed_trash_def() -> LabwareDefinition: """Get the definition of the OT-3 fixed trash.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("opentrons_1_trash_3200ml_fixed", 1) ) @@ -102,7 +102,7 @@ def ot3_fixed_trash_def() -> LabwareDefinition: @pytest.fixture(scope="session") def ot3_absorbance_reader_lid() -> LabwareDefinition: """Get the definition of the OT-3 plate reader lid.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("opentrons_flex_lid_absorbance_plate_reader_module", 1) ) @@ -110,7 +110,7 @@ def ot3_absorbance_reader_lid() -> LabwareDefinition: @pytest.fixture(scope="session") def well_plate_def() -> LabwareDefinition: """Get the definition of a 96 well plate.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("corning_96_wellplate_360ul_flat", 2) ) @@ -118,7 +118,7 @@ def well_plate_def() -> LabwareDefinition: @pytest.fixture(scope="session") def flex_50uL_tiprack() -> LabwareDefinition: """Get the definition of a Flex 50uL tiprack.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("opentrons_flex_96_filtertiprack_50ul", 1) ) @@ -126,7 +126,7 @@ def flex_50uL_tiprack() -> LabwareDefinition: @pytest.fixture(scope="session") def adapter_plate_def() -> LabwareDefinition: """Get the definition of a h/s adapter plate.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("opentrons_universal_flat_adapter", 1) ) @@ -134,25 +134,31 @@ def adapter_plate_def() -> LabwareDefinition: @pytest.fixture(scope="session") def reservoir_def() -> LabwareDefinition: """Get the definition of single-row reservoir.""" - return LabwareDefinition.parse_obj(load_definition("nest_12_reservoir_15ml", 1)) + return LabwareDefinition.model_validate( + load_definition("nest_12_reservoir_15ml", 1) + ) @pytest.fixture(scope="session") def tip_rack_def() -> LabwareDefinition: """Get the definition of Opentrons 300 uL tip rack.""" - return LabwareDefinition.parse_obj(load_definition("opentrons_96_tiprack_300ul", 1)) + return LabwareDefinition.model_validate( + load_definition("opentrons_96_tiprack_300ul", 1) + ) @pytest.fixture(scope="session") def adapter_def() -> LabwareDefinition: """Get the definition of Opentrons 96 PCR adapter.""" - return LabwareDefinition.parse_obj(load_definition("opentrons_96_pcr_adapter", 1)) + return LabwareDefinition.model_validate( + load_definition("opentrons_96_pcr_adapter", 1) + ) @pytest.fixture(scope="session") def falcon_tuberack_def() -> LabwareDefinition: """Get the definition of the 6-well Falcon tuberack.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("opentrons_6_tuberack_falcon_50ml_conical", 1) ) @@ -160,7 +166,7 @@ def falcon_tuberack_def() -> LabwareDefinition: @pytest.fixture(scope="session") def magdeck_well_plate_def() -> LabwareDefinition: """Get the definition of a well place compatible with magdeck.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( load_definition("nest_96_wellplate_100ul_pcr_full_skirt", 1) ) @@ -169,63 +175,63 @@ def magdeck_well_plate_def() -> LabwareDefinition: def tempdeck_v1_def() -> ModuleDefinition: """Get the definition of a V1 tempdeck.""" definition = load_shared_data("module/definitions/3/temperatureModuleV1.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") def tempdeck_v2_def() -> ModuleDefinition: """Get the definition of a V2 tempdeck.""" definition = load_shared_data("module/definitions/3/temperatureModuleV2.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") def magdeck_v1_def() -> ModuleDefinition: """Get the definition of a V1 magdeck.""" definition = load_shared_data("module/definitions/3/magneticModuleV1.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") def magdeck_v2_def() -> ModuleDefinition: """Get the definition of a V2 magdeck.""" definition = load_shared_data("module/definitions/3/magneticModuleV2.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") def thermocycler_v1_def() -> ModuleDefinition: """Get the definition of a V2 thermocycler.""" definition = load_shared_data("module/definitions/3/thermocyclerModuleV1.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") def thermocycler_v2_def() -> ModuleDefinition: """Get the definition of a V2 thermocycler.""" definition = load_shared_data("module/definitions/3/thermocyclerModuleV2.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") def heater_shaker_v1_def() -> ModuleDefinition: """Get the definition of a V1 heater-shaker.""" definition = load_shared_data("module/definitions/3/heaterShakerModuleV1.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") def mag_block_v1_def() -> ModuleDefinition: """Get the definition of a V1 Mag Block.""" definition = load_shared_data("module/definitions/3/magneticBlockV1.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") def abs_reader_v1_def() -> ModuleDefinition: """Get the definition of a V1 absorbance plate reader.""" definition = load_shared_data("module/definitions/3/absorbanceReaderV1.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture(scope="session") diff --git a/api/tests/opentrons/protocol_engine/errors/test_error_occurrence.py b/api/tests/opentrons/protocol_engine/errors/test_error_occurrence.py index a2feb8261f7..531c2decc98 100644 --- a/api/tests/opentrons/protocol_engine/errors/test_error_occurrence.py +++ b/api/tests/opentrons/protocol_engine/errors/test_error_occurrence.py @@ -1,4 +1,5 @@ """Test ErrorOccurrence module.""" + import datetime from typing import List @@ -11,7 +12,7 @@ def test_error_occurrence_schema() -> None: This is explicitly tested because we are overriding the schema due to a default value for errorCode. """ - required_items: List[str] = ErrorOccurrence.schema()["definitions"][ + required_items: List[str] = ErrorOccurrence.model_json_schema()["$defs"][ "ErrorOccurrence" ]["required"] assert "errorCode" in required_items @@ -24,7 +25,7 @@ def test_parse_error_occurrence() -> None: """ input = '{"id": "abcdefg","errorType": "a bad one","createdAt": "2023-06-12 15:08:54.730451","detail": "This is a bad error"}' - result = ErrorOccurrence.parse_raw(input) + result = ErrorOccurrence.model_validate_json(input) expected = ErrorOccurrence( id="abcdefg", diff --git a/api/tests/opentrons/protocol_engine/execution/test_command_executor.py b/api/tests/opentrons/protocol_engine/execution/test_command_executor.py index eb84ceb018b..d838eaded87 100644 --- a/api/tests/opentrons/protocol_engine/execution/test_command_executor.py +++ b/api/tests/opentrons/protocol_engine/execution/test_command_executor.py @@ -1,11 +1,12 @@ """Smoke tests for the CommandExecutor class.""" + import asyncio from datetime import datetime -from typing import Any, Optional, Type, Union, cast +from typing import Optional, Type, cast, Any, Union import pytest from decoy import Decoy, matchers -from pydantic import BaseModel +from pydantic import BaseModel, PrivateAttr from opentrons.hardware_control import HardwareControlAPI, OT2HardwareControlAPI @@ -13,6 +14,7 @@ from opentrons.protocol_engine.error_recovery_policy import ( ErrorRecoveryPolicy, ErrorRecoveryType, + never_recover, ) from opentrons.protocol_engine.errors.error_occurrence import ErrorOccurrence from opentrons.protocol_engine.errors.exceptions import ( @@ -253,6 +255,12 @@ async def test_execute( TestCommandImplCls = decoy.mock(func=_TestCommandImpl) command_impl = decoy.mock(cls=_TestCommandImpl) + # Note: private attrs (which are attrs that start with _) are instantiated via deep + # copy from a provided default in the model, so if + # _TestCommand()._ImplementationCls != _TestCommand._ImplementationCls.default if + # we provide a default. Therefore, provide a default factory, so we can always have + # the same object. + class _TestCommand( BaseCommand[_TestCommandParams, _TestCommandResult, ErrorOccurrence] ): @@ -260,14 +268,16 @@ class _TestCommand( params: _TestCommandParams result: Optional[_TestCommandResult] - _ImplementationCls: Type[_TestCommandImpl] = TestCommandImplCls + _ImplementationCls: Type[_TestCommandImpl] = PrivateAttr( + default_factory=lambda: TestCommandImplCls + ) command_params = _TestCommandParams() command_result = SuccessData(public=_TestCommandResult()) queued_command = cast( Command, - _TestCommand( + _TestCommand.model_construct( # type: ignore[call-arg] id="command-id", key="command-key", createdAt=datetime(year=2021, month=1, day=1), @@ -278,7 +288,7 @@ class _TestCommand( running_command = cast( Command, - _TestCommand( + _TestCommand.model_construct( # type: ignore[call-arg] id="command-id", key="command-key", createdAt=datetime(year=2021, month=1, day=1), @@ -299,7 +309,7 @@ class _TestCommand( expected_completed_command = cast( Command, - _TestCommand( + _TestCommand.model_construct( id="command-id", key="command-key", createdAt=datetime(year=2021, month=1, day=1), @@ -327,7 +337,6 @@ class _TestCommand( state_store.commands.get(command_id="command-id") ).then_return(running_command) ) - decoy.when( queued_command._ImplementationCls( state_view=state_store, @@ -358,6 +367,10 @@ class _TestCommand( datetime(year=2023, month=3, day=3), ) + decoy.when(state_store.commands.get_error_recovery_policy()).then_return( + never_recover + ) + await subject.execute("command-id") decoy.verify( @@ -421,13 +434,15 @@ class _TestCommand( params: _TestCommandParams result: Optional[_TestCommandResult] - _ImplementationCls: Type[_TestCommandImpl] = TestCommandImplCls + _ImplementationCls: Type[_TestCommandImpl] = PrivateAttr( + default_factory=lambda: TestCommandImplCls + ) command_params = _TestCommandParams() queued_command = cast( Command, - _TestCommand( + _TestCommand.model_construct( # type: ignore[call-arg] id="command-id", key="command-key", createdAt=datetime(year=2021, month=1, day=1), @@ -438,7 +453,7 @@ class _TestCommand( running_command = cast( Command, - _TestCommand( + _TestCommand.model_construct( # type: ignore[call-arg] id="command-id", key="command-key", createdAt=datetime(year=2021, month=1, day=1), @@ -556,7 +571,9 @@ class _TestCommand( params: _TestCommandParams result: Optional[_TestCommandResult] - _ImplementationCls: Type[_TestCommandImpl] = TestCommandImplCls + _ImplementationCls: Type[_TestCommandImpl] = PrivateAttr( + default_factory=lambda: TestCommandImplCls + ) command_params = _TestCommandParams() command_id = "command-id" @@ -569,7 +586,7 @@ class _TestCommand( ) queued_command = cast( Command, - _TestCommand( + _TestCommand.model_construct( # type: ignore[call-arg] id=command_id, key="command-key", createdAt=created_at, @@ -579,7 +596,7 @@ class _TestCommand( ) running_command = cast( Command, - _TestCommand( + _TestCommand.model_construct( # type: ignore[call-arg] id=command_id, key="command-key", createdAt=created_at, diff --git a/api/tests/opentrons/protocol_engine/execution/test_equipment_handler.py b/api/tests/opentrons/protocol_engine/execution/test_equipment_handler.py index 39208184754..29117a894b5 100644 --- a/api/tests/opentrons/protocol_engine/execution/test_equipment_handler.py +++ b/api/tests/opentrons/protocol_engine/execution/test_equipment_handler.py @@ -1,4 +1,5 @@ """Test equipment command execution side effects.""" + import pytest from _pytest.fixtures import SubRequest import inspect @@ -648,7 +649,7 @@ async def test_load_pipette( decoy.when(state_store.config.use_virtual_pipettes).then_return(False) decoy.when(model_utils.generate_id()).then_return("unique-id") decoy.when(state_store.pipettes.get_by_mount(MountType.RIGHT)).then_return( - LoadedPipette.construct(pipetteName=PipetteNameType.P300_MULTI) # type: ignore[call-arg] + LoadedPipette.model_construct(pipetteName=PipetteNameType.P300_MULTI) # type: ignore[call-arg] ) decoy.when(hardware_api.get_attached_instrument(mount=HwMount.LEFT)).then_return( pipette_dict diff --git a/api/tests/opentrons/protocol_engine/execution/test_tip_handler.py b/api/tests/opentrons/protocol_engine/execution/test_tip_handler.py index 4e9a10fdfaa..23f701db80b 100644 --- a/api/tests/opentrons/protocol_engine/execution/test_tip_handler.py +++ b/api/tests/opentrons/protocol_engine/execution/test_tip_handler.py @@ -1,4 +1,5 @@ """Pipetting execution handler.""" + import pytest from decoy import Decoy, matchers @@ -51,7 +52,7 @@ def mock_labware_data_provider(decoy: Decoy) -> LabwareDataProvider: @pytest.fixture def tip_rack_definition() -> LabwareDefinition: """Get a tip rack defintion value object.""" - return LabwareDefinition.construct(namespace="test", version=42) # type: ignore[call-arg] + return LabwareDefinition.model_construct(namespace="test", version=42) # type: ignore[call-arg] MOCK_MAP = NozzleMap.build( diff --git a/api/tests/opentrons/protocol_engine/resources/test_labware_data_provider.py b/api/tests/opentrons/protocol_engine/resources/test_labware_data_provider.py index 92718c70d89..a666e7a697d 100644 --- a/api/tests/opentrons/protocol_engine/resources/test_labware_data_provider.py +++ b/api/tests/opentrons/protocol_engine/resources/test_labware_data_provider.py @@ -22,7 +22,7 @@ async def test_labware_data_gets_standard_definition() -> None: version=1, ) - assert result == LabwareDefinition.parse_obj(expected) + assert result == LabwareDefinition.model_validate(expected) async def test_labware_hash_match() -> None: @@ -38,9 +38,9 @@ async def test_labware_hash_match() -> None: version=1, ) - labware_model = LabwareDefinition.parse_obj(labware_dict) + labware_model = LabwareDefinition.model_validate(labware_dict) labware_model_dict = cast( - LabwareDefDict, labware_model.dict(exclude_none=True, exclude_unset=True) + LabwareDefDict, labware_model.model_dump(exclude_none=True, exclude_unset=True) ) assert hash_labware_def(labware_dict) == hash_labware_def(labware_model_dict) diff --git a/api/tests/opentrons/protocol_engine/resources/test_labware_validation.py b/api/tests/opentrons/protocol_engine/resources/test_labware_validation.py index 663aec7337f..fbe9d6c21a4 100644 --- a/api/tests/opentrons/protocol_engine/resources/test_labware_validation.py +++ b/api/tests/opentrons/protocol_engine/resources/test_labware_validation.py @@ -14,9 +14,18 @@ @pytest.mark.parametrize( ("definition", "expected_result"), [ - (LabwareDefinition.construct(allowedRoles=[LabwareRole.labware]), True), # type: ignore[call-arg] - (LabwareDefinition.construct(allowedRoles=[]), True), # type: ignore[call-arg] - (LabwareDefinition.construct(allowedRoles=[LabwareRole.adapter]), False), # type: ignore[call-arg] + ( + LabwareDefinition.model_construct(allowedRoles=[LabwareRole.labware]), # type: ignore[call-arg] + True, + ), + ( + LabwareDefinition.model_construct(allowedRoles=[]), # type: ignore[call-arg] + True, + ), + ( + LabwareDefinition.model_construct(allowedRoles=[LabwareRole.adapter]), # type: ignore[call-arg] + False, + ), ], ) def test_validate_definition_is_labware( @@ -29,9 +38,18 @@ def test_validate_definition_is_labware( @pytest.mark.parametrize( ("definition", "expected_result"), [ - (LabwareDefinition.construct(allowedRoles=[LabwareRole.adapter]), True), # type: ignore[call-arg] - (LabwareDefinition.construct(allowedRoles=[]), False), # type: ignore[call-arg] - (LabwareDefinition.construct(allowedRoles=[LabwareRole.labware]), False), # type: ignore[call-arg] + ( + LabwareDefinition.model_construct(allowedRoles=[LabwareRole.adapter]), # type: ignore[call-arg] + True, + ), + ( + LabwareDefinition.model_construct(allowedRoles=[]), # type: ignore[call-arg] + False, + ), + ( + LabwareDefinition.model_construct(allowedRoles=[LabwareRole.labware]), # type: ignore[call-arg] + False, + ), ], ) def test_validate_definition_is_adapter( @@ -44,9 +62,22 @@ def test_validate_definition_is_adapter( @pytest.mark.parametrize( ("definition", "expected_result"), [ - (LabwareDefinition.construct(stackingOffsetWithLabware={"labware123": OverlapOffset(x=4, y=5, z=6)}), True), # type: ignore[call-arg] - (LabwareDefinition.construct(stackingOffsetWithLabware={"labwareXYZ": OverlapOffset(x=4, y=5, z=6)}), False), # type: ignore[call-arg] - (LabwareDefinition.construct(stackingOffsetWithLabware={}), False), # type: ignore[call-arg] + ( + LabwareDefinition.model_construct( # type: ignore[call-arg] + stackingOffsetWithLabware={"labware123": OverlapOffset(x=4, y=5, z=6)} + ), + True, + ), + ( + LabwareDefinition.model_construct( # type: ignore[call-arg] + stackingOffsetWithLabware={"labwareXYZ": OverlapOffset(x=4, y=5, z=6)} + ), + False, + ), + ( + LabwareDefinition.model_construct(stackingOffsetWithLabware={}), # type: ignore[call-arg] + False, + ), ], ) def test_validate_labware_can_be_stacked( @@ -62,9 +93,24 @@ def test_validate_labware_can_be_stacked( @pytest.mark.parametrize( ("definition", "expected_result"), [ - (LabwareDefinition.construct(parameters=Parameters.construct(quirks=None)), True), # type: ignore[call-arg] - (LabwareDefinition.construct(parameters=Parameters.construct(quirks=["foo"])), True), # type: ignore[call-arg] - (LabwareDefinition.construct(parameters=Parameters.construct(quirks=["gripperIncompatible"])), False), # type: ignore[call-arg] + ( + LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(quirks=None) # type: ignore[call-arg] + ), + True, + ), + ( + LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(quirks=["foo"]) # type: ignore[call-arg] + ), + True, + ), + ( + LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(quirks=["gripperIncompatible"]) # type: ignore[call-arg] + ), + False, + ), ], ) def test_validate_gripper_compatible( diff --git a/api/tests/opentrons/protocol_engine/state/command_fixtures.py b/api/tests/opentrons/protocol_engine/state/command_fixtures.py index 5ac522095f2..31f06858b53 100644 --- a/api/tests/opentrons/protocol_engine/state/command_fixtures.py +++ b/api/tests/opentrons/protocol_engine/state/command_fixtures.py @@ -1,4 +1,5 @@ """Command factories to use in tests as data fixtures.""" + from datetime import datetime from pydantic import BaseModel from typing import Optional, cast, Dict @@ -21,6 +22,12 @@ ) +class FixtureModel(BaseModel): + """Fixture Model.""" + + ... + + def create_queued_command( command_id: str = "command-id", command_key: str = "command-key", @@ -29,6 +36,10 @@ def create_queued_command( params: Optional[BaseModel] = None, ) -> cmd.Command: """Given command data, build a pending command model.""" + + class DummyParams(BaseModel): + pass + return cast( cmd.Command, cmd.BaseCommand( @@ -37,7 +48,7 @@ def create_queued_command( commandType=command_type, createdAt=datetime(year=2021, month=1, day=1), status=cmd.CommandStatus.QUEUED, - params=params or BaseModel(), + params=params or DummyParams(), intent=intent, ), ) @@ -59,7 +70,7 @@ def create_running_command( createdAt=created_at, commandType=command_type, status=cmd.CommandStatus.RUNNING, - params=params or BaseModel(), + params=params or FixtureModel(), ), ) @@ -84,7 +95,7 @@ def create_failed_command( completedAt=completed_at, commandType=command_type, status=cmd.CommandStatus.FAILED, - params=params or BaseModel(), + params=params or FixtureModel(), error=error, intent=intent, ), @@ -108,8 +119,8 @@ def create_succeeded_command( createdAt=created_at, commandType=command_type, status=cmd.CommandStatus.SUCCEEDED, - params=params or BaseModel(), - result=result or BaseModel(), + params=params or FixtureModel(), + result=result or FixtureModel(), ), ) @@ -193,7 +204,7 @@ def create_load_module_command( moduleId=module_id, model=model, serialNumber=None, - definition=ModuleDefinition.construct(), # type: ignore[call-arg] + definition=ModuleDefinition.model_construct(), # type: ignore[call-arg] ) return cmd.LoadModule( diff --git a/api/tests/opentrons/protocol_engine/state/test_addressable_area_store_old.py b/api/tests/opentrons/protocol_engine/state/test_addressable_area_store_old.py index 1bbccf96d42..b04237c702d 100644 --- a/api/tests/opentrons/protocol_engine/state/test_addressable_area_store_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_addressable_area_store_old.py @@ -45,7 +45,7 @@ def _make_deck_config() -> DeckConfigurationType: def _dummy_command() -> Command: """Return a placeholder command.""" - return Comment.construct() # type: ignore[call-arg] + return Comment.model_construct() # type: ignore[call-arg] @pytest.fixture diff --git a/api/tests/opentrons/protocol_engine/state/test_command_history.py b/api/tests/opentrons/protocol_engine/state/test_command_history.py index 14eaa2a42f3..fabf17e26d1 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_history.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_history.py @@ -202,13 +202,13 @@ def test_set_fixit_running_command_id(command_history: CommandHistory) -> None: """It should set the ID of the currently running fixit command.""" command_entry = create_queued_command() command_history.append_queued_command(command_entry) - running_command = command_entry.copy( + running_command = command_entry.model_copy( update={ "status": CommandStatus.RUNNING, } ) command_history.set_command_running(running_command) - finished_command = command_entry.copy( + finished_command = command_entry.model_copy( update={ "status": CommandStatus.SUCCEEDED, } @@ -218,7 +218,7 @@ def test_set_fixit_running_command_id(command_history: CommandHistory) -> None: command_id="fixit-id", intent=CommandIntent.FIXIT ) command_history.append_queued_command(fixit_command_entry) - fixit_running_command = fixit_command_entry.copy( + fixit_running_command = fixit_command_entry.model_copy( update={ "status": CommandStatus.RUNNING, } diff --git a/api/tests/opentrons/protocol_engine/state/test_geometry_view.py b/api/tests/opentrons/protocol_engine/state/test_geometry_view.py index b145458649d..fda32a56ce0 100644 --- a/api/tests/opentrons/protocol_engine/state/test_geometry_view.py +++ b/api/tests/opentrons/protocol_engine/state/test_geometry_view.py @@ -1,4 +1,5 @@ """Test state getters for retrieving geometry views of state.""" + import inspect import json from datetime import datetime @@ -257,7 +258,7 @@ def addressable_area_view( @pytest.fixture def nice_labware_definition() -> LabwareDefinition: """Load a nice labware def that won't blow up your terminal.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( json.loads( load_shared_data("labware/fixtures/2/fixture_12_trough_v2.json").decode( "utf-8" @@ -269,7 +270,7 @@ def nice_labware_definition() -> LabwareDefinition: @pytest.fixture def nice_adapter_definition() -> LabwareDefinition: """Load a friendly adapter definition.""" - return LabwareDefinition.parse_obj( + return LabwareDefinition.model_validate( json.loads( load_shared_data( "labware/definitions/2/opentrons_aluminum_flat_bottom_plate/1.json" @@ -849,8 +850,8 @@ def test_get_all_obstacle_highest_z_with_modules( subject: GeometryView, ) -> None: """It should get the highest Z including modules.""" - module_1 = LoadedModule.construct(id="module-id-1") # type: ignore[call-arg] - module_2 = LoadedModule.construct(id="module-id-2") # type: ignore[call-arg] + module_1 = LoadedModule.model_construct(id="module-id-1") # type: ignore[call-arg] + module_2 = LoadedModule.model_construct(id="module-id-2") # type: ignore[call-arg] decoy.when(mock_labware_view.get_all()).then_return([]) decoy.when(mock_addressable_area_view.get_all()).then_return([]) @@ -939,7 +940,7 @@ def test_get_highest_z_in_slot_with_single_module( ) -> None: """It should get the highest Z in slot with just a single module.""" # Case: Slot has a module that doesn't have any labware on it. Highest z is equal to module height. - module_in_slot = LoadedModule.construct( + module_in_slot = LoadedModule.model_construct( id="only-module", model=ModuleModel.THERMOCYCLER_MODULE_V2, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_4), @@ -1094,7 +1095,7 @@ def test_get_highest_z_in_slot_with_labware_stack_on_module( location=ModuleLocation(moduleId="module-id"), offsetId="offset-id2", ) - module_on_slot = LoadedModule.construct( + module_on_slot = LoadedModule.model_construct( id="module-id", model=ModuleModel.THERMOCYCLER_MODULE_V2, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_4), @@ -1982,7 +1983,7 @@ def test_get_relative_well_location( assert result == WellLocation( origin=WellOrigin.TOP, - offset=WellOffset.construct( + offset=WellOffset.model_construct( x=cast(float, pytest.approx(7)), y=cast(float, pytest.approx(8)), z=cast(float, pytest.approx(9)), @@ -2007,7 +2008,7 @@ def test_get_relative_liquid_handling_well_location( assert result == LiquidHandlingWellLocation( origin=WellOrigin.MENISCUS, - offset=WellOffset.construct( + offset=WellOffset.model_construct( x=0.0, y=0.0, z=cast(float, pytest.approx(-2)), @@ -2457,8 +2458,8 @@ def test_get_slot_item( subject: GeometryView, ) -> None: """It should get items in certain slots.""" - labware = LoadedLabware.construct(id="cool-labware") # type: ignore[call-arg] - module = LoadedModule.construct(id="cool-module") # type: ignore[call-arg] + labware = LoadedLabware.model_construct(id="cool-labware") # type: ignore[call-arg] + module = LoadedModule.model_construct(id="cool-module") # type: ignore[call-arg] decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_1)).then_return(None) decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_2)).then_return(labware) @@ -2485,7 +2486,7 @@ def test_get_slot_item_that_is_overflowed_module( subject: GeometryView, ) -> None: """It should return the module that occupies the slot, even if not loaded on it.""" - module = LoadedModule.construct(id="cool-module") # type: ignore[call-arg] + module = LoadedModule.model_construct(id="cool-module") # type: ignore[call-arg] decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_3)).then_return(None) decoy.when(mock_module_view.get_by_slot(DeckSlotName.SLOT_3)).then_return(None) decoy.when( @@ -2901,19 +2902,19 @@ def test_check_gripper_labware_tip_collision( ) ) - definition = LabwareDefinition.construct( # type: ignore[call-arg] + definition = LabwareDefinition.model_construct( # type: ignore[call-arg] namespace="hello", - dimensions=LabwareDimensions.construct( + dimensions=LabwareDimensions.model_construct( yDimension=1, zDimension=2, xDimension=3 ), version=1, - parameters=LabwareDefinitionParameters.construct( + parameters=LabwareDefinitionParameters.model_construct( format="96Standard", loadName="labware-id", isTiprack=True, isMagneticModuleCompatible=False, ), - cornerOffsetFromSlot=CornerOffsetFromSlot.construct(x=1, y=2, z=3), + cornerOffsetFromSlot=CornerOffsetFromSlot.model_construct(x=1, y=2, z=3), ordering=[], ) diff --git a/api/tests/opentrons/protocol_engine/state/test_labware_view_old.py b/api/tests/opentrons/protocol_engine/state/test_labware_view_old.py index 0b6886040c6..7ace6d767ad 100644 --- a/api/tests/opentrons/protocol_engine/state/test_labware_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_labware_view_old.py @@ -261,13 +261,13 @@ def test_find_custom_labware_params( namespace: Optional[str], version: Optional[int] ) -> None: """It should find the missing (if any) load labware parameters.""" - labware_def = LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct(loadName="hello"), # type: ignore[call-arg] + labware_def = LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(loadName="hello"), # type: ignore[call-arg] namespace="world", version=123, ) - standard_def = LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct(loadName="goodbye"), # type: ignore[call-arg] + standard_def = LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(loadName="goodbye"), # type: ignore[call-arg] namespace="opentrons", version=456, ) @@ -551,12 +551,12 @@ def test_validate_liquid_allowed_raises_incompatible_labware() -> None: ), }, definitions_by_uri={ - "some-tiprack-uri": LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct(isTiprack=True), # type: ignore[call-arg] + "some-tiprack-uri": LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(isTiprack=True), # type: ignore[call-arg] wells={}, ), - "some-adapter-uri": LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct(isTiprack=False), # type: ignore[call-arg] + "some-adapter-uri": LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(isTiprack=False), # type: ignore[call-arg] allowedRoles=[LabwareRole.adapter], wells={}, ), @@ -598,8 +598,8 @@ def test_get_tip_length_gets_length_from_definition( def test_get_tip_drop_z_offset() -> None: """It should get a tip drop z offset by scaling the tip length.""" - tip_rack_def = LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct( # type: ignore[call-arg] + tip_rack_def = LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct( # type: ignore[call-arg] tipLength=100, ) ) @@ -693,7 +693,7 @@ def test_get_labware_overlap_offsets() -> None: """It should get the labware overlap offsets.""" subject = get_labware_view() result = subject.get_labware_overlap_offsets( - definition=LabwareDefinition.construct( # type: ignore[call-arg] + definition=LabwareDefinition.model_construct( # type: ignore[call-arg] stackingOffsetWithLabware={ "bottom-labware-name": SharedDataOverlapOffset(x=1, y=2, z=3) } @@ -779,7 +779,7 @@ def test_get_module_overlap_offsets( deck_definition=spec_deck_definition, ) result = subject.get_module_overlap_offsets( - definition=LabwareDefinition.construct( # type: ignore[call-arg] + definition=LabwareDefinition.model_construct( # type: ignore[call-arg] stackingOffsetWithModule=stacking_offset_with_module ), module_model=module_model, @@ -1131,13 +1131,13 @@ def test_raise_if_labware_in_location( def test_get_by_slot() -> None: """It should get the labware in a given slot.""" - labware_1 = LoadedLabware.construct( # type: ignore[call-arg] + labware_1 = LoadedLabware.model_construct( # type: ignore[call-arg] id="1", location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1) ) - labware_2 = LoadedLabware.construct( # type: ignore[call-arg] + labware_2 = LoadedLabware.model_construct( # type: ignore[call-arg] id="2", location=DeckSlotLocation(slotName=DeckSlotName.SLOT_2) ) - labware_3 = LoadedLabware.construct( # type: ignore[call-arg] + labware_3 = LoadedLabware.model_construct( # type: ignore[call-arg] id="3", location=ModuleLocation(moduleId="cool-module") ) @@ -1183,7 +1183,7 @@ def test_get_edge_path_type( offsetId=None, ) - labware_def = LabwareDefinition.construct( # type: ignore[call-arg] + labware_def = LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[["abc", "def"], ["ghi", "jkl"], ["mno", "pqr"]] ) @@ -1307,10 +1307,8 @@ def test_raise_if_labware_cannot_be_stacked_is_adapter() -> None: errors.LabwareCannotBeStackedError, match="defined as an adapter" ): subject.raise_if_labware_cannot_be_stacked( - top_labware_definition=LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct( # type: ignore[call-arg] - loadName="name" - ), + top_labware_definition=LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(loadName="name"), # type: ignore[call-arg] allowedRoles=[LabwareRole.adapter], ), bottom_labware_id="labware-id", @@ -1334,10 +1332,8 @@ def test_raise_if_labware_cannot_be_stacked_not_validated() -> None: errors.LabwareCannotBeStackedError, match="loaded onto labware test" ): subject.raise_if_labware_cannot_be_stacked( - top_labware_definition=LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct( # type: ignore[call-arg] - loadName="name" - ), + top_labware_definition=LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(loadName="name"), # type: ignore[call-arg] stackingOffsetWithLabware={}, ), bottom_labware_id="labware-id", @@ -1356,7 +1352,7 @@ def test_raise_if_labware_cannot_be_stacked_on_module_not_adapter() -> None: ) }, definitions_by_uri={ - "def-uri": LabwareDefinition.construct( # type: ignore[call-arg] + "def-uri": LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[LabwareRole.labware] ) }, @@ -1364,10 +1360,8 @@ def test_raise_if_labware_cannot_be_stacked_on_module_not_adapter() -> None: with pytest.raises(errors.LabwareCannotBeStackedError, match="module"): subject.raise_if_labware_cannot_be_stacked( - top_labware_definition=LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct( # type: ignore[call-arg] - loadName="name" - ), + top_labware_definition=LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(loadName="name"), # type: ignore[call-arg] stackingOffsetWithLabware={ "test": SharedDataOverlapOffset(x=0, y=0, z=0) }, @@ -1394,10 +1388,10 @@ def test_raise_if_labware_cannot_be_stacked_on_labware_on_adapter() -> None: ), }, definitions_by_uri={ - "def-uri-1": LabwareDefinition.construct( # type: ignore[call-arg] + "def-uri-1": LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[LabwareRole.labware] ), - "def-uri-2": LabwareDefinition.construct( # type: ignore[call-arg] + "def-uri-2": LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=[LabwareRole.adapter] ), }, @@ -1407,10 +1401,8 @@ def test_raise_if_labware_cannot_be_stacked_on_labware_on_adapter() -> None: errors.LabwareCannotBeStackedError, match="cannot be loaded to stack" ): subject.raise_if_labware_cannot_be_stacked( - top_labware_definition=LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct( # type: ignore[call-arg] - loadName="name" - ), + top_labware_definition=LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct(loadName="name"), # type: ignore[call-arg] stackingOffsetWithLabware={ "test": SharedDataOverlapOffset(x=0, y=0, z=0) }, @@ -1472,9 +1464,9 @@ def test_labware_stacking_height_passes_or_raises( ), }, definitions_by_uri={ - "def-uri-1": LabwareDefinition.construct( # type: ignore[call-arg] + "def-uri-1": LabwareDefinition.model_construct( # type: ignore[call-arg] allowedRoles=allowed_roles, - parameters=Parameters.construct( + parameters=Parameters.model_construct( format="irregular", quirks=stacking_quirks, isTiprack=False, @@ -1487,8 +1479,8 @@ def test_labware_stacking_height_passes_or_raises( with exception: subject.raise_if_labware_cannot_be_stacked( - top_labware_definition=LabwareDefinition.construct( # type: ignore[call-arg] - parameters=Parameters.construct( + top_labware_definition=LabwareDefinition.model_construct( # type: ignore[call-arg] + parameters=Parameters.model_construct( format="irregular", quirks=stacking_quirks, isTiprack=False, @@ -1554,7 +1546,7 @@ def test_get_labware_gripper_offsets_default_no_slots( ) }, definitions_by_uri={ - "some-labware-uri": LabwareDefinition.construct( # type: ignore[call-arg] + "some-labware-uri": LabwareDefinition.model_construct( # type: ignore[call-arg] gripperOffsets={ "default": GripperOffsets( pickUpOffset=OffsetVector(x=1, y=2, z=3), @@ -1618,7 +1610,7 @@ def test_calculates_well_bounding_box( labware_to_check: str, well_bbox: Dimensions ) -> None: """It should be able to calculate well bounding boxes.""" - definition = LabwareDefinition.parse_obj(load_definition(labware_to_check, 1)) + definition = LabwareDefinition.model_validate(load_definition(labware_to_check, 1)) subject = get_labware_view() assert subject.get_well_bbox(definition).x == pytest.approx(well_bbox.x) assert subject.get_well_bbox(definition).y == pytest.approx(well_bbox.y) diff --git a/api/tests/opentrons/protocol_engine/state/test_liquid_class_store_old.py b/api/tests/opentrons/protocol_engine/state/test_liquid_class_store_old.py index 57397ec61cb..9d910d9495a 100644 --- a/api/tests/opentrons/protocol_engine/state/test_liquid_class_store_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_liquid_class_store_old.py @@ -39,7 +39,7 @@ def test_handles_add_liquid_class( subject.handle_action( actions.SucceedCommandAction( - command=LoadLiquidClass.construct(), # type: ignore[call-arg] + command=LoadLiquidClass.model_construct(), # type: ignore[call-arg] state_update=update_types.StateUpdate( liquid_class_loaded=update_types.LiquidClassLoadedUpdate( liquid_class_id="liquid-class-id", diff --git a/api/tests/opentrons/protocol_engine/state/test_module_store_old.py b/api/tests/opentrons/protocol_engine/state/test_module_store_old.py index e4ab52ebaf8..4767ecad16b 100644 --- a/api/tests/opentrons/protocol_engine/state/test_module_store_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_module_store_old.py @@ -202,7 +202,7 @@ def test_load_module( ) -> None: """It should handle a successful LoadModule command.""" action = actions.SucceedCommandAction( - command=commands.LoadModule.construct( # type: ignore[call-arg] + command=commands.LoadModule.model_construct( # type: ignore[call-arg] params=commands.LoadModuleParams( model=params_model, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), @@ -265,7 +265,7 @@ def test_load_thermocycler_in_thermocycler_slot( ) -> None: """It should update additional slots for thermocycler module.""" action = actions.SucceedCommandAction( - command=commands.LoadModule.construct( # type: ignore[call-arg] + command=commands.LoadModule.model_construct( # type: ignore[call-arg] params=commands.LoadModuleParams( model=ModuleModel.THERMOCYCLER_MODULE_V2, location=DeckSlotLocation(slotName=tc_slot), @@ -389,7 +389,7 @@ def test_add_module_action( def test_handle_hs_temperature_commands(heater_shaker_v1_def: ModuleDefinition) -> None: """It should update `plate_target_temperature` correctly.""" - load_module_cmd = commands.LoadModule.construct( # type: ignore[call-arg] + load_module_cmd = commands.LoadModule.model_construct( # type: ignore[call-arg] params=commands.LoadModuleParams( model=ModuleModel.HEATER_SHAKER_MODULE_V1, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), @@ -401,11 +401,11 @@ def test_handle_hs_temperature_commands(heater_shaker_v1_def: ModuleDefinition) definition=heater_shaker_v1_def, ), ) - set_temp_cmd = hs_commands.SetTargetTemperature.construct( # type: ignore[call-arg] + set_temp_cmd = hs_commands.SetTargetTemperature.model_construct( # type: ignore[call-arg] params=hs_commands.SetTargetTemperatureParams(moduleId="module-id", celsius=42), result=hs_commands.SetTargetTemperatureResult(), ) - deactivate_cmd = hs_commands.DeactivateHeater.construct( # type: ignore[call-arg] + deactivate_cmd = hs_commands.DeactivateHeater.model_construct( # type: ignore[call-arg] params=hs_commands.DeactivateHeaterParams(moduleId="module-id"), result=hs_commands.DeactivateHeaterResult(), ) @@ -437,7 +437,7 @@ def test_handle_hs_temperature_commands(heater_shaker_v1_def: ModuleDefinition) def test_handle_hs_shake_commands(heater_shaker_v1_def: ModuleDefinition) -> None: """It should update heater-shaker's `is_plate_shaking` correctly.""" - load_module_cmd = commands.LoadModule.construct( # type: ignore[call-arg] + load_module_cmd = commands.LoadModule.model_construct( # type: ignore[call-arg] params=commands.LoadModuleParams( model=ModuleModel.HEATER_SHAKER_MODULE_V1, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), @@ -449,11 +449,11 @@ def test_handle_hs_shake_commands(heater_shaker_v1_def: ModuleDefinition) -> Non definition=heater_shaker_v1_def, ), ) - set_shake_cmd = hs_commands.SetAndWaitForShakeSpeed.construct( # type: ignore[call-arg] + set_shake_cmd = hs_commands.SetAndWaitForShakeSpeed.model_construct( # type: ignore[call-arg] params=hs_commands.SetAndWaitForShakeSpeedParams(moduleId="module-id", rpm=111), result=hs_commands.SetAndWaitForShakeSpeedResult(pipetteRetracted=False), ) - deactivate_cmd = hs_commands.DeactivateShaker.construct( # type: ignore[call-arg] + deactivate_cmd = hs_commands.DeactivateShaker.model_construct( # type: ignore[call-arg] params=hs_commands.DeactivateShakerParams(moduleId="module-id"), result=hs_commands.DeactivateShakerResult(), ) @@ -487,7 +487,7 @@ def test_handle_hs_labware_latch_commands( heater_shaker_v1_def: ModuleDefinition, ) -> None: """It should update heater-shaker's `is_labware_latch_closed` correctly.""" - load_module_cmd = commands.LoadModule.construct( # type: ignore[call-arg] + load_module_cmd = commands.LoadModule.model_construct( # type: ignore[call-arg] params=commands.LoadModuleParams( model=ModuleModel.HEATER_SHAKER_MODULE_V1, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), @@ -499,11 +499,11 @@ def test_handle_hs_labware_latch_commands( definition=heater_shaker_v1_def, ), ) - close_latch_cmd = hs_commands.CloseLabwareLatch.construct( # type: ignore[call-arg] + close_latch_cmd = hs_commands.CloseLabwareLatch.model_construct( # type: ignore[call-arg] params=hs_commands.CloseLabwareLatchParams(moduleId="module-id"), result=hs_commands.CloseLabwareLatchResult(), ) - open_latch_cmd = hs_commands.OpenLabwareLatch.construct( # type: ignore[call-arg] + open_latch_cmd = hs_commands.OpenLabwareLatch.model_construct( # type: ignore[call-arg] params=hs_commands.OpenLabwareLatchParams(moduleId="module-id"), result=hs_commands.OpenLabwareLatchResult(pipetteRetracted=False), ) @@ -546,7 +546,7 @@ def test_handle_tempdeck_temperature_commands( tempdeck_v2_def: ModuleDefinition, ) -> None: """It should update Tempdeck's `plate_target_temperature` correctly.""" - load_module_cmd = commands.LoadModule.construct( # type: ignore[call-arg] + load_module_cmd = commands.LoadModule.model_construct( # type: ignore[call-arg] params=commands.LoadModuleParams( model=ModuleModel.TEMPERATURE_MODULE_V2, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), @@ -558,13 +558,13 @@ def test_handle_tempdeck_temperature_commands( definition=tempdeck_v2_def, ), ) - set_temp_cmd = temp_commands.SetTargetTemperature.construct( # type: ignore[call-arg] + set_temp_cmd = temp_commands.SetTargetTemperature.model_construct( # type: ignore[call-arg] params=temp_commands.SetTargetTemperatureParams( moduleId="module-id", celsius=42.4 ), result=temp_commands.SetTargetTemperatureResult(targetTemperature=42), ) - deactivate_cmd = temp_commands.DeactivateTemperature.construct( # type: ignore[call-arg] + deactivate_cmd = temp_commands.DeactivateTemperature.model_construct( # type: ignore[call-arg] params=temp_commands.DeactivateTemperatureParams(moduleId="module-id"), result=temp_commands.DeactivateTemperatureResult(), ) @@ -592,7 +592,7 @@ def test_handle_thermocycler_temperature_commands( thermocycler_v1_def: ModuleDefinition, ) -> None: """It should update thermocycler's temperature statuses correctly.""" - load_module_cmd = commands.LoadModule.construct( # type: ignore[call-arg] + load_module_cmd = commands.LoadModule.model_construct( # type: ignore[call-arg] params=commands.LoadModuleParams( model=ModuleModel.THERMOCYCLER_MODULE_V1, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), @@ -604,23 +604,23 @@ def test_handle_thermocycler_temperature_commands( definition=thermocycler_v1_def, ), ) - set_block_temp_cmd = tc_commands.SetTargetBlockTemperature.construct( # type: ignore[call-arg] + set_block_temp_cmd = tc_commands.SetTargetBlockTemperature.model_construct( # type: ignore[call-arg] params=tc_commands.SetTargetBlockTemperatureParams( moduleId="module-id", celsius=42.4 ), result=tc_commands.SetTargetBlockTemperatureResult(targetBlockTemperature=42.4), ) - deactivate_block_cmd = tc_commands.DeactivateBlock.construct( # type: ignore[call-arg] + deactivate_block_cmd = tc_commands.DeactivateBlock.model_construct( # type: ignore[call-arg] params=tc_commands.DeactivateBlockParams(moduleId="module-id"), result=tc_commands.DeactivateBlockResult(), ) - set_lid_temp_cmd = tc_commands.SetTargetLidTemperature.construct( # type: ignore[call-arg] + set_lid_temp_cmd = tc_commands.SetTargetLidTemperature.model_construct( # type: ignore[call-arg] params=tc_commands.SetTargetLidTemperatureParams( moduleId="module-id", celsius=35.3 ), result=tc_commands.SetTargetLidTemperatureResult(targetLidTemperature=35.3), ) - deactivate_lid_cmd = tc_commands.DeactivateLid.construct( # type: ignore[call-arg] + deactivate_lid_cmd = tc_commands.DeactivateLid.model_construct( # type: ignore[call-arg] params=tc_commands.DeactivateLidParams(moduleId="module-id"), result=tc_commands.DeactivateLidResult(), ) @@ -672,7 +672,7 @@ def test_handle_thermocycler_lid_commands( thermocycler_v1_def: ModuleDefinition, ) -> None: """It should update thermocycler's lid status after executing lid commands.""" - load_module_cmd = commands.LoadModule.construct( # type: ignore[call-arg] + load_module_cmd = commands.LoadModule.model_construct( # type: ignore[call-arg] params=commands.LoadModuleParams( model=ModuleModel.THERMOCYCLER_MODULE_V1, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), @@ -685,11 +685,11 @@ def test_handle_thermocycler_lid_commands( ), ) - open_lid_cmd = tc_commands.OpenLid.construct( # type: ignore[call-arg] + open_lid_cmd = tc_commands.OpenLid.model_construct( # type: ignore[call-arg] params=tc_commands.OpenLidParams(moduleId="module-id"), result=tc_commands.OpenLidResult(), ) - close_lid_cmd = tc_commands.CloseLid.construct( # type: ignore[call-arg] + close_lid_cmd = tc_commands.CloseLid.model_construct( # type: ignore[call-arg] params=tc_commands.CloseLidParams(moduleId="module-id"), result=tc_commands.CloseLidResult(), ) diff --git a/api/tests/opentrons/protocol_engine/state/test_module_view_old.py b/api/tests/opentrons/protocol_engine/state/test_module_view_old.py index 3902eedc76f..65e1a467977 100644 --- a/api/tests/opentrons/protocol_engine/state/test_module_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_module_view_old.py @@ -149,7 +149,7 @@ def get_sample_parent_module_view( ) -> ModuleView: """Get a ModuleView with attached modules including a requested matching module.""" definition = load_shared_data("module/definitions/2/magneticModuleV1.json") - magdeck_def = ModuleDefinition.parse_raw(definition) + magdeck_def = ModuleDefinition.model_validate_json(definition) return make_module_view( slot_by_module_id={ @@ -1735,13 +1735,13 @@ def test_get_by_slot() -> None: hardware_by_module_id={ "1": HardwareModule( serial_number="serial-number-1", - definition=ModuleDefinition.construct( # type: ignore[call-arg] + definition=ModuleDefinition.model_construct( # type: ignore[call-arg] model=ModuleModel.TEMPERATURE_MODULE_V1 ), ), "2": HardwareModule( serial_number="serial-number-2", - definition=ModuleDefinition.construct( # type: ignore[call-arg] + definition=ModuleDefinition.model_construct( # type: ignore[call-arg] model=ModuleModel.TEMPERATURE_MODULE_V2 ), ), @@ -1773,13 +1773,13 @@ def test_get_by_slot_prefers_later() -> None: hardware_by_module_id={ "1": HardwareModule( serial_number="serial-number-1", - definition=ModuleDefinition.construct( # type: ignore[call-arg] + definition=ModuleDefinition.model_construct( # type: ignore[call-arg] model=ModuleModel.TEMPERATURE_MODULE_V1 ), ), "1-again": HardwareModule( serial_number="serial-number-1-again", - definition=ModuleDefinition.construct( # type: ignore[call-arg] + definition=ModuleDefinition.model_construct( # type: ignore[call-arg] model=ModuleModel.TEMPERATURE_MODULE_V1 ), ), diff --git a/api/tests/opentrons/protocol_engine/state/test_motion_view.py b/api/tests/opentrons/protocol_engine/state/test_motion_view.py index 9e7307f29a7..3e9d60da79a 100644 --- a/api/tests/opentrons/protocol_engine/state/test_motion_view.py +++ b/api/tests/opentrons/protocol_engine/state/test_motion_view.py @@ -928,6 +928,7 @@ def test_get_touch_tip_waypoints( x_radius=1.2, y_radius=3.4, edge_path_type=_move_types.EdgePathType.RIGHT, + mm_from_edge=0.456, ) ).then_return([Point(x=11, y=22, z=33), Point(x=44, y=55, z=66)]) @@ -937,6 +938,7 @@ def test_get_touch_tip_waypoints( well_name="B2", center_point=center_point, radius=0.123, + mm_from_edge=0.456, ) assert result == [ diff --git a/api/tests/opentrons/protocol_engine/state/test_move_types.py b/api/tests/opentrons/protocol_engine/state/test_move_types.py index 9d46cb8a1ab..27f43ffbc0d 100644 --- a/api/tests/opentrons/protocol_engine/state/test_move_types.py +++ b/api/tests/opentrons/protocol_engine/state/test_move_types.py @@ -53,43 +53,47 @@ def test_get_move_type_to_well( ( subject.EdgePathType.LEFT, [ - Point(5, 20, 30), + Point(8, 20, 30), Point(10, 20, 30), - Point(10, 30, 30), - Point(10, 10, 30), + Point(10, 27, 30), + Point(10, 13, 30), Point(10, 20, 30), ], ), ( subject.EdgePathType.RIGHT, [ - Point(15, 20, 30), + Point(12, 20, 30), Point(10, 20, 30), - Point(10, 30, 30), - Point(10, 10, 30), + Point(10, 27, 30), + Point(10, 13, 30), Point(10, 20, 30), ], ), ( subject.EdgePathType.DEFAULT, [ - Point(15, 20, 30), - Point(5, 20, 30), + Point(12, 20, 30), + Point(8, 20, 30), Point(10, 20, 30), - Point(10, 30, 30), - Point(10, 10, 30), + Point(10, 27, 30), + Point(10, 13, 30), Point(10, 20, 30), ], ), ], ) -def get_edge_point_list( +def test_get_edge_point_list( edge_path_type: subject.EdgePathType, expected_result: List[Point], ) -> None: """It should get a list of well edge points.""" result = subject.get_edge_point_list( - Point(x=10, y=20, z=30), x_radius=5, y_radius=10, edge_path_type=edge_path_type + Point(x=10, y=20, z=30), + x_radius=5, + y_radius=10, + mm_from_edge=3, + edge_path_type=edge_path_type, ) assert result == expected_result diff --git a/api/tests/opentrons/protocol_engine/state/test_pipette_store_old.py b/api/tests/opentrons/protocol_engine/state/test_pipette_store_old.py index b88844bb53d..9e4db725415 100644 --- a/api/tests/opentrons/protocol_engine/state/test_pipette_store_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_pipette_store_old.py @@ -770,8 +770,8 @@ def test_add_pipette_config( available_sensors: pipette_definition.AvailableSensorDefinition, ) -> None: """It should update state from any pipette config private result.""" - command = cmd.LoadPipette.construct( # type: ignore[call-arg] - params=cmd.LoadPipetteParams.construct( + command = cmd.LoadPipette.model_construct( + params=cmd.LoadPipetteParams.model_construct( # type: ignore[call-arg] mount=MountType.LEFT, pipetteName="p300_single" # type: ignore[arg-type] ), result=cmd.LoadPipetteResult(pipetteId="pipette-id"), diff --git a/api/tests/opentrons/protocol_engine/state/test_tip_state.py b/api/tests/opentrons/protocol_engine/state/test_tip_state.py index 7a958a37e5f..7246a5f4cb2 100644 --- a/api/tests/opentrons/protocol_engine/state/test_tip_state.py +++ b/api/tests/opentrons/protocol_engine/state/test_tip_state.py @@ -32,7 +32,7 @@ get_default_nozzle_map, ) -_tip_rack_parameters = LabwareParameters.construct(isTiprack=True) # type: ignore[call-arg] +_tip_rack_parameters = LabwareParameters.model_construct(isTiprack=True) # type: ignore[call-arg] @pytest.fixture @@ -50,7 +50,7 @@ def subject() -> TipStore: @pytest.fixture def labware_definition() -> LabwareDefinition: """Get a labware definition value object.""" - return LabwareDefinition.construct( # type: ignore[call-arg] + return LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[ ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"], ["A2", "B2", "C2", "D2", "E2", "F2", "G2", "H2"], @@ -90,14 +90,12 @@ def load_labware_action( def _dummy_command() -> commands.Command: """Return a placeholder command.""" - return commands.Comment.construct() # type: ignore[call-arg] + return commands.Comment.model_construct() # type: ignore[call-arg] @pytest.mark.parametrize( "labware_definition", - [ - LabwareDefinition.construct(ordering=[], parameters=_tip_rack_parameters) # type: ignore[call-arg] - ], + [LabwareDefinition.model_construct(ordering=[], parameters=_tip_rack_parameters)], # type: ignore[call-arg] ) def test_get_next_tip_returns_none( load_labware_action: actions.SucceedCommandAction, @@ -594,7 +592,6 @@ def test_get_next_tip_with_1_channel_followed_by_8_channel( command=_dummy_command(), ) ) - config_update_2 = update_types.PipetteConfigUpdate( pipette_id="pipette-id2", serial_number="pipette-serial2", @@ -929,9 +926,9 @@ def test_handle_pipette_config_action( @pytest.mark.parametrize( "labware_definition", [ - LabwareDefinition.construct( # type: ignore[call-arg] + LabwareDefinition.model_construct( # type: ignore[call-arg] ordering=[["A1"]], - parameters=LabwareParameters.construct(isTiprack=False), # type: ignore[call-arg] + parameters=LabwareParameters.model_construct(isTiprack=False), # type: ignore[call-arg] ) ], ) diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index cd6ffeb99cb..95289d681b8 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -1,4 +1,5 @@ """Tests for the ProtocolEngine class.""" + import inspect from datetime import datetime from typing import Any @@ -803,9 +804,9 @@ async def test_finish_with_estop_error_will_not_drop_tip_and_home( ) -> None: """It should be able to tell the engine it's finished because of an error and will not drop tip and home.""" error = ProtocolCommandFailedError( - original_error=ErrorOccurrence.construct( # type: ignore[call-arg] + original_error=ErrorOccurrence.model_construct( # type: ignore[call-arg] wrappedErrors=[ - ErrorOccurrence.construct(errorCode="3008") # type: ignore[call-arg] + ErrorOccurrence.model_construct(errorCode="3008") # type: ignore[call-arg] ] ) ) diff --git a/api/tests/opentrons/protocol_engine/test_types.py b/api/tests/opentrons/protocol_engine/test_types.py index ccf6b91de7f..d48c67ee61e 100644 --- a/api/tests/opentrons/protocol_engine/test_types.py +++ b/api/tests/opentrons/protocol_engine/test_types.py @@ -9,10 +9,14 @@ def test_hex_validation(hex_color: str) -> None: """Should allow creating a HexColor.""" # make sure noting is raised when instantiating this class - assert HexColor(__root__=hex_color) + assert HexColor(hex_color) + assert HexColor.model_validate_json(f'"{hex_color}"') -def test_handles_invalid_hex() -> None: +@pytest.mark.parametrize("invalid_hex_color", ["true", "null", "#123456789"]) +def test_handles_invalid_hex(invalid_hex_color: str) -> None: """Should raise a validation error.""" with pytest.raises(ValidationError): - HexColor(__root__="#123456789") + HexColor(invalid_hex_color) + with pytest.raises(ValidationError): + HexColor.model_validate_json(f'"{invalid_hex_color}"') diff --git a/api/tests/opentrons/protocol_runner/smoke_tests/conftest.py b/api/tests/opentrons/protocol_runner/smoke_tests/conftest.py index 5a758922e59..ade6ed4dae8 100644 --- a/api/tests/opentrons/protocol_runner/smoke_tests/conftest.py +++ b/api/tests/opentrons/protocol_runner/smoke_tests/conftest.py @@ -17,7 +17,7 @@ def tempdeck_v1_def() -> ModuleDefinition: """Get the definition of a V1 tempdeck.""" definition = load_shared_data("module/definitions/3/temperatureModuleV1.json") - return ModuleDefinition.parse_raw(definition) + return ModuleDefinition.model_validate_json(definition) @pytest.fixture() diff --git a/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_command_mapper.py b/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_command_mapper.py index a652d76eac3..0cc542c4971 100644 --- a/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_command_mapper.py +++ b/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_command_mapper.py @@ -159,7 +159,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: assert len(commands_result) == 32 - assert commands_result[0] == commands.Home.construct( + assert commands_result[0] == commands.Home.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -170,7 +170,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.HomeResult(), ) - assert commands_result[1] == commands.LoadLabware.construct( + assert commands_result[1] == commands.LoadLabware.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -186,7 +186,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=tiprack_1_result_captor, ) - assert commands_result[2] == commands.LoadLabware.construct( + assert commands_result[2] == commands.LoadLabware.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -202,7 +202,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=tiprack_2_result_captor, ) - assert commands_result[3] == commands.LoadModule.construct( + assert commands_result[3] == commands.LoadModule.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -217,7 +217,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=module_1_result_captor, ) - assert commands_result[4] == commands.LoadLabware.construct( + assert commands_result[4] == commands.LoadLabware.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -233,7 +233,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=well_plate_1_result_captor, ) - assert commands_result[5] == commands.LoadLabware.construct( + assert commands_result[5] == commands.LoadLabware.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -250,7 +250,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: result=module_plate_1_result_captor, ) - assert commands_result[6] == commands.LoadPipette.construct( + assert commands_result[6] == commands.LoadPipette.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -264,7 +264,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: result=pipette_left_result_captor, ) - assert commands_result[7] == commands.LoadPipette.construct( + assert commands_result[7] == commands.LoadPipette.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -278,16 +278,14 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: result=pipette_right_result_captor, ) - # TODO(mc, 2021-11-11): not sure why I have to dict-access these properties - # might be a bug in Decoy, might be something weird that Pydantic does - tiprack_1_id = tiprack_1_result_captor.value["labwareId"] - tiprack_2_id = tiprack_2_result_captor.value["labwareId"] - well_plate_1_id = well_plate_1_result_captor.value["labwareId"] - module_plate_1_id = module_plate_1_result_captor.value["labwareId"] - pipette_left_id = pipette_left_result_captor.value["pipetteId"] - pipette_right_id = pipette_right_result_captor.value["pipetteId"] + tiprack_1_id = tiprack_1_result_captor.value.labwareId + tiprack_2_id = tiprack_2_result_captor.value.labwareId + well_plate_1_id = well_plate_1_result_captor.value.labwareId + module_plate_1_id = module_plate_1_result_captor.value.labwareId + pipette_left_id = pipette_left_result_captor.value.pipetteId + pipette_right_id = pipette_right_result_captor.value.pipetteId - assert commands_result[8] == commands.PickUpTip.construct( + assert commands_result[8] == commands.PickUpTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -304,7 +302,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: tipVolume=300.0, tipLength=51.83, position=DeckPoint(x=0, y=0, z=0) ), ) - assert commands_result[9] == commands.PickUpTip.construct( + assert commands_result[9] == commands.PickUpTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -322,7 +320,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: ), ) - assert commands_result[10] == commands.DropTip.construct( + assert commands_result[10] == commands.DropTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -338,7 +336,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: result=commands.DropTipResult(position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[11] == commands.PickUpTip.construct( + assert commands_result[11] == commands.PickUpTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -355,7 +353,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: tipVolume=300.0, tipLength=51.83, position=DeckPoint(x=0, y=0, z=0) ), ) - assert commands_result[12] == commands.Aspirate.construct( + assert commands_result[12] == commands.Aspirate.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -372,7 +370,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.AspirateResult(volume=40, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[13] == commands.Dispense.construct( + assert commands_result[13] == commands.Dispense.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -389,7 +387,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.DispenseResult(volume=35, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[14] == commands.Aspirate.construct( + assert commands_result[14] == commands.Aspirate.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -406,7 +404,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.AspirateResult(volume=40, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[15] == commands.Dispense.construct( + assert commands_result[15] == commands.Dispense.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -423,7 +421,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.DispenseResult(volume=35, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[16] == commands.BlowOut.construct( + assert commands_result[16] == commands.BlowOut.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -439,7 +437,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.BlowOutResult(position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[17] == commands.Aspirate.construct( + assert commands_result[17] == commands.Aspirate.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -456,7 +454,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.AspirateResult(volume=50, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[18] == commands.Dispense.construct( + assert commands_result[18] == commands.Dispense.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -473,7 +471,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.DispenseResult(volume=50, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[19] == commands.BlowOut.construct( + assert commands_result[19] == commands.BlowOut.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -489,7 +487,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.BlowOutResult(position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[20] == commands.Aspirate.construct( + assert commands_result[20] == commands.Aspirate.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -506,7 +504,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.AspirateResult(volume=300, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[21] == commands.Dispense.construct( + assert commands_result[21] == commands.Dispense.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -523,7 +521,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.DispenseResult(volume=300, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[22] == commands.BlowOut.construct( + assert commands_result[22] == commands.BlowOut.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -540,7 +538,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: result=commands.BlowOutResult(position=DeckPoint(x=0, y=0, z=0)), ) # TODO:(jr, 15.08.2022): this should map to move_to when move_to is mapped in a followup ticket RSS-62 - assert commands_result[23] == commands.Custom.construct( + assert commands_result[23] == commands.Custom.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -556,7 +554,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: ) # TODO:(jr, 15.08.2022): aspirate commands with no labware get filtered # into custom. Refactor this in followup legacy command mapping - assert commands_result[24] == commands.Custom.construct( + assert commands_result[24] == commands.Custom.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -572,7 +570,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: ) # TODO:(jr, 15.08.2022): dispense commands with no labware get filtered # into custom. Refactor this in followup legacy command mapping - assert commands_result[25] == commands.Custom.construct( + assert commands_result[25] == commands.Custom.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -588,7 +586,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: ) # TODO:(jr, 15.08.2022): blow_out commands with no labware get filtered # into custom. Refactor this in followup legacy command mapping - assert commands_result[26] == commands.Custom.construct( + assert commands_result[26] == commands.Custom.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -602,7 +600,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.CustomResult(), ) - assert commands_result[27] == commands.Aspirate.construct( + assert commands_result[27] == commands.Aspirate.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -619,7 +617,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.AspirateResult(volume=50, position=DeckPoint(x=0, y=0, z=0)), ) - assert commands_result[28] == commands.Dispense.construct( + assert commands_result[28] == commands.Dispense.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -638,7 +636,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: ) # TODO:(jr, 15.08.2022): aspirate commands with no labware get filtered # into custom. Refactor this in followup legacy command mapping - assert commands_result[29] == commands.Custom.construct( + assert commands_result[29] == commands.Custom.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -654,7 +652,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: ) # TODO:(jr, 15.08.2022): dispense commands with no labware get filtered # into custom. Refactor this in followup legacy command mapping - assert commands_result[30] == commands.Custom.construct( + assert commands_result[30] == commands.Custom.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -668,7 +666,7 @@ async def test_big_protocol_commands(big_protocol_file: Path) -> None: notes=[], result=commands.CustomResult(), ) - assert commands_result[31] == commands.DropTip.construct( + assert commands_result[31] == commands.DropTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -814,7 +812,7 @@ def run(protocol): ) result_commands = await simulate_and_get_commands(path) [initial_home, comment] = result_commands - assert comment == commands.Comment.construct( + assert comment == commands.Comment.model_construct( status=commands.CommandStatus.SUCCEEDED, params=commands.CommentParams(message="oy."), notes=[], diff --git a/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_custom_labware.py b/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_custom_labware.py index 7ed54b17ebe..dcc95593c38 100644 --- a/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_custom_labware.py +++ b/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_custom_labware.py @@ -58,7 +58,7 @@ async def test_legacy_custom_labware(custom_labware_protocol_files: List[Path]) ) result = await subject.run(deck_configuration=[], protocol_source=protocol_source) - expected_labware = LoadedLabware.construct( + expected_labware = LoadedLabware.model_construct( id=matchers.Anything(), location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), loadName="fixture_96_plate", diff --git a/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_module_commands.py b/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_module_commands.py index de14413a7ab..5650312b5f6 100644 --- a/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_module_commands.py +++ b/api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_module_commands.py @@ -75,7 +75,7 @@ async def test_runner_with_modules_in_legacy_python( thermocycler_result_captor = matchers.Captor() heater_shaker_result_captor = matchers.Captor() - assert commands_result[0] == commands.Home.construct( + assert commands_result[0] == commands.Home.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -86,7 +86,7 @@ async def test_runner_with_modules_in_legacy_python( notes=[], result=commands.HomeResult(), ) - assert commands_result[1] == commands.LoadLabware.construct( + assert commands_result[1] == commands.LoadLabware.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -98,7 +98,7 @@ async def test_runner_with_modules_in_legacy_python( result=matchers.Anything(), ) - assert commands_result[2] == commands.LoadModule.construct( + assert commands_result[2] == commands.LoadModule.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -110,7 +110,7 @@ async def test_runner_with_modules_in_legacy_python( result=temp_module_result_captor, ) - assert commands_result[3] == commands.LoadModule.construct( + assert commands_result[3] == commands.LoadModule.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -122,7 +122,7 @@ async def test_runner_with_modules_in_legacy_python( result=mag_module_result_captor, ) - assert commands_result[4] == commands.LoadModule.construct( + assert commands_result[4] == commands.LoadModule.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -134,7 +134,7 @@ async def test_runner_with_modules_in_legacy_python( result=thermocycler_result_captor, ) - assert commands_result[5] == commands.LoadModule.construct( + assert commands_result[5] == commands.LoadModule.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -146,12 +146,9 @@ async def test_runner_with_modules_in_legacy_python( result=heater_shaker_result_captor, ) - assert temp_module_result_captor.value["model"] == ModuleModel.TEMPERATURE_MODULE_V1 - assert mag_module_result_captor.value["model"] == ModuleModel.MAGNETIC_MODULE_V1 + assert temp_module_result_captor.value.model == ModuleModel.TEMPERATURE_MODULE_V1 + assert mag_module_result_captor.value.model == ModuleModel.MAGNETIC_MODULE_V1 + assert thermocycler_result_captor.value.model == ModuleModel.THERMOCYCLER_MODULE_V1 assert ( - thermocycler_result_captor.value["model"] == ModuleModel.THERMOCYCLER_MODULE_V1 - ) - assert ( - heater_shaker_result_captor.value["model"] - == ModuleModel.HEATER_SHAKER_MODULE_V1 + heater_shaker_result_captor.value.model == ModuleModel.HEATER_SHAKER_MODULE_V1 ) diff --git a/api/tests/opentrons/protocol_runner/smoke_tests/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/smoke_tests/test_protocol_runner.py index 1a8da30bd76..5db66e55eb2 100644 --- a/api/tests/opentrons/protocol_runner/smoke_tests/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/smoke_tests/test_protocol_runner.py @@ -58,13 +58,13 @@ async def test_runner_with_python( pipette_id_captor = matchers.Captor() labware_id_captor = matchers.Captor() - expected_pipette = LoadedPipette.construct( + expected_pipette = LoadedPipette.model_construct( id=pipette_id_captor, pipetteName=PipetteNameType.P300_SINGLE, mount=MountType.LEFT, ) - expected_labware = LoadedLabware.construct( + expected_labware = LoadedLabware.model_construct( id=labware_id_captor, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), loadName="opentrons_96_tiprack_300ul", @@ -75,7 +75,7 @@ async def test_runner_with_python( offsetId=None, ) - expected_module = LoadedModule.construct( + expected_module = LoadedModule.model_construct( id=matchers.IsA(str), model=ModuleModel.TEMPERATURE_MODULE_V1, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_3), @@ -86,7 +86,7 @@ async def test_runner_with_python( assert expected_labware in labware_result assert expected_module in modules_result - expected_command = commands.PickUpTip.construct( + expected_command = commands.PickUpTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -148,7 +148,7 @@ async def test_runner_with_json(json_protocol_file: Path) -> None: assert expected_pipette in pipettes_result assert expected_labware in labware_result - expected_command = commands.PickUpTip.construct( + expected_command = commands.PickUpTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -196,13 +196,13 @@ async def test_runner_with_legacy_python(legacy_python_protocol_file: Path) -> N pipette_id_captor = matchers.Captor() labware_id_captor = matchers.Captor() - expected_pipette = LoadedPipette.construct( + expected_pipette = LoadedPipette.model_construct( id=pipette_id_captor, pipetteName=PipetteNameType.P300_SINGLE, mount=MountType.LEFT, ) - expected_labware = LoadedLabware.construct( + expected_labware = LoadedLabware.model_construct( id=labware_id_captor, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), loadName="opentrons_96_tiprack_300ul", @@ -215,7 +215,7 @@ async def test_runner_with_legacy_python(legacy_python_protocol_file: Path) -> N assert expected_pipette in pipettes_result assert expected_labware in labware_result - expected_command = commands.PickUpTip.construct( + expected_command = commands.PickUpTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -260,13 +260,13 @@ async def test_runner_with_legacy_json(legacy_json_protocol_file: Path) -> None: pipette_id_captor = matchers.Captor() labware_id_captor = matchers.Captor() - expected_pipette = LoadedPipette.construct( + expected_pipette = LoadedPipette.model_construct( id=pipette_id_captor, pipetteName=PipetteNameType.P300_SINGLE, mount=MountType.LEFT, ) - expected_labware = LoadedLabware.construct( + expected_labware = LoadedLabware.model_construct( id=labware_id_captor, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), loadName="opentrons_96_tiprack_300ul", @@ -280,7 +280,7 @@ async def test_runner_with_legacy_json(legacy_json_protocol_file: Path) -> None: assert expected_pipette in pipettes_result assert expected_labware in labware_result - expected_command = commands.PickUpTip.construct( + expected_command = commands.PickUpTip.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, @@ -327,13 +327,13 @@ async def test_runner_with_python_and_run_time_parameters( tiprack_id_captor = matchers.Captor() reservoir_id_captor = matchers.Captor() - expected_pipette = LoadedPipette.construct( + expected_pipette = LoadedPipette.model_construct( id=pipette_id_captor, pipetteName=PipetteNameType.P300_SINGLE, mount=MountType.LEFT, ) - expected_tiprack = LoadedLabware.construct( + expected_tiprack = LoadedLabware.model_construct( id=tiprack_id_captor, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), loadName="opentrons_96_tiprack_300ul", @@ -344,7 +344,7 @@ async def test_runner_with_python_and_run_time_parameters( offsetId=None, ) - expected_reservoir = LoadedLabware.construct( + expected_reservoir = LoadedLabware.model_construct( id=reservoir_id_captor, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_2), loadName="nest_1_reservoir_195ml", @@ -361,14 +361,14 @@ async def test_runner_with_python_and_run_time_parameters( assert result.state_summary.status == EngineStatus.SUCCEEDED - expected_command = commands.Aspirate.construct( + expected_command = commands.Aspirate.model_construct( id=matchers.IsA(str), key=matchers.IsA(str), status=commands.CommandStatus.SUCCEEDED, createdAt=matchers.IsA(datetime), startedAt=matchers.IsA(datetime), completedAt=matchers.IsA(datetime), - params=commands.AspirateParams.construct( + params=commands.AspirateParams.model_construct( labwareId=reservoir_id_captor.value, wellName=matchers.IsA(str), wellLocation=matchers.Anything(), diff --git a/api/tests/opentrons/protocol_runner/test_json_translator.py b/api/tests/opentrons/protocol_runner/test_json_translator.py index e2735e4cdbc..b48c18f95c9 100644 --- a/api/tests/opentrons/protocol_runner/test_json_translator.py +++ b/api/tests/opentrons/protocol_runner/test_json_translator.py @@ -1,4 +1,5 @@ """Tests for the JSON JsonTranslator interface.""" + import pytest from typing import Dict, List @@ -193,7 +194,7 @@ wellName="A1", ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="dropTip", params={ "pipetteId": "pipette-id-1", @@ -230,7 +231,7 @@ wellName="A1", ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="pickUpTip", params={ "pipetteId": "pipette-id-1", @@ -272,7 +273,7 @@ ), ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="touchTip", params={ "pipetteId": "pipette-id-1", @@ -307,7 +308,7 @@ pipetteId="pipette-id-1", mount="left", pipetteName="p10_single" ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="loadPipette", params={ "pipetteId": "pipette-id-1", @@ -339,7 +340,7 @@ location=Location(slotName="3"), ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="loadModule", params={ "moduleId": "module-id-1", @@ -374,7 +375,7 @@ displayName="Trash", ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="loadLabware", params={ "labwareId": "labware-id-2", @@ -423,7 +424,7 @@ flowRate=1.23, ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="blowout", params={ "pipetteId": "pipette-id-1", @@ -458,7 +459,7 @@ commandType="delay", params=protocol_schema_v7.Params(waitForResume=True, message="hello world"), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="delay", params={"waitForResume": True, "message": "hello world"}, ), @@ -475,7 +476,7 @@ commandType="delay", params=protocol_schema_v7.Params(seconds=12.34, message="hello world"), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="delay", params={"seconds": 12.34, "message": "hello world"}, ), @@ -495,7 +496,7 @@ commandType="waitForResume", params=protocol_schema_v7.Params(message="hello world"), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="waitForResume", params={"message": "hello world"}, ), @@ -512,7 +513,7 @@ commandType="waitForDuration", params=protocol_schema_v7.Params(seconds=12.34, message="hello world"), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="waitForDuration", params={"seconds": 12.34, "message": "hello world"}, ), @@ -542,7 +543,7 @@ forceDirect=True, ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="moveToCoordinates", params={ "pipetteId": "pipette-id-1", @@ -595,7 +596,7 @@ ], ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="thermocycler/runProfile", params={ "moduleId": "module-id-2", @@ -646,7 +647,7 @@ volumeByWell={"A1": 32, "B2": 50}, ), ), - protocol_schema_v8.Command( + protocol_schema_v8.Command.model_construct( commandType="loadLiquid", key=None, params={ @@ -873,6 +874,6 @@ def test_load_liquid( id="liquid-id-555", displayName="water", description="water description", - displayColor=HexColor(__root__="#F00"), + displayColor=HexColor("#F00"), ) ] diff --git a/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py b/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py index 42c589ba7d3..a91066c01f8 100644 --- a/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py +++ b/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py @@ -117,7 +117,7 @@ def test_map_after_command() -> None: assert result == [ pe_actions.SucceedCommandAction( - command=pe_commands.Comment.construct( + command=pe_commands.Comment.model_construct( id="command.COMMENT-0", key="command.COMMENT-0", status=pe_commands.CommandStatus.SUCCEEDED, @@ -240,7 +240,7 @@ def test_command_stack() -> None: command_id="command.COMMENT-1", started_at=matchers.IsA(datetime) ), pe_actions.SucceedCommandAction( - command=pe_commands.Comment.construct( + command=pe_commands.Comment.model_construct( id="command.COMMENT-0", key="command.COMMENT-0", status=pe_commands.CommandStatus.SUCCEEDED, @@ -302,7 +302,7 @@ def test_map_labware_load(minimal_labware_def: LabwareDefinition) -> None: started_at=matchers.IsA(datetime), ) expected_succeed = pe_actions.SucceedCommandAction( - command=pe_commands.LoadLabware.construct( + command=pe_commands.LoadLabware.model_construct( id=expected_id_and_key, key=expected_id_and_key, params=expected_params, @@ -310,7 +310,7 @@ def test_map_labware_load(minimal_labware_def: LabwareDefinition) -> None: createdAt=matchers.IsA(datetime), startedAt=matchers.IsA(datetime), completedAt=matchers.IsA(datetime), - result=pe_commands.LoadLabwareResult.construct( + result=pe_commands.LoadLabwareResult.model_construct( labwareId=matchers.IsA(str), # Trusting that the exact fields within in the labware definition # get passed through correctly. @@ -352,7 +352,7 @@ def test_map_instrument_load(decoy: Decoy) -> None: ).then_return(pipette_config) expected_id_and_key = "commands.LOAD_PIPETTE-0" - expected_params = pe_commands.LoadPipetteParams.construct( + expected_params = pe_commands.LoadPipetteParams.model_construct( pipetteName=PipetteNameType.P1000_SINGLE_GEN2, mount=MountType.LEFT ) expected_queue = pe_actions.QueueCommandAction( @@ -367,7 +367,7 @@ def test_map_instrument_load(decoy: Decoy) -> None: command_id=expected_id_and_key, started_at=matchers.IsA(datetime) ) expected_succeed = pe_actions.SucceedCommandAction( - command=pe_commands.LoadPipette.construct( + command=pe_commands.LoadPipette.model_construct( id=expected_id_and_key, key=expected_id_and_key, status=pe_commands.CommandStatus.SUCCEEDED, @@ -410,7 +410,7 @@ def test_map_module_load( module_data_provider: ModuleDataProvider, ) -> None: """It should correctly map a module load.""" - test_definition = ModuleDefinition.parse_obj(minimal_module_def) + test_definition = ModuleDefinition.model_validate(minimal_module_def) input = LegacyModuleLoadInfo( requested_model=TemperatureModuleModel.TEMPERATURE_V1, loaded_model=TemperatureModuleModel.TEMPERATURE_V2, @@ -423,7 +423,7 @@ def test_map_module_load( ).then_return(test_definition) expected_id_and_key = "commands.LOAD_MODULE-0" - expected_params = pe_commands.LoadModuleParams.construct( + expected_params = pe_commands.LoadModuleParams.model_construct( model=ModuleModel.TEMPERATURE_MODULE_V1, location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1), moduleId=matchers.IsA(str), @@ -440,7 +440,7 @@ def test_map_module_load( command_id=expected_id_and_key, started_at=matchers.IsA(datetime) ) expected_succeed = pe_actions.SucceedCommandAction( - command=pe_commands.LoadModule.construct( + command=pe_commands.LoadModule.model_construct( id=expected_id_and_key, key=expected_id_and_key, status=pe_commands.CommandStatus.SUCCEEDED, @@ -448,7 +448,7 @@ def test_map_module_load( startedAt=matchers.IsA(datetime), completedAt=matchers.IsA(datetime), params=expected_params, - result=pe_commands.LoadModuleResult.construct( + result=pe_commands.LoadModuleResult.model_construct( moduleId=matchers.IsA(str), serialNumber="module-serial", definition=test_definition, @@ -481,7 +481,7 @@ def test_map_module_labware_load(minimal_labware_def: LabwareDefinition) -> None ) expected_id_and_key = "commands.LOAD_LABWARE-0" - expected_params = pe_commands.LoadLabwareParams.construct( + expected_params = pe_commands.LoadLabwareParams.model_construct( location=ModuleLocation(moduleId="module-123"), namespace="some_namespace", loadName="some_load_name", @@ -503,7 +503,7 @@ def test_map_module_labware_load(minimal_labware_def: LabwareDefinition) -> None started_at=matchers.IsA(datetime), ) expected_succeed = pe_actions.SucceedCommandAction( - command=pe_commands.LoadLabware.construct( + command=pe_commands.LoadLabware.model_construct( id=expected_id_and_key, key=expected_id_and_key, params=expected_params, @@ -511,7 +511,7 @@ def test_map_module_labware_load(minimal_labware_def: LabwareDefinition) -> None createdAt=matchers.IsA(datetime), startedAt=matchers.IsA(datetime), completedAt=matchers.IsA(datetime), - result=pe_commands.LoadLabwareResult.construct( + result=pe_commands.LoadLabwareResult.model_construct( labwareId=matchers.IsA(str), # Trusting that the exact fields within in the labware definition # get passed through correctly. @@ -578,7 +578,7 @@ def test_map_pause() -> None: started_at=matchers.IsA(datetime), ), pe_actions.SucceedCommandAction( - command=pe_commands.WaitForResume.construct( + command=pe_commands.WaitForResume.model_construct( id="command.PAUSE-0", key="command.PAUSE-0", status=pe_commands.CommandStatus.SUCCEEDED, diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 15e0192175e..2080ec69587 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -361,7 +361,7 @@ async def test_run_json_runner_stop_requested_stops_enqueuing( json_translator: JsonTranslator, ) -> None: """It should run a protocol to completion.""" - labware_definition = LabwareDefinition.construct() # type: ignore[call-arg] + labware_definition = LabwareDefinition.model_construct() # type: ignore[call-arg] json_protocol_source = ProtocolSource( directory=Path("/dev/null"), main_file=Path("/dev/null/abc.json"), @@ -388,7 +388,7 @@ async def test_run_json_runner_stop_requested_stops_enqueuing( Liquid(id="water-id", displayName="water", description="water desc") ] - json_protocol = ProtocolSchemaV6.construct() # type: ignore[call-arg] + json_protocol = ProtocolSchemaV6.model_construct() # type: ignore[call-arg] decoy.when( await protocol_reader.extract_labware_definitions(json_protocol_source) @@ -401,7 +401,7 @@ async def test_run_json_runner_stop_requested_stops_enqueuing( pe_commands.HomeCreate(params=pe_commands.HomeParams()), ) ).then_return( - pe_commands.Home.construct(status=pe_commands.CommandStatus.SUCCEEDED) # type: ignore[call-arg] + pe_commands.Home.model_construct(status=pe_commands.CommandStatus.SUCCEEDED) # type: ignore[call-arg] ) decoy.when( await protocol_engine.add_and_execute_command_wait_for_recovery( @@ -410,7 +410,7 @@ async def test_run_json_runner_stop_requested_stops_enqueuing( ), ) ).then_return( - pe_commands.WaitForDuration.construct( # type: ignore[call-arg] + pe_commands.WaitForDuration.model_construct( # type: ignore[call-arg] id="protocol-command-id", error=pe_errors.ErrorOccurrence.from_failed( id="some-id", @@ -452,8 +452,8 @@ async def test_run_json_runner_stop_requested_stops_enqueuing( @pytest.mark.parametrize( "schema_version, json_protocol", [ - (6, ProtocolSchemaV6.construct()), # type: ignore[call-arg] - (7, ProtocolSchemaV7.construct()), # type: ignore[call-arg] + (6, ProtocolSchemaV6.model_construct()), # type: ignore[call-arg] + (7, ProtocolSchemaV7.model_construct()), # type: ignore[call-arg] ], ) async def test_load_json_runner( @@ -467,7 +467,7 @@ async def test_load_json_runner( json_protocol: Union[ProtocolSchemaV6, ProtocolSchemaV7], ) -> None: """It should load a JSON protocol file.""" - labware_definition = LabwareDefinition.construct() # type: ignore[call-arg] + labware_definition = LabwareDefinition.model_construct() # type: ignore[call-arg] json_protocol_source = ProtocolSource( directory=Path("/dev/null"), @@ -528,7 +528,7 @@ async def test_load_json_runner( ), ) ).then_return( - pe_commands.WaitForResume.construct( # type: ignore[call-arg] + pe_commands.WaitForResume.model_construct( # type: ignore[call-arg] id="command-id-1", status=CommandStatus.SUCCEEDED, error=None, @@ -541,7 +541,7 @@ async def test_load_json_runner( ), ) ).then_return( - pe_commands.WaitForResume.construct( # type: ignore[call-arg] + pe_commands.WaitForResume.model_construct( # type: ignore[call-arg] id="command-id-2", status=CommandStatus.SUCCEEDED, error=None, @@ -556,7 +556,7 @@ async def test_load_json_runner( ), ) ).then_return( - pe_commands.WaitForResume.construct( # type: ignore[call-arg] + pe_commands.WaitForResume.model_construct( # type: ignore[call-arg] id="command-id-3", status=CommandStatus.SUCCEEDED, error=None, @@ -601,7 +601,7 @@ async def test_load_legacy_python( python_runner_subject: PythonAndLegacyRunner, ) -> None: """It should load a legacy context-based Python protocol.""" - labware_definition = LabwareDefinition.construct() # type: ignore[call-arg] + labware_definition = LabwareDefinition.model_construct() # type: ignore[call-arg] legacy_protocol_source = ProtocolSource( directory=Path("/dev/null"), @@ -752,7 +752,7 @@ async def test_load_legacy_json( python_runner_subject: PythonAndLegacyRunner, ) -> None: """It should load a legacy context-based JSON protocol.""" - labware_definition = LabwareDefinition.construct() # type: ignore[call-arg] + labware_definition = LabwareDefinition.model_construct() # type: ignore[call-arg] legacy_protocol_source = ProtocolSource( directory=Path("/dev/null"), diff --git a/api/tests/opentrons/protocol_runner/test_run_orchestrator.py b/api/tests/opentrons/protocol_runner/test_run_orchestrator.py index c2cea3e0e7e..b7281953f22 100644 --- a/api/tests/opentrons/protocol_runner/test_run_orchestrator.py +++ b/api/tests/opentrons/protocol_runner/test_run_orchestrator.py @@ -256,11 +256,11 @@ async def test_add_command_and_wait_for_interval( verify_calls: int, ) -> None: """Should add a command a wait for it to complete.""" - load_command = pe_commands.HomeCreate.construct( - params=pe_commands.HomeParams.construct() + load_command = pe_commands.HomeCreate.model_construct( + params=pe_commands.HomeParams.model_construct() ) added_command = pe_commands.Home( - params=pe_commands.HomeParams.construct(), + params=pe_commands.HomeParams.model_construct(), id="test-123", createdAt=datetime(year=2024, month=1, day=1), key="123", diff --git a/api/tests/opentrons/protocols/models/test_json_protocol.py b/api/tests/opentrons/protocols/models/test_json_protocol.py index 696524ac84a..afb2770f21a 100644 --- a/api/tests/opentrons/protocols/models/test_json_protocol.py +++ b/api/tests/opentrons/protocols/models/test_json_protocol.py @@ -25,7 +25,7 @@ def test_json_protocol_model( ) # Create the model - d = json_protocol.Model.parse_obj(fx) + d = json_protocol.Model.model_validate(fx) # Compare the dict created by pydantic to the loaded json - assert d.dict(exclude_unset=True, by_alias=True) == fx + assert d.model_dump(exclude_unset=True, by_alias=True) == fx diff --git a/api/tests/opentrons/test_execute.py b/api/tests/opentrons/test_execute.py index b9f791f4253..1e72a3757bf 100644 --- a/api/tests/opentrons/test_execute.py +++ b/api/tests/opentrons/test_execute.py @@ -131,6 +131,7 @@ def test_execute_function_apiv2( converted_model_v15.pipette_type, converted_model_v15.pipette_channels, converted_model_v15.pipette_version, + converted_model_v15.oem_type, ), "id": "testid", } @@ -139,6 +140,7 @@ def test_execute_function_apiv2( converted_model_v1.pipette_type, converted_model_v1.pipette_channels, converted_model_v1.pipette_version, + converted_model_v1.oem_type, ), "id": "testid2", } @@ -177,6 +179,7 @@ def emit_runlog(entry: Any) -> None: converted_model_v15.pipette_type, converted_model_v15.pipette_channels, converted_model_v15.pipette_version, + converted_model_v15.oem_type, ), "id": "testid", } @@ -215,6 +218,7 @@ def emit_runlog(entry: Any) -> None: converted_model_v15.pipette_type, converted_model_v15.pipette_channels, converted_model_v15.pipette_version, + converted_model_v15.oem_type, ), "id": "testid", } @@ -253,6 +257,7 @@ def emit_runlog(entry: Any) -> None: converted_model_v15.pipette_type, converted_model_v15.pipette_channels, converted_model_v15.pipette_version, + converted_model_v15.oem_type, ), "id": "testid", } @@ -292,6 +297,7 @@ def emit_runlog(entry: Any) -> None: converted_model_v15.pipette_type, converted_model_v15.pipette_channels, converted_model_v15.pipette_version, + converted_model_v15.oem_type, ), "id": "testid", } diff --git a/app-shell/build/release-notes-internal.md b/app-shell/build/release-notes-internal.md index 5459cc2593e..565c5e1aa9b 100644 --- a/app-shell/build/release-notes-internal.md +++ b/app-shell/build/release-notes-internal.md @@ -1,6 +1,10 @@ For more details about this release, please see the full [technical changelog][]. [technical change log]: https://github.com/Opentrons/opentrons/releases +## Internal Release 2.3.0-alpha.2 + +This internal release, pulled from the `edge` branch, contains features being developed for 8.3.0. It's for internal testing only. + ## Internal Release 2.3.0-alpha.1 This internal release, pulled from the `edge` branch, contains features being developed for 8.3.0. It's for internal testing only. diff --git a/app/Makefile b/app/Makefile index b11fed0b2b3..8f77700e3b7 100644 --- a/app/Makefile +++ b/app/Makefile @@ -47,7 +47,7 @@ clean: dist: export NODE_ENV := production dist: echo "Building app JS bundle (browser layer)" - vite build + NODE_OPTIONS="--max-old-space-size=8192" vite build # development ##################################################################### diff --git a/app/src/App/DesktopApp.tsx b/app/src/App/DesktopApp.tsx index 029ec99ee26..196a6cf547c 100644 --- a/app/src/App/DesktopApp.tsx +++ b/app/src/App/DesktopApp.tsx @@ -1,4 +1,5 @@ import { useState, Fragment } from 'react' +import { useTranslation } from 'react-i18next' import { Navigate, Route, Routes, useMatch } from 'react-router-dom' import { ErrorBoundary } from 'react-error-boundary' import { @@ -43,6 +44,7 @@ import { useFeatureFlag } from '../redux/config' import type { RouteProps } from './types' export const DesktopApp = (): JSX.Element => { + const { t } = useTranslation('top_navigation') useSoftwareUpdatePoll() const [ isEmergencyStopModalDismissed, @@ -68,55 +70,55 @@ export const DesktopApp = (): JSX.Element => { const desktopRoutes: RouteProps[] = [ { Component: ProtocolsLanding, - name: 'protocols', + name: t('protocols'), navLinkTo: '/protocols', path: '/protocols', }, { Component: ProtocolDetails, - name: 'Protocol Details', + name: t('protocol_details'), path: '/protocols/:protocolKey', }, { Component: ProtocolTimeline, - name: 'Protocol Timeline', + name: t('protocol_timeline'), path: '/protocols/:protocolKey/timeline', }, { Component: Labware, - name: 'labware', + name: t('labware'), navLinkTo: '/labware', path: '/labware', }, { Component: DevicesLanding, - name: 'devices', + name: t('devices'), navLinkTo: '/devices', path: '/devices', }, { Component: DeviceDetails, - name: 'Device', + name: t('device'), path: '/devices/:robotName', }, { Component: RobotSettings, - name: 'Robot Settings', + name: t('robot_settings'), path: '/devices/:robotName/robot-settings/:robotSettingsTab?', }, { Component: CalibrationDashboard, - name: 'Calibration Dashboard', + name: t('calibration_dashboard'), path: '/devices/:robotName/robot-settings/calibration/dashboard', }, { Component: ProtocolRunDetails, - name: 'Run Details', + name: t('run_details'), path: '/devices/:robotName/protocol-runs/:runId/:protocolRunDetailsTab?', }, { Component: AppSettings, - name: 'App Settings', + name: t('app_settings'), path: '/app-settings/:appSettingsTab?', }, ] diff --git a/app/src/App/Navbar.tsx b/app/src/App/Navbar.tsx index 1471ca4c593..90e62b608ae 100644 --- a/app/src/App/Navbar.tsx +++ b/app/src/App/Navbar.tsx @@ -1,5 +1,4 @@ import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' import { NavLink, useNavigate } from 'react-router-dom' import styled from 'styled-components' import debounce from 'lodash/debounce' @@ -26,6 +25,7 @@ import logoSvgThree from '/app/assets/images/logo_nav_three.svg' import { NAV_BAR_WIDTH } from './constants' +import type { MouseEvent } from 'react' import type { RouteProps } from './types' const SALESFORCE_HELP_LINK = 'https://support.opentrons.com/s/' @@ -111,8 +111,6 @@ const LogoImg = styled('img')` ` export function Navbar({ routes }: { routes: RouteProps[] }): JSX.Element { - const { t } = useTranslation('top_navigation') - const navigate = useNavigate() const navRoutes = routes.filter( ({ navLinkTo }: RouteProps) => navLinkTo != null @@ -151,7 +149,7 @@ export function Navbar({ routes }: { routes: RouteProps[] }): JSX.Element { as="h3" margin={`${SPACING.spacing8} 0 ${SPACING.spacing8} ${SPACING.spacing12}`} > - {t(name)} + {name} ))} @@ -164,7 +162,7 @@ export function Navbar({ routes }: { routes: RouteProps[] }): JSX.Element { ) => { + onClick={(e: MouseEvent) => { e.preventDefault() debouncedNavigate('/app-settings') }} diff --git a/app/src/App/__mocks__/portal.tsx b/app/src/App/__mocks__/portal.tsx index f80b1deb44e..498f3220f65 100644 --- a/app/src/App/__mocks__/portal.tsx +++ b/app/src/App/__mocks__/portal.tsx @@ -1,8 +1,8 @@ // mock portal for enzyme tests -import type * as React from 'react' +import type { ReactNode } from 'react' interface Props { - children: React.ReactNode + children: ReactNode } // replace Portal with a pass-through React.Fragment diff --git a/app/src/App/__tests__/hooks.test.tsx b/app/src/App/__tests__/hooks.test.tsx index 5b3f315049b..2423414c748 100644 --- a/app/src/App/__tests__/hooks.test.tsx +++ b/app/src/App/__tests__/hooks.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, beforeEach, afterEach, expect, it } from 'vitest' import { renderHook } from '@testing-library/react' import { createStore } from 'redux' @@ -9,11 +8,12 @@ import { i18n } from '/app/i18n' import { checkShellUpdate } from '/app/redux/shell' import { useSoftwareUpdatePoll } from '../hooks' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' describe('useSoftwareUpdatePoll', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> let store: Store beforeEach(() => { vi.useFakeTimers() diff --git a/app/src/App/index.tsx b/app/src/App/index.tsx index f0ba1de0304..0ffcf0dd751 100644 --- a/app/src/App/index.tsx +++ b/app/src/App/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useSelector } from 'react-redux' import { Flex, POSITION_FIXED, DIRECTION_ROW } from '@opentrons/components' @@ -9,7 +8,9 @@ import { DesktopApp } from './DesktopApp' import { OnDeviceDisplayApp } from './OnDeviceDisplayApp' import { TopPortalRoot } from './portal' -const stopEvent = (event: React.MouseEvent): void => { +import type { MouseEvent } from 'react' + +const stopEvent = (event: MouseEvent): void => { event.preventDefault() } diff --git a/app/src/App/types.ts b/app/src/App/types.ts index 87d8f77d4a1..c6a0822260d 100644 --- a/app/src/App/types.ts +++ b/app/src/App/types.ts @@ -1,11 +1,11 @@ -import type * as React from 'react' +import type { FC } from 'react' export interface RouteProps { /** * the component rendered by a route match * drop developed components into slots held by placeholder div components */ - Component: React.FC + Component: FC /** * a route/page name to render in the nav bar */ diff --git a/app/src/LocalizationProvider.tsx b/app/src/LocalizationProvider.tsx index df2bbc8bc40..3c8fcf9feab 100644 --- a/app/src/LocalizationProvider.tsx +++ b/app/src/LocalizationProvider.tsx @@ -7,10 +7,10 @@ import { i18n, i18nCb, i18nConfig } from '/app/i18n' import { getAppLanguage } from '/app/redux/config' import { useIsOEMMode } from '/app/resources/robot-settings/hooks' -import type * as React from 'react' +import type { ReactNode } from 'react' export interface LocalizationProviderProps { - children?: React.ReactNode + children?: ReactNode } export const BRANDED_RESOURCE = 'branded' diff --git a/app/src/__testing-utils__/renderWithProviders.tsx b/app/src/__testing-utils__/renderWithProviders.tsx index 11e3ba16d9b..4c3115281f5 100644 --- a/app/src/__testing-utils__/renderWithProviders.tsx +++ b/app/src/__testing-utils__/renderWithProviders.tsx @@ -1,6 +1,5 @@ // render using targetted component using @testing-library/react // with wrapping providers for i18next and redux -import type * as React from 'react' import { QueryClient, QueryClientProvider } from 'react-query' import { I18nextProvider } from 'react-i18next' import { Provider } from 'react-redux' @@ -8,16 +7,22 @@ import { vi } from 'vitest' import { render } from '@testing-library/react' import { createStore } from 'redux' +import type { + ComponentProps, + ComponentType, + PropsWithChildren, + ReactElement, +} from 'react' import type { PreloadedState, Store } from 'redux' import type { RenderOptions, RenderResult } from '@testing-library/react' export interface RenderWithProvidersOptions extends RenderOptions { initialState?: State - i18nInstance: React.ComponentProps['i18n'] + i18nInstance: ComponentProps['i18n'] } export function renderWithProviders( - Component: React.ReactElement, + Component: ReactElement, options?: RenderWithProvidersOptions ): [RenderResult, Store] { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions @@ -32,7 +37,7 @@ export function renderWithProviders( const queryClient = new QueryClient() - const ProviderWrapper: React.ComponentType> = ({ + const ProviderWrapper: ComponentType> = ({ children, }) => { const BaseWrapper = ( diff --git a/app/src/assets/localization/en/anonymous.json b/app/src/assets/localization/en/anonymous.json index 280e602088d..bcfb90dc7eb 100644 --- a/app/src/assets/localization/en/anonymous.json +++ b/app/src/assets/localization/en/anonymous.json @@ -71,6 +71,7 @@ "storage_limit_reached_description": "Your robot has reached the limit of quick transfers that it can store. You must delete an existing quick transfer before creating a new one.", "system_language_preferences_update_description": "Your system’s language was recently updated. Would you like to use the updated language as the default for the app?", "these_are_advanced_settings": "These are advanced settings. Please do not attempt to adjust without assistance from support. Changing these settings may affect the lifespan of your pipette.These settings do not override any pipette settings defined in protocols.", + "u2e_driver_description": "The OT-2 uses this adapter for its USB connection to the desktop app.", "unexpected_error": "An unexpected error has occurred. If the issue persists, contact customer support for assistance.", "update_requires_restarting_app": "Updating requires restarting the app.", "update_robot_software_description": "Bypass the auto-update process and update the robot software manually.", diff --git a/app/src/assets/localization/en/app_settings.json b/app/src/assets/localization/en/app_settings.json index b4d5f654291..c6c4b595597 100644 --- a/app/src/assets/localization/en/app_settings.json +++ b/app/src/assets/localization/en/app_settings.json @@ -37,7 +37,9 @@ "connect_ip_link": "Learn more about connecting a robot manually", "discovery_timeout": "Discovery timed out.", "dont_change": "Don’t change", + "dont_remind_me": "Don't remind me again", "download_update": "Downloading update...", + "driver_out_of_date": "Realtek USB-to-Ethernet Driver Update Available", "enable_dev_tools": "Developer Tools", "enable_dev_tools_description": "Enabling this setting opens Developer Tools on app launch, enables additional logging and gives access to feature flags.", "error_boundary_desktop_app_description": "You need to reload the app. Contact support with the following error message:", @@ -46,6 +48,7 @@ "error_recovery_mode_description": "Pause on protocol errors instead of canceling the run.", "feature_flags": "Feature Flags", "general": "General", + "get_update": "get update", "heater_shaker_attach_description": "Display a reminder to attach the Heater-Shaker properly before running a test shake or using it in a protocol.", "heater_shaker_attach_visible": "Confirm Heater-Shaker Module Attachment", "how_to_restore": "How to Restore a Previous Software Version", @@ -66,6 +69,7 @@ "ot2_advanced_settings": "OT-2 Advanced Settings", "override_path": "override path", "override_path_to_python": "Override Path to Python", + "please_update_driver": "Please update your computer's driver to ensure a reliable connection to your OT-2.", "prevent_robot_caching": "Prevent Robot Caching", "prevent_robot_caching_description": "The app will immediately clear unavailable robots and will not remember unavailable robots while this is enabled. On networks with many robots, preventing caching may improve network performance at the expense of slower and less reliable robot discovery on app launch.", "privacy": "Privacy", @@ -94,6 +98,7 @@ "trash_bin": "Always use trash bin to calibrate", "try_restarting_the_update": "Try restarting the update.", "turn_off_updates": "Turn off software update notifications in App Settings.", + "u2e_driver_outdated_message": "There is an updated Realtek USB-to-Ethernet adapter driver available for your computer.", "up_to_date": "Up to date", "update_alerts": "Software Update Alerts", "update_app_now": "Update app now", @@ -113,5 +118,6 @@ "usb_to_ethernet_unknown_product": "Unknown Adapter", "use_system_language": "Use system language", "view_software_update": "View software update", + "view_adapter_info": "view adapter info", "view_update": "View Update" } diff --git a/app/src/assets/localization/en/branded.json b/app/src/assets/localization/en/branded.json index 0760c3061b4..42c22baa8ad 100644 --- a/app/src/assets/localization/en/branded.json +++ b/app/src/assets/localization/en/branded.json @@ -71,6 +71,7 @@ "storage_limit_reached_description": "Your Opentrons Flex has reached the limit of quick transfers that it can store. You must delete an existing quick transfer before creating a new one.", "system_language_preferences_update_description": "Your system’s language was recently updated. Would you like to use the updated language as the default for the Opentrons App?", "these_are_advanced_settings": "These are advanced settings. Please do not attempt to adjust without assistance from Opentrons Support. Changing these settings may affect the lifespan of your pipette.These settings do not override any pipette settings defined in protocols.", + "u2e_driver_description": "The OT-2 uses this adapter for its USB connection to the Opentrons App.", "unexpected_error": "An unexpected error has occurred. If the issue persists, contact Opentrons Support for assistance.", "update_requires_restarting_app": "Updating requires restarting the Opentrons App.", "update_robot_software_description": "Bypass the Opentrons App auto-update process and update the robot software manually.", diff --git a/app/src/assets/localization/en/device_settings.json b/app/src/assets/localization/en/device_settings.json index 79416a09f73..06a67d39d35 100644 --- a/app/src/assets/localization/en/device_settings.json +++ b/app/src/assets/localization/en/device_settings.json @@ -3,6 +3,7 @@ "about_calibration_description": "For the robot to move accurately and precisely, you need to calibrate it. Positional calibration happens in three parts: deck calibration, pipette offset calibration and tip length calibration.", "about_calibration_description_ot3": "For the robot to move accurately and precisely, you need to calibrate it. Pipette and gripper calibration is an automated process that uses a calibration probe or pin.After calibration is complete, you can save the calibration data to your computer as a JSON file.", "about_calibration_title": "About Calibration", + "add_new": "Add new...", "advanced": "Advanced", "alpha_description": "Warning: alpha releases are feature-complete but may contain significant bugs.", "alternative_security_types": "Alternative security types", @@ -10,10 +11,12 @@ "apply_historic_offsets": "Apply Labware Offsets", "are_you_sure_you_want_to_disconnect": "Are you sure you want to disconnect from {{ssid}}?", "attach_a_pipette_before_calibrating": "Attach a pipette in order to perform calibration", + "authentication": "Authentication", "boot_scripts": "Boot scripts", "both": "Both", "browse_file_system": "Browse file system", "bug_fixes": "Bug Fixes", + "but_we_expected": "but we expected", "calibrate_deck": "Calibrate deck", "calibrate_deck_description": "For pre-2019 robots that do not have crosses etched on the deck.", "calibrate_deck_to_dots": "Calibrate deck to dots", @@ -28,8 +31,10 @@ "change_network": "Change network", "characters_max": "17 characters max", "check_for_updates": "Check for updates", + "check_to_verify_update": "Check your robot's settings page to verify whether or not the update was successful", "checking_for_updates": "Checking for updates", "choose": "Choose...", + "choose_a_network": "Choose a network...", "choose_file": "Choose file", "choose_network_type": "Choose network type", "choose_reset_settings": "Choose reset settings", @@ -56,7 +61,9 @@ "confirm_device_reset_heading": "Are you sure you want to reset your device?", "connect": "Connect", "connect_the_estop_to_continue": "Connect the E-stop to continue", + "connect_to_ssid": "Connect to {{ssid}}", "connect_to_wifi_network": "Connect to Wi-Fi network", + "connect_to_wifi_network_failure": "Your robot was unable to connect to Wi-Fi network {{ssid}}", "connect_via": "Connect via {{type}}", "connect_via_usb_description_1": "1. Connect the USB A-to-B cable to the robot’s USB-B port.", "connect_via_usb_description_2": "2. Connect the cable to an open USB port on your computer.", @@ -65,6 +72,7 @@ "connected_to_ssid": "Connected to {{ssid}}", "connected_via": "Connected via {{networkInterface}}", "connecting_to": "Connecting to {{ssid}}...", + "connecting_to_wifi_network": "Connecting to Wi-Fi network {{ssid}}", "connection_description_ethernet": "Connect to your lab's wired network.", "connection_description_wifi": "Find a network in your lab or enter your own.", "connection_to_robot_lost": "Connection to robot lost", @@ -96,6 +104,7 @@ "display_sleep_settings": "Display Sleep Settings", "do_not_turn_off": "This could take up to {{minutes}} minutes. Don't turn off the robot.", "done": "Done", + "downgrade": "downgrade", "download": "Download", "download_calibration_data": "Download calibration logs", "download_error": "Download error", @@ -109,6 +118,7 @@ "enable_status_light_description": "Turn on or off the strip of color LEDs on the front of the robot.", "engaged": "Engaged", "enter_factory_password": "Enter factory password", + "enter_name_security_type": "Enter the network name and security type.", "enter_network_name": "Enter network name", "enter_password": "Enter password", "estop": "E-stop", @@ -127,6 +137,8 @@ "factory_resets_cannot_be_undone": "Factory resets cannot be undone.", "failed_to_connect_to_ssid": "Failed to connect to {{ssid}}", "feature_flags": "Feature Flags", + "field_is_required": "{{field}} is required", + "find_and_join_network": "Find and join a Wi-Fi network", "finish_setup": "Finish setup", "firmware_version": "Firmware Version", "fully_calibrate_before_checking_health": "Fully calibrate your robot before checking calibration health", @@ -154,6 +166,7 @@ "last_calibrated_label": "Last Calibrated", "launch_jupyter_notebook": "Launch Jupyter Notebook", "legacy_settings": "Legacy Settings", + "likely_incorrect_password": "Likely incorrect network password.", "mac_address": "MAC Address", "manage_oem_settings": "Manage OEM settings", "minutes": "{{minute}} minutes", @@ -171,7 +184,10 @@ "name_your_robot": "Name your robot", "name_your_robot_description": "Don’t worry, you can always change this in your settings.", "need_another_security_type": "Need another security type?", + "network_is_unsecured": "Wi-Fi network {{ssid}} is unsecured", "network_name": "Network Name", + "network_requires_auth": "Wi-Fi network {{ssid}} requires 802.1X authentication", + "network_requires_wpa_password": "Wi-Fi network {{ssid}} requires a WPA2 password", "network_settings": "Network Settings", "networking": "Networking", "never": "Never", @@ -183,6 +199,7 @@ "no_modules_attached": "No modules attached", "no_network_found": "No network found", "no_pipette_attached": "No pipette attached", + "no_update_files": "Unable to retrieve update for this robot. Ensure your computer is connected to the internet and try again later.", "none_description": "Not recommended", "not_calibrated": "Not calibrated yet", "not_calibrated_short": "Not calibrated", @@ -197,8 +214,10 @@ "on": "On", "one_hour": "1 hour", "other_networks": "Other Networks", + "other_robot_updating": "Unable to update because the app is currently updating a different robot.", "password": "Password", "password_error_message": "Must be at least 8 characters", + "password_not_long_enough": "Password must be at least {{minLength}} characters", "pause_protocol": "Pause protocol when robot door opens", "pause_protocol_description": "When enabled, opening the robot door during a run will pause the robot after it has completed its current motion.", "pipette_calibrations_description": "Pipette calibration uses a metal probe to determine the pipette's exact position relative to precision-cut squares on deck slots.", @@ -208,6 +227,7 @@ "pipette_offset_calibration_recommended": "Pipette Offset calibration recommended", "pipette_offset_calibrations_history": "See all Pipette Offset Calibration history", "pipette_offset_calibrations_title": "Pipette Offset Calibrations", + "please_check_credentials": "Please double-check your network credentials", "privacy": "Privacy", "problem_during_update": "This update is taking longer than usual.", "proceed_without_updating": "Proceed without update", @@ -238,9 +258,12 @@ "returns_your_device_to_new_state": "This returns your device to a new state.", "robot_busy_protocol": "This robot cannot be updated while a protocol is running on it", "robot_calibration_data": "Robot Calibration Data", + "robot_has_bad_capabilities": "Robot has incorrect capabilities shape", "robot_initializing": "Initializing robot...", "robot_name": "Robot Name", "robot_operating_update_available": "Robot Operating System Update Available", + "robot_reconnected_with version": "Robot reconnected with version", + "robot_requires_premigration": "This robot must be updated by the system before a custom update can occur", "robot_serial_number": "Robot Serial Number", "robot_server_version": "Robot Server Version", "robot_settings": "Robot Settings", @@ -259,7 +282,9 @@ "select_a_network": "Select a network", "select_a_security_type": "Select a security type", "select_all_settings": "Select all settings", + "select_auth_method_short": "Select authentication method", "select_authentication_method": "Select authentication method for your selected network.", + "select_file": "Select file", "sending_software": "Sending software...", "serial": "Serial", "setup_mode": "Setup mode", @@ -275,6 +300,9 @@ "subnet_mask": "Subnet Mask", "successfully_connected": "Successfully connected!", "successfully_connected_to_network": "Successfully connected to {{ssid}}!", + "successfully_connected_to_ssid": "Your robot has successfully connected to Wi-Fi network {{ssid}}", + "successfully_connected_to_wifi": "Successfully connected to Wi-Fi", + "successfully_disconnected_from_wifi": "Successfully disconnected from Wi-Fi", "supported_protocol_api_versions": "Supported Protocol API Versions", "text_size": "Text Size", "text_size_description": "Text on all screens will adjust to the size you choose below.", @@ -286,6 +314,14 @@ "troubleshooting": "Troubleshooting", "try_again": "Try again", "try_restarting_the_update": "Try restarting the update.", + "unable_to_cancel_update": "Unable to cancel in-progress update session", + "unable_to_commit_update": "Unable to commit update", + "unable_to_connect": "Unable to connect to Wi-Fi", + "unable_to_disconnect": "Unable to disconnect from Wi-Fi", + "unable_to_find_system_file": "Unable to find system file for update", + "unable_to_find_robot_with_name": "Unable to find online robot with name", + "unable_to_restart": "Unable to restart robot", + "unable_to_start_update_session": "Unable to start update session", "up_to_date": "up to date", "update_available": "Update Available", "update_channel_description": "Stable receives the latest stable releases. Beta allows you to try out new in-progress features before they launch in Stable channel, but they have not completed testing yet.", @@ -294,7 +330,10 @@ "update_requires_restarting_robot": "Updating the robot software requires restarting the robot", "update_robot_now": "Update robot now", "update_robot_software": "Update robot software manually with a local file (.zip)", + "update_server_unavailable": "Unable to update because your robot's update server is not responding.", + "update_unavailable": "Update unavailable", "updating": "Updating", + "upgrade": "upgrade", "upload_custom_logo": "Upload custom logo", "upload_custom_logo_description": "Upload a logo for the robot to display during boot up.", "upload_custom_logo_dimensions": "The logo must fit within dimensions 1024 x 600 and be a PNG file (.png).", diff --git a/app/src/assets/localization/en/gripper_wizard_flows.json b/app/src/assets/localization/en/gripper_wizard_flows.json index ff98d8e07f0..70df5688820 100644 --- a/app/src/assets/localization/en/gripper_wizard_flows.json +++ b/app/src/assets/localization/en/gripper_wizard_flows.json @@ -5,7 +5,6 @@ "before_you_begin": "Before you begin", "begin_calibration": "Begin calibration", "calibrate_gripper": "Calibrate Gripper", - "calibration_pin": "Calibration Pin", "calibration_pin_touching": "The calibration pin will touch the calibration square in slot {{slot}} to determine its exact position.", "complete_calibration": "Complete calibration", "continue": "Continue", @@ -17,7 +16,6 @@ "gripper_calibration": "Gripper Calibration", "gripper_recalibration": "Gripper Recalibration", "gripper_successfully_attached": "Gripper successfully attached", - "hex_screwdriver": "2.5 mm Hex Screwdriver", "hold_gripper_and_loosen_screws": "Hold the gripper in place and loosen the top gripper screw first. After that move onto the bottom screw. (The screws are captive and will not come apart from the gripper.) Then carefully remove the gripper.", "insert_pin_into_front_jaw": "Insert calibration pin in front jaw", "insert_pin_into_rear_jaw": "Insert calibration pin in rear jaw", diff --git a/app/src/assets/localization/en/pipette_wizard_flows.json b/app/src/assets/localization/en/pipette_wizard_flows.json index 78dc2b852a6..1154d6f9659 100644 --- a/app/src/assets/localization/en/pipette_wizard_flows.json +++ b/app/src/assets/localization/en/pipette_wizard_flows.json @@ -67,6 +67,7 @@ "pipette_heavy": "The 96-Channel Pipette is heavy ({{weight}}). Ask a labmate for help, if needed.", "please_install_correct_pip": "Install {{pipetteName}} instead", "progress_will_be_lost": "{{flow}} progress will be lost", + "provided_with_robot": "Provided with the robot. Using another size can strip the instruments’s screws.", "reattach_carriage": "reattach z-axis carriage", "recalibrate_pipette": "recalibrate {{mount}} pipette", "remove_cal_probe": "remove calibration probe", diff --git a/app/src/assets/localization/en/protocol_command_text.json b/app/src/assets/localization/en/protocol_command_text.json index 2842f9dc30d..8037b8f2778 100644 --- a/app/src/assets/localization/en/protocol_command_text.json +++ b/app/src/assets/localization/en/protocol_command_text.json @@ -6,11 +6,13 @@ "adapter_in_mod_in_slot": "{{adapter}} on {{module}} in Slot {{slot}}", "adapter_in_slot": "{{adapter}} in Slot {{slot}}", "air_gap_in_place": "Air gapping {{volume}} µL", + "all_nozzles": "all nozzles", "aspirate": "Aspirating {{volume}} µL from well {{well_name}} of {{labware}} in {{labware_location}} at {{flow_rate}} µL/sec", "aspirate_in_place": "Aspirating {{volume}} µL in place at {{flow_rate}} µL/sec ", "blowout": "Blowing out at well {{well_name}} of {{labware}} in {{labware_location}} at {{flow_rate}} µL/sec", "blowout_in_place": "Blowing out in place at {{flow_rate}} µL/sec", "closing_tc_lid": "Closing Thermocycler lid", + "column_layout": "column layout", "comment": "Comment", "configure_for_volume": "Configure {{pipette}} to aspirate {{volume}} µL", "configure_nozzle_layout": "Configure {{pipette}} to use {{layout}}", @@ -59,11 +61,13 @@ "opening_tc_lid": "Opening Thermocycler lid", "pause": "Pause", "pause_on": "Pause on {{robot_name}}", + "partial_layout": "partial layout", "pickup_tip": "Picking up tip(s) from {{well_range}} of {{labware}} in {{labware_location}}", "prepare_to_aspirate": "Preparing {{pipette}} to aspirate", "reloading_labware": "Reloading {{labware}}", "return_tip": "Returning tip to {{well_name}} of {{labware}} in {{labware_location}}", "right": "Right", + "row_layout": "row layout", "save_position": "Saving position", "set_and_await_hs_shake": "Setting Heater-Shaker to shake at {{rpm}} rpm and waiting until reached", "setting_hs_temp": "Setting Target Temperature of Heater-Shaker to {{temp}}", @@ -71,6 +75,7 @@ "setting_thermocycler_block_temp": "Setting Thermocycler block temperature to {{temp}} with hold time of {{hold_time_seconds}} seconds after target reached", "setting_thermocycler_lid_temp": "Setting Thermocycler lid temperature to {{temp}}", "single": "single", + "single_nozzle_layout": "single nozzle layout", "slot": "Slot {{slot_name}}", "target_temperature": "target temperature", "tc_awaiting_for_duration": "Waiting for Thermocycler profile to complete", diff --git a/app/src/assets/localization/en/top_navigation.json b/app/src/assets/localization/en/top_navigation.json index 178b02042b9..16d5e2d011d 100644 --- a/app/src/assets/localization/en/top_navigation.json +++ b/app/src/assets/localization/en/top_navigation.json @@ -1,7 +1,10 @@ { + "app_settings": "App Settings", "attached_pipettes_do_not_match": "Attached pipettes do not match pipettes specified in loaded protocol", "calibrate_deck_to_proceed": "Calibrate your deck to proceed", + "calibration_dashboard": "Calibration Dashboard", "deck_setup": "Deck Setup", + "device": "Device", "devices": "Devices", "instruments": "Instruments", "labware": "Labware", @@ -10,10 +13,13 @@ "pipettes": "pipettes", "please_connect_to_a_robot": "Please connect to a robot to proceed", "please_load_a_protocol": "Please load a protocol to proceed", + "protocol_details": "Protocol Details", "protocol_runs": "Protocol Runs", + "protocol_timeline": "Protocol Timeline", "protocols": "Protocols", "quick_transfer": "Quick Transfer", "robot_settings": "Robot Settings", "run": "run", + "run_details": "Run Details", "settings": "Settings" } diff --git a/app/src/atoms/InlineNotification/__tests__/InlineNotification.test.tsx b/app/src/atoms/InlineNotification/__tests__/InlineNotification.test.tsx index 0e91433e7b2..bb5ba0669b9 100644 --- a/app/src/atoms/InlineNotification/__tests__/InlineNotification.test.tsx +++ b/app/src/atoms/InlineNotification/__tests__/InlineNotification.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { screen, fireEvent } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { InlineNotification } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('InlineNotification', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/InlineNotification/index.tsx b/app/src/atoms/InlineNotification/index.tsx index 5b5bf21aafa..cf413b652cc 100644 --- a/app/src/atoms/InlineNotification/index.tsx +++ b/app/src/atoms/InlineNotification/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { ALIGN_CENTER, @@ -19,6 +18,7 @@ import { Link, } from '@opentrons/components' +import type { MouseEventHandler } from 'react' import type { IconProps, StyleProps } from '@opentrons/components' type InlineNotificationType = 'alert' | 'error' | 'neutral' | 'success' @@ -32,9 +32,9 @@ export interface InlineNotificationProps extends StyleProps { /** Optional dynamic width based on contents */ hug?: boolean /** optional handler to show close button/clear alert */ - onCloseClick?: (() => void) | React.MouseEventHandler + onCloseClick?: (() => void) | MouseEventHandler linkText?: string - onLinkClick?: (() => void) | React.MouseEventHandler + onLinkClick?: (() => void) | MouseEventHandler } const INLINE_NOTIFICATION_PROPS_BY_TYPE: Record< diff --git a/app/src/atoms/InstrumentContainer/__tests__/InstrumentContainer.test.tsx b/app/src/atoms/InstrumentContainer/__tests__/InstrumentContainer.test.tsx index e5fa872ab54..318175558fe 100644 --- a/app/src/atoms/InstrumentContainer/__tests__/InstrumentContainer.test.tsx +++ b/app/src/atoms/InstrumentContainer/__tests__/InstrumentContainer.test.tsx @@ -1,15 +1,16 @@ -import type * as React from 'react' import { describe, it } from 'vitest' import { screen } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' import { InstrumentContainer } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('InstrumentContainer', () => { - let props: React.ComponentProps + let props: ComponentProps it('renders an instrument display name', () => { props = { diff --git a/app/src/atoms/Link/ExternalLink.tsx b/app/src/atoms/Link/ExternalLink.tsx index 5d24a06fdb4..2490266927e 100644 --- a/app/src/atoms/Link/ExternalLink.tsx +++ b/app/src/atoms/Link/ExternalLink.tsx @@ -1,22 +1,33 @@ -import type * as React from 'react' +import { css } from 'styled-components' +import { + DISPLAY_INLINE_BLOCK, + Icon, + Link, + SPACING, + TYPOGRAPHY, +} from '@opentrons/components' -import { Link, Icon, TYPOGRAPHY, SPACING } from '@opentrons/components' +import type { ReactNode } from 'react' import type { LinkProps } from '@opentrons/components' - export interface ExternalLinkProps extends LinkProps { href: string id?: string - children: React.ReactNode + children: ReactNode } export const ExternalLink = (props: ExternalLinkProps): JSX.Element => ( {props.children} + ) + +const SPAN_STYLE = css` + display: ${DISPLAY_INLINE_BLOCK}; + width: 0.4375rem; +` diff --git a/app/src/atoms/Link/__tests__/ExternalLink.test.tsx b/app/src/atoms/Link/__tests__/ExternalLink.test.tsx index e245541c514..863a10e886e 100644 --- a/app/src/atoms/Link/__tests__/ExternalLink.test.tsx +++ b/app/src/atoms/Link/__tests__/ExternalLink.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, beforeEach } from 'vitest' import { screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' @@ -6,14 +5,16 @@ import { COLORS } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { ExternalLink } from '../ExternalLink' +import type { ComponentProps } from 'react' + const TEST_URL = 'https://opentrons.com' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('ExternalLink', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -38,6 +39,5 @@ describe('ExternalLink', () => { const icon = screen.getByLabelText('open_in_new_icon') expect(icon).toBeInTheDocument() expect(icon).toHaveStyle('width: 0.5rem; height: 0.5rem') - expect(icon).toHaveStyle('margin-left: 0.4375rem') }) }) diff --git a/app/src/atoms/ProgressBar/__tests__/ProgressBar.test.tsx b/app/src/atoms/ProgressBar/__tests__/ProgressBar.test.tsx index 6d5b3d3fa40..557a234e969 100644 --- a/app/src/atoms/ProgressBar/__tests__/ProgressBar.test.tsx +++ b/app/src/atoms/ProgressBar/__tests__/ProgressBar.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -7,12 +6,14 @@ import { COLORS } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { ProgressBar } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders() } describe('ProgressBar', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/ProgressBar/index.tsx b/app/src/atoms/ProgressBar/index.tsx index 5852dadf2b5..bd598c0105b 100644 --- a/app/src/atoms/ProgressBar/index.tsx +++ b/app/src/atoms/ProgressBar/index.tsx @@ -1,7 +1,7 @@ -import type * as React from 'react' import { css } from 'styled-components' import { COLORS, Box } from '@opentrons/components' +import type { ReactNode } from 'react' import type { FlattenSimpleInterpolation } from 'styled-components' interface ProgressBarProps { @@ -12,7 +12,7 @@ interface ProgressBarProps { /** extra styles to be filled progress element */ innerStyles?: FlattenSimpleInterpolation /** extra elements to be rendered within container */ - children?: React.ReactNode + children?: ReactNode } export function ProgressBar({ diff --git a/app/src/atoms/SelectField/index.tsx b/app/src/atoms/SelectField/index.tsx index 50deed28266..a51ed7a9ecd 100644 --- a/app/src/atoms/SelectField/index.tsx +++ b/app/src/atoms/SelectField/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import find from 'lodash/find' import { Select } from './Select' import { @@ -11,6 +10,7 @@ import { } from '@opentrons/components' import { css } from 'styled-components' +import type { ReactNode } from 'react' import type { SelectProps, SelectOption } from './Select' import type { ActionMeta, MultiValue, SingleValue } from 'react-select' @@ -32,9 +32,9 @@ export interface SelectFieldProps { /** render function for the option label passed to react-select */ formatOptionLabel?: SelectProps['formatOptionLabel'] /** optional title */ - title?: React.ReactNode + title?: ReactNode /** optional caption. hidden when `error` is given */ - caption?: React.ReactNode + caption?: ReactNode /** if included, use error style and display error instead of caption */ error?: string | null /** change handler called with (name, value, actionMeta) */ diff --git a/app/src/atoms/Skeleton/__tests__/Skeleton.test.tsx b/app/src/atoms/Skeleton/__tests__/Skeleton.test.tsx index b03bd72e98d..471506eb864 100644 --- a/app/src/atoms/Skeleton/__tests__/Skeleton.test.tsx +++ b/app/src/atoms/Skeleton/__tests__/Skeleton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -6,7 +5,9 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { Skeleton } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/atoms/Slideout/__tests__/Slideout.test.tsx b/app/src/atoms/Slideout/__tests__/Slideout.test.tsx index 8e3301ad374..cca0cb02929 100644 --- a/app/src/atoms/Slideout/__tests__/Slideout.test.tsx +++ b/app/src/atoms/Slideout/__tests__/Slideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen, fireEvent } from '@testing-library/react' @@ -6,14 +5,16 @@ import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { Slideout } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('Slideout', () => { - let props: React.ComponentProps + let props: ComponentProps const mockOnClick = vi.fn() beforeEach(() => { props = { diff --git a/app/src/atoms/Slideout/index.tsx b/app/src/atoms/Slideout/index.tsx index 53a6888efa0..7260da7c342 100644 --- a/app/src/atoms/Slideout/index.tsx +++ b/app/src/atoms/Slideout/index.tsx @@ -22,17 +22,19 @@ import { import { Divider } from '../structure' +import type { ReactElement, ReactNode } from 'react' + export interface MultiSlideoutSpecs { currentStep: number maxSteps: number } export interface SlideoutProps { - title: string | React.ReactElement - children: React.ReactNode + title: string | ReactElement + children: ReactNode onCloseClick: () => void // isExpanded is for collapse and expand animation isExpanded?: boolean - footer?: React.ReactNode + footer?: ReactNode multiSlideoutSpecs?: MultiSlideoutSpecs } diff --git a/app/src/atoms/SoftwareKeyboard/AlphanumericKeyboard/index.tsx b/app/src/atoms/SoftwareKeyboard/AlphanumericKeyboard/index.tsx index 73ec9306fee..d536b18b30c 100644 --- a/app/src/atoms/SoftwareKeyboard/AlphanumericKeyboard/index.tsx +++ b/app/src/atoms/SoftwareKeyboard/AlphanumericKeyboard/index.tsx @@ -7,6 +7,8 @@ import { layoutCandidates, customDisplay, } from '../constants' + +import type { MutableRefObject } from 'react' import type { KeyboardReactInterface } from 'react-simple-keyboard' import '../index.css' @@ -15,7 +17,7 @@ import './index.css' // TODO (kk:04/05/2024) add debug to make debugging easy interface AlphanumericKeyboardProps { onChange: (input: string) => void - keyboardRef: React.MutableRefObject + keyboardRef: MutableRefObject debug?: boolean } @@ -49,7 +51,7 @@ export function AlphanumericKeyboard({ return ( (keyboardRef.current = r)} - theme={'hg-theme-default oddTheme1 alphanumericKeyboard'} + theme="hg-theme-default oddTheme1 alphanumericKeyboard" onChange={onChange} onKeyPress={onKeyPress} layoutName={layoutName} diff --git a/app/src/atoms/SoftwareKeyboard/FullKeyboard/index.tsx b/app/src/atoms/SoftwareKeyboard/FullKeyboard/index.tsx index 2846930ad1e..f1f9a962da6 100644 --- a/app/src/atoms/SoftwareKeyboard/FullKeyboard/index.tsx +++ b/app/src/atoms/SoftwareKeyboard/FullKeyboard/index.tsx @@ -60,7 +60,7 @@ export function FullKeyboard({ return ( (keyboardRef.current = r)} - theme={'hg-theme-default oddTheme1'} + theme="hg-theme-default oddTheme1" onChange={onChange} onKeyPress={onKeyPress} layoutName={layoutName} diff --git a/app/src/atoms/SoftwareKeyboard/IndividualKey/index.tsx b/app/src/atoms/SoftwareKeyboard/IndividualKey/index.tsx index 1ba1ec5e150..23ed71f9851 100644 --- a/app/src/atoms/SoftwareKeyboard/IndividualKey/index.tsx +++ b/app/src/atoms/SoftwareKeyboard/IndividualKey/index.tsx @@ -1,6 +1,8 @@ -import type * as React from 'react' import { KeyboardReact as Keyboard } from 'react-simple-keyboard' + +import type { MutableRefObject } from 'react' import type { KeyboardReactInterface } from 'react-simple-keyboard' + import '../index.css' import './index.css' @@ -11,7 +13,7 @@ const customDisplay = { // TODO (kk:04/05/2024) add debug to make debugging easy interface IndividualKeyProps { onChange: (input: string) => void - keyboardRef: React.MutableRefObject + keyboardRef: MutableRefObject keyText: string debug?: boolean } @@ -34,7 +36,7 @@ export function IndividualKey({ */ (keyboardRef.current = r)} - theme={'hg-theme-default oddTheme1 individual-key'} + theme="hg-theme-default oddTheme1 individual-key" onChange={onChange} layoutName="default" display={customDisplay} diff --git a/app/src/atoms/SoftwareKeyboard/NumericalKeyboard/index.tsx b/app/src/atoms/SoftwareKeyboard/NumericalKeyboard/index.tsx index f0025b6c972..16180ecbbdb 100644 --- a/app/src/atoms/SoftwareKeyboard/NumericalKeyboard/index.tsx +++ b/app/src/atoms/SoftwareKeyboard/NumericalKeyboard/index.tsx @@ -1,15 +1,16 @@ -import type * as React from 'react' import { KeyboardReact as Keyboard } from 'react-simple-keyboard' import { numericalKeyboardLayout, numericalCustom } from '../constants' +import type { MutableRefObject } from 'react' import type { KeyboardReactInterface } from 'react-simple-keyboard' + import '../index.css' import './index.css' // Note (kk:04/05/2024) add debug to make debugging easy interface NumericalKeyboardProps { onChange: (input: string) => void - keyboardRef: React.MutableRefObject + keyboardRef: MutableRefObject isDecimal?: boolean hasHyphen?: boolean debug?: boolean @@ -36,7 +37,7 @@ export function NumericalKeyboard({ */ (keyboardRef.current = r)} - theme={'hg-theme-default oddTheme1 numerical-keyboard'} + theme="hg-theme-default oddTheme1 numerical-keyboard" onInit={keyboard => { keyboard.setInput(initialValue) }} diff --git a/app/src/atoms/StatusLabel/__tests__/StatusLabel.test.tsx b/app/src/atoms/StatusLabel/__tests__/StatusLabel.test.tsx index 568326f0065..48a7753cab5 100644 --- a/app/src/atoms/StatusLabel/__tests__/StatusLabel.test.tsx +++ b/app/src/atoms/StatusLabel/__tests__/StatusLabel.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -6,12 +5,14 @@ import { C_SKY_BLUE, COLORS } from '@opentrons/components' import { StatusLabel } from '..' import { renderWithProviders } from '/app/__testing-utils__' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('StatusLabel', () => { - let props: React.ComponentProps + let props: ComponentProps it('renders an engaged status label with a blue background and text', () => { props = { diff --git a/app/src/atoms/StepMeter/__tests__/StepMeter.test.tsx b/app/src/atoms/StepMeter/__tests__/StepMeter.test.tsx index 90f027b0f7a..dcd98b06159 100644 --- a/app/src/atoms/StepMeter/__tests__/StepMeter.test.tsx +++ b/app/src/atoms/StepMeter/__tests__/StepMeter.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -6,14 +5,16 @@ import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { StepMeter } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('StepMeter', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/buttons/BackButton.tsx b/app/src/atoms/buttons/BackButton.tsx index 29657e1f1b2..2819c0eb48e 100644 --- a/app/src/atoms/buttons/BackButton.tsx +++ b/app/src/atoms/buttons/BackButton.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' @@ -11,11 +10,13 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import type { HTMLProps } from 'react' + // TODO(bh, 2022-12-7): finish styling when designs finalized export function BackButton({ onClick, children, -}: React.HTMLProps): JSX.Element { +}: HTMLProps): JSX.Element { const navigate = useNavigate() const { t } = useTranslation('shared') diff --git a/app/src/atoms/buttons/FloatingActionButton.tsx b/app/src/atoms/buttons/FloatingActionButton.tsx index b7e870eab16..6088a5675b4 100644 --- a/app/src/atoms/buttons/FloatingActionButton.tsx +++ b/app/src/atoms/buttons/FloatingActionButton.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { @@ -15,9 +14,10 @@ import { StyledText, } from '@opentrons/components' +import type { ComponentProps } from 'react' import type { IconName } from '@opentrons/components' -interface FloatingActionButtonProps extends React.ComponentProps { +interface FloatingActionButtonProps extends ComponentProps { buttonText: string disabled?: boolean iconName?: IconName diff --git a/app/src/atoms/buttons/IconButton.tsx b/app/src/atoms/buttons/IconButton.tsx index ee754472ff1..43c935d462f 100644 --- a/app/src/atoms/buttons/IconButton.tsx +++ b/app/src/atoms/buttons/IconButton.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { BORDERS, @@ -10,8 +9,10 @@ import { } from '@opentrons/components' import { ODD_FOCUS_VISIBLE } from './constants' -interface IconButtonProps extends React.ComponentProps { - iconName: React.ComponentProps['name'] +import type { ComponentProps } from 'react' + +interface IconButtonProps extends ComponentProps { + iconName: ComponentProps['name'] hasBackground?: boolean } diff --git a/app/src/atoms/buttons/MediumButton.tsx b/app/src/atoms/buttons/MediumButton.tsx index d784029696a..7439cb39a46 100644 --- a/app/src/atoms/buttons/MediumButton.tsx +++ b/app/src/atoms/buttons/MediumButton.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { ALIGN_CENTER, @@ -16,6 +15,7 @@ import { } from '@opentrons/components' import { ODD_FOCUS_VISIBLE } from './constants' +import type { MouseEventHandler, ReactNode } from 'react' import type { IconName, StyleProps } from '@opentrons/components' import type { ButtonCategory } from './SmallButton' @@ -28,12 +28,12 @@ type MediumButtonTypes = | 'tertiaryLowLight' interface MediumButtonProps extends StyleProps { - buttonText: React.ReactNode + buttonText: ReactNode buttonType?: MediumButtonTypes disabled?: boolean iconName?: IconName buttonCategory?: ButtonCategory - onClick: React.MouseEventHandler + onClick: MouseEventHandler } export function MediumButton(props: MediumButtonProps): JSX.Element { diff --git a/app/src/atoms/buttons/SmallButton.tsx b/app/src/atoms/buttons/SmallButton.tsx index e659d52fd58..7ad8c3d1a99 100644 --- a/app/src/atoms/buttons/SmallButton.tsx +++ b/app/src/atoms/buttons/SmallButton.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { ALIGN_CENTER, @@ -15,6 +14,8 @@ import { TYPOGRAPHY, } from '@opentrons/components' import { ODD_FOCUS_VISIBLE } from './constants' + +import type { MouseEventHandler, ReactNode } from 'react' import type { IconName, StyleProps } from '@opentrons/components' export type SmallButtonTypes = @@ -28,9 +29,9 @@ export type ButtonCategory = 'default' | 'rounded' export type IconPlacement = 'startIcon' | 'endIcon' interface SmallButtonProps extends StyleProps { - onClick: React.MouseEventHandler + onClick: MouseEventHandler buttonType?: SmallButtonTypes - buttonText: React.ReactNode + buttonText: ReactNode iconPlacement?: IconPlacement | null iconName?: IconName | null buttonCategory?: ButtonCategory // if not specified, it will be 'default' diff --git a/app/src/atoms/buttons/SubmitPrimaryButton.tsx b/app/src/atoms/buttons/SubmitPrimaryButton.tsx index cdbf3442a65..cc53717bab0 100644 --- a/app/src/atoms/buttons/SubmitPrimaryButton.tsx +++ b/app/src/atoms/buttons/SubmitPrimaryButton.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { SPACING, @@ -8,10 +7,12 @@ import { styleProps, } from '@opentrons/components' +import type { MouseEvent } from 'react' + interface SubmitPrimaryButtonProps { form: string value: string - onClick?: (event: React.MouseEvent) => unknown + onClick?: (event: MouseEvent) => unknown disabled?: boolean } export const SubmitPrimaryButton = ( diff --git a/app/src/atoms/buttons/TextOnlyButton.tsx b/app/src/atoms/buttons/TextOnlyButton.tsx index de3bbc969ab..0acdaf058ed 100644 --- a/app/src/atoms/buttons/TextOnlyButton.tsx +++ b/app/src/atoms/buttons/TextOnlyButton.tsx @@ -1,7 +1,8 @@ -import type * as React from 'react' +import { css } from 'styled-components' import { Btn, StyledText, COLORS, RESPONSIVENESS } from '@opentrons/components' + +import type { ReactNode } from 'react' import type { StyleProps } from '@opentrons/components' -import { css } from 'styled-components' const GO_BACK_BUTTON_STYLE = css` color: ${COLORS.grey50}; @@ -26,7 +27,7 @@ const GO_BACK_BUTTON_DISABLED_STYLE = css` interface TextOnlyButtonProps extends StyleProps { onClick: () => void - buttonText: React.ReactNode + buttonText: ReactNode disabled?: boolean } diff --git a/app/src/atoms/buttons/ToggleButton.tsx b/app/src/atoms/buttons/ToggleButton.tsx index b814f45da1d..42efbde32fb 100644 --- a/app/src/atoms/buttons/ToggleButton.tsx +++ b/app/src/atoms/buttons/ToggleButton.tsx @@ -1,8 +1,8 @@ -import type * as React from 'react' import { css } from 'styled-components' -import { Btn, Icon, COLORS, SIZE_1, SIZE_2 } from '@opentrons/components' +import { Btn, COLORS, Icon } from '@opentrons/components' +import type { MouseEvent } from 'react' import type { StyleProps } from '@opentrons/components' const TOGGLE_DISABLED_STYLES = css` @@ -42,7 +42,7 @@ interface ToggleButtonProps extends StyleProps { toggledOn: boolean disabled?: boolean | null id?: string - onClick?: (e: React.MouseEvent) => unknown + onClick?: (e: MouseEvent) => unknown } export const ToggleButton = (props: ToggleButtonProps): JSX.Element => { @@ -55,12 +55,12 @@ export const ToggleButton = (props: ToggleButtonProps): JSX.Element => { role="switch" aria-label={label} aria-checked={toggledOn} - size={size ?? SIZE_2} + size={size ?? '2rem'} css={props.toggledOn ? TOGGLE_ENABLED_STYLES : TOGGLE_DISABLED_STYLES} {...buttonProps} > {/* TODO(bh, 2022-10-05): implement small and large sizes from design system */} - + ) } diff --git a/app/src/atoms/buttons/__tests__/BackButton.test.tsx b/app/src/atoms/buttons/__tests__/BackButton.test.tsx index 510abd0ee7d..67a65d107da 100644 --- a/app/src/atoms/buttons/__tests__/BackButton.test.tsx +++ b/app/src/atoms/buttons/__tests__/BackButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -9,7 +8,9 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { BackButton } from '..' -const render = (props?: React.HTMLProps) => { +import type { HTMLProps } from 'react' + +const render = (props?: HTMLProps) => { return renderWithProviders( ) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('FloatingActionButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/buttons/__tests__/MediumButton.test.tsx b/app/src/atoms/buttons/__tests__/MediumButton.test.tsx index 988248413d9..1ce59db9217 100644 --- a/app/src/atoms/buttons/__tests__/MediumButton.test.tsx +++ b/app/src/atoms/buttons/__tests__/MediumButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, vi, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -7,12 +6,14 @@ import { renderWithProviders } from '/app/__testing-utils__' import { MediumButton } from '../MediumButton' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('MediumButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onClick: vi.fn(), diff --git a/app/src/atoms/buttons/__tests__/QuaternaryButton.test.tsx b/app/src/atoms/buttons/__tests__/QuaternaryButton.test.tsx index 4e10831c73c..7fd27eb874c 100644 --- a/app/src/atoms/buttons/__tests__/QuaternaryButton.test.tsx +++ b/app/src/atoms/buttons/__tests__/QuaternaryButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, beforeEach, vi } from 'vitest' import { screen } from '@testing-library/react' @@ -7,6 +6,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { QuaternaryButton } from '..' +import type { ComponentProps } from 'react' + vi.mock('styled-components', async () => { const actual = await vi.importActual( 'styled-components/dist/styled-components.browser.esm.js' @@ -14,12 +15,12 @@ vi.mock('styled-components', async () => { return actual }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('QuaternaryButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/buttons/__tests__/SmallButton.test.tsx b/app/src/atoms/buttons/__tests__/SmallButton.test.tsx index 283f21daf4e..84341d3b39b 100644 --- a/app/src/atoms/buttons/__tests__/SmallButton.test.tsx +++ b/app/src/atoms/buttons/__tests__/SmallButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -7,12 +6,14 @@ import { COLORS, BORDERS } from '@opentrons/components' import { SmallButton } from '../SmallButton' import { renderWithProviders } from '/app/__testing-utils__' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('SmallButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/buttons/__tests__/SubmitPrimaryButton.test.tsx b/app/src/atoms/buttons/__tests__/SubmitPrimaryButton.test.tsx index a62ba9c4a95..6af258d9d3f 100644 --- a/app/src/atoms/buttons/__tests__/SubmitPrimaryButton.test.tsx +++ b/app/src/atoms/buttons/__tests__/SubmitPrimaryButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -6,15 +5,16 @@ import { COLORS, SPACING, TYPOGRAPHY, BORDERS } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { SubmitPrimaryButton } from '..' +import type { ComponentProps } from 'react' const mockOnClick = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('SubmitPrimaryButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/buttons/__tests__/TertiaryButton.test.tsx b/app/src/atoms/buttons/__tests__/TertiaryButton.test.tsx index 4a3f95369c8..30b15c5bea2 100644 --- a/app/src/atoms/buttons/__tests__/TertiaryButton.test.tsx +++ b/app/src/atoms/buttons/__tests__/TertiaryButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, beforeEach } from 'vitest' import { screen } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' @@ -7,12 +6,14 @@ import { COLORS, SPACING, TYPOGRAPHY, BORDERS } from '@opentrons/components' import { TertiaryButton } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('TertiaryButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/buttons/__tests__/ToggleButton.test.tsx b/app/src/atoms/buttons/__tests__/ToggleButton.test.tsx index 3f61d43c5d0..29991d4ae0f 100644 --- a/app/src/atoms/buttons/__tests__/ToggleButton.test.tsx +++ b/app/src/atoms/buttons/__tests__/ToggleButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import { screen, fireEvent } from '@testing-library/react' import { COLORS, SIZE_2 } from '@opentrons/components' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { ToggleButton } from '..' +import type { ComponentProps } from 'react' + const mockOnClick = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('ToggleButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/structure/Divider.tsx b/app/src/atoms/structure/Divider.tsx index db55ad84f44..a253f27dce4 100644 --- a/app/src/atoms/structure/Divider.tsx +++ b/app/src/atoms/structure/Divider.tsx @@ -1,7 +1,7 @@ -import type * as React from 'react' import { Box, COLORS, SPACING } from '@opentrons/components' +import type { ComponentProps } from 'react' -type Props = React.ComponentProps +type Props = ComponentProps export function Divider(props: Props): JSX.Element { const { marginY } = props diff --git a/app/src/atoms/structure/Line.tsx b/app/src/atoms/structure/Line.tsx index ecbbecc24cd..8eb456233f6 100644 --- a/app/src/atoms/structure/Line.tsx +++ b/app/src/atoms/structure/Line.tsx @@ -1,7 +1,7 @@ -import type * as React from 'react' import { Box, BORDERS } from '@opentrons/components' +import type { ComponentProps } from 'react' -type Props = React.ComponentProps +type Props = ComponentProps export function Line(props: Props): JSX.Element { return diff --git a/app/src/atoms/structure/__tests__/Divider.test.tsx b/app/src/atoms/structure/__tests__/Divider.test.tsx index 27460be938d..0aa40edb9d4 100644 --- a/app/src/atoms/structure/__tests__/Divider.test.tsx +++ b/app/src/atoms/structure/__tests__/Divider.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -6,12 +5,14 @@ import { SPACING, COLORS } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { Divider } from '../index' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('Divider', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/atoms/structure/__tests__/Line.test.tsx b/app/src/atoms/structure/__tests__/Line.test.tsx index d9a9caefba2..c4e3e267565 100644 --- a/app/src/atoms/structure/__tests__/Line.test.tsx +++ b/app/src/atoms/structure/__tests__/Line.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -6,12 +5,14 @@ import { SPACING, COLORS } from '@opentrons/components' import { Line } from '../index' import { renderWithProviders } from '/app/__testing-utils__' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('Line', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/local-resources/commands/hooks/useCommandTextString/utils/commandText/getConfigureNozzleLayoutCommandText.ts b/app/src/local-resources/commands/hooks/useCommandTextString/utils/commandText/getConfigureNozzleLayoutCommandText.ts index 8c9e12f3d5b..f6440d69e1d 100644 --- a/app/src/local-resources/commands/hooks/useCommandTextString/utils/commandText/getConfigureNozzleLayoutCommandText.ts +++ b/app/src/local-resources/commands/hooks/useCommandTextString/utils/commandText/getConfigureNozzleLayoutCommandText.ts @@ -13,13 +13,12 @@ export function getConfigureNozzleLayoutCommandText({ pip => pip.id === pipetteId )?.pipetteName - // TODO(cb, 2024-09-10): confirm these strings for copy consistency and add them to i18n const ConfigAmount = { - SINGLE: 'single nozzle layout', - COLUMN: 'column layout', - ROW: 'row layout', - QUADRANT: 'partial layout', - ALL: 'all nozzles', + SINGLE: t('single_nozzle_layout'), + COLUMN: t('column_layout'), + ROW: t('row_layout'), + QUADRANT: t('partial_layout'), + ALL: t('all_nozzles'), } return t('configure_nozzle_layout', { diff --git a/app/src/molecules/BackgroundOverlay/__tests__/BackgroundOverlay.test.tsx b/app/src/molecules/BackgroundOverlay/__tests__/BackgroundOverlay.test.tsx index e09b3c11765..882c4a644b4 100644 --- a/app/src/molecules/BackgroundOverlay/__tests__/BackgroundOverlay.test.tsx +++ b/app/src/molecules/BackgroundOverlay/__tests__/BackgroundOverlay.test.tsx @@ -1,15 +1,16 @@ -import type * as React from 'react' import { describe, it, expect, vi } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' import { BackgroundOverlay } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('BackgroundOverlay', () => { - let props: React.ComponentProps + let props: ComponentProps it('renders background overlay', () => { props = { onClick: vi.fn() } render(props) diff --git a/app/src/molecules/BackgroundOverlay/index.tsx b/app/src/molecules/BackgroundOverlay/index.tsx index 3d6c6d976c6..c0a711ffa91 100644 --- a/app/src/molecules/BackgroundOverlay/index.tsx +++ b/app/src/molecules/BackgroundOverlay/index.tsx @@ -1,8 +1,9 @@ -import type * as React from 'react' import { css } from 'styled-components' import { COLORS, Flex, POSITION_FIXED } from '@opentrons/components' +import type { ComponentProps, MouseEventHandler } from 'react' + const BACKGROUND_OVERLAY_STYLE = css` position: ${POSITION_FIXED}; inset: 0; @@ -10,10 +11,9 @@ const BACKGROUND_OVERLAY_STYLE = css` background-color: ${COLORS.black90}${COLORS.opacity60HexCode}; ` -export interface BackgroundOverlayProps - extends React.ComponentProps { +export interface BackgroundOverlayProps extends ComponentProps { // onClick handler so when you click anywhere in the overlay, the modal/menu closes - onClick: React.MouseEventHandler + onClick: MouseEventHandler } export function BackgroundOverlay(props: BackgroundOverlayProps): JSX.Element { diff --git a/app/src/molecules/CardButton/__tests__/CardButton.test.tsx b/app/src/molecules/CardButton/__tests__/CardButton.test.tsx index 820dfbdc4e9..177d61dfd2d 100644 --- a/app/src/molecules/CardButton/__tests__/CardButton.test.tsx +++ b/app/src/molecules/CardButton/__tests__/CardButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -7,6 +6,8 @@ import { COLORS } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { CardButton } from '..' + +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockNavigate = vi.fn() @@ -19,7 +20,7 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -31,7 +32,7 @@ const render = (props: React.ComponentProps) => { } describe('CardButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/Command/CommandText.tsx b/app/src/molecules/Command/CommandText.tsx index e690115a88b..a2d872af5a8 100644 --- a/app/src/molecules/Command/CommandText.tsx +++ b/app/src/molecules/Command/CommandText.tsx @@ -1,5 +1,5 @@ -import type * as React from 'react' import { pick } from 'lodash' +import { css } from 'styled-components' import { ALIGN_CENTER, @@ -13,6 +13,7 @@ import { import { useCommandTextString } from '/app/local-resources/commands' +import type { ComponentProps } from 'react' import type { LabwareDefinition2, RobotType, @@ -26,13 +27,13 @@ import type { } from '/app/local-resources/commands' interface LegacySTProps { - as?: React.ComponentProps['as'] + as?: ComponentProps['as'] modernStyledTextDefaults?: false } interface ModernSTProps { - desktopStyle?: React.ComponentProps['desktopStyle'] - oddStyle?: React.ComponentProps['oddStyle'] + desktopStyle?: ComponentProps['desktopStyle'] + oddStyle?: ComponentProps['oddStyle'] modernStyledTextDefaults: true } @@ -174,21 +175,10 @@ function ThermocyclerRunProfile( >
    {shouldPropagateTextLimit(propagateTextLimit, isOnDevice) ? ( -
  • - {stepTexts[0]} -
  • +
  • {stepTexts[0]}
  • ) : ( stepTexts.map((step: string, index: number) => ( -
  • +
  • {' '} {step}
  • @@ -252,11 +242,7 @@ function ThermocyclerRunExtendedProfile( >
      {shouldPropagateTextLimit(propagateTextLimit, isOnDevice) ? ( -
    • +
    • {profileElementTexts[0].kind === 'step' ? profileElementTexts[0].stepText : profileElementTexts[0].cycleText} @@ -264,30 +250,18 @@ function ThermocyclerRunExtendedProfile( ) : ( profileElementTexts.map((element, index: number) => element.kind === 'step' ? ( -
    • +
    • {' '} {element.stepText}
    • ) : ( -
    • +
    • {element.cycleText}
        {element.stepTexts.map( ({ stepText }, stepIndex: number) => (
      • {' '} @@ -305,3 +279,7 @@ function ThermocyclerRunExtendedProfile( ) } + +const LIST_STYLE = css` + margin-left: ${SPACING.spacing4}; +` diff --git a/app/src/molecules/FileUpload/__tests__/FileUpload.test.tsx b/app/src/molecules/FileUpload/__tests__/FileUpload.test.tsx index cd5b5adfd82..9f3c22db269 100644 --- a/app/src/molecules/FileUpload/__tests__/FileUpload.test.tsx +++ b/app/src/molecules/FileUpload/__tests__/FileUpload.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { screen } from '@testing-library/react' @@ -7,7 +6,9 @@ import { i18n } from '/app/i18n' import { FileUpload } from '..' import testFile from './test-file.png' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -16,7 +17,7 @@ const render = (props: React.ComponentProps) => { const handleClick = vi.fn() describe('FileUpload', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { const file = new File([testFile], 'a-file-to-test.png') diff --git a/app/src/molecules/GenericWizardTile/__tests__/GenericWizardTile.test.tsx b/app/src/molecules/GenericWizardTile/__tests__/GenericWizardTile.test.tsx index 1f53800ff6d..ee1d680d4d2 100644 --- a/app/src/molecules/GenericWizardTile/__tests__/GenericWizardTile.test.tsx +++ b/app/src/molecules/GenericWizardTile/__tests__/GenericWizardTile.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, vi, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -7,16 +6,18 @@ import { renderWithProviders } from '/app/__testing-utils__' import { getIsOnDevice } from '/app/redux/config' import { GenericWizardTile } from '..' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('GenericWizardTile', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/GenericWizardTile/index.tsx b/app/src/molecules/GenericWizardTile/index.tsx index 24883a6ffea..3948daa292a 100644 --- a/app/src/molecules/GenericWizardTile/index.tsx +++ b/app/src/molecules/GenericWizardTile/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useSelector } from 'react-redux' import styled, { css } from 'styled-components' import { useTranslation } from 'react-i18next' @@ -26,6 +25,8 @@ import { getIsOnDevice } from '/app/redux/config' import { NeedHelpLink } from '/app/molecules/OT2CalibrationNeedHelpLink' import { SmallButton, TextOnlyButton } from '/app/atoms/buttons' +import type { ReactNode } from 'react' + const ALIGN_BUTTONS = css` align-items: ${ALIGN_FLEX_END}; @@ -59,13 +60,13 @@ const TILE_CONTAINER_STYLE = css` } ` export interface GenericWizardTileProps { - rightHandBody: React.ReactNode - bodyText: React.ReactNode - header: string | React.ReactNode + rightHandBody: ReactNode + bodyText: ReactNode + header: string | ReactNode getHelp?: string back?: () => void proceed?: () => void - proceedButtonText?: React.ReactNode + proceedButtonText?: ReactNode proceedIsDisabled?: boolean proceedButton?: JSX.Element backIsDisabled?: boolean diff --git a/app/src/molecules/InProgressModal/InProgressModal.tsx b/app/src/molecules/InProgressModal/InProgressModal.tsx index c6fefe761a2..82693b34429 100644 --- a/app/src/molecules/InProgressModal/InProgressModal.tsx +++ b/app/src/molecules/InProgressModal/InProgressModal.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { ALIGN_CENTER, @@ -13,9 +12,11 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import type { ReactNode } from 'react' + interface Props { // optional override of the spinner - alternativeSpinner?: React.ReactNode + alternativeSpinner?: ReactNode description?: string body?: string children?: JSX.Element diff --git a/app/src/molecules/InProgressModal/__tests__/InProgressModal.test.tsx b/app/src/molecules/InProgressModal/__tests__/InProgressModal.test.tsx index f670fa221c3..db7ac2294cf 100644 --- a/app/src/molecules/InProgressModal/__tests__/InProgressModal.test.tsx +++ b/app/src/molecules/InProgressModal/__tests__/InProgressModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach, vi } from 'vitest' import { i18n } from '/app/i18n' @@ -6,15 +5,17 @@ import { getIsOnDevice } from '/app/redux/config' import { renderWithProviders } from '/app/__testing-utils__' import { InProgressModal } from '../InProgressModal' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('InProgressModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(getIsOnDevice).mockReturnValue(false) }) diff --git a/app/src/molecules/InfoMessage/__tests__/InfoMessage.test.tsx b/app/src/molecules/InfoMessage/__tests__/InfoMessage.test.tsx index 5ff6948976f..378e1eff503 100644 --- a/app/src/molecules/InfoMessage/__tests__/InfoMessage.test.tsx +++ b/app/src/molecules/InfoMessage/__tests__/InfoMessage.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { describe, it, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { screen } from '@testing-library/react' import { i18n } from '/app/i18n' import { InfoMessage } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('InfoMessage', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/InstrumentCard/index.tsx b/app/src/molecules/InstrumentCard/index.tsx index bcb2ccb47ac..2cd838a08bd 100644 --- a/app/src/molecules/InstrumentCard/index.tsx +++ b/app/src/molecules/InstrumentCard/index.tsx @@ -1,5 +1,3 @@ -import type * as React from 'react' - import { ALIGN_CENTER, ALIGN_FLEX_START, @@ -22,6 +20,7 @@ import flexGripper from '/app/assets/images/flex_gripper.png' import { MenuOverlay } from './MenuOverlay' +import type { ReactNode } from 'react' import type { InstrumentDiagramProps, StyleProps } from '@opentrons/components' import type { MenuOverlayItemProps } from './MenuOverlay' @@ -33,7 +32,7 @@ interface InstrumentCardProps extends StyleProps { instrumentDiagramProps?: InstrumentDiagramProps // special casing the gripper at least for now isGripperAttached?: boolean - banner?: React.ReactNode + banner?: ReactNode isEstopNotDisengaged: boolean } @@ -91,7 +90,7 @@ export function InstrumentCard(props: InstrumentCardProps): JSX.Element { pipetteSpecs={instrumentDiagramProps.pipetteSpecs} mount={instrumentDiagramProps.mount} transform="scale(0.3)" - transformOrigin={'-5% 52%'} + transformOrigin="-5% 52%" /> ) : null} diff --git a/app/src/molecules/InterventionModal/InterventionContent/index.tsx b/app/src/molecules/InterventionModal/InterventionContent/index.tsx index 8d2bbba9c8c..e9b156cf0e6 100644 --- a/app/src/molecules/InterventionModal/InterventionContent/index.tsx +++ b/app/src/molecules/InterventionModal/InterventionContent/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Flex, StyledText, @@ -7,15 +6,17 @@ import { RESPONSIVENESS, } from '@opentrons/components' import { InlineNotification } from '/app/atoms/InlineNotification' - import { InterventionInfo } from './InterventionInfo' + +import type { ComponentProps } from 'react' + export type { InterventionInfoProps } from './InterventionInfo' export { InterventionInfo } export interface InterventionContentProps { headline: string - infoProps: React.ComponentProps - notificationProps?: React.ComponentProps + infoProps: ComponentProps + notificationProps?: ComponentProps } export function InterventionContent({ diff --git a/app/src/molecules/InterventionModal/ModalContentMixed.tsx b/app/src/molecules/InterventionModal/ModalContentMixed.tsx index 4c41003c3d8..c7f4a43ea6e 100644 --- a/app/src/molecules/InterventionModal/ModalContentMixed.tsx +++ b/app/src/molecules/InterventionModal/ModalContentMixed.tsx @@ -152,7 +152,7 @@ function ModalContentMixedSpinner( return ( 0 && imageUrl[0].length > 0 return hasComponent ? ( - + ) : null } diff --git a/app/src/molecules/InterventionModal/TwoColumn.tsx b/app/src/molecules/InterventionModal/TwoColumn.tsx index fc9072232be..600386ec60f 100644 --- a/app/src/molecules/InterventionModal/TwoColumn.tsx +++ b/app/src/molecules/InterventionModal/TwoColumn.tsx @@ -1,11 +1,11 @@ -import type * as React from 'react' - import { Flex, Box, DIRECTION_ROW, SPACING, WRAP } from '@opentrons/components' import type { StyleProps } from '@opentrons/components' import { TWO_COLUMN_ELEMENT_MIN_WIDTH } from './constants' +import type { ReactNode } from 'react' + export interface TwoColumnProps extends StyleProps { - children: [React.ReactNode, React.ReactNode] + children: [ReactNode, ReactNode] } export function TwoColumn({ diff --git a/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx b/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx index a063bee13bc..bdbe4c95e70 100644 --- a/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx +++ b/app/src/molecules/InterventionModal/__tests__/InterventionModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { when } from 'vitest-when' import '@testing-library/jest-dom/vitest' @@ -12,6 +11,7 @@ import { getIsOnDevice } from '/app/redux/config' import { InterventionModal } from '../' +import type { ComponentProps } from 'react' import type { ModalType } from '../' import type { State } from '/app/redux/types' @@ -23,7 +23,7 @@ const MOCK_STATE: State = { }, } as any -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, initialState: MOCK_STATE, @@ -31,7 +31,7 @@ const render = (props: React.ComponentProps) => { } describe('InterventionModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/InterventionModal/__tests__/ModalContentOneColSimpleButtons.test.tsx b/app/src/molecules/InterventionModal/__tests__/ModalContentOneColSimpleButtons.test.tsx index 27a40d82ad4..bb25d211619 100644 --- a/app/src/molecules/InterventionModal/__tests__/ModalContentOneColSimpleButtons.test.tsx +++ b/app/src/molecules/InterventionModal/__tests__/ModalContentOneColSimpleButtons.test.tsx @@ -1,9 +1,10 @@ -import type * as React from 'react' import { vi, describe, it, expect } from 'vitest' import { render, screen, fireEvent } from '@testing-library/react' import { ModalContentOneColSimpleButtons } from '../ModalContentOneColSimpleButtons' +import type { ChangeEventHandler } from 'react' + /* eslint-disable testing-library/no-node-access */ const inputElForButtonFromButtonText = (text: string): HTMLInputElement => ((screen.getByText(text)?.parentElement?.parentElement @@ -17,7 +18,7 @@ describe('InterventionModal', () => { it('renders headline', () => { render( @@ -27,7 +28,7 @@ describe('InterventionModal', () => { it('renders buttons', () => { render( { it('enforces single-item selection', () => { render( { it('can start with a button selected', () => { render( ) expect(inputElForButtonFromButtonText('first button').checked).toBeFalsy() @@ -84,11 +85,11 @@ describe('InterventionModal', () => { const onChange = vi.fn() render( , + onChange: onChange as ChangeEventHandler, }} secondButton={{ label: 'second button', value: 'second' }} furtherButtons={[{ label: 'third button', value: 'third' }]} @@ -110,7 +111,7 @@ describe('InterventionModal', () => { const onSelect = vi.fn() render( void /** overall style hint */ @@ -128,7 +128,7 @@ export interface InterventionModalProps { /* Optional icon size override. */ iconSize?: string /** modal contents */ - children: React.ReactNode + children: ReactNode } export function InterventionModal({ @@ -160,7 +160,7 @@ export function InterventionModal({ {...modalStyle} flexDirection={DIRECTION_COLUMN} border={border} - onClick={(e: React.MouseEvent) => { + onClick={(e: MouseEvent) => { e.stopPropagation() }} > diff --git a/app/src/molecules/InterventionModal/story-utils/StandIn.tsx b/app/src/molecules/InterventionModal/story-utils/StandIn.tsx index 33eb868816d..56153551df3 100644 --- a/app/src/molecules/InterventionModal/story-utils/StandIn.tsx +++ b/app/src/molecules/InterventionModal/story-utils/StandIn.tsx @@ -1,14 +1,14 @@ -import type * as React from 'react' import { Box, BORDERS } from '@opentrons/components' +import type { ReactNode } from 'react' export function StandInContent({ children, }: { - children?: React.ReactNode + children?: ReactNode }): JSX.Element { return ( 0) setCurrentStepSize(stepSizes[i - 1]) } - const handleStepSelect = ( - event: React.MouseEvent - ): void => { + const handleStepSelect = (event: MouseEvent): void => { setCurrentStepSize(Number(event.currentTarget.value) as StepSize) event.currentTarget.blur() } diff --git a/app/src/molecules/MiniCard/__tests__/MiniCard.test.tsx b/app/src/molecules/MiniCard/__tests__/MiniCard.test.tsx index fcc76f38505..0e26b805f16 100644 --- a/app/src/molecules/MiniCard/__tests__/MiniCard.test.tsx +++ b/app/src/molecules/MiniCard/__tests__/MiniCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -6,12 +5,14 @@ import { COLORS, SPACING, BORDERS, CURSOR_POINTER } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { MiniCard } from '../' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('MiniCard', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/MiniCard/index.tsx b/app/src/molecules/MiniCard/index.tsx index b65ccbbb59d..d95c42e149f 100644 --- a/app/src/molecules/MiniCard/index.tsx +++ b/app/src/molecules/MiniCard/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { BORDERS, @@ -8,12 +7,13 @@ import { SPACING, } from '@opentrons/components' +import type { ReactNode } from 'react' import type { StyleProps } from '@opentrons/components' interface MiniCardProps extends StyleProps { onClick: () => void isSelected: boolean - children: React.ReactNode + children: ReactNode isError?: boolean isWarning?: boolean } diff --git a/app/src/molecules/ModuleIcon/__tests__/ModuleIcon.test.tsx b/app/src/molecules/ModuleIcon/__tests__/ModuleIcon.test.tsx index 73c44639e51..6e20f8997fa 100644 --- a/app/src/molecules/ModuleIcon/__tests__/ModuleIcon.test.tsx +++ b/app/src/molecules/ModuleIcon/__tests__/ModuleIcon.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { COLORS, SPACING } from '@opentrons/components' import { describe, it, expect, vi, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -6,6 +5,7 @@ import '@testing-library/jest-dom/vitest' import { renderWithProviders } from '/app/__testing-utils__' import { ModuleIcon } from '../' +import type { ComponentProps } from 'react' import type { AttachedModule } from '/app/redux/modules/types' import type * as OpentronsComponents from '@opentrons/components' @@ -17,7 +17,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } @@ -46,7 +46,7 @@ const mockHeaterShakerModule = { } as AttachedModule describe('ModuleIcon', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/ModuleInfo/__tests__/ModuleInfo.test.tsx b/app/src/molecules/ModuleInfo/__tests__/ModuleInfo.test.tsx index 1d732faeb18..de5eef75577 100644 --- a/app/src/molecules/ModuleInfo/__tests__/ModuleInfo.test.tsx +++ b/app/src/molecules/ModuleInfo/__tests__/ModuleInfo.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -7,11 +6,13 @@ import { when } from 'vitest-when' import { i18n } from '/app/i18n' import { ModuleInfo } from '../ModuleInfo' import { useRunHasStarted } from '/app/resources/runs' + +import type { ComponentProps } from 'react' import type { ModuleModel, ModuleType } from '@opentrons/shared-data' vi.mock('/app/resources/runs') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -26,7 +27,7 @@ const mockTCModule = { const MOCK_RUN_ID = '1' describe('ModuleInfo', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { moduleModel: mockTCModule.model, diff --git a/app/src/molecules/NavTab/__tests__/NavTab.test.tsx b/app/src/molecules/NavTab/__tests__/NavTab.test.tsx index 176c76b60cc..e6a2c9a504f 100644 --- a/app/src/molecules/NavTab/__tests__/NavTab.test.tsx +++ b/app/src/molecules/NavTab/__tests__/NavTab.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, beforeEach } from 'vitest' @@ -7,7 +6,9 @@ import { SPACING, COLORS, TYPOGRAPHY, BORDERS } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { NavTab } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders( @@ -16,7 +17,7 @@ const render = (props: React.ComponentProps) => { } describe('NavTab', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/NavTab/index.tsx b/app/src/molecules/NavTab/index.tsx index e170da56e43..6cdf6cb0e6b 100644 --- a/app/src/molecules/NavTab/index.tsx +++ b/app/src/molecules/NavTab/index.tsx @@ -3,6 +3,8 @@ import { NavLink } from 'react-router-dom' import { BORDERS, COLORS, SPACING, TYPOGRAPHY } from '@opentrons/components' +import type { ComponentProps } from 'react' + export const TAB_BORDER_STYLE = css` border-bottom-style: ${BORDERS.styleSolid}; border-bottom-width: 2px; @@ -15,7 +17,7 @@ interface NavTabProps { disabled?: boolean } -const StyledNavLink = styled(NavLink)>` +const StyledNavLink = styled(NavLink)>` padding: 0 ${SPACING.spacing4} ${SPACING.spacing8}; ${TYPOGRAPHY.labelSemiBold} color: ${COLORS.grey50}; diff --git a/app/src/molecules/ODDBackButton/__tests__/ODDBackButton.test.tsx b/app/src/molecules/ODDBackButton/__tests__/ODDBackButton.test.tsx index 2b520279cf8..5b487b2e7dc 100644 --- a/app/src/molecules/ODDBackButton/__tests__/ODDBackButton.test.tsx +++ b/app/src/molecules/ODDBackButton/__tests__/ODDBackButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, vi, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -6,12 +5,14 @@ import { COLORS } from '@opentrons/components' import { ODDBackButton } from '..' import { renderWithProviders } from '/app/__testing-utils__' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('ODDBackButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/ODDBackButton/index.tsx b/app/src/molecules/ODDBackButton/index.tsx index 396feaa5e19..0390dc21284 100644 --- a/app/src/molecules/ODDBackButton/index.tsx +++ b/app/src/molecules/ODDBackButton/index.tsx @@ -1,5 +1,3 @@ -import type * as React from 'react' - import { ALIGN_CENTER, Btn, @@ -11,8 +9,10 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import type { HTMLProps } from 'react' + export function ODDBackButton( - props: React.HTMLProps + props: HTMLProps ): JSX.Element { const { onClick, label } = props diff --git a/app/src/molecules/OT2CalibrationNeedHelpLink/NeedHelpLink.tsx b/app/src/molecules/OT2CalibrationNeedHelpLink/NeedHelpLink.tsx index a87a7808827..6dd9a5f8d86 100644 --- a/app/src/molecules/OT2CalibrationNeedHelpLink/NeedHelpLink.tsx +++ b/app/src/molecules/OT2CalibrationNeedHelpLink/NeedHelpLink.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { Flex, @@ -11,9 +10,11 @@ import { SPACING, } from '@opentrons/components' +import type { ComponentProps } from 'react' + const SUPPORT_PAGE_URL = 'https://support.opentrons.com/s/ot2-calibration' -interface NeedHelpLinkProps extends React.ComponentProps { +interface NeedHelpLinkProps extends ComponentProps { href?: string } diff --git a/app/src/molecules/OddModal/OddModal.tsx b/app/src/molecules/OddModal/OddModal.tsx index 9b32974000c..e95aaa1d9a4 100644 --- a/app/src/molecules/OddModal/OddModal.tsx +++ b/app/src/molecules/OddModal/OddModal.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { ALIGN_CENTER, BORDERS, @@ -11,14 +10,15 @@ import { import { BackgroundOverlay } from '../BackgroundOverlay' import { OddModalHeader } from './OddModalHeader' +import type { MouseEvent, MouseEventHandler, ReactNode } from 'react' import type { StyleProps } from '@opentrons/components' import type { OddModalHeaderBaseProps, ModalSize } from './types' interface OddModalProps extends StyleProps { /** clicking anywhere outside of the modal closes it */ - onOutsideClick?: React.MouseEventHandler + onOutsideClick?: MouseEventHandler /** modal content */ - children: React.ReactNode + children: ReactNode /** for small, medium, or large modal sizes, medium by default */ modalSize?: ModalSize /** see OddModalHeader component for more details */ @@ -66,7 +66,7 @@ export function OddModal(props: OddModalProps): JSX.Element { margin={SPACING.spacing32} flexDirection={DIRECTION_COLUMN} aria-label={`modal_${modalSize}`} - onClick={(e: React.MouseEvent) => { + onClick={(e: MouseEvent) => { e.stopPropagation() }} > diff --git a/app/src/molecules/OddModal/__tests__/OddModal.test.tsx b/app/src/molecules/OddModal/__tests__/OddModal.test.tsx index d8c129a8b01..9d9097b4729 100644 --- a/app/src/molecules/OddModal/__tests__/OddModal.test.tsx +++ b/app/src/molecules/OddModal/__tests__/OddModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -7,14 +6,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { OddModalHeader } from '../OddModalHeader' import { OddModal } from '../OddModal' +import type { ComponentProps } from 'react' + vi.mock('../OddModalHeader') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('OddModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onOutsideClick: vi.fn(), diff --git a/app/src/molecules/OddModal/__tests__/OddModalHeader.test.tsx b/app/src/molecules/OddModal/__tests__/OddModalHeader.test.tsx index e824e49648c..94499d35abf 100644 --- a/app/src/molecules/OddModal/__tests__/OddModalHeader.test.tsx +++ b/app/src/molecules/OddModal/__tests__/OddModalHeader.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, vi, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -6,12 +5,14 @@ import { COLORS } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { OddModalHeader } from '../OddModalHeader' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('OddModalHeader', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { title: 'title', diff --git a/app/src/molecules/OddModal/types.ts b/app/src/molecules/OddModal/types.ts index b0fa6d103ae..e4f07cc52ad 100644 --- a/app/src/molecules/OddModal/types.ts +++ b/app/src/molecules/OddModal/types.ts @@ -1,10 +1,11 @@ +import type { MouseEventHandler } from 'react' import type { IconName, StyleProps } from '@opentrons/components' export type ModalSize = 'small' | 'medium' | 'large' export interface OddModalHeaderBaseProps extends StyleProps { title: string | JSX.Element - onClick?: React.MouseEventHandler + onClick?: MouseEventHandler hasExitIcon?: boolean iconName?: IconName iconColor?: string diff --git a/app/src/molecules/OffsetVector/__tests__/OffsetVector.test.tsx b/app/src/molecules/OffsetVector/__tests__/OffsetVector.test.tsx index bb247c0175a..e3b4c87da6c 100644 --- a/app/src/molecules/OffsetVector/__tests__/OffsetVector.test.tsx +++ b/app/src/molecules/OffsetVector/__tests__/OffsetVector.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, beforeEach } from 'vitest' @@ -7,12 +6,14 @@ import { renderWithProviders } from '/app/__testing-utils__' import { OffsetVector } from '../' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('OffsetVector', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/OffsetVector/index.tsx b/app/src/molecules/OffsetVector/index.tsx index 155019e8074..af4d8c5dc68 100644 --- a/app/src/molecules/OffsetVector/index.tsx +++ b/app/src/molecules/OffsetVector/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Flex, SPACING, @@ -6,13 +5,14 @@ import { LegacyStyledText, } from '@opentrons/components' +import type { ComponentProps } from 'react' import type { StyleProps } from '@opentrons/components' interface OffsetVectorProps extends StyleProps { x: number y: number z: number - as?: React.ComponentProps['as'] + as?: ComponentProps['as'] } export function OffsetVector(props: OffsetVectorProps): JSX.Element { diff --git a/app/src/molecules/SimpleWizardBody/SimpleWizardBodyContent.tsx b/app/src/molecules/SimpleWizardBody/SimpleWizardBodyContent.tsx index 16113e28c9a..1a179f1b987 100644 --- a/app/src/molecules/SimpleWizardBody/SimpleWizardBodyContent.tsx +++ b/app/src/molecules/SimpleWizardBody/SimpleWizardBodyContent.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useSelector } from 'react-redux' import { css } from 'styled-components' import { @@ -21,13 +20,15 @@ import SuccessIcon from '/app/assets/images/icon_success.png' import { getIsOnDevice } from '/app/redux/config' import { Skeleton } from '/app/atoms/Skeleton' + +import type { ReactNode } from 'react' import type { RobotType } from '@opentrons/shared-data' interface Props { iconColor: string header: string isSuccess: boolean - children?: React.ReactNode + children?: ReactNode subHeader?: string | JSX.Element isPending?: boolean robotType?: RobotType diff --git a/app/src/molecules/SimpleWizardBody/SimpleWizardInProgressBody.tsx b/app/src/molecules/SimpleWizardBody/SimpleWizardInProgressBody.tsx index 10882025dfc..92f65aa2cf2 100644 --- a/app/src/molecules/SimpleWizardBody/SimpleWizardInProgressBody.tsx +++ b/app/src/molecules/SimpleWizardBody/SimpleWizardInProgressBody.tsx @@ -1,9 +1,10 @@ -import type * as React from 'react' -import type { StyleProps } from '@opentrons/components' import { InProgressModal } from '../InProgressModal/InProgressModal' import { SimpleWizardBodyContainer } from './SimpleWizardBodyContainer' -export type SimpleWizardInProgressBodyProps = React.ComponentProps< +import type { ComponentProps } from 'react' +import type { StyleProps } from '@opentrons/components' + +export type SimpleWizardInProgressBodyProps = ComponentProps< typeof InProgressModal > & StyleProps diff --git a/app/src/molecules/SimpleWizardBody/__tests__/SimpleWizardBody.test.tsx b/app/src/molecules/SimpleWizardBody/__tests__/SimpleWizardBody.test.tsx index 9849e8fa03c..f21e14f0580 100644 --- a/app/src/molecules/SimpleWizardBody/__tests__/SimpleWizardBody.test.tsx +++ b/app/src/molecules/SimpleWizardBody/__tests__/SimpleWizardBody.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import { COLORS } from '@opentrons/components' @@ -7,14 +6,16 @@ import { Skeleton } from '/app/atoms/Skeleton' import { getIsOnDevice } from '/app/redux/config' import { SimpleWizardBody } from '..' +import type { ComponentProps } from 'react' + vi.mock('/app/atoms/Skeleton') vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('SimpleWizardBody', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { iconColor: COLORS.red60, diff --git a/app/src/molecules/SimpleWizardBody/index.tsx b/app/src/molecules/SimpleWizardBody/index.tsx index b554b71b90e..c49c49be2a8 100644 --- a/app/src/molecules/SimpleWizardBody/index.tsx +++ b/app/src/molecules/SimpleWizardBody/index.tsx @@ -1,8 +1,9 @@ -import type * as React from 'react' - import { SimpleWizardBodyContainer } from './SimpleWizardBodyContainer' import { SimpleWizardBodyContent } from './SimpleWizardBodyContent' import { SimpleWizardInProgressBody } from './SimpleWizardInProgressBody' + +import type { ComponentProps } from 'react' + export { SimpleWizardBodyContainer, SimpleWizardBodyContent, @@ -10,8 +11,8 @@ export { } export function SimpleWizardBody( - props: React.ComponentProps & - React.ComponentProps + props: ComponentProps & + ComponentProps ): JSX.Element { const { children, ...rest } = props return ( diff --git a/app/src/molecules/ToggleGroup/__tests__/useToggleGroup.test.tsx b/app/src/molecules/ToggleGroup/__tests__/useToggleGroup.test.tsx index c4829b79615..b90da347489 100644 --- a/app/src/molecules/ToggleGroup/__tests__/useToggleGroup.test.tsx +++ b/app/src/molecules/ToggleGroup/__tests__/useToggleGroup.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Provider } from 'react-redux' import { createStore } from 'redux' import '@testing-library/jest-dom/vitest' @@ -7,6 +6,7 @@ import { renderHook, render, fireEvent, screen } from '@testing-library/react' import { useTrackEvent } from '/app/redux/analytics' import { useToggleGroup } from '../useToggleGroup' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' @@ -23,7 +23,7 @@ describe('useToggleGroup', () => { }) it('should return default selectedValue and toggle buttons', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} @@ -35,7 +35,7 @@ describe('useToggleGroup', () => { expect(result.current[0]).toBe('List View') }) it('should record an analytics event for list view', async () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} @@ -53,7 +53,7 @@ describe('useToggleGroup', () => { }) }) it('should record an analytics event for map view', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} diff --git a/app/src/molecules/UnorderedList/index.tsx b/app/src/molecules/UnorderedList/index.tsx index cf9937266a8..f48c3e685ba 100644 --- a/app/src/molecules/UnorderedList/index.tsx +++ b/app/src/molecules/UnorderedList/index.tsx @@ -1,9 +1,10 @@ -import type * as React from 'react' import { css } from 'styled-components' import { SPACING, LegacyStyledText } from '@opentrons/components' +import type { ReactNode } from 'react' + interface UnorderedListProps { - items: React.ReactNode[] + items: ReactNode[] } export function UnorderedList(props: UnorderedListProps): JSX.Element { const { items } = props diff --git a/app/src/molecules/UpdateBanner/__tests__/UpdateBanner.test.tsx b/app/src/molecules/UpdateBanner/__tests__/UpdateBanner.test.tsx index 52b074b37d8..1f8470efcd0 100644 --- a/app/src/molecules/UpdateBanner/__tests__/UpdateBanner.test.tsx +++ b/app/src/molecules/UpdateBanner/__tests__/UpdateBanner.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { when } from 'vitest-when' @@ -9,10 +8,12 @@ import { useIsEstopNotDisengaged } from '/app/resources/devices/hooks/useIsEstop import { UpdateBanner } from '..' import { renderWithProviders } from '/app/__testing-utils__' +import type { ComponentProps } from 'react' + vi.mock('/app/redux-resources/robots') vi.mock('/app/resources/devices/hooks/useIsEstopNotDisengaged') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, initialState: { robotsByName: 'test' }, @@ -20,7 +21,7 @@ const render = (props: React.ComponentProps) => { } describe('Module Update Banner', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/WizardHeader/__tests__/WizardHeader.test.tsx b/app/src/molecules/WizardHeader/__tests__/WizardHeader.test.tsx index 0e11447cac2..436e594dd59 100644 --- a/app/src/molecules/WizardHeader/__tests__/WizardHeader.test.tsx +++ b/app/src/molecules/WizardHeader/__tests__/WizardHeader.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -8,17 +7,19 @@ import { StepMeter } from '/app/atoms/StepMeter' import { WizardHeader } from '..' import { renderWithProviders } from '/app/__testing-utils__' +import type { ComponentProps } from 'react' + vi.mock('/app/atoms/StepMeter') vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('WizardHeader', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/molecules/WizardRequiredEquipmentList/index.tsx b/app/src/molecules/WizardRequiredEquipmentList/index.tsx index f6f7457a71a..3a39d904639 100644 --- a/app/src/molecules/WizardRequiredEquipmentList/index.tsx +++ b/app/src/molecules/WizardRequiredEquipmentList/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { css } from 'styled-components' @@ -23,9 +22,10 @@ import { Divider } from '/app/atoms/structure' import { labwareImages } from '/app/local-resources/labware' import { equipmentImages } from './equipmentImages' +import type { ComponentProps } from 'react' import type { StyleProps } from '@opentrons/components' interface WizardRequiredEquipmentListProps extends StyleProps { - equipmentList: Array> + equipmentList: Array> footer?: string } export function WizardRequiredEquipmentList( diff --git a/app/src/molecules/modals/BottomButtonBar.tsx b/app/src/molecules/modals/BottomButtonBar.tsx index d5c202e943c..f7f78f067e4 100644 --- a/app/src/molecules/modals/BottomButtonBar.tsx +++ b/app/src/molecules/modals/BottomButtonBar.tsx @@ -1,18 +1,18 @@ // bottom button bar for modals // TODO(mc, 2018-08-18): maybe make this the default AlertModal behavior -import type * as React from 'react' import cx from 'classnames' import { OutlineButton } from '@opentrons/components' import styles from './styles.module.css' +import type { ReactNode } from 'react' import type { ButtonProps } from '@opentrons/components' type MaybeButtonProps = ButtonProps | null | undefined interface Props { buttons: MaybeButtonProps[] className?: string | null - description?: React.ReactNode | null + description?: ReactNode | null } export function BottomButtonBar(props: Props): JSX.Element { diff --git a/app/src/molecules/modals/ScrollableAlertModal.tsx b/app/src/molecules/modals/ScrollableAlertModal.tsx index c98846899b8..d8ebdc18543 100644 --- a/app/src/molecules/modals/ScrollableAlertModal.tsx +++ b/app/src/molecules/modals/ScrollableAlertModal.tsx @@ -1,12 +1,13 @@ // AlertModal with vertical scrolling -import type * as React from 'react' import omit from 'lodash/omit' import { AlertModal } from '@opentrons/components' import { BottomButtonBar } from './BottomButtonBar' import styles from './styles.module.css' -type Props = React.ComponentProps +import type { ComponentProps } from 'react' + +type Props = ComponentProps export function ScrollableAlertModal(props: Props): JSX.Element { return ( diff --git a/app/src/organisms/Desktop/AdvancedSettings/AdditionalCustomLabwareSourceFolder.tsx b/app/src/organisms/Desktop/AdvancedSettings/AdditionalCustomLabwareSourceFolder.tsx index 57708f2854d..75c0d411cff 100644 --- a/app/src/organisms/Desktop/AdvancedSettings/AdditionalCustomLabwareSourceFolder.tsx +++ b/app/src/organisms/Desktop/AdvancedSettings/AdditionalCustomLabwareSourceFolder.tsx @@ -8,10 +8,10 @@ import { Flex, Icon, JUSTIFY_SPACE_BETWEEN, + LegacyStyledText, Link, SPACING_AUTO, SPACING, - LegacyStyledText, TYPOGRAPHY, } from '@opentrons/components' diff --git a/app/src/organisms/Desktop/AdvancedSettings/OT2AdvancedSettings.tsx b/app/src/organisms/Desktop/AdvancedSettings/OT2AdvancedSettings.tsx index 7ab3ebb0bd0..3ff3313a840 100644 --- a/app/src/organisms/Desktop/AdvancedSettings/OT2AdvancedSettings.tsx +++ b/app/src/organisms/Desktop/AdvancedSettings/OT2AdvancedSettings.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useSelector, useDispatch } from 'react-redux' import { useTranslation } from 'react-i18next' import { css } from 'styled-components' @@ -17,6 +16,7 @@ import { } from '/app/redux/calibration' import { getUseTrashSurfaceForTipCal } from '/app/redux/config' +import type { ChangeEvent } from 'react' import type { Dispatch, State } from '/app/redux/types' const ALWAYS_BLOCK: 'always-block' = 'always-block' @@ -74,7 +74,7 @@ export function OT2AdvancedSettings(): JSX.Element { ? ALWAYS_BLOCK : ALWAYS_PROMPT } - onChange={(event: React.ChangeEvent) => { + onChange={(event: ChangeEvent) => { // you know this is a limited-selection field whose values are only // the elements of BlockSelection; i know this is a limited-selection // field whose values are only the elements of BlockSelection; but sadly, diff --git a/app/src/organisms/Desktop/AdvancedSettings/OverridePathToPython.tsx b/app/src/organisms/Desktop/AdvancedSettings/OverridePathToPython.tsx index d0e5b7d6d93..a87d739172f 100644 --- a/app/src/organisms/Desktop/AdvancedSettings/OverridePathToPython.tsx +++ b/app/src/organisms/Desktop/AdvancedSettings/OverridePathToPython.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { useSelector, useDispatch } from 'react-redux' @@ -27,6 +26,7 @@ import { ANALYTICS_CHANGE_PATH_TO_PYTHON_DIRECTORY, } from '/app/redux/analytics' +import type { MouseEventHandler } from 'react' import type { Dispatch } from '/app/redux/types' export function OverridePathToPython(): JSX.Element { @@ -35,7 +35,7 @@ export function OverridePathToPython(): JSX.Element { const dispatch = useDispatch() const trackEvent = useTrackEvent() - const handleClickPythonDirectoryChange: React.MouseEventHandler = _event => { + const handleClickPythonDirectoryChange: MouseEventHandler = _event => { dispatch(changePythonPathOverrideConfig()) trackEvent({ name: ANALYTICS_CHANGE_PATH_TO_PYTHON_DIRECTORY, diff --git a/app/src/organisms/Desktop/AdvancedSettings/UpdatedChannel.tsx b/app/src/organisms/Desktop/AdvancedSettings/UpdatedChannel.tsx index aca0348cb7b..ec261c75083 100644 --- a/app/src/organisms/Desktop/AdvancedSettings/UpdatedChannel.tsx +++ b/app/src/organisms/Desktop/AdvancedSettings/UpdatedChannel.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' @@ -19,6 +18,7 @@ import { updateConfigValue, } from '/app/redux/config' +import type { ComponentProps } from 'react' import type { SelectOption } from '/app/atoms/SelectField/Select' import type { Dispatch } from '/app/redux/types' @@ -31,7 +31,7 @@ export function UpdatedChannel(): JSX.Element { dispatch(updateConfigValue('update.channel', value)) } - const formatOptionLabel: React.ComponentProps< + const formatOptionLabel: ComponentProps< typeof SelectField >['formatOptionLabel'] = (option, index): JSX.Element => { const { label, value } = option diff --git a/app/src/organisms/Desktop/Alerts/U2EDriverOutdatedAlert.tsx b/app/src/organisms/Desktop/Alerts/U2EDriverOutdatedAlert.tsx index fbb79a6b935..a327fce6a31 100644 --- a/app/src/organisms/Desktop/Alerts/U2EDriverOutdatedAlert.tsx +++ b/app/src/organisms/Desktop/Alerts/U2EDriverOutdatedAlert.tsx @@ -1,5 +1,6 @@ import { Link as InternalLink } from 'react-router-dom' import styled from 'styled-components' +import { useTranslation } from 'react-i18next' import { AlertModal, @@ -12,20 +13,9 @@ import { ANALYTICS_U2E_DRIVE_ALERT_DISMISSED, ANALYTICS_U2E_DRIVE_LINK_CLICKED, } from '/app/redux/analytics' -import { - U2E_DRIVER_UPDATE_URL, - U2E_DRIVER_OUTDATED_MESSAGE, - U2E_DRIVER_DESCRIPTION, - U2E_DRIVER_OUTDATED_CTA, -} from '/app/redux/system-info' +import { U2E_DRIVER_UPDATE_URL } from '/app/redux/system-info' import type { AlertProps } from './types' -// TODO(mc, 2020-05-07): i18n -const DRIVER_OUT_OF_DATE = 'Realtek USB-to-Ethernet Driver Update Available' -const VIEW_ADAPTER_INFO = 'view adapter info' -const GET_UPDATE = 'get update' -const DONT_REMIND_ME_AGAIN = "Don't remind me again" - const ADAPTER_INFO_URL = '/more/network-and-system' const LinkButton = styled(Link)` @@ -42,19 +32,20 @@ const IgnoreCheckbox = styled(DeprecatedCheckboxField)` export function U2EDriverOutdatedAlert(props: AlertProps): JSX.Element { const trackEvent = useTrackEvent() + const { t } = useTranslation(['app_settings', 'branded']) const [rememberDismiss, toggleRememberDismiss] = useToggle() const { dismissAlert } = props return ( { dismissAlert(rememberDismiss) trackEvent({ @@ -67,7 +58,7 @@ export function U2EDriverOutdatedAlert(props: AlertProps): JSX.Element { Component: LinkButton, href: U2E_DRIVER_UPDATE_URL, external: true, - children: GET_UPDATE, + children: t('get_update'), onClick: () => { dismissAlert(rememberDismiss) trackEvent({ @@ -79,11 +70,11 @@ export function U2EDriverOutdatedAlert(props: AlertProps): JSX.Element { ]} >

        - {U2E_DRIVER_OUTDATED_MESSAGE} {U2E_DRIVER_DESCRIPTION} + {t('u2e_driver_outdated_message')} {t('branded:u2e_driver_description')}

        -

        {U2E_DRIVER_OUTDATED_CTA}

        +

        {t('please_update_driver')}

        diff --git a/app/src/organisms/Desktop/AppSettings/__tests__/ConnectRobotSlideout.test.tsx b/app/src/organisms/Desktop/AppSettings/__tests__/ConnectRobotSlideout.test.tsx index c92e1f495a0..4cddd3e1763 100644 --- a/app/src/organisms/Desktop/AppSettings/__tests__/ConnectRobotSlideout.test.tsx +++ b/app/src/organisms/Desktop/AppSettings/__tests__/ConnectRobotSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -8,17 +7,19 @@ import { getConfig } from '/app/redux/config' import { renderWithProviders } from '/app/__testing-utils__' import { ConnectRobotSlideout } from '../ConnectRobotSlideout' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/discovery') vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ConnectRobotSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(getScanning).mockReturnValue(true) @@ -54,7 +55,7 @@ describe('ConnectRobotSlideout', () => { checkIpAndHostname: vi.fn(), isExpanded: true, onCloseClick: vi.fn(), - } as React.ComponentProps + } as ComponentProps }) it('renders correct title, body, and footer for ConnectRobotSlideout', () => { diff --git a/app/src/organisms/Desktop/AppSettings/__tests__/PreviousVersionModal.test.tsx b/app/src/organisms/Desktop/AppSettings/__tests__/PreviousVersionModal.test.tsx index b4449623c05..59eff47aa2c 100644 --- a/app/src/organisms/Desktop/AppSettings/__tests__/PreviousVersionModal.test.tsx +++ b/app/src/organisms/Desktop/AppSettings/__tests__/PreviousVersionModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -10,12 +9,14 @@ import { PREVIOUS_RELEASES_URL, } from '../PreviousVersionModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } -const props: React.ComponentProps = { +const props: ComponentProps = { closeModal: vi.fn(), } diff --git a/app/src/organisms/Desktop/CalibrateDeck/__tests__/CalibrateDeck.test.tsx b/app/src/organisms/Desktop/CalibrateDeck/__tests__/CalibrateDeck.test.tsx index 3f90064c03c..13415c8e47c 100644 --- a/app/src/organisms/Desktop/CalibrateDeck/__tests__/CalibrateDeck.test.tsx +++ b/app/src/organisms/Desktop/CalibrateDeck/__tests__/CalibrateDeck.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, beforeEach, expect, it } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -14,6 +13,7 @@ import { CalibrationError, } from '/app/organisms/Desktop/CalibrationError' +import type { ComponentProps, ComponentType } from 'react' import type { DeckCalibrationStep } from '/app/redux/sessions/types' import type { DispatchRequestsType } from '/app/redux/robot-api' @@ -41,14 +41,14 @@ describe('CalibrateDeck', () => { ...mockDeckCalibrationSessionAttributes, } const render = ( - props: Partial> = {} + props: Partial> = {} ) => { const { showSpinner = false, isJogging = false, session = mockDeckCalSession, } = props - return renderWithProviders>( + return renderWithProviders>( { const actual = await importOriginal() @@ -35,16 +36,14 @@ interface CalibratePipetteOffsetSpec { describe('CalibratePipetteOffset', () => { let dispatchRequests: DispatchRequestsType const render = ( - props: Partial> = {} + props: Partial> = {} ) => { const { showSpinner = false, isJogging = false, session = mockPipOffsetCalSession, } = props - return renderWithProviders< - React.ComponentType - >( + return renderWithProviders>( { const onResponse = vi.fn() const render = () => { return renderWithProviders< - React.ComponentProps + ComponentProps >( { @@ -41,14 +41,14 @@ describe('CalibrateTipLength', () => { ...mockTipLengthCalibrationSessionAttributes, } const render = ( - props: Partial> = {} + props: Partial> = {} ) => { const { showSpinner = false, isJogging = false, session = mockTipLengthSession, } = props - return renderWithProviders>( + return renderWithProviders>( { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/CalibrationPanels/CompleteConfirmation.tsx b/app/src/organisms/Desktop/CalibrationPanels/CompleteConfirmation.tsx index cbd0e214cd1..3f6de1fed33 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/CompleteConfirmation.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/CompleteConfirmation.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { ALIGN_CENTER, @@ -16,11 +15,13 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import type { MouseEventHandler, ReactNode } from 'react' + interface CompleteConfirmationProps { - proceed: React.MouseEventHandler + proceed: MouseEventHandler flowName?: string body?: string - visualAid?: React.ReactNode + visualAid?: ReactNode } export function CompleteConfirmation( diff --git a/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Body.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Body.test.tsx index c1300fb3218..26682dff0cc 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Body.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Body.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { it, describe } from 'vitest' import { screen } from '@testing-library/react' @@ -8,7 +7,9 @@ import * as Sessions from '/app/redux/sessions' import { i18n } from '/app/i18n' import { Body } from '../Body' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Introduction.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Introduction.test.tsx index a34460571ed..4005f6c996c 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Introduction.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/Introduction/__tests__/Introduction.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -9,6 +8,8 @@ import { i18n } from '/app/i18n' import { Introduction } from '../' import { ChooseTipRack } from '../../ChooseTipRack' +import type { ComponentProps } from 'react' + vi.mock('../../ChooseTipRack') const mockCalInvalidationHandler = vi.fn() @@ -17,9 +18,7 @@ describe('Introduction', () => { const mockSendCommands = vi.fn() const mockCleanUpAndExit = vi.fn() - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { return renderWithProviders( ) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ChooseTipRack', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(Select).mockReturnValue(
        mock select
        ) diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/ChosenTipRackRender.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/ChosenTipRackRender.test.tsx index 5eb7fa1db8b..27bc9a7d233 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/ChosenTipRackRender.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/ChosenTipRackRender.test.tsx @@ -1,13 +1,14 @@ -import type * as React from 'react' import { it, describe, beforeEach } from 'vitest' import { screen } from '@testing-library/react' import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { ChosenTipRackRender } from '../ChosenTipRackRender' + +import type { ComponentProps } from 'react' import type { SelectOption } from '/app/atoms/SelectField/Select' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -19,7 +20,7 @@ const mockSelectValue = { } as SelectOption describe('ChosenTipRackRender', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { selectedValue: mockSelectValue, diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/CompleteConfirmation.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/CompleteConfirmation.test.tsx index 99dc73310d1..8dd9575a2c3 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/CompleteConfirmation.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/CompleteConfirmation.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -7,10 +6,12 @@ import { i18n } from '/app/i18n' import { CompleteConfirmation } from '../CompleteConfirmation' +import type { ComponentProps } from 'react' + describe('CompleteConfirmation', () => { const mockCleanUpAndExit = vi.fn() const render = ( - props: Partial> = {} + props: Partial> = {} ) => { const { proceed = mockCleanUpAndExit, flowName, body, visualAid } = props return renderWithProviders( diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/ConfirmCrashRecovery.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/ConfirmCrashRecovery.test.tsx index 53fb8559e55..11581f72c55 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/ConfirmCrashRecovery.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/ConfirmCrashRecovery.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -6,11 +5,13 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConfirmCrashRecovery } from '../ConfirmCrashRecovery' +import type { ComponentProps } from 'react' + describe('ConfirmCrashRecovery', () => { const mockBack = vi.fn() const mockConfirm = vi.fn() const render = ( - props: Partial> = {} + props: Partial> = {} ) => { const { back = mockBack, confirm = mockConfirm } = props return renderWithProviders( diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/ConfirmExit.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/ConfirmExit.test.tsx index 086c122e8bf..c09bf0dcd84 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/ConfirmExit.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/ConfirmExit.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -7,12 +6,12 @@ import { i18n } from '/app/i18n' import { ConfirmExit } from '../ConfirmExit' +import type { ComponentProps } from 'react' + describe('ConfirmExit', () => { const mockBack = vi.fn() const mockExit = vi.fn() - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { const { heading, body } = props return renderWithProviders( { const mockSendCommands = vi.fn() const mockDeleteSession = vi.fn() - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { const { mount = 'left', isMulti = false, diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/MeasureNozzle.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/MeasureNozzle.test.tsx index d0de251b0ad..e95c612ec01 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/MeasureNozzle.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/MeasureNozzle.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -11,11 +10,13 @@ import { import * as Sessions from '/app/redux/sessions' import { MeasureNozzle } from '../MeasureNozzle' +import type { ComponentProps } from 'react' + describe('MeasureNozzle', () => { const mockSendCommands = vi.fn() const mockDeleteSession = vi.fn() const render = ( - props: Partial> = {} + props: Partial> = {} ) => { const { mount = 'left', diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/MeasureTip.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/MeasureTip.test.tsx index 70d8ab54a6b..6f37d74fca5 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/MeasureTip.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/MeasureTip.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -12,12 +11,12 @@ import * as Sessions from '/app/redux/sessions' import { MeasureTip } from '../MeasureTip' +import type { ComponentProps } from 'react' + describe('MeasureTip', () => { const mockSendCommands = vi.fn() const mockDeleteSession = vi.fn() - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { const { mount = 'left', isMulti = false, diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/SaveXYPoint.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/SaveXYPoint.test.tsx index 3d7dbda32a9..1965c0b1a14 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/SaveXYPoint.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/SaveXYPoint.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -9,12 +8,12 @@ import { mockDeckCalTipRack } from '/app/redux/sessions/__fixtures__' import * as Sessions from '/app/redux/sessions' import { SaveXYPoint } from '../SaveXYPoint' +import type { ComponentProps } from 'react' + describe('SaveXYPoint', () => { const mockSendCommands = vi.fn() const mockDeleteSession = vi.fn() - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { const { mount = 'left', isMulti = false, diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/SaveZPoint.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/SaveZPoint.test.tsx index 36f04c1ebb2..dab0a5ee2b4 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/SaveZPoint.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/SaveZPoint.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -12,13 +11,13 @@ import { import * as Sessions from '/app/redux/sessions' import { SaveZPoint } from '../SaveZPoint' +import type { ComponentProps } from 'react' + describe('SaveZPoint', () => { const mockSendCommands = vi.fn() const mockDeleteSession = vi.fn() - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { const { mount = 'left', isMulti = false, diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/TipConfirmation.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/TipConfirmation.test.tsx index f9eee22d78b..979db72ecb0 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/TipConfirmation.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/TipConfirmation.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -8,11 +7,13 @@ import { mockDeckCalTipRack } from '/app/redux/sessions/__fixtures__' import * as Sessions from '/app/redux/sessions' import { TipConfirmation } from '../TipConfirmation' +import type { ComponentProps } from 'react' + describe('TipConfirmation', () => { const mockSendCommands = vi.fn() const mockDeleteSession = vi.fn() const render = ( - props: Partial> = {} + props: Partial> = {} ) => { const { mount = 'left', diff --git a/app/src/organisms/Desktop/CalibrationPanels/__tests__/TipPickUp.test.tsx b/app/src/organisms/Desktop/CalibrationPanels/__tests__/TipPickUp.test.tsx index b6836cf96fb..6b8aac3fe88 100644 --- a/app/src/organisms/Desktop/CalibrationPanels/__tests__/TipPickUp.test.tsx +++ b/app/src/organisms/Desktop/CalibrationPanels/__tests__/TipPickUp.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -8,12 +7,12 @@ import { mockDeckCalTipRack } from '/app/redux/sessions/__fixtures__' import * as Sessions from '/app/redux/sessions' import { TipPickUp } from '../TipPickUp' +import type { ComponentProps } from 'react' + describe('TipPickUp', () => { const mockSendCommands = vi.fn() const mockDeleteSession = vi.fn() - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { const { mount = 'left', isMulti = false, diff --git a/app/src/organisms/Desktop/CalibrationStatusCard/__tests__/CalibrationStatusCard.test.tsx b/app/src/organisms/Desktop/CalibrationStatusCard/__tests__/CalibrationStatusCard.test.tsx index 400e73b3078..3a3e09c4005 100644 --- a/app/src/organisms/Desktop/CalibrationStatusCard/__tests__/CalibrationStatusCard.test.tsx +++ b/app/src/organisms/Desktop/CalibrationStatusCard/__tests__/CalibrationStatusCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -18,9 +17,11 @@ import { expectedTaskList, } from '../../Devices/hooks/__fixtures__/taskListFixtures' +import type { ComponentProps } from 'react' + vi.mock('../../Devices/hooks') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -38,7 +39,7 @@ describe('CalibrationStatusCard', () => { vi.mocked(useCalibrationTaskList).mockReturnValue(expectedTaskList) }) - const props: React.ComponentProps = { + const props: ComponentProps = { robotName: 'otie', setShowHowCalibrationWorksModal: mockSetShowHowCalibrationWorksModal, } diff --git a/app/src/organisms/Desktop/CalibrationTaskList/__tests__/CalibrationTaskList.test.tsx b/app/src/organisms/Desktop/CalibrationTaskList/__tests__/CalibrationTaskList.test.tsx index ebada58f1f3..884bf35020c 100644 --- a/app/src/organisms/Desktop/CalibrationTaskList/__tests__/CalibrationTaskList.test.tsx +++ b/app/src/organisms/Desktop/CalibrationTaskList/__tests__/CalibrationTaskList.test.tsx @@ -82,7 +82,7 @@ describe('CalibrationTaskList', () => { rerender( { rerender( { rerender( { rerender( { rerender( { rerender( { rerender( { rerender( { rerender( + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -17,7 +18,7 @@ const render = ( } describe('CalibrationHealthCheckResults', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { isCalibrationRecommended: false, diff --git a/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/CalibrationResult.test.tsx b/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/CalibrationResult.test.tsx index 70432556944..c4d6bd4e67d 100644 --- a/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/CalibrationResult.test.tsx +++ b/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/CalibrationResult.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -7,16 +6,18 @@ import { i18n } from '/app/i18n' import { RenderResult } from '../RenderResult' import { CalibrationResult } from '../CalibrationResult' +import type { ComponentProps } from 'react' + vi.mock('../RenderResult') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('PipetteCalibrationResult', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/RenderMountInformation.test.tsx b/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/RenderMountInformation.test.tsx index bae133b6522..3a5fc13d0c1 100644 --- a/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/RenderMountInformation.test.tsx +++ b/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/RenderMountInformation.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -10,6 +9,8 @@ import { LEFT, RIGHT } from '/app/redux/pipettes' import * as Fixtures from '/app/redux/sessions/__fixtures__' import { RenderMountInformation } from '../RenderMountInformation' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/shared-data', async importOriginal => { const actual = await importOriginal() return { @@ -20,14 +21,14 @@ vi.mock('@opentrons/shared-data', async importOriginal => { const mockSessionDetails = Fixtures.mockRobotCalibrationCheckSessionDetails -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('RenderMountInformation', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/RenderResult.test.tsx b/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/RenderResult.test.tsx index 90ed47fc126..7b60f864218 100644 --- a/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/RenderResult.test.tsx +++ b/app/src/organisms/Desktop/CheckCalibration/ResultsSummary/__tests__/RenderResult.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { it, describe, expect, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -9,14 +8,16 @@ import { i18n } from '/app/i18n' import { RenderResult } from '../RenderResult' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('RenderResult', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/CheckCalibration/__tests__/CheckCalibration.test.tsx b/app/src/organisms/Desktop/CheckCalibration/__tests__/CheckCalibration.test.tsx index d5f5a9814d7..853716d3d46 100644 --- a/app/src/organisms/Desktop/CheckCalibration/__tests__/CheckCalibration.test.tsx +++ b/app/src/organisms/Desktop/CheckCalibration/__tests__/CheckCalibration.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { when } from 'vitest-when' import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest' @@ -11,6 +10,8 @@ import * as Sessions from '/app/redux/sessions' import { mockCalibrationCheckSessionAttributes } from '/app/redux/sessions/__fixtures__' import { CheckCalibration } from '../index' + +import type { ComponentProps, ComponentType } from 'react' import type { RobotCalibrationCheckStep } from '/app/redux/sessions/types' vi.mock('/app/redux/calibration/selectors') @@ -36,14 +37,14 @@ describe('CheckCalibration', () => { } const render = ( - props: Partial> = {} + props: Partial> = {} ) => { const { showSpinner = false, isJogging = false, session = mockCalibrationCheckSession, } = props - return renderWithProviders>( + return renderWithProviders>( ) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/ChooseProtocolSlideout/index.tsx b/app/src/organisms/Desktop/ChooseProtocolSlideout/index.tsx index 8e762ed798d..9368fb1b697 100644 --- a/app/src/organisms/Desktop/ChooseProtocolSlideout/index.tsx +++ b/app/src/organisms/Desktop/ChooseProtocolSlideout/index.tsx @@ -492,7 +492,7 @@ export function ChooseProtocolSlideoutComponent( ), }} diff --git a/app/src/organisms/Desktop/ChooseRobotSlideout/__tests__/ChooseRobotSlideout.test.tsx b/app/src/organisms/Desktop/ChooseRobotSlideout/__tests__/ChooseRobotSlideout.test.tsx index 998c82462bc..33f601165d2 100644 --- a/app/src/organisms/Desktop/ChooseRobotSlideout/__tests__/ChooseRobotSlideout.test.tsx +++ b/app/src/organisms/Desktop/ChooseRobotSlideout/__tests__/ChooseRobotSlideout.test.tsx @@ -1,6 +1,4 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' - import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { OT2_ROBOT_TYPE } from '@opentrons/shared-data' @@ -22,13 +20,15 @@ import { import { getNetworkInterfaces } from '/app/redux/networking' import { ChooseRobotSlideout } from '..' import { useNotifyDataReady } from '/app/resources/useNotifyDataReady' + +import type { ComponentProps } from 'react' import type { RunTimeParameter } from '@opentrons/shared-data' vi.mock('/app/redux/discovery') vi.mock('/app/redux/robot-update') vi.mock('/app/redux/networking') vi.mock('/app/resources/useNotifyDataReady') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/ChooseRobotSlideout/__tests__/FileCard.test.tsx b/app/src/organisms/Desktop/ChooseRobotSlideout/__tests__/FileCard.test.tsx index 9a151cd1704..9a150141526 100644 --- a/app/src/organisms/Desktop/ChooseRobotSlideout/__tests__/FileCard.test.tsx +++ b/app/src/organisms/Desktop/ChooseRobotSlideout/__tests__/FileCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect } from 'vitest' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' @@ -7,6 +6,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { FileCard } from '../FileCard' +import type { ComponentProps } from 'react' import type { CsvFileParameter } from '@opentrons/shared-data' vi.mock('/app/redux/discovery') @@ -15,7 +15,7 @@ vi.mock('/app/redux/networking') vi.mock('/app/resources/useNotifyDataReady') vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx b/app/src/organisms/Desktop/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx index 3c6416a5290..19e3d39c370 100644 --- a/app/src/organisms/Desktop/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx +++ b/app/src/organisms/Desktop/ChooseRobotToRunProtocolSlideout/__tests__/ChooseRobotToRunProtocolSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen, waitFor } from '@testing-library/react' @@ -32,6 +31,7 @@ import { ChooseRobotToRunProtocolSlideout } from '../' import { useNotifyDataReady } from '/app/resources/useNotifyDataReady' import { useCurrentRunId, useCloseCurrentRun } from '/app/resources/runs' +import type { ComponentProps } from 'react' import type { State } from '/app/redux/types' vi.mock('/app/organisms/Desktop/Devices/hooks') @@ -49,7 +49,7 @@ vi.mock('/app/resources/useNotifyDataReady') vi.mock('/app/resources/runs') const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/ConfirmPipette.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/ConfirmPipette.tsx index f756059d003..6f6585cefe4 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/ConfirmPipette.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/ConfirmPipette.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { OT2_ROBOT_TYPE } from '@opentrons/shared-data' import { @@ -13,6 +12,7 @@ import { CheckPipettesButton } from './CheckPipettesButton' import { SimpleWizardBody } from '/app/molecules/SimpleWizardBody' import { LevelPipette } from './LevelPipette' +import type { Dispatch, SetStateAction } from 'react' import type { PipetteNameSpecs, PipetteModelSpecs, @@ -36,11 +36,9 @@ export interface ConfirmPipetteProps { // wrongWantedPipette is referring to if the user attaches a pipette that is different // from wantedPipette and they want to use it anyway wrongWantedPipette: PipetteNameSpecs | null - setWrongWantedPipette: React.Dispatch< - React.SetStateAction - > + setWrongWantedPipette: Dispatch> confirmPipetteLevel: boolean - setConfirmPipetteLevel: React.Dispatch> + setConfirmPipetteLevel: Dispatch> tryAgain: () => void exit: () => void nextStep: () => void diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/InstructionStep.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/InstructionStep.tsx index 5b6338be6a5..94d617f2ea4 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/InstructionStep.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/InstructionStep.tsx @@ -1,14 +1,15 @@ -import type * as React from 'react' import { Box, Flex, JUSTIFY_SPACE_EVENLY, SPACING } from '@opentrons/components' import type { PipetteChannels, PipetteDisplayCategory, } from '@opentrons/shared-data' + +import type { ReactNode } from 'react' import type { Mount } from '@opentrons/components' import type { Diagram, Direction } from './types' interface Props { - children: React.ReactNode + children: ReactNode direction: Direction mount: Mount channels: PipetteChannels diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/PipetteSelection.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/PipetteSelection.tsx index c306f132154..f998887afbd 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/PipetteSelection.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/PipetteSelection.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { DIRECTION_COLUMN, @@ -10,7 +9,9 @@ import { import { OT3_PIPETTES } from '@opentrons/shared-data' import { PipetteSelect } from '/app/molecules/PipetteSelect' -export type PipetteSelectionProps = React.ComponentProps +import type { ComponentProps } from 'react' + +export type PipetteSelectionProps = ComponentProps export function PipetteSelection(props: PipetteSelectionProps): JSX.Element { const { t } = useTranslation('change_pipette') diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ChangePipette.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ChangePipette.test.tsx index 89112a484d4..6cedab5a47b 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ChangePipette.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ChangePipette.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -21,6 +20,7 @@ import { ExitModal } from '../ExitModal' import { ConfirmPipette } from '../ConfirmPipette' import { ChangePipette } from '..' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' import type { PipetteNameSpecs } from '@opentrons/shared-data' import type { AttachedPipette } from '/app/redux/pipettes/types' @@ -54,7 +54,7 @@ vi.mock('../ConfirmPipette') vi.mock('/app/resources/instruments') vi.mock('/app/assets/images') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -78,7 +78,7 @@ const mockAttachedPipettes = { } describe('ChangePipette', () => { - let props: React.ComponentProps + let props: ComponentProps let dispatchApiRequest: DispatchApiRequestType beforeEach(() => { diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/CheckPipettesButton.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/CheckPipettesButton.test.tsx index a5fa3a50bd6..ce6f7f2db25 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/CheckPipettesButton.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/CheckPipettesButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect, beforeEach } from 'vitest' @@ -8,16 +7,18 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { CheckPipettesButton } from '../CheckPipettesButton' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('CheckPipettesButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotName: 'otie', diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ClearDeckModal.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ClearDeckModal.test.tsx index f4af4566141..85ae7a52495 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ClearDeckModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ClearDeckModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect, beforeEach } from 'vitest' @@ -6,13 +5,15 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ClearDeckModal } from '../ClearDeckModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ClearDeckModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onContinueClick: vi.fn(), diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ConfirmPipette.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ConfirmPipette.test.tsx index a328df38383..ebb06f3dc91 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ConfirmPipette.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ConfirmPipette.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect } from 'vitest' @@ -9,6 +8,7 @@ import { mockPipetteInfo } from '/app/redux/pipettes/__fixtures__' import { CheckPipettesButton } from '../CheckPipettesButton' import { ConfirmPipette } from '../ConfirmPipette' +import type { ComponentProps } from 'react' import type { PipetteModelSpecs, PipetteNameSpecs, @@ -25,7 +25,7 @@ vi.mock('../LevelPipette', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -87,7 +87,7 @@ const MOCK_WANTED_PIPETTE = { } as PipetteNameSpecs describe('ConfirmPipette', () => { - let props: React.ComponentProps + let props: ComponentProps it('Should detach a pipette successfully', () => { props = { diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ExitModal.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ExitModal.test.tsx index 29bf417071c..0b4155ebd09 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ExitModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/ExitModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect, beforeEach } from 'vitest' @@ -6,13 +5,15 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ExitModal } from '../ExitModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ExitModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { back: vi.fn(), diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/InstructionStep.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/InstructionStep.test.tsx index 80cdde63972..39e53564b68 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/InstructionStep.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/InstructionStep.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { it, describe, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -8,13 +7,15 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { InstructionStep } from '../InstructionStep' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('InstructionStep', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { children:
        children
        , diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/Instructions.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/Instructions.test.tsx index 024be58e798..4f01b748286 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/Instructions.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/Instructions.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -12,9 +11,11 @@ import { mockPipetteInfo } from '/app/redux/pipettes/__fixtures__' import { Instructions } from '../Instructions' import { CheckPipettesButton } from '../CheckPipettesButton' +import type { ComponentProps } from 'react' + vi.mock('../CheckPipettesButton') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -29,7 +30,7 @@ const MOCK_ACTUAL_PIPETTE = { } as PipetteModelSpecs describe('Instructions', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/LevelPipette.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/LevelPipette.test.tsx index 78c2e9f8d6d..3b74cf10ea1 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/LevelPipette.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/LevelPipette.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -6,9 +5,11 @@ import { LEFT } from '@opentrons/shared-data' import { nestedTextMatcher, renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { LevelPipette } from '../LevelPipette' + +import type { ComponentProps } from 'react' import type { PipetteNameSpecs } from '@opentrons/shared-data' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -57,7 +58,7 @@ const MOCK_WANTED_PIPETTE = { } as PipetteNameSpecs describe('LevelPipette', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/PipetteSelection.test.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/PipetteSelection.test.tsx index f3619e9930d..9f507dc8a36 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/PipetteSelection.test.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/__tests__/PipetteSelection.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -7,15 +6,17 @@ import { i18n } from '/app/i18n' import { PipetteSelect } from '/app/molecules/PipetteSelect' import { PipetteSelection } from '../PipetteSelection' +import type { ComponentProps } from 'react' + vi.mock('/app/molecules/PipetteSelect') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('PipetteSelection', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { pipetteName: null, diff --git a/app/src/organisms/Desktop/Devices/ConfigurePipette/ConfigFormGroup.tsx b/app/src/organisms/Desktop/Devices/ConfigurePipette/ConfigFormGroup.tsx index 9c8a2c25acf..e006efe1c5c 100644 --- a/app/src/organisms/Desktop/Devices/ConfigurePipette/ConfigFormGroup.tsx +++ b/app/src/organisms/Desktop/Devices/ConfigurePipette/ConfigFormGroup.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Controller } from 'react-hook-form' import { CheckboxField, @@ -12,11 +11,12 @@ import { } from '@opentrons/components' import styles from './styles.module.css' +import type { ReactNode } from 'react' import type { Control } from 'react-hook-form' import type { DisplayFieldProps, DisplayQuirkFieldProps } from './ConfigForm' export interface FormColumnProps { - children: React.ReactNode + children: ReactNode } export function FormColumn(props: FormColumnProps): JSX.Element { @@ -65,7 +65,7 @@ export function ConfigFormGroup(props: ConfigFormGroupProps): JSX.Element { export interface ConfigFormRowProps { label: string labelFor: string - children: React.ReactNode + children: ReactNode } const FIELD_ID_PREFIX = '__PipetteConfig__' diff --git a/app/src/organisms/Desktop/Devices/ConfigurePipette/ConfigMessage.tsx b/app/src/organisms/Desktop/Devices/ConfigurePipette/ConfigMessage.tsx deleted file mode 100644 index d6a32fa6d4b..00000000000 --- a/app/src/organisms/Desktop/Devices/ConfigurePipette/ConfigMessage.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import styles from './styles.module.css' - -// TODO (ka 2019-2-12): Add intercom onClick to assistance text -export function ConfigMessage(): JSX.Element { - return ( -
        -

        Warning:

        -

        - These are advanced settings. Please do not attempt to adjust without - assistance from an Opentrons support team member, as doing - so may affect the lifespan of your pipette. -

        -

        - Note that these settings will not override any pipette settings - pre-defined in protocols. -

        -
        - ) -} diff --git a/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigFormResetButton.test.tsx b/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigFormResetButton.test.tsx index 4c2ec08c7d4..7e26987f573 100644 --- a/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigFormResetButton.test.tsx +++ b/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigFormResetButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, expect, describe, beforeEach } from 'vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConfigFormResetButton } from '../ConfigFormResetButton' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ConfigFormResetButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onClick: vi.fn(), diff --git a/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigFormSubmitButton.test.tsx b/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigFormSubmitButton.test.tsx index 3dbab681883..73db2ec1596 100644 --- a/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigFormSubmitButton.test.tsx +++ b/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigFormSubmitButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { it, expect, describe, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConfigFormSubmitButton } from '../ConfigFormSubmitButton' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ConfigFormSubmitButton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { disabled: false, diff --git a/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigurePipette.test.tsx b/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigurePipette.test.tsx index 2d7790bdd24..5f810b3b97c 100644 --- a/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigurePipette.test.tsx +++ b/app/src/organisms/Desktop/Devices/ConfigurePipette/__tests__/ConfigurePipette.test.tsx @@ -1,6 +1,6 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { vi, it, expect, describe, beforeEach } from 'vitest' +import { screen } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' @@ -9,14 +9,14 @@ import { ConfigurePipette } from '../../ConfigurePipette' import { mockPipetteSettingsFieldsMap } from '/app/redux/pipettes/__fixtures__' import { getConfig } from '/app/redux/config' +import type { ComponentProps } from 'react' import type { DispatchApiRequestType } from '/app/redux/robot-api' import type { State } from '/app/redux/types' -import { screen } from '@testing-library/react' vi.mock('/app/redux/robot-api') vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -26,7 +26,7 @@ const mockRobotName = 'mockRobotName' describe('ConfigurePipette', () => { let dispatchApiRequest: DispatchApiRequestType - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ErrorRecoveryBanner/__tests__/ErrorRecoveryBanner.test.tsx b/app/src/organisms/Desktop/Devices/ErrorRecoveryBanner/__tests__/ErrorRecoveryBanner.test.tsx index fc234a52629..58a79593fe6 100644 --- a/app/src/organisms/Desktop/Devices/ErrorRecoveryBanner/__tests__/ErrorRecoveryBanner.test.tsx +++ b/app/src/organisms/Desktop/Devices/ErrorRecoveryBanner/__tests__/ErrorRecoveryBanner.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -6,6 +5,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useErrorRecoveryBanner, ErrorRecoveryBanner } from '..' +import type { ComponentProps } from 'react' + vi.mock('..', async importOriginal => { const actualReact = await importOriginal() return { @@ -14,7 +15,7 @@ vi.mock('..', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/Desktop/Devices/GripperCard/__tests__/AboutGripperSlideout.test.tsx b/app/src/organisms/Desktop/Devices/GripperCard/__tests__/AboutGripperSlideout.test.tsx index 9ad4381a40c..373ba5256af 100644 --- a/app/src/organisms/Desktop/Devices/GripperCard/__tests__/AboutGripperSlideout.test.tsx +++ b/app/src/organisms/Desktop/Devices/GripperCard/__tests__/AboutGripperSlideout.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { screen, fireEvent } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { AboutGripperSlideout } from '../AboutGripperSlideout' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('AboutGripperSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { serialNumber: '123', diff --git a/app/src/organisms/Desktop/Devices/GripperCard/__tests__/GripperCard.test.tsx b/app/src/organisms/Desktop/Devices/GripperCard/__tests__/GripperCard.test.tsx index ccdbc80c2b3..32538ba2cb5 100644 --- a/app/src/organisms/Desktop/Devices/GripperCard/__tests__/GripperCard.test.tsx +++ b/app/src/organisms/Desktop/Devices/GripperCard/__tests__/GripperCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -7,20 +6,22 @@ import { i18n } from '/app/i18n' import { GripperWizardFlows } from '/app/organisms/GripperWizardFlows' import { AboutGripperSlideout } from '../AboutGripperSlideout' import { GripperCard } from '../' + +import type { ComponentProps } from 'react' import type { GripperData } from '@opentrons/api-client' vi.mock('/app/organisms/GripperWizardFlows') vi.mock('../AboutGripperSlideout') vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('GripperCard', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { attachedGripper: { diff --git a/app/src/organisms/Desktop/Devices/HistoricalProtocolRunOverflowMenu.tsx b/app/src/organisms/Desktop/Devices/HistoricalProtocolRunOverflowMenu.tsx index 18dc44cb11b..b696d8285cb 100644 --- a/app/src/organisms/Desktop/Devices/HistoricalProtocolRunOverflowMenu.tsx +++ b/app/src/organisms/Desktop/Devices/HistoricalProtocolRunOverflowMenu.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { NavLink, useNavigate } from 'react-router-dom' @@ -38,6 +37,7 @@ import { useIsEstopNotDisengaged } from '/app/resources/devices' import { useTrackProtocolRunEvent } from '/app/redux-resources/analytics' import { useRobot } from '/app/redux-resources/robots' +import type { MouseEventHandler } from 'react' import type { Run } from '@opentrons/api-client' export interface HistoricalProtocolRunOverflowMenuProps { @@ -99,7 +99,7 @@ export function HistoricalProtocolRunOverflowMenu( } interface MenuDropdownProps extends HistoricalProtocolRunOverflowMenuProps { - closeOverflowMenu: React.MouseEventHandler + closeOverflowMenu: MouseEventHandler downloadRunLog: () => void isRunLogLoading: boolean } @@ -126,7 +126,7 @@ function MenuDropdown(props: MenuDropdownProps): JSX.Element { `/devices/${robotName}/protocol-runs/${createRunResponse.data.id}/run-preview` ) } - const onDownloadClick: React.MouseEventHandler = e => { + const onDownloadClick: MouseEventHandler = e => { e.preventDefault() e.stopPropagation() downloadRunLog() @@ -143,9 +143,7 @@ function MenuDropdown(props: MenuDropdownProps): JSX.Element { const robotSerialNumber = robot?.health?.robot_serial ?? robot?.serverHealth?.serialNumber ?? null - const handleResetClick: React.MouseEventHandler = ( - e - ): void => { + const handleResetClick: MouseEventHandler = (e): void => { e.preventDefault() e.stopPropagation() @@ -160,7 +158,7 @@ function MenuDropdown(props: MenuDropdownProps): JSX.Element { trackProtocolRunEvent({ name: ANALYTICS_PROTOCOL_RUN_ACTION.AGAIN }) } - const handleDeleteClick: React.MouseEventHandler = e => { + const handleDeleteClick: MouseEventHandler = e => { e.preventDefault() e.stopPropagation() deleteRun(runId) diff --git a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/AboutPipetteSlideout.test.tsx b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/AboutPipetteSlideout.test.tsx index dd2274a3ab3..c39a0e286fc 100644 --- a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/AboutPipetteSlideout.test.tsx +++ b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/AboutPipetteSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -7,16 +6,18 @@ import { i18n } from '/app/i18n' import { AboutPipetteSlideout } from '../AboutPipetteSlideout' import { mockLeftSpecs } from '/app/redux/pipettes/__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('AboutPipetteSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { pipetteId: '123', diff --git a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/FlexPipetteCard.test.tsx b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/FlexPipetteCard.test.tsx index bd753e9f9d3..6bbb7eacbd9 100644 --- a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/FlexPipetteCard.test.tsx +++ b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/FlexPipetteCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -11,8 +10,9 @@ import { FlexPipetteCard } from '../FlexPipetteCard' import { ChoosePipette } from '/app/organisms/PipetteWizardFlows/ChoosePipette' import { useDropTipWizardFlows } from '/app/organisms/DropTipWizardFlows' -import type { PipetteData } from '@opentrons/api-client' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' +import type { PipetteData } from '@opentrons/api-client' vi.mock('/app/organisms/PipetteWizardFlows') vi.mock('/app/organisms/PipetteWizardFlows/ChoosePipette') @@ -20,7 +20,7 @@ vi.mock('../AboutPipetteSlideout') vi.mock('/app/organisms/DropTipWizardFlows') vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -29,7 +29,7 @@ const render = (props: React.ComponentProps) => { let mockDTWizToggle: Mock describe('FlexPipetteCard', () => { - let props: React.ComponentProps + let props: ComponentProps mockDTWizToggle = vi.fn() beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteCard.test.tsx b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteCard.test.tsx index e04796bd491..c71ee4ec4b4 100644 --- a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteCard.test.tsx +++ b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -15,6 +14,7 @@ import { useDropTipWizardFlows } from '/app/organisms/DropTipWizardFlows' import { mockLeftSpecs, mockRightSpecs } from '/app/redux/pipettes/__fixtures__' +import type { ComponentProps } from 'react' import type { DispatchApiRequestType } from '/app/redux/robot-api' vi.mock('../PipetteOverflowMenu') @@ -24,7 +24,7 @@ vi.mock('@opentrons/react-api-client') vi.mock('/app/redux/pipettes') vi.mock('/app/organisms/DropTipWizardFlows') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -33,7 +33,7 @@ const render = (props: React.ComponentProps) => { const mockRobotName = 'mockRobotName' describe('PipetteCard', () => { let dispatchApiRequest: DispatchApiRequestType - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { dispatchApiRequest = vi.fn() diff --git a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteOverflowMenu.test.tsx b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteOverflowMenu.test.tsx index 155d955b6ea..273d6bdf7b6 100644 --- a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteOverflowMenu.test.tsx +++ b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteOverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -11,8 +10,9 @@ import { } from '/app/redux/pipettes/__fixtures__' import { isFlexPipette } from '@opentrons/shared-data' -import type { Mount } from '/app/redux/pipettes/types' +import type { ComponentProps } from 'react' import type * as SharedData from '@opentrons/shared-data' +import type { Mount } from '/app/redux/pipettes/types' vi.mock('/app/redux/config') vi.mock('@opentrons/shared-data', async importOriginal => { @@ -23,7 +23,7 @@ vi.mock('@opentrons/shared-data', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -31,7 +31,7 @@ const render = (props: React.ComponentProps) => { const LEFT = 'left' as Mount describe('PipetteOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteSettingsSlideout.test.tsx b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteSettingsSlideout.test.tsx index 37b6f66b863..c75945cd549 100644 --- a/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteSettingsSlideout.test.tsx +++ b/app/src/organisms/Desktop/Devices/PipetteCard/__tests__/PipetteSettingsSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { fireEvent, waitFor, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -16,13 +15,12 @@ import { mockPipetteSettingsFieldsMap, } from '/app/redux/pipettes/__fixtures__' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('@opentrons/react-api-client') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -31,7 +29,7 @@ const render = ( const mockRobotName = 'mockRobotName' describe('PipetteSettingsSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps let mockUpdatePipetteSettings: Mock beforeEach(() => { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/BackToTopButton.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/BackToTopButton.tsx index a8524988bf2..909b49f1548 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/BackToTopButton.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/BackToTopButton.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' import { useRobot } from '/app/redux-resources/robots' @@ -10,8 +9,10 @@ import { ANALYTICS_PROTOCOL_PROCEED_TO_RUN, } from '/app/redux/analytics' +import type { RefObject } from 'react' + interface BackToTopButtonProps { - protocolRunHeaderRef: React.RefObject | null + protocolRunHeaderRef: RefObject | null robotName: string runId: string sourceLocation: string diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/EmptySetupStep.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/EmptySetupStep.tsx index 24c2b449083..5d9eb70774e 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/EmptySetupStep.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/EmptySetupStep.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { COLORS, DIRECTION_COLUMN, @@ -10,10 +9,12 @@ import { JUSTIFY_SPACE_BETWEEN, } from '@opentrons/components' +import type { ReactNode } from 'react' + interface EmptySetupStepProps { - title: React.ReactNode + title: ReactNode description: string - rightElement?: React.ReactNode + rightElement?: ReactNode } export function EmptySetupStep(props: EmptySetupStepProps): JSX.Element { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderBannerContainer/__tests__/ProtocolAnalysisErrorBanner.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderBannerContainer/__tests__/ProtocolAnalysisErrorBanner.test.tsx index 5b60de044d7..fceea1d15c0 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderBannerContainer/__tests__/ProtocolAnalysisErrorBanner.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderBannerContainer/__tests__/ProtocolAnalysisErrorBanner.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach } from 'vitest' @@ -6,16 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ProtocolAnalysisErrorBanner } from '../ProtocolAnalysisErrorBanner' -const render = ( - props: React.ComponentProps -) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ProtocolAnalysisErrorBanner', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/ActionButton/index.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/ActionButton/index.tsx index 4b8c0f68076..31495064a14 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/ActionButton/index.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/ActionButton/index.tsx @@ -1,5 +1,3 @@ -import type * as React from 'react' - import { RUN_STATUS_STOP_REQUESTED } from '@opentrons/api-client' import { ALIGN_CENTER, @@ -28,12 +26,13 @@ import { useActionBtnDisabledUtils, useActionButtonProperties } from './hooks' import { getFallbackRobotSerialNumber, isRunAgainStatus } from '../../utils' import { useIsRobotOnWrongVersionOfSoftware } from '/app/redux/robot-update' +import type { MutableRefObject } from 'react' import type { RunHeaderContentProps } from '..' export type BaseActionButtonProps = RunHeaderContentProps interface ActionButtonProps extends BaseActionButtonProps { - isResetRunLoadingRef: React.MutableRefObject + isResetRunLoadingRef: MutableRefObject } export function ActionButton(props: ActionButtonProps): JSX.Element { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/LabeledValue.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/LabeledValue.tsx index 135dd72bbae..183f6194a3f 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/LabeledValue.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/LabeledValue.tsx @@ -1,5 +1,3 @@ -import type * as React from 'react' - import { DIRECTION_COLUMN, COLORS, @@ -8,9 +6,11 @@ import { StyledText, } from '@opentrons/components' +import type { ReactNode } from 'react' + interface LabeledValueProps { label: string - value: React.ReactNode + value: ReactNode } export function LabeledValue(props: LabeledValueProps): JSX.Element { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/index.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/index.tsx index 51908e4435d..7d653c6f439 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/index.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderContent/index.tsx @@ -1,16 +1,15 @@ -import type * as React from 'react' - import { RunHeaderSectionUpper } from './RunHeaderSectionUpper' import { RunHeaderSectionLower } from './RunHeaderSectionLower' -import type { ProtocolRunHeaderProps } from '..' +import type { MutableRefObject } from 'react' import type { AttachedModule, RunStatus } from '@opentrons/api-client' +import type { ProtocolRunHeaderProps } from '..' import type { RunControls } from '/app/organisms/RunTimeControl' import type { UseRunHeaderModalContainerResult } from '../RunHeaderModalContainer' export type RunHeaderContentProps = ProtocolRunHeaderProps & { runStatus: RunStatus | null - isResetRunLoadingRef: React.MutableRefObject + isResetRunLoadingRef: MutableRefObject attachedModules: AttachedModule[] protocolRunControls: RunControls runHeaderModalContainerUtils: UseRunHeaderModalContainerResult diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/HeaterShakerIsRunningModal.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/HeaterShakerIsRunningModal.test.tsx index 03b59af1b57..5960c2a39fa 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/HeaterShakerIsRunningModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/HeaterShakerIsRunningModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -12,6 +11,7 @@ import { HeaterShakerModuleCard } from '../HeaterShakerModuleCard' import { useAttachedModules } from '/app/resources/modules' import { useMostRecentCompletedAnalysis } from '/app/resources/runs' +import type { ComponentProps } from 'react' import type * as ReactApiClient from '@opentrons/react-api-client' vi.mock('@opentrons/react-api-client', async importOriginal => { @@ -69,16 +69,14 @@ const mockMovingHeaterShakerTwo = { usbPort: { path: '/dev/ot_module_heatershaker0', port: 1 }, } as any -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('HeaterShakerIsRunningModal', () => { - let props: React.ComponentProps + let props: ComponentProps let mockCreateLiveCommand = vi.fn() beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/HeaterShakerModuleCard.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/HeaterShakerModuleCard.test.tsx index 4cd20822890..8e98d4778fa 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/HeaterShakerModuleCard.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/HeaterShakerModuleCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, vi, beforeEach } from 'vitest' @@ -8,16 +7,18 @@ import { i18n } from '/app/i18n' import { HeaterShakerModuleCard } from '../HeaterShakerModuleCard' import { HeaterShakerModuleData } from '/app/organisms/ModuleCard/HeaterShakerModuleData' +import type { ComponentProps } from 'react' + vi.mock('/app/organisms/ModuleCard/HeaterShakerModuleData') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('HeaterShakerModuleCard', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { module: mockHeaterShaker, diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/hooks.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/hooks.test.tsx index c5028c6a821..8c9c83fcaf9 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/hooks.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/HeaterShakerIsRunningModal/__tests__/hooks.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Provider } from 'react-redux' import { describe, it, vi, beforeEach, expect } from 'vitest' import { createStore } from 'redux' @@ -10,6 +9,7 @@ import { RUN_ID_1 } from '/app/resources/runs/__fixtures__' import { useMostRecentCompletedAnalysis } from '/app/resources/runs' import { useHeaterShakerModuleIdsFromRun } from '../hooks' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' @@ -57,7 +57,7 @@ describe('useHeaterShakerModuleIdsFromRun', () => { }, ], } as any) - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook( @@ -127,7 +127,7 @@ describe('useHeaterShakerModuleIdsFromRun', () => { ], } as any) - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook( diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ConfirmCancelModal.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ConfirmCancelModal.test.tsx index c6421040e17..08559f181be 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ConfirmCancelModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ConfirmCancelModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' @@ -17,6 +16,7 @@ import { useIsFlex } from '/app/redux-resources/robots' import { useTrackEvent } from '/app/redux/analytics' import { ConfirmCancelModal } from '../ConfirmCancelModal' +import type { ComponentProps } from 'react' import type * as ApiClient from '@opentrons/react-api-client' vi.mock('@opentrons/react-api-client', async importOriginal => { @@ -30,7 +30,7 @@ vi.mock('/app/redux/analytics') vi.mock('/app/redux-resources/analytics') vi.mock('/app/redux-resources/robots') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -43,7 +43,7 @@ let mockTrackProtocolRunEvent: any const ROBOT_NAME = 'otie' describe('ConfirmCancelModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { mockTrackEvent = vi.fn() mockStopRun = vi.fn((_runId, opts) => opts.onSuccess()) diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ProtocolAnalysisErrorModal.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ProtocolAnalysisErrorModal.test.tsx index 44fcb0278ad..027f6b76b03 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ProtocolAnalysisErrorModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ProtocolAnalysisErrorModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, expect, vi } from 'vitest' @@ -6,16 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ProtocolAnalysisErrorModal } from '../ProtocolAnalysisErrorModal' -const render = ( - props: React.ComponentProps -) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ProtocolAnalysisErrorModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ProtocolDropTipModal.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ProtocolDropTipModal.test.tsx index 0d95071a969..4e583be0648 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ProtocolDropTipModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/ProtocolDropTipModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { renderHook, act, screen, fireEvent } from '@testing-library/react' @@ -10,6 +9,7 @@ import { ProtocolDropTipModal, } from '../ProtocolDropTipModal' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/local-resources/instruments') @@ -104,14 +104,14 @@ describe('useProtocolDropTipModal', () => { }) }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ProtocolDropTipModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/RunFailedModal.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/RunFailedModal.test.tsx index d49875a0859..75ea8c0c720 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/RunFailedModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/modals/__tests__/RunFailedModal.test.tsx @@ -1,5 +1,5 @@ -import type * as React from 'react' import { describe, it, beforeEach, vi, expect, afterEach } from 'vitest' +import { fireEvent, screen } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' @@ -7,8 +7,9 @@ import { useDownloadRunLog } from '../../../../../hooks' import { RunFailedModal } from '../RunFailedModal' import { RUN_STATUS_FAILED } from '@opentrons/api-client' + +import type { ComponentProps } from 'react' import type { RunError } from '@opentrons/api-client' -import { fireEvent, screen } from '@testing-library/react' vi.mock('../../../../../hooks') @@ -25,14 +26,14 @@ const mockError: RunError = { wrappedErrors: [], } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('RunFailedModal - DesktopApp', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/useRunHeaderModalContainer.ts b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/useRunHeaderModalContainer.ts index 48eda0ebfa5..1e0d1e5c073 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/useRunHeaderModalContainer.ts +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/RunHeaderModalContainer/useRunHeaderModalContainer.ts @@ -16,9 +16,15 @@ import { useProtocolDetailsForRun } from '/app/resources/runs' import { getFallbackRobotSerialNumber } from '../utils' import { ANALYTICS_PROTOCOL_PROCEED_TO_RUN, + ANALYTICS_PROTOCOL_RUN_ACTION, useTrackEvent, } from '/app/redux/analytics' +import { + useRobotAnalyticsData, + useTrackProtocolRunEvent, +} from '/app/redux-resources/analytics' import { useRobot, useRobotType } from '/app/redux-resources/robots' + import type { AttachedModule, RunStatus, Run } from '@opentrons/api-client' import type { UseErrorRecoveryResult } from '/app/organisms/ErrorRecoveryFlows' import type { @@ -71,7 +77,9 @@ export function useRunHeaderModalContainer({ const robot = useRobot(robotName) const robotSerialNumber = getFallbackRobotSerialNumber(robot) const trackEvent = useTrackEvent() + const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId, robotName) const robotType = useRobotType(robotName) + const robotAnalyticsData = useRobotAnalyticsData(robotName) function handleProceedToRunClick(): void { navigate(`/devices/${robotName}/protocol-runs/${runId}/run-preview`) @@ -79,6 +87,10 @@ export function useRunHeaderModalContainer({ name: ANALYTICS_PROTOCOL_PROCEED_TO_RUN, properties: { robotSerialNumber }, }) + trackProtocolRunEvent({ + name: ANALYTICS_PROTOCOL_RUN_ACTION.START, + properties: robotAnalyticsData ?? {}, + }) protocolRunControls.play() } diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/__tests__/ProtocolRunHeader.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/__tests__/ProtocolRunHeader.test.tsx index e82d58cb75e..f6c88119707 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/__tests__/ProtocolRunHeader.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/__tests__/ProtocolRunHeader.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' import { useNavigate } from 'react-router-dom' @@ -26,6 +25,8 @@ import { useRunHeaderRunControls, } from '../hooks' +import type { ComponentProps } from 'react' + vi.mock('react-router-dom') vi.mock('@opentrons/react-api-client') vi.mock('/app/redux-resources/robots') @@ -43,7 +44,7 @@ const MOCK_RUN_ID = 'MOCK_RUN_ID' const MOCK_ROBOT = 'MOCK_ROBOT' describe('ProtocolRunHeader', () => { - let props: React.ComponentProps + let props: ComponentProps const mockNavigate = vi.fn() beforeEach(() => { @@ -92,7 +93,7 @@ describe('ProtocolRunHeader', () => { vi.resetAllMocks() }) - const render = (props: React.ComponentProps) => { + const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/hooks/useRunAnalytics.ts b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/hooks/useRunAnalytics.ts index 31399cbc541..95658999f4a 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/hooks/useRunAnalytics.ts +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/ProtocolRunHeader/hooks/useRunAnalytics.ts @@ -28,15 +28,12 @@ export function useRunAnalytics({ useEffect(() => { const areReportConditionsValid = - isRunCurrent && - runId != null && - robotAnalyticsData != null && - isTerminalRunStatus(runStatus) + isRunCurrent && runId != null && isTerminalRunStatus(runStatus) if (areReportConditionsValid) { trackProtocolRunEvent({ name: ANALYTICS_PROTOCOL_RUN_ACTION.FINISH, - properties: robotAnalyticsData, + properties: robotAnalyticsData ?? undefined, }) } }, [runStatus, isRunCurrent, runId, robotAnalyticsData]) diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/LabwareListItem.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/LabwareListItem.test.tsx index eabef743182..89622cd7e5e 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/LabwareListItem.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/LabwareListItem.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' import { MemoryRouter } from 'react-router-dom' @@ -21,6 +20,8 @@ import { getLocationInfoNames } from '/app/transformations/commands' import { mockLabwareDef } from '/app/organisms/LegacyLabwarePositionCheck/__fixtures__/mockLabwareDef' import { SecureLabwareModal } from '../SecureLabwareModal' import { LabwareListItem } from '../LabwareListItem' + +import type { ComponentProps } from 'react' import type { LoadLabwareRunTimeCommand, ModuleModel, @@ -78,7 +79,7 @@ const mockThermocyclerModuleDefinition = { const mockModuleId = 'moduleId' const mockNickName = 'nickName' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/OffDeckLabwareList.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/OffDeckLabwareList.test.tsx index 0204958f0f0..b543c3f27c8 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/OffDeckLabwareList.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/OffDeckLabwareList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -9,9 +8,11 @@ import { mockLabwareDef } from '/app/organisms/LegacyLabwarePositionCheck/__fixt import { LabwareListItem } from '../LabwareListItem' import { OffDeckLabwareList } from '../OffDeckLabwareList' +import type { ComponentProps } from 'react' + vi.mock('../LabwareListItem') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SecureLabwareModal.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SecureLabwareModal.test.tsx index 80147006dc1..f367e007375 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SecureLabwareModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SecureLabwareModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -6,7 +5,9 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { SecureLabwareModal } from '../SecureLabwareModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -15,7 +16,7 @@ const mockTypeMagDeck = 'magneticModuleType' const mockTypeTC = 'thermocyclerModuleType' describe('SecureLabwareModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { type: mockTypeMagDeck, onCloseClick: vi.fn() } }) diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabwareList.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabwareList.test.tsx index 85068d39a1b..9f9860de1d4 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabwareList.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabwareList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { describe, it, beforeEach, vi } from 'vitest' import { screen } from '@testing-library/react' @@ -9,13 +8,15 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { SetupLabwareList } from '../SetupLabwareList' import { LabwareListItem } from '../LabwareListItem' + +import type { ComponentProps } from 'react' import type { CompletedProtocolAnalysis } from '@opentrons/shared-data' vi.mock('../LabwareListItem') const protocolWithTC = (multiple_tipacks_with_tc as unknown) as CompletedProtocolAnalysis -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabwareMap.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabwareMap.test.tsx index 40f63cbc170..57a84b1f737 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabwareMap.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabware/__tests__/SetupLabwareMap.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { MemoryRouter } from 'react-router-dom' import { describe, it, beforeEach, vi, afterEach, expect } from 'vitest' @@ -20,6 +19,7 @@ import { } from '/app/transformations/analysis' import { SetupLabwareMap } from '../SetupLabwareMap' +import type { ComponentProps } from 'react' import type { CompletedProtocolAnalysis, LabwareDefinition2, @@ -86,7 +86,7 @@ const mockTCModule = { type: 'thermocyclerModuleType' as ModuleType, } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/CurrentOffsetsTable.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/CurrentOffsetsTable.test.tsx index 334d5328cec..db2e5a8b5a1 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/CurrentOffsetsTable.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/CurrentOffsetsTable.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, vi, expect, afterEach } from 'vitest' import { screen } from '@testing-library/react' @@ -15,6 +14,7 @@ import { useLPCDisabledReason } from '/app/resources/runs' import { getLatestCurrentOffsets } from '/app/transformations/runs' import { CurrentOffsetsTable } from '../CurrentOffsetsTable' +import type { ComponentProps } from 'react' import type { CompletedProtocolAnalysis } from '@opentrons/shared-data' import type { LabwareOffset } from '@opentrons/api-client' @@ -31,7 +31,7 @@ vi.mock('@opentrons/shared-data', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -64,7 +64,7 @@ const mockCurrentOffsets: LabwareOffset[] = [ ] describe('CurrentOffsetsTable', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { currentOffsets: mockCurrentOffsets, diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/HowLPCWorksModal.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/HowLPCWorksModal.test.tsx index 5dd3d15db69..a12b6948d75 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/HowLPCWorksModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLabwarePositionCheck/__tests__/HowLPCWorksModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { HowLPCWorksModal } from '../HowLPCWorksModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('HowLPCWorksModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onCloseClick: vi.fn() } }) diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquids.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquids.test.tsx index 736d5f5bc85..8436072e2f1 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquids.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquids.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, vi, expect } from 'vitest' import { screen, fireEvent } from '@testing-library/react' @@ -11,6 +10,8 @@ import { SetupLiquidsList } from '../SetupLiquidsList' import { SetupLiquidsMap } from '../SetupLiquidsMap' import { useRunHasStarted } from '/app/resources/runs' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/components', async () => { const actual = await vi.importActual('@opentrons/components') return { @@ -24,7 +25,7 @@ vi.mock('/app/resources/runs') describe('SetupLiquids', () => { const render = ( - props: React.ComponentProps & { + props: ComponentProps & { startConfirmed?: boolean } ) => { @@ -47,7 +48,7 @@ describe('SetupLiquids', () => { ) } - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(SetupLiquidsList).mockReturnValue(
        Mock setup liquids list
        diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquidsList.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquidsList.test.tsx index 60cb6a759a0..2d96d408044 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquidsList.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquidsList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { when } from 'vitest-when' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -25,6 +24,7 @@ import { import { LiquidsLabwareDetailsModal } from '/app/organisms/LiquidsLabwareDetailsModal' import { useNotifyRunQuery } from '/app/resources/runs' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' import type * as SharedData from '@opentrons/shared-data' @@ -70,7 +70,7 @@ vi.mock('@opentrons/shared-data', async importOriginal => { vi.mock('/app/redux/analytics') vi.mock('/app/resources/runs') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -78,7 +78,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEvent: Mock describe('SetupLiquidsList', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { runId: '123', robotName: 'test_flex' } vi.mocked(getTotalVolumePerLiquidId).mockReturnValue(400) diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquidsMap.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquidsMap.test.tsx index 023e73af09c..97e96232b7e 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquidsMap.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupLiquids/__tests__/SetupLiquidsMap.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { screen } from '@testing-library/react' import { describe, it, beforeEach, vi, afterEach, expect } from 'vitest' @@ -35,6 +34,7 @@ import { mockFetchModulesSuccessActionPayloadModules } from '/app/redux/modules/ import { SetupLiquidsMap } from '../SetupLiquidsMap' +import type { ComponentProps } from 'react' import type { ModuleModel, ModuleType, @@ -102,7 +102,7 @@ const mockMagneticModule = { quirks: [], } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -113,7 +113,7 @@ const mockProtocolAnalysis = { } as any describe('SetupLiquidsMap', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { runId: RUN_ID, diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/NotConfiguredModal.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/NotConfiguredModal.test.tsx index 9f371faaa64..fc2b584cf22 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/NotConfiguredModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/NotConfiguredModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -9,20 +8,21 @@ import { i18n } from '/app/i18n' import { NotConfiguredModal } from '../NotConfiguredModal' import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { DeckConfiguration } from '@opentrons/shared-data' vi.mock('@opentrons/react-api-client') vi.mock('/app/resources/deck_configuration') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('NotConfiguredModal', () => { - let props: React.ComponentProps + let props: ComponentProps const mockUpdate = vi.fn() beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupFixtureList.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupFixtureList.test.tsx index dd0236ac96d..61708168788 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupFixtureList.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupFixtureList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -16,6 +15,7 @@ import { NotConfiguredModal } from '../NotConfiguredModal' import { LocationConflictModal } from '/app/organisms/LocationConflictModal' import { DeckFixtureSetupInstructionsModal } from '/app/organisms/DeviceDetailsDeckConfiguration/DeckFixtureSetupInstructionsModal' +import type { ComponentProps } from 'react' import type { CutoutConfigAndCompatibility } from '/app/resources/deck_configuration/hooks' vi.mock('/app/resources/deck_configuration/hooks') @@ -61,14 +61,14 @@ const mockConflictDeckConfigCompatibility: CutoutConfigAndCompatibility[] = [ }, ] -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('SetupFixtureList', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { deckConfigCompatibility: mockDeckConfigCompatibility, diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesAndDeck.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesAndDeck.test.tsx index 21926d3c823..615bb5a487d 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesAndDeck.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesAndDeck.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { describe, it, beforeEach, expect, vi } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -20,6 +19,8 @@ import { SetupModulesList } from '../SetupModulesList' import { SetupModulesMap } from '../SetupModulesMap' import { SetupFixtureList } from '../SetupFixtureList' +import type { ComponentProps } from 'react' + vi.mock('/app/redux-resources/robots') vi.mock('../SetupModulesList') vi.mock('../SetupModulesMap') @@ -31,14 +32,14 @@ vi.mock('/app/resources/runs') const MOCK_ROBOT_NAME = 'otie' const MOCK_RUN_ID = '1' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('SetupModuleAndDeck', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotName: MOCK_ROBOT_NAME, diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesList.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesList.test.tsx index e62d600cea6..95c95fced5a 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesList.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { fireEvent, screen, waitFor } from '@testing-library/react' import { describe, it, beforeEach, expect, vi } from 'vitest' @@ -27,6 +26,7 @@ import { UnMatchedModuleWarning } from '../UnMatchedModuleWarning' import { SetupModulesList } from '../SetupModulesList' import { LocationConflictModal } from '/app/organisms/LocationConflictModal' +import type { ComponentProps } from 'react' import type { ModuleModel, ModuleType } from '@opentrons/shared-data' import type { DiscoveredRobot } from '/app/redux/discovery/types' @@ -77,14 +77,14 @@ const mockCalibratedData = { last_modified: '2023-06-01T14:42:20.131798+00:00', } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('SetupModulesList', () => { - let props: React.ComponentProps + let props: ComponentProps let mockChainLiveCommands = vi.fn() beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesMap.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesMap.test.tsx index 9d9449283dc..c7619b44f9b 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesMap.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupModuleAndDeck/__tests__/SetupModulesMap.test.tsx @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ - -import type * as React from 'react' import { when } from 'vitest-when' import { MemoryRouter } from 'react-router-dom' import { describe, it, beforeEach, vi, afterEach, expect } from 'vitest' @@ -18,6 +16,8 @@ import { useMostRecentCompletedAnalysis } from '/app/resources/runs' import { ModuleInfo } from '/app/molecules/ModuleInfo' import { SetupModulesMap } from '../SetupModulesMap' import { getAttachedProtocolModuleMatches } from '/app/transformations/analysis' + +import type { ComponentProps } from 'react' import type { CompletedProtocolAnalysis, ModuleModel, @@ -47,7 +47,7 @@ vi.mock('/app/transformations/analysis') vi.mock('/app/molecules/ModuleInfo') vi.mock('/app/resources/modules') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -98,7 +98,7 @@ const mockTCModule = { } describe('SetupModulesMap', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { runId: MOCK_RUN_ID, diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupStep.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupStep.tsx index 25f2baf1d64..2d5fedf7700 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/SetupStep.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/SetupStep.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { @@ -18,19 +17,21 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import type { ReactNode } from 'react' + interface SetupStepProps { /** whether or not to show the full contents of the step */ expanded: boolean /** always shown text name of the step */ - title: React.ReactNode + title: ReactNode /** always shown text that provides a one sentence explanation of the contents */ description: string /** callback that should toggle the expanded state (managed by parent) */ toggleExpanded: () => void /** contents to be shown only when expanded */ - children: React.ReactNode + children: ReactNode /** element to be shown (right aligned) regardless of expanded state */ - rightElement: React.ReactNode + rightElement: ReactNode } const EXPANDED_STYLE = css` diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/EmptySetupStep.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/EmptySetupStep.test.tsx index 673d8b4806c..b716de57f06 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/EmptySetupStep.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/EmptySetupStep.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { EmptySetupStep } from '../EmptySetupStep' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('EmptySetupStep', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { title: 'mockTitle', diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/LabwareInfoOverlay.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/LabwareInfoOverlay.test.tsx index 0198aa9e448..a47f37e1136 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/LabwareInfoOverlay.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/LabwareInfoOverlay.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { screen } from '@testing-library/react' import { describe, it, beforeEach, vi, afterEach, expect } from 'vitest' @@ -13,6 +12,8 @@ import { getLabwareLocation } from '/app/transformations/commands' import { LabwareInfoOverlay } from '../LabwareInfoOverlay' import { getLabwareDefinitionUri } from '/app/transformations/protocols' import { useLabwareOffsetForLabware } from '../useLabwareOffsetForLabware' + +import type { ComponentProps } from 'react' import type { LabwareDefinition2, ProtocolFile, @@ -33,7 +34,7 @@ vi.mock('@opentrons/shared-data', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -51,7 +52,7 @@ const MOCK_LABWARE_VECTOR = { x: 1, y: 2, z: 3 } const MOCK_RUN_ID = 'fake_run_id' describe('LabwareInfoOverlay', () => { - let props: React.ComponentProps + let props: ComponentProps let labware: LoadedLabware[] let labwareDefinitions: ProtocolFile<{}>['labwareDefinitions'] beforeEach(() => { diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/ProtocolRunModuleControls.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/ProtocolRunModuleControls.test.tsx index 5dd1507f893..1895c2e4eca 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/ProtocolRunModuleControls.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/ProtocolRunModuleControls.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { describe, it, beforeEach, vi, afterEach } from 'vitest' import { screen } from '@testing-library/react' @@ -15,6 +14,8 @@ import { mockThermocycler, mockHeaterShaker, } from '/app/redux/modules/__fixtures__' + +import type { ComponentProps } from 'react' import type { ModuleModel, ModuleType } from '@opentrons/shared-data' vi.mock('@opentrons/react-api-client') @@ -49,9 +50,7 @@ const mockTCModule = { } const MOCK_TC_COORDS = [20, 30, 0] -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/ProtocolRunRuntimeParameters.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/ProtocolRunRuntimeParameters.test.tsx index 59425f6ff6b..99922644961 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/ProtocolRunRuntimeParameters.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/ProtocolRunRuntimeParameters.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest' import { screen } from '@testing-library/react' import { when } from 'vitest-when' @@ -16,6 +15,7 @@ import { } from '/app/resources/runs/__fixtures__' import { ProtocolRunRuntimeParameters } from '../ProtocolRunRunTimeParameters' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { Run } from '@opentrons/api-client' import type { @@ -100,16 +100,14 @@ const mockCsvRtp = { }, } -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ProtocolRunRuntimeParameters', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { runId: RUN_ID, diff --git a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/SetupCalibrationItem.test.tsx b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/SetupCalibrationItem.test.tsx index e39e5d7c83c..9002eb0da0d 100644 --- a/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/SetupCalibrationItem.test.tsx +++ b/app/src/organisms/Desktop/Devices/ProtocolRun/__tests__/SetupCalibrationItem.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { when } from 'vitest-when' import { describe, it, beforeEach, vi, afterEach } from 'vitest' @@ -9,6 +8,8 @@ import { useRunHasStarted } from '/app/resources/runs' import { formatTimestamp } from '/app/transformations/runs' import { SetupCalibrationItem } from '../SetupCalibrationItem' +import type { ComponentProps } from 'react' + vi.mock('/app/resources/runs') vi.mock('/app/transformations/runs') @@ -21,7 +22,7 @@ describe('SetupCalibrationItem', () => { title = 'stub title', button = , runId = RUN_ID, - }: Partial> = {}) => { + }: Partial> = {}) => { return renderWithProviders( { const render = ({ mount = 'left', runId = RUN_ID, - }: Partial< - React.ComponentProps - > = {}) => { + }: Partial> = {}) => { return renderWithProviders( { mount = 'left', robotName = ROBOT_NAME, runId = RUN_ID, - }: Partial< - React.ComponentProps - > = {}) => { + }: Partial> = {}) => { return renderWithProviders( { nextStep = 'module_setup_step', calibrationStatus = { complete: true }, expandStep = mockExpandStep, - }: Partial> = {}) => { + }: Partial> = {}) => { return renderWithProviders( { @@ -16,7 +16,7 @@ describe('SetupStep', () => { toggleExpanded = toggleExpandedMock, children = , rightElement =
        right element
        , - }: Partial> = {}) => { + }: Partial> = {}) => { return renderWithProviders( { hasCalibrated = false, tipRackDefinition = fixtureTiprack300ul as LabwareDefinition2, isExtendedPipOffset = false, - }: Partial< - React.ComponentProps - > = {}) => { + }: Partial> = {}) => { return renderWithProviders( ) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/__tests__/RenameRobotSlideout.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/__tests__/RenameRobotSlideout.test.tsx index 6f7b1b4ab28..13612c63f19 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/__tests__/RenameRobotSlideout.test.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/__tests__/RenameRobotSlideout.test.tsx @@ -2,6 +2,9 @@ import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen, waitFor } from '@testing-library/react' import { describe, it, vi, expect, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' + +import { OT2_ROBOT_TYPE } from '@opentrons/shared-data' + import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useTrackEvent, ANALYTICS_RENAME_ROBOT } from '/app/redux/analytics' @@ -14,7 +17,6 @@ import { mockConnectableRobot, mockReachableRobot, } from '/app/redux/discovery/__fixtures__' - import { RenameRobotSlideout } from '../RenameRobotSlideout' import { useIsFlex } from '/app/redux-resources/robots' @@ -111,7 +113,11 @@ describe('RobotSettings RenameRobotSlideout', () => { await waitFor(() => { expect(mockTrackEvent).toHaveBeenCalledWith({ name: ANALYTICS_RENAME_ROBOT, - properties: { newRobotName: 'mockInput', previousRobotName: 'otie' }, + properties: { + newRobotName: 'mockInput', + previousRobotName: 'otie', + robotType: OT2_ROBOT_TYPE, + }, }) }) }) diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/DeviceReset.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/DeviceReset.tsx index 2249e453fe6..b9cd537894a 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/DeviceReset.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/DeviceReset.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { @@ -14,6 +13,8 @@ import { import { TertiaryButton } from '/app/atoms/buttons' +import type { MouseEventHandler } from 'react' + interface DeviceResetProps { updateIsExpanded: ( isExpanded: boolean, @@ -28,7 +29,7 @@ export function DeviceReset({ }: DeviceResetProps): JSX.Element { const { t } = useTranslation('device_settings') - const handleClick: React.MouseEventHandler = () => { + const handleClick: MouseEventHandler = () => { if (!isRobotBusy) { updateIsExpanded(true, 'deviceReset') } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/DisplayRobotName.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/DisplayRobotName.tsx index c6a380cd6e5..7f1d51022b4 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/DisplayRobotName.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/DisplayRobotName.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { @@ -14,6 +13,8 @@ import { } from '@opentrons/components' import { TertiaryButton } from '/app/atoms/buttons' + +import type { MouseEventHandler } from 'react' interface DisplayRobotNameProps { robotName: string updateIsExpanded: ( @@ -30,7 +31,7 @@ export function DisplayRobotName({ }: DisplayRobotNameProps): JSX.Element { const { t } = useTranslation('device_settings') - const handleClick: React.MouseEventHandler = () => { + const handleClick: MouseEventHandler = () => { if (!isRobotBusy) { updateIsExpanded(true, 'renameRobot') } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/FactoryMode.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/FactoryMode.tsx index 14cb3766040..8fea7e0f82d 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/FactoryMode.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/FactoryMode.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { @@ -14,9 +13,11 @@ import { import { TertiaryButton } from '/app/atoms/buttons' +import type { Dispatch, SetStateAction } from 'react' + interface FactoryModeProps { isRobotBusy: boolean - setShowFactoryModeSlideout: React.Dispatch> + setShowFactoryModeSlideout: Dispatch> sn: string | null } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/GantryHoming.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/GantryHoming.tsx index 96ea82a59cf..6537d1fdf1a 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/GantryHoming.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/GantryHoming.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useDispatch } from 'react-redux' import { useTranslation } from 'react-i18next' @@ -15,6 +14,7 @@ import { import { ToggleButton } from '/app/atoms/buttons' import { updateSetting } from '/app/redux/robot-settings' +import type { MouseEventHandler } from 'react' import type { Dispatch } from '/app/redux/types' import type { RobotSettingsField } from '/app/redux/robot-settings/types' @@ -34,7 +34,7 @@ export function GantryHoming({ const value = settings?.value ? settings.value : false const id = settings?.id ? settings.id : 'disableHomeOnBoot' - const handleClick: React.MouseEventHandler = () => { + const handleClick: MouseEventHandler = () => { if (!isRobotBusy) { dispatch(updateSetting(robotName, id, !value)) } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/LegacySettings.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/LegacySettings.tsx index 5ea0ae6e5ec..7947243d13f 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/LegacySettings.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/LegacySettings.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useDispatch } from 'react-redux' import { useTranslation } from 'react-i18next' @@ -15,6 +14,7 @@ import { import { ToggleButton } from '/app/atoms/buttons' import { updateSetting } from '/app/redux/robot-settings' +import type { MouseEventHandler } from 'react' import type { Dispatch } from '/app/redux/types' import type { RobotSettingsField } from '/app/redux/robot-settings/types' @@ -34,7 +34,7 @@ export function LegacySettings({ const value = settings?.value ? settings.value : false const id = settings?.id ? settings.id : 'deckCalibrationDots' - const handleClick: React.MouseEventHandler = () => { + const handleClick: MouseEventHandler = () => { if (!isRobotBusy) { dispatch(updateSetting(robotName, id, !value)) } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/ShortTrashBin.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/ShortTrashBin.tsx index 5bc00476406..6152163c223 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/ShortTrashBin.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/ShortTrashBin.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useDispatch } from 'react-redux' import { useTranslation } from 'react-i18next' @@ -15,6 +14,7 @@ import { import { ToggleButton } from '/app/atoms/buttons' import { updateSetting } from '/app/redux/robot-settings' +import type { MouseEventHandler } from 'react' import type { Dispatch } from '/app/redux/types' import type { RobotSettingsField } from '/app/redux/robot-settings/types' @@ -34,7 +34,7 @@ export function ShortTrashBin({ const value = settings?.value ? settings.value : false const id = settings?.id ? settings.id : 'shortTrashBin' - const handleClick: React.MouseEventHandler = () => { + const handleClick: MouseEventHandler = () => { if (!isRobotBusy) { dispatch(updateSetting(robotName, id, !value)) } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UpdateRobotSoftware.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UpdateRobotSoftware.tsx index a893c616508..01917e5b483 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UpdateRobotSoftware.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UpdateRobotSoftware.tsx @@ -23,6 +23,7 @@ import { ExternalLink } from '/app/atoms/Link/ExternalLink' import { TertiaryButton } from '/app/atoms/buttons' import { getRobotUpdateDisplayInfo } from '/app/redux/robot-update' import { useDispatchStartRobotUpdate } from '/app/redux/robot-update/hooks' +import { remote } from '/app/redux/shell/remote' import type { ChangeEventHandler, MouseEventHandler } from 'react' import type { State } from '/app/redux/types' @@ -55,14 +56,19 @@ export function UpdateRobotSoftware({ const handleChange: ChangeEventHandler = event => { const { files } = event.target - if (files?.length === 1 && !updateDisabled) { - dispatchStartRobotUpdate(robotName, files[0].path) - onUpdateStart() - } - // this is to reset the state of the file picker so users can reselect the same - // system image if the upload fails - if (inputRef.current?.value != null) { - inputRef.current.value = '' + + if (files != null) { + void remote.getFilePathFrom(files[0]).then(filePath => { + if (files.length === 1 && !updateDisabled) { + dispatchStartRobotUpdate(robotName, filePath) + onUpdateStart() + } + // this is to reset the state of the file picker so users can reselect the same + // system image if the upload fails + if (inputRef.current?.value != null) { + inputRef.current.value = '' + } + }) } } @@ -106,7 +112,7 @@ export function UpdateRobotSoftware({ {updateFromFileDisabledReason != null && ( - {updateFromFileDisabledReason} + {t(updateFromFileDisabledReason)} )} diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UsageSettings.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UsageSettings.tsx index e8843af6019..c44f573f7a2 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UsageSettings.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UsageSettings.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useDispatch } from 'react-redux' import { useTranslation } from 'react-i18next' @@ -15,6 +14,7 @@ import { import { ToggleButton } from '/app/atoms/buttons' import { updateSetting } from '/app/redux/robot-settings' +import type { MouseEventHandler } from 'react' import type { Dispatch } from '/app/redux/types' import type { RobotSettingsField } from '/app/redux/robot-settings/types' @@ -34,7 +34,7 @@ export function UsageSettings({ const value = settings?.value ? settings.value : false const id = settings?.id ? settings.id : 'enableDoorSafetySwitch' - const handleClick: React.MouseEventHandler = () => { + const handleClick: MouseEventHandler = () => { if (!isRobotBusy) { dispatch(updateSetting(robotName, id, !value)) } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UseOlderAspirateBehavior.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UseOlderAspirateBehavior.tsx index c3496621f18..c12941a12a0 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UseOlderAspirateBehavior.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/UseOlderAspirateBehavior.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useDispatch } from 'react-redux' import { useTranslation } from 'react-i18next' @@ -15,6 +14,7 @@ import { import { ToggleButton } from '/app/atoms/buttons' import { updateSetting } from '/app/redux/robot-settings' +import type { MouseEventHandler } from 'react' import type { Dispatch } from '/app/redux/types' import type { RobotSettingsField } from '/app/redux/robot-settings/types' @@ -34,7 +34,7 @@ export function UseOlderAspirateBehavior({ const value = settings?.value ? settings.value : false const id = settings?.id ? settings.id : 'useOldAspirationFunctions' - const handleClick: React.MouseEventHandler = () => { + const handleClick: MouseEventHandler = () => { if (!isRobotBusy) { dispatch(updateSetting(robotName, id, !value)) } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/EnableErrorRecoveryMode.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/EnableErrorRecoveryMode.test.tsx index 9406e38f768..b3ff80c9341 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/EnableErrorRecoveryMode.test.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/EnableErrorRecoveryMode.test.tsx @@ -5,21 +5,19 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useErrorRecoverySettingsToggle } from '/app/resources/errorRecovery' import { EnableErrorRecoveryMode } from '../EnableErrorRecoveryMode' -import type * as React from 'react' +import type { ComponentProps } from 'react' vi.mock('/app/resources/errorRecovery') const mockToggleERSettings = vi.fn() -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('EnableErrorRecoveryMode', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { isRobotBusy: false } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/EnableStatusLight.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/EnableStatusLight.test.tsx index 2e2cc956bde..c36d79a04db 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/EnableStatusLight.test.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/EnableStatusLight.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, expect, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -8,18 +7,20 @@ import { i18n } from '/app/i18n' import { useLEDLights } from '/app/resources/robot-settings' import { EnableStatusLight } from '../EnableStatusLight' +import type { ComponentProps } from 'react' + vi.mock('/app/resources/robot-settings') const ROBOT_NAME = 'otie' const mockToggleLights = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('EnableStatusLight', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/OpenJupyterControl.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/OpenJupyterControl.test.tsx index 57a01e25680..b6c5b2d9be4 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/OpenJupyterControl.test.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/OpenJupyterControl.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -8,6 +7,8 @@ import { i18n } from '/app/i18n' import { useTrackEvent, ANALYTICS_JUPYTER_OPEN } from '/app/redux/analytics' import { OpenJupyterControl } from '../OpenJupyterControl' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/analytics') const mockIpAddress = '1.1.1.1' @@ -18,7 +19,7 @@ global.window = Object.create(window) Object.defineProperty(window, 'open', { writable: true, configurable: true }) window.open = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -28,7 +29,7 @@ const render = (props: React.ComponentProps) => { } describe('RobotSettings OpenJupyterControl', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotIp: mockIpAddress, diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/Troubleshooting.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/Troubleshooting.test.tsx index a5f28ee7da2..ef9cc7ff185 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/Troubleshooting.test.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/AdvancedTab/__tests__/Troubleshooting.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { act, waitFor, screen } from '@testing-library/react' import { when } from 'vitest-when' @@ -16,6 +15,7 @@ import { import { useRobot } from '/app/redux-resources/robots' import { Troubleshooting } from '../Troubleshooting' +import type { ComponentProps } from 'react' import type { HostConfig } from '@opentrons/api-client' import type { ToasterContextType } from '/app/organisms/ToasterOven/ToasterContext' @@ -29,7 +29,7 @@ const HOST_CONFIG: HostConfig = { hostname: 'localhost' } const MOCK_MAKE_TOAST = vi.fn() const MOCK_EAT_TOAST = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -39,7 +39,7 @@ const render = (props: React.ComponentProps) => { } describe('RobotSettings Troubleshooting', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotName: ROBOT_NAME, diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/FormModal.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/FormModal.tsx index 60ce3d2a88e..73f6004eb73 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/FormModal.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/FormModal.tsx @@ -1,17 +1,17 @@ import { Controller } from 'react-hook-form' import styled, { css } from 'styled-components' - +import { useTranslation } from 'react-i18next' import { FONT_SIZE_BODY_1, BUTTON_TYPE_SUBMIT, Flex, } from '@opentrons/components' +import { SECURITY_WPA_PSK, SECURITY_WPA_EAP } from '/app/redux/networking' import { ScrollableAlertModal } from '/app/molecules/modals' import { TextField } from './TextField' import { KeyFileField } from './KeyFileField' import { SecurityField } from './SecurityField' import { FIELD_TYPE_KEY_FILE, FIELD_TYPE_SECURITY } from '../constants' -import * as Copy from '../i18n' import type { Control } from 'react-hook-form' import type { ConnectFormField, ConnectFormValues, WifiNetwork } from '../types' @@ -53,16 +53,23 @@ export interface FormModalProps { export const FormModal = (props: FormModalProps): JSX.Element => { const { id, network, fields, isValid, onCancel, control } = props + const { t } = useTranslation(['device_settings', 'shared']) const heading = network !== null - ? Copy.CONNECT_TO_SSID(network.ssid) - : Copy.FIND_AND_JOIN_A_NETWORK + ? t('connect_to_ssid', { ssid: network.ssid }) + : t('find_and_join_network') - const body = - network !== null - ? Copy.NETWORK_REQUIRES_SECURITY(network) - : Copy.ENTER_NAME_AND_SECURITY_TYPE + let bodyText = t('enter_name_security_type') + if (network != null) { + if (network.securityType === SECURITY_WPA_PSK) { + bodyText = t('network_requires_wpa_password', { ssid: network.ssid }) + } else if (network.securityType === SECURITY_WPA_EAP) { + bodyText = t('network_requires_auth', { ssid: network.ssid }) + } else { + bodyText = t('network_is_unsecured', { ssid: network.ssid }) + } + } return ( { iconName="wifi" onCloseClick={onCancel} buttons={[ - { children: Copy.CANCEL, onClick: props.onCancel }, + { children: t('shared:cancel'), onClick: props.onCancel }, { - children: Copy.CONNECT, + children: t('connect'), type: BUTTON_TYPE_SUBMIT, form: id, disabled: !isValid, }, ]} > - {body} + {bodyText} {fields.map(fieldProps => { const { name } = fieldProps diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/FormRow.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/FormRow.tsx index 1481d3f40f9..fddda5bc96a 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/FormRow.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/FormRow.tsx @@ -1,12 +1,13 @@ // presentational components for the wifi connect form -import type * as React from 'react' import styled from 'styled-components' import { FONT_WEIGHT_SEMIBOLD, SPACING } from '@opentrons/components' +import type { ReactNode } from 'react' + export interface FormRowProps { label: string labelFor: string - children: React.ReactNode + children: ReactNode } const StyledRow = styled.div` diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/KeyFileField.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/KeyFileField.tsx index 376048ba420..b7ea68d5e36 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/KeyFileField.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/KeyFileField.tsx @@ -1,9 +1,9 @@ import { useRef } from 'react' +import { useTranslation } from 'react-i18next' import { SelectField } from '@opentrons/components' import { FormRow } from './FormRow' import { UploadKeyInput } from './UploadKeyInput' -import { LABEL_ADD_NEW_KEY } from '../i18n' import { useConnectFormField } from './form-state' import type { WifiKey } from '../types' @@ -27,10 +27,6 @@ export interface KeyFileFieldProps { const ADD_NEW_KEY_VALUE = '__addNewKey__' -const ADD_NEW_KEY_OPTION_GROUP = { - options: [{ value: ADD_NEW_KEY_VALUE, label: LABEL_ADD_NEW_KEY }], -} - const makeKeyOptions = ( keys: WifiKey[] ): { options: Array<{ value: string; label: string }> } => ({ @@ -48,6 +44,11 @@ export const KeyFileField = (props: KeyFileFieldProps): JSX.Element => { field, fieldState, } = props + const { t } = useTranslation('device_settings') + const ADD_NEW_KEY_OPTION_GROUP = { + options: [{ value: ADD_NEW_KEY_VALUE, label: t('add_new') }], + } + const { value, error, setValue, setTouched } = useConnectFormField( field, fieldState @@ -81,7 +82,7 @@ export const KeyFileField = (props: KeyFileFieldProps): JSX.Element => { diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/SecurityField.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/SecurityField.tsx index c9fa4e0c069..cb7c2cbf615 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/SecurityField.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/SecurityField.tsx @@ -1,7 +1,7 @@ +import { useTranslation } from 'react-i18next' import { SelectField } from '@opentrons/components' import { SECURITY_NONE, SECURITY_WPA_PSK } from '../constants' -import { LABEL_SECURITY_NONE, LABEL_SECURITY_PSK } from '../i18n' import { useConnectFormField } from './form-state' import { FormRow } from './FormRow' @@ -25,8 +25,8 @@ export interface SecurityFieldProps { } const ALL_SECURITY_OPTIONS = [ - { options: [{ value: SECURITY_NONE, label: LABEL_SECURITY_NONE }] }, - { options: [{ value: SECURITY_WPA_PSK, label: LABEL_SECURITY_PSK }] }, + { options: [{ value: SECURITY_NONE, label: 'shared:none' }] }, + { options: [{ value: SECURITY_WPA_PSK, label: 'wpa2_personal' }] }, ] const makeEapOptionsGroup = ( @@ -39,6 +39,7 @@ const makeEapOptionsGroup = ( }) export const SecurityField = (props: SecurityFieldProps): JSX.Element => { + const { t } = useTranslation(['device_settings', 'shared']) const { id, name, @@ -62,7 +63,7 @@ export const SecurityField = (props: SecurityFieldProps): JSX.Element => { ] return ( - + { + const { t } = useTranslation('device_settings') const { id, name, label, isPassword, className, field, fieldState } = props const { value, error, onChange, onBlur } = useConnectFormField( field, @@ -42,7 +43,7 @@ export const TextField = (props: TextFieldProps): JSX.Element => { /> {isPassword && ( diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/__tests__/form-fields.test.ts b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/__tests__/form-fields.test.ts deleted file mode 100644 index 80336fb0139..00000000000 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/__tests__/form-fields.test.ts +++ /dev/null @@ -1,379 +0,0 @@ -import * as Fixtures from '/app/redux/networking/__fixtures__' -import { describe, it, expect } from 'vitest' - -import { - CONFIGURE_FIELD_SSID, - CONFIGURE_FIELD_PSK, - CONFIGURE_FIELD_SECURITY_TYPE, - SECURITY_WPA_EAP, - SECURITY_WPA_PSK, - SECURITY_NONE, -} from '/app/redux/networking' - -import { - FIELD_TYPE_TEXT, - FIELD_TYPE_KEY_FILE, - FIELD_TYPE_SECURITY, -} from '../../constants' - -import { - LABEL_SECURITY, - LABEL_SSID, - LABEL_PSK, - SELECT_AUTHENTICATION_METHOD, - SELECT_FILE, -} from '../../i18n' - -import { - getConnectFormFields, - validateConnectFormFields, - connectFormToConfigureRequest, -} from '../form-fields' - -describe('getConnectFormFields', () => { - it('should add a string field for SSID if network is unknown', () => { - const fields = getConnectFormFields(null, 'robot-name', [], [], {}) - - expect(fields).toContainEqual({ - type: FIELD_TYPE_TEXT, - name: CONFIGURE_FIELD_SSID, - label: `* ${LABEL_SSID}`, - isPassword: false, - }) - }) - - it('should add a security dropdown field if network is unknown', () => { - const eapOptions = [Fixtures.mockEapOption] - const fields = getConnectFormFields(null, 'robot-name', eapOptions, [], {}) - - expect(fields).toContainEqual({ - type: FIELD_TYPE_SECURITY, - name: CONFIGURE_FIELD_SECURITY_TYPE, - label: `* ${LABEL_SECURITY}`, - eapOptions, - showAllOptions: true, - placeholder: SELECT_AUTHENTICATION_METHOD, - }) - }) - - it('should add a security dropdown field if known network has EAP security', () => { - const eapOptions = [Fixtures.mockEapOption] - const network = { - ...Fixtures.mockWifiNetwork, - securityType: SECURITY_WPA_EAP, - } - const fields = getConnectFormFields( - network, - 'robot-name', - eapOptions, - [], - {} - ) - - expect(fields).toContainEqual({ - type: FIELD_TYPE_SECURITY, - name: CONFIGURE_FIELD_SECURITY_TYPE, - label: `* ${LABEL_SECURITY}`, - eapOptions, - showAllOptions: false, - placeholder: SELECT_AUTHENTICATION_METHOD, - }) - }) - - it('should add a password field for PSK if known network as PSK security', () => { - const network = { - ...Fixtures.mockWifiNetwork, - securityType: SECURITY_WPA_PSK, - } - const fields = getConnectFormFields(network, 'robot-name', [], [], {}) - - expect(fields).toContainEqual({ - type: FIELD_TYPE_TEXT, - name: CONFIGURE_FIELD_PSK, - label: `* ${LABEL_PSK}`, - isPassword: true, - }) - }) - - it('should add a password field for PSK if unknown network and user selects PSK', () => { - const fields = getConnectFormFields(null, 'robot-name', [], [], { - securityType: SECURITY_WPA_PSK, - }) - - expect(fields).toContainEqual({ - type: FIELD_TYPE_TEXT, - name: CONFIGURE_FIELD_PSK, - label: `* ${LABEL_PSK}`, - isPassword: true, - }) - }) - - it('should add EAP options based on the selected eapType if network is unknown', () => { - const eapOptions = [ - { ...Fixtures.mockEapOption, name: 'someEapType', options: [] }, - { ...Fixtures.mockEapOption, name: 'someOtherEapType' }, - ] - const wifiKeys = [Fixtures.mockWifiKey] - const fields = getConnectFormFields( - null, - 'robot-name', - eapOptions, - wifiKeys, - { - securityType: 'someOtherEapType', - } - ) - - expect(fields).toEqual( - expect.arrayContaining([ - { - type: FIELD_TYPE_TEXT, - name: 'eapConfig.stringField', - label: '* String Field', - isPassword: false, - }, - { - type: FIELD_TYPE_TEXT, - name: 'eapConfig.passwordField', - label: 'Password Field', - isPassword: true, - }, - { - type: FIELD_TYPE_KEY_FILE, - name: 'eapConfig.fileField', - label: '* File Field', - robotName: 'robot-name', - wifiKeys, - placeholder: SELECT_FILE, - }, - ]) - ) - }) - - it('should add EAP options based on the selected eapType if network is EAP', () => { - const network = { - ...Fixtures.mockWifiNetwork, - securityType: SECURITY_WPA_EAP, - } - const eapOptions = [ - { ...Fixtures.mockEapOption, name: 'someEapType' }, - { ...Fixtures.mockEapOption, name: 'someOtherEapType', options: [] }, - ] - const wifiKeys = [Fixtures.mockWifiKey] - const fields = getConnectFormFields( - network, - 'robot-name', - eapOptions, - wifiKeys, - { securityType: 'someEapType' } - ) - - expect(fields).toEqual( - expect.arrayContaining([ - { - type: FIELD_TYPE_TEXT, - name: 'eapConfig.stringField', - label: '* String Field', - isPassword: false, - }, - { - type: FIELD_TYPE_TEXT, - name: 'eapConfig.passwordField', - label: 'Password Field', - isPassword: true, - }, - { - type: FIELD_TYPE_KEY_FILE, - name: 'eapConfig.fileField', - label: '* File Field', - robotName: 'robot-name', - wifiKeys, - placeholder: SELECT_FILE, - }, - ]) - ) - }) -}) - -describe('validateConnectFormFields', () => { - it('should error if network is hidden and ssid is blank', () => { - const errors = validateConnectFormFields( - null, - [], - { - securityType: SECURITY_WPA_PSK, - psk: '12345678', - }, - {} - ) - - expect(errors).toEqual({ - ssid: { message: `${LABEL_SSID} is required`, type: 'ssidError' }, - }) - }) - - it('should error if network is hidden and securityType is blank', () => { - const errors = validateConnectFormFields(null, [], { ssid: 'foobar' }, {}) - - expect(errors).toEqual({ - securityType: { - message: `${LABEL_SECURITY} is required`, - type: 'securityTypeError', - }, - }) - }) - - it('should error if network is PSK and psk is blank', () => { - const network = { - ...Fixtures.mockWifiNetwork, - securityType: SECURITY_WPA_PSK, - } - const errors = validateConnectFormFields(network, [], { psk: '' }, {}) - - expect(errors).toEqual({ - psk: { - message: `${LABEL_PSK} must be at least 8 characters`, - type: 'pskError', - }, - }) - }) - - it('should error if selected security is PSK and psk is blank', () => { - const values = { ssid: 'foobar', securityType: SECURITY_WPA_PSK } - const errors = validateConnectFormFields(null, [], values, {}) - - expect(errors).toEqual({ - psk: { - message: `${LABEL_PSK} must be at least 8 characters`, - type: 'pskError', - }, - }) - }) - - it('should error if network is EAP and securityType is blank', () => { - const network = { - ...Fixtures.mockWifiNetwork, - securityType: SECURITY_WPA_EAP, - } - const errors = validateConnectFormFields(network, [], {}, {}) - - expect(errors).toEqual({ - securityType: { - message: `${LABEL_SECURITY} is required`, - type: 'securityTypeError', - }, - }) - }) - - it('should error if any required EAP fields are missing', () => { - const network = { - ...Fixtures.mockWifiNetwork, - securityType: SECURITY_WPA_EAP, - } - const eapOptions = [ - { ...Fixtures.mockEapOption, name: 'someEapType', options: [] }, - { ...Fixtures.mockEapOption, name: 'someOtherEapType' }, - ] - const values = { - securityType: 'someOtherEapType', - eapConfig: { fileField: '123' }, - } - const errors = validateConnectFormFields(network, eapOptions, values, {}) - - expect(errors).toEqual({ - 'eapConfig.stringField': { - message: `String Field is required`, - type: 'eapError', - }, - }) - }) -}) - -describe('connectFormToConfigureRequest', () => { - it('should return null if unknown network and no ssid', () => { - const values = { securityType: SECURITY_NONE } - const result = connectFormToConfigureRequest(null, values) - - expect(result).toEqual(null) - }) - - it('should set ssid and securityType from values if unknown network', () => { - const values = { ssid: 'foobar', securityType: SECURITY_NONE } - const result = connectFormToConfigureRequest(null, values) - - expect(result).toEqual({ - ssid: 'foobar', - securityType: SECURITY_NONE, - hidden: true, - }) - }) - - it('should set ssid from network if known', () => { - const network = { - ...Fixtures.mockWifiNetwork, - ssid: 'foobar', - securityType: SECURITY_NONE, - } - const values = {} - const result = connectFormToConfigureRequest(network, values) - - expect(result).toEqual({ - ssid: 'foobar', - securityType: SECURITY_NONE, - hidden: false, - }) - }) - - it('should set psk from values', () => { - const network = { - ...Fixtures.mockWifiNetwork, - ssid: 'foobar', - securityType: SECURITY_WPA_PSK, - } - const values = { psk: '12345678' } - const result = connectFormToConfigureRequest(network, values) - - expect(result).toEqual({ - ssid: 'foobar', - securityType: SECURITY_WPA_PSK, - hidden: false, - psk: '12345678', - }) - }) - - it('should set eapConfig from values with known network', () => { - const network = { - ...Fixtures.mockWifiNetwork, - ssid: 'foobar', - securityType: SECURITY_WPA_EAP, - } - const values = { - securityType: 'someEapType', - eapConfig: { option1: 'fizzbuzz' }, - } - const result = connectFormToConfigureRequest(network, values) - - expect(result).toEqual({ - ssid: 'foobar', - securityType: SECURITY_WPA_EAP, - hidden: false, - eapConfig: { eapType: 'someEapType', option1: 'fizzbuzz' }, - }) - }) - - it('should set eapConfig from values with unknown network', () => { - const values = { - ssid: 'foobar', - securityType: 'someEapType', - eapConfig: { option1: 'fizzbuzz' }, - } - const result = connectFormToConfigureRequest(null, values) - - expect(result).toEqual({ - ssid: 'foobar', - securityType: SECURITY_WPA_EAP, - hidden: true, - eapConfig: { eapType: 'someEapType', option1: 'fizzbuzz' }, - }) - }) -}) diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/__tests__/form-fields.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/__tests__/form-fields.test.tsx new file mode 100644 index 00000000000..638d9f1b76c --- /dev/null +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/__tests__/form-fields.test.tsx @@ -0,0 +1,422 @@ +import * as Fixtures from '/app/redux/networking/__fixtures__' +import { describe, it, expect } from 'vitest' +import { screen } from '@testing-library/react' +import { useTranslation } from 'react-i18next' + +import { + SECURITY_WPA_EAP, + SECURITY_WPA_PSK, + SECURITY_NONE, +} from '/app/redux/networking' +import { renderWithProviders } from '/app/__testing-utils__' +import { i18n } from '/app/i18n' + +import { + getConnectFormFields, + validateConnectFormFields, + connectFormToConfigureRequest, +} from '../form-fields' + +import type { FieldError } from 'react-hook-form' +import type { + WifiNetwork, + WifiKey, + EapOption, + ConnectFormValues, +} from '../../types' +import type { ComponentProps } from 'react' + +const TestWrapperConnectFormFields = ({ + network, + robotName, + eapOptions, + wifiKeys, + values, +}: { + network: WifiNetwork | null + robotName: string + eapOptions: EapOption[] + wifiKeys: WifiKey[] + values: ConnectFormValues +}) => { + const { t } = useTranslation('device_settings') + const fields = getConnectFormFields( + network, + robotName, + eapOptions, + wifiKeys, + values, + t + ) + return
        {JSON.stringify(fields)}
        +} + +const renderConnectFormFields = ( + props: ComponentProps +) => { + return renderWithProviders(, { + i18nInstance: i18n, + })[0] +} + +describe('getConnectFormFields', () => { + it('should add a string field for SSID if network is unknown', () => { + const props = { + network: null, + robotName: 'robot-name', + eapOptions: [], + wifiKeys: [], + values: {}, + } + renderConnectFormFields(props) + screen.getByText(/text/) + screen.getByText(/ssid/) + screen.getByText(/ * Network Name/) + }) + + it('should add a security dropdown field if network is unknown', () => { + const props = { + network: null, + robotName: 'robot-name', + eapOptions: [Fixtures.mockEapOption], + wifiKeys: [], + values: {}, + } + renderConnectFormFields(props) + screen.getByText(/security/) + screen.getByText(/ * Authentication/) + screen.getByText(/Select authentication method/) + }) + + it('should add a security dropdown field if known network has EAP security', () => { + const network = { + ...Fixtures.mockWifiNetwork, + securityType: SECURITY_WPA_EAP, + } + const props = { + network: network, + robotName: 'robot-name', + eapOptions: [Fixtures.mockEapOption], + wifiKeys: [], + values: {}, + } + renderConnectFormFields(props) + + screen.getByText(/security/) + screen.getByText(/ * Authentication/) + screen.getByText(/Select authentication method/) + screen.getByText(/EAP Option/) + screen.getByText(/String Field/) + screen.getByText(/Password Field/) + screen.getByText(/File Field/) + }) + + it('should add a password field for PSK if known network as PSK security', () => { + const network = { + ...Fixtures.mockWifiNetwork, + securityType: SECURITY_WPA_PSK, + } + const props = { + network: network, + robotName: 'robot-name', + eapOptions: [], + wifiKeys: [], + values: {}, + } + renderConnectFormFields(props) + + screen.getByText(/psk/) + screen.getByText(/ * Password/) + }) + + it('should add a password field for PSK if unknown network and user selects PSK', () => { + const props = { + network: null, + robotName: 'robot-name', + eapOptions: [], + wifiKeys: [], + values: { securityType: SECURITY_WPA_PSK }, + } + + renderConnectFormFields(props) + screen.getByText(/psk/) + screen.getByText(/ * Password/) + }) + + it('should add EAP options based on the selected eapType if network is unknown', () => { + const eapOptions = [ + { ...Fixtures.mockEapOption, name: 'someEapType', options: [] }, + { ...Fixtures.mockEapOption, name: 'someOtherEapType' }, + ] + const wifiKeys = [Fixtures.mockWifiKey] + const props = { + network: null, + robotName: 'robot-name', + eapOptions: eapOptions, + wifiKeys: wifiKeys, + values: {}, + } + renderConnectFormFields(props) + + screen.getByText(/someEapType/) + screen.getByText(/someOtherEapType/) + screen.getByText(/stringField/) + screen.getByText(/String Field/) + screen.getByText(/passwordField/) + screen.getByText(/Password Field/) + screen.getByText(/fileField/) + screen.getByText(/File Field/) + }) + + it('should add EAP options based on the selected eapType if network is EAP', () => { + const network = { + ...Fixtures.mockWifiNetwork, + securityType: SECURITY_WPA_EAP, + } + const eapOptions = [ + { ...Fixtures.mockEapOption, name: 'someEapType' }, + { ...Fixtures.mockEapOption, name: 'someOtherEapType', options: [] }, + ] + const wifiKeys = [Fixtures.mockWifiKey] + const props = { + network: network, + robotName: 'robot-name', + eapOptions: eapOptions, + wifiKeys: wifiKeys, + values: { securityType: 'someEapType' }, + } + renderConnectFormFields(props) + screen.getByText(/ * String Field/) + screen.getByText(/Password Field/) + screen.getByText(/ * File Field/) + }) +}) + +const TestWrapperValidateFormFields = ({ + network, + eapOptions, + values, + errors, +}: { + network: WifiNetwork | null + eapOptions: EapOption[] + values: ConnectFormValues + errors: Record +}) => { + const { t } = useTranslation('device_settings') + const validationErrors = validateConnectFormFields( + network, + eapOptions, + values, + errors, + t + ) + return
        {JSON.stringify(validationErrors)}
        +} + +const renderValidateFormFields = ( + props: ComponentProps +) => { + return renderWithProviders(, { + i18nInstance: i18n, + })[0] +} + +describe('validateConnectFormFields', () => { + it('should error if network is hidden and ssid is blank', () => { + const props = { + network: null, + eapOptions: [], + values: { + securityType: SECURITY_WPA_PSK, + psk: '12345678', + }, + errors: {}, + } + renderValidateFormFields(props) + screen.getByText(/ssid/) + screen.getByText(/ssidError/) + screen.getByText(/Network Name is required/) + }) + + it('should error if network is hidden and securityType is blank', () => { + const props = { + network: null, + eapOptions: [], + values: { + ssid: 'foobar', + }, + errors: {}, + } + renderValidateFormFields(props) + screen.getByText(/securityType/) + screen.getByText(/securityTypeError/) + screen.getByText(/Authentication is required/) + }) + + it('should error if network is PSK and psk is blank', () => { + const network = { + ...Fixtures.mockWifiNetwork, + securityType: SECURITY_WPA_PSK, + } + const props = { + network: network, + eapOptions: [], + values: { + psk: '', + }, + errors: {}, + } + renderValidateFormFields(props) + screen.getByText(/psk/) + screen.getByText(/pskError/) + screen.getByText(/Password must be at least 8 characters/) + }) + + it('should error if selected security is PSK and psk is blank', () => { + const values = { ssid: 'foobar', securityType: SECURITY_WPA_PSK } + const props = { + network: null, + eapOptions: [], + values: values, + errors: {}, + } + renderValidateFormFields(props) + screen.getByText(/psk/) + screen.getByText(/pskError/) + screen.getByText(/Password must be at least 8 characters/) + }) + + it('should error if network is EAP and securityType is blank', () => { + const network = { + ...Fixtures.mockWifiNetwork, + securityType: SECURITY_WPA_EAP, + } + const props = { + network: network, + eapOptions: [], + values: {}, + errors: {}, + } + + renderValidateFormFields(props) + screen.getByText(/securityType/) + screen.getByText(/securityTypeError/) + screen.getByText(/Authentication is required/) + }) + + it('should error if any required EAP fields are missing', () => { + const network = { + ...Fixtures.mockWifiNetwork, + securityType: SECURITY_WPA_EAP, + } + const eapOptions = [ + { ...Fixtures.mockEapOption, name: 'someEapType', options: [] }, + { ...Fixtures.mockEapOption, name: 'someOtherEapType' }, + ] + const values = { + securityType: 'someOtherEapType', + eapConfig: { fileField: '123' }, + } + const props = { + network: network, + eapOptions: eapOptions, + values: values, + errors: {}, + } + + renderValidateFormFields(props) + screen.getByText(/eapConfig.stringField/) + screen.getByText(/eapError/) + screen.getByText(/String Field is required/) + }) +}) + +describe('connectFormToConfigureRequest', () => { + it('should return null if unknown network and no ssid', () => { + const values = { securityType: SECURITY_NONE } + const result = connectFormToConfigureRequest(null, values) + + expect(result).toEqual(null) + }) + + it('should set ssid and securityType from values if unknown network', () => { + const values = { ssid: 'foobar', securityType: SECURITY_NONE } + const result = connectFormToConfigureRequest(null, values) + + expect(result).toEqual({ + ssid: 'foobar', + securityType: SECURITY_NONE, + hidden: true, + }) + }) + + it('should set ssid from network if known', () => { + const network = { + ...Fixtures.mockWifiNetwork, + ssid: 'foobar', + securityType: SECURITY_NONE, + } + const values = {} + const result = connectFormToConfigureRequest(network, values) + + expect(result).toEqual({ + ssid: 'foobar', + securityType: SECURITY_NONE, + hidden: false, + }) + }) + + it('should set psk from values', () => { + const network = { + ...Fixtures.mockWifiNetwork, + ssid: 'foobar', + securityType: SECURITY_WPA_PSK, + } + const values = { psk: '12345678' } + const result = connectFormToConfigureRequest(network, values) + + expect(result).toEqual({ + ssid: 'foobar', + securityType: SECURITY_WPA_PSK, + hidden: false, + psk: '12345678', + }) + }) + + it('should set eapConfig from values with known network', () => { + const network = { + ...Fixtures.mockWifiNetwork, + ssid: 'foobar', + securityType: SECURITY_WPA_EAP, + } + const values = { + securityType: 'someEapType', + eapConfig: { option1: 'fizzbuzz' }, + } + const result = connectFormToConfigureRequest(network, values) + + expect(result).toEqual({ + ssid: 'foobar', + securityType: SECURITY_WPA_EAP, + hidden: false, + eapConfig: { eapType: 'someEapType', option1: 'fizzbuzz' }, + }) + }) + + it('should set eapConfig from values with unknown network', () => { + const values = { + ssid: 'foobar', + securityType: 'someEapType', + eapConfig: { option1: 'fizzbuzz' }, + } + const result = connectFormToConfigureRequest(null, values) + + expect(result).toEqual({ + ssid: 'foobar', + securityType: SECURITY_WPA_EAP, + hidden: true, + eapConfig: { eapType: 'someEapType', option1: 'fizzbuzz' }, + }) + }) +}) diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/form-fields.ts b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/form-fields.ts index 1a91d2ac994..b8caeb3824c 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/form-fields.ts +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/form-fields.ts @@ -1,9 +1,9 @@ import get from 'lodash/get' import * as Constants from '../constants' -import * as Copy from '../i18n' import type { FieldError } from 'react-hook-form' +import type { TFunction } from 'i18next' import type { WifiNetwork, WifiKey, @@ -24,28 +24,29 @@ type Errors = Record export const renderLabel = (label: string, required: boolean): string => `${required ? '* ' : ''}${label}` -const FIELD_SSID: ConnectFormTextField = { +const makeFieldSsid = (t: TFunction): ConnectFormTextField => ({ type: Constants.FIELD_TYPE_TEXT, name: Constants.CONFIGURE_FIELD_SSID, - label: renderLabel(Copy.LABEL_SSID, true), + label: renderLabel(t('network_name'), true), isPassword: false, -} +}) -const FIELD_PSK: ConnectFormTextField = { +const makeFieldPsk = (t: TFunction): ConnectFormTextField => ({ type: Constants.FIELD_TYPE_TEXT, name: Constants.CONFIGURE_FIELD_PSK, - label: renderLabel(Copy.LABEL_PSK, true), + label: renderLabel(t('password'), true), isPassword: true, -} +}) const makeSecurityField = ( eapOptions: EapOption[], - showAllOptions: boolean + showAllOptions: boolean, + t: TFunction ): ConnectFormSecurityField => ({ type: Constants.FIELD_TYPE_SECURITY, name: Constants.CONFIGURE_FIELD_SECURITY_TYPE, - label: renderLabel(Copy.LABEL_SECURITY, true), - placeholder: Copy.SELECT_AUTHENTICATION_METHOD, + label: renderLabel(t('authentication'), true), + placeholder: t('select_auth_method_short'), eapOptions, showAllOptions, }) @@ -77,21 +78,22 @@ export function getConnectFormFields( robotName: string, eapOptions: EapOption[], wifiKeys: WifiKey[], - values: ConnectFormValues + values: ConnectFormValues, + t: TFunction ): ConnectFormField[] { const { securityType: formSecurityType } = values const fields = [] // if the network is unknown, display a field to enter the SSID if (network === null) { - fields.push(FIELD_SSID) + fields.push(makeFieldSsid(t)) } // if the network is unknown or the known network is EAP, display a // security dropdown; security dropdown will handle which options to // display based on known or unknown network if (!network || network.securityType === Constants.SECURITY_WPA_EAP) { - fields.push(makeSecurityField(eapOptions, !network)) + fields.push(makeSecurityField(eapOptions, !network, t)) } // if known network is PSK or network is unknown and user has selected PSK @@ -100,7 +102,7 @@ export function getConnectFormFields( network?.securityType === Constants.SECURITY_WPA_PSK || formSecurityType === Constants.SECURITY_WPA_PSK ) { - fields.push(FIELD_PSK) + fields.push(makeFieldPsk(t)) } // if known network is EAP or user selected EAP, map eap options to fields @@ -121,7 +123,7 @@ export function getConnectFormFields( label, robotName, wifiKeys, - placeholder: Copy.SELECT_FILE, + placeholder: t('select_file'), } } @@ -142,7 +144,8 @@ export function validateConnectFormFields( network: WifiNetwork | null, eapOptions: EapOption[], values: ConnectFormValues, - errors: Errors + errors: Errors, + t: TFunction ): Errors { const { ssid: formSsid, @@ -152,7 +155,7 @@ export function validateConnectFormFields( let errorMessage: string | undefined if (network === null && (formSsid == null || formSsid.length === 0)) { - errorMessage = Copy.FIELD_IS_REQUIRED(Copy.LABEL_SSID) + errorMessage = t('field_is_required', { field: t('network_name') }) return errorMessage != null ? { ...errors, @@ -168,7 +171,7 @@ export function validateConnectFormFields( (network === null || network.securityType === Constants.SECURITY_WPA_EAP) && !formSecurityType ) { - errorMessage = Copy.FIELD_IS_REQUIRED(Copy.LABEL_SECURITY) + errorMessage = t('field_is_required', { field: t('authentication') }) return errorMessage != null ? { ...errors, @@ -185,10 +188,9 @@ export function validateConnectFormFields( formSecurityType === Constants.SECURITY_WPA_PSK) && (!formPsk || formPsk.length < Constants.CONFIGURE_PSK_MIN_LENGTH) ) { - errorMessage = Copy.FIELD_NOT_LONG_ENOUGH( - Copy.LABEL_PSK, - Constants.CONFIGURE_PSK_MIN_LENGTH - ) + errorMessage = t('password_not_long_enough', { + minLength: Constants.CONFIGURE_PSK_MIN_LENGTH, + }) return errorMessage != null ? { ...errors, @@ -215,7 +217,9 @@ export function validateConnectFormFields( ) => { const fieldName = getEapFieldName(name) const errorMessage = - displayName != null ? Copy.FIELD_IS_REQUIRED(displayName) : '' + displayName != null + ? t('field_is_required', { field: displayName }) + : '' if (errorMessage != null) { acc[fieldName] = { diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/index.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/index.tsx index 3e1c731d33e..2b5d228c12b 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/index.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ConnectModal/index.tsx @@ -1,4 +1,5 @@ import { useForm } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { useResetFormOnSecurityChange } from './form-state' import { @@ -10,6 +11,7 @@ import { import { FormModal } from './FormModal' import type { Control, Resolver } from 'react-hook-form' +import type { TFunction } from 'i18next' import type { ConnectFormValues, WifiConfigureRequest, @@ -35,6 +37,7 @@ interface ConnectModalComponentProps extends ConnectModalProps { } export const ConnectModal = (props: ConnectModalProps): JSX.Element => { + const { t } = useTranslation(['device_settings', 'shared']) const { network, eapOptions, onConnect } = props const onSubmit = (values: ConnectFormValues): void => { @@ -45,7 +48,13 @@ export const ConnectModal = (props: ConnectModalProps): JSX.Element => { const handleValidate: Resolver = values => { let errors = {} - errors = validateConnectFormFields(network, eapOptions, values, errors) + errors = validateConnectFormFields( + network, + eapOptions, + values, + errors, + t as TFunction + ) return { values, errors } } @@ -78,6 +87,7 @@ export const ConnectModal = (props: ConnectModalProps): JSX.Element => { export const ConnectModalComponent = ( props: ConnectModalComponentProps ): JSX.Element => { + const { t } = useTranslation(['device_settings', 'shared']) const { robotName, network, @@ -95,7 +105,8 @@ export const ConnectModalComponent = ( robotName, eapOptions, wifiKeys, - values + values, + t as TFunction ) useResetFormOnSecurityChange() diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ResultModal.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ResultModal.tsx index 6628c35dfc5..3a372c6df66 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ResultModal.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/ResultModal.tsx @@ -1,6 +1,6 @@ +import { useTranslation } from 'react-i18next' import { AlertModal, SpinnerModal } from '@opentrons/components' -import * as Copy from './i18n' import { ErrorModal } from '/app/molecules/modals' import { DISCONNECT } from './constants' import { PENDING, FAILURE } from '/app/redux/robot-api' @@ -18,29 +18,32 @@ export interface ResultModalProps { export const ResultModal = (props: ResultModalProps): JSX.Element => { const { type, ssid, requestStatus, error, onClose } = props + const { t } = useTranslation(['device_settings', 'shared']) const isDisconnect = type === DISCONNECT if (requestStatus === PENDING) { const message = isDisconnect - ? Copy.DISCONNECTING_FROM_NETWORK(ssid) - : Copy.CONNECTING_TO_NETWORK(ssid) + ? t('disconnecting_from_wifi_network', { ssid: ssid }) + : t('connecting_to_wifi_network', { ssid: ssid }) return } if (error || requestStatus === FAILURE) { const heading = isDisconnect - ? Copy.UNABLE_TO_DISCONNECT - : Copy.UNABLE_TO_CONNECT + ? t('unable_to_disconnect') + : t('unable_to_connect') const message = isDisconnect - ? Copy.YOUR_ROBOT_WAS_UNABLE_TO_DISCONNECT(ssid) - : Copy.YOUR_ROBOT_WAS_UNABLE_TO_CONNECT(ssid) + ? t('disconnect_from_wifi_network_failure', { ssid: ssid }) + : t('connect_to_wifi_network_failure', { ssid: ssid }) - const retryMessage = !isDisconnect ? ` ${Copy.CHECK_YOUR_CREDENTIALS}.` : '' + const retryMessage = !isDisconnect ? t('please_check_credentials') : '' const placeholderError = { - message: `Likely incorrect network password. ${Copy.CHECK_YOUR_CREDENTIALS}.`, + message: `${t('likely_incorrect_password')} ${t( + 'please_check_credentials' + )}.`, } return ( @@ -54,12 +57,12 @@ export const ResultModal = (props: ResultModalProps): JSX.Element => { } const heading = isDisconnect - ? Copy.SUCCESSFULLY_DISCONNECTED - : Copy.SUCCESSFULLY_CONNECTED + ? t('successfully_disconnected') + : t('successfully_connected_to_wifi') const message = isDisconnect - ? Copy.YOUR_ROBOT_HAS_DISCONNECTED(ssid) - : Copy.YOUR_ROBOT_HAS_CONNECTED(ssid) + ? t('disconnect_from_wifi_network_success') + : t('successfully_connected_to_ssid', { ssid: ssid }) return ( { iconName="wifi" heading={heading} onCloseClick={props.onClose} - buttons={[{ children: Copy.CLOSE, onClick: onClose }]} + buttons={[{ children: t('shared:close'), onClick: onClose }]} > {message} diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/SelectSsid/index.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/SelectSsid/index.tsx index b85cc72d563..02de4cfabaa 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/SelectSsid/index.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/SelectSsid/index.tsx @@ -1,11 +1,11 @@ -import type * as React from 'react' +import { useTranslation } from 'react-i18next' import { CONTEXT_MENU } from '@opentrons/components' import { SelectField } from '/app/atoms/SelectField' -import * as Copy from '../i18n' import { NetworkOptionLabel, NetworkActionLabel } from './NetworkOptionLabel' +import type { ComponentProps } from 'react' +import type { TFunction } from 'i18next' import type { SelectOptionOrGroup } from '@opentrons/components' - import type { WifiNetwork } from '../types' export interface SelectSsidProps { @@ -20,11 +20,16 @@ const FIELD_NAME = '__SelectSsid__' const JOIN_OTHER_VALUE = '__join-other-network__' -const SELECT_JOIN_OTHER_GROUP = { - options: [{ value: JOIN_OTHER_VALUE, label: Copy.LABEL_JOIN_OTHER_NETWORK }], -} +const formatOptions = ( + list: WifiNetwork[], + t: TFunction +): SelectOptionOrGroup[] => { + const SELECT_JOIN_OTHER_GROUP = { + options: [ + { value: JOIN_OTHER_VALUE, label: `${t('join_other_network')}...` }, + ], + } -const formatOptions = (list: WifiNetwork[]): SelectOptionOrGroup[] => { const ssidOptionsList = { options: list?.map(({ ssid }) => ({ value: ssid })), } @@ -34,6 +39,7 @@ const formatOptions = (list: WifiNetwork[]): SelectOptionOrGroup[] => { } export function SelectSsid(props: SelectSsidProps): JSX.Element { + const { t } = useTranslation('device_settings') const { list, value, onConnect, onJoinOther, isRobotBusy } = props const handleValueChange = (_: string, value: string): void => { @@ -44,7 +50,7 @@ export function SelectSsid(props: SelectSsidProps): JSX.Element { } } - const formatOptionLabel: React.ComponentProps< + const formatOptionLabel: ComponentProps< typeof SelectField >['formatOptionLabel'] = (option, { context }): JSX.Element | null => { const { value, label } = option @@ -69,8 +75,8 @@ export function SelectSsid(props: SelectSsidProps): JSX.Element { disabled={isRobotBusy} name={FIELD_NAME} value={value} - options={formatOptions(list)} - placeholder={Copy.SELECT_NETWORK} + options={formatOptions(list, t as TFunction)} + placeholder={t('choose_a_network')} onValueChange={handleValueChange} formatOptionLabel={formatOptionLabel} width="16rem" diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/i18n.ts b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/i18n.ts deleted file mode 100644 index cfee1e77d89..00000000000 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/i18n.ts +++ /dev/null @@ -1,103 +0,0 @@ -// TODO(mc, 2020-03-11): i18n -import { - SECURITY_WPA_PSK, - SECURITY_WPA_EAP, - SECURITY_NONE, -} from '/app/redux/networking' - -import type { WifiNetwork } from './types' - -const SECURITY_DESC = { - [SECURITY_WPA_PSK]: 'requires a WPA2 password', - [SECURITY_WPA_EAP]: 'requires 802.1X authentication', - [SECURITY_NONE]: 'is unsecured', -} - -export const FIND_AND_JOIN_A_NETWORK = 'Find and join a Wi-Fi network' - -export const ENTER_NAME_AND_SECURITY_TYPE = - 'Enter the network name and security type.' - -const WIFI_NETWORK = 'Wi-Fi network' - -export const CANCEL = 'cancel' - -export const CONNECT = 'connect' - -export const CLOSE = 'close' - -export const DISCONNECT = 'disconnect' - -export const LABEL_SSID = 'Network Name (SSID)' - -export const LABEL_PSK = 'Password' - -export const LABEL_SECURITY = 'Authentication' - -export const LABEL_SECURITY_NONE = 'None' - -export const LABEL_SECURITY_PSK = 'WPA2 Personal' - -export const LABEL_ADD_NEW_KEY = 'Add new...' - -export const LABEL_SHOW_PASSWORD = 'Show password' - -export const LABEL_JOIN_OTHER_NETWORK = 'Join other network...' - -export const SELECT_AUTHENTICATION_METHOD = 'Select authentication method' - -export const SELECT_FILE = 'Select file' - -export const SELECT_NETWORK = 'Choose a network...' - -export const SUCCESSFULLY_DISCONNECTED = 'Successfully disconnected from Wi-Fi' - -export const SUCCESSFULLY_CONNECTED = 'Successfully connected to Wi-Fi' - -export const UNABLE_TO_DISCONNECT = 'Unable to disconnect from Wi-Fi' - -export const UNABLE_TO_CONNECT = 'Unable to connect to Wi-Fi' - -export const CHECK_YOUR_CREDENTIALS = - 'Please double-check your network credentials' - -export const CONNECT_TO_SSID = (ssid: string): string => `Connect to ${ssid}` - -export const DISCONNECT_FROM_SSID = (ssid: string): string => - `Disconnect from ${ssid}` - -export const ARE_YOU_SURE_YOU_WANT_TO_DISCONNECT = (ssid: string): string => - `Are you sure you want to disconnect from ${ssid}?` - -export const NETWORK_REQUIRES_SECURITY = (network: WifiNetwork): string => - `${WIFI_NETWORK} ${network.ssid} ${SECURITY_DESC[network.securityType]}` - -export const FIELD_IS_REQUIRED = (name: string): string => `${name} is required` - -export const FIELD_NOT_LONG_ENOUGH = ( - name: string, - minLength: number -): string => `${name} must be at least ${minLength} characters` - -const renderMaybeSsid = (ssid: string | null): string => - ssid !== null ? ` network ${ssid}` : '' - -export const CONNECTING_TO_NETWORK = (ssid: string | null): string => - `Connecting to Wi-Fi${renderMaybeSsid(ssid)}` - -export const DISCONNECTING_FROM_NETWORK = (ssid: string | null): string => - `Disconnecting from Wi-Fi${renderMaybeSsid(ssid)}` - -export const YOUR_ROBOT_WAS_UNABLE_TO_CONNECT = (ssid: string | null): string => - `Your robot was unable to connect to Wi-Fi${renderMaybeSsid(ssid)}` - -export const YOUR_ROBOT_WAS_UNABLE_TO_DISCONNECT = ( - ssid: string | null -): string => - `Your robot was unable to disconnect from Wi-Fi${renderMaybeSsid(ssid)}` - -export const YOUR_ROBOT_HAS_DISCONNECTED = (ssid: string | null): string => - `Your robot has successfully disconnected from Wi-Fi${renderMaybeSsid(ssid)}` - -export const YOUR_ROBOT_HAS_CONNECTED = (ssid: string | null): string => - `Your robot has successfully connected to Wi-Fi${renderMaybeSsid(ssid)}` diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/types.ts b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/types.ts index 050d26c08c3..ccfa37a7a44 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/types.ts +++ b/app/src/organisms/Desktop/Devices/RobotSettings/ConnectNetwork/types.ts @@ -1,3 +1,4 @@ +import type { ChangeEventHandler, FocusEventHandler } from 'react' import type { FieldError } from 'react-hook-form' import type { WifiNetwork, @@ -80,8 +81,8 @@ export type ConnectFormField = export type ConnectFormFieldProps = Readonly<{ value: string | null error: string | null - onChange: React.ChangeEventHandler - onBlur: React.FocusEventHandler + onChange: ChangeEventHandler + onBlur: FocusEventHandler setValue: (value: string) => unknown setTouched: (touched: boolean) => unknown }> diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/SettingToggle.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/SettingToggle.tsx index 4407779d888..bb5b3c0d824 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/SettingToggle.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/SettingToggle.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useDispatch } from 'react-redux' import { @@ -13,6 +12,8 @@ import { import { ToggleButton } from '/app/atoms/buttons' import { updateSetting } from '/app/redux/robot-settings' + +import type { MouseEventHandler } from 'react' import type { Dispatch } from '/app/redux/types' import type { RobotSettingsField } from '/app/redux/robot-settings/types' @@ -38,7 +39,7 @@ export function SettingToggle({ if (id == null) return null - const handleClick: React.MouseEventHandler = () => { + const handleClick: MouseEventHandler = () => { dispatch(updateSetting(robotName, id, !value)) } diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/UpdateRobotModal.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/UpdateRobotModal.tsx index 4b2225fe868..da77dee89c9 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/UpdateRobotModal.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/UpdateRobotModal.tsx @@ -88,7 +88,7 @@ export function UpdateRobotModal({ let disabledReason: string = '' if (updateFromFileDisabledReason) - disabledReason = updateFromFileDisabledReason + disabledReason = t(updateFromFileDisabledReason) else if (isRobotBusy) disabledReason = t('robot_busy_protocol') useEffect(() => { diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/RobotUpdateProgressModal.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/RobotUpdateProgressModal.test.tsx index a2139f636bd..927c1acfdc8 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/RobotUpdateProgressModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/RobotUpdateProgressModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { i18n } from '/app/i18n' import { act, fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -21,6 +20,7 @@ import { INIT_STATUS, } from '/app/resources/health/hooks' +import type { ComponentProps } from 'react' import type { SetStatusBarCreateCommand } from '@opentrons/shared-data' import type { RobotUpdateSession } from '/app/redux/robot-update/types' @@ -30,9 +30,7 @@ vi.mock('/app/redux/robot-update') vi.mock('/app/redux/robot-update/hooks') vi.mock('/app/resources/health/hooks') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -50,8 +48,9 @@ describe('DownloadUpdateModal', () => { error: null, } - let props: React.ComponentProps + let props: ComponentProps const mockCreateLiveCommand = vi.fn() + const mockDispatchStartRobotUpdate = vi.fn() beforeEach(() => { mockCreateLiveCommand.mockResolvedValue(null) @@ -68,7 +67,9 @@ describe('DownloadUpdateModal', () => { progressPercent: 50, }) vi.mocked(getRobotSessionIsManualFile).mockReturnValue(false) - vi.mocked(useDispatchStartRobotUpdate).mockReturnValue(vi.fn) + vi.mocked(useDispatchStartRobotUpdate).mockReturnValue( + mockDispatchStartRobotUpdate + ) vi.mocked(getRobotUpdateDownloadError).mockReturnValue(null) }) diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/UpdateRobotModal.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/UpdateRobotModal.test.tsx index e77a0df9533..e4c6b7bfb12 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/UpdateRobotModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/UpdateRobotModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { createStore } from 'redux' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -14,6 +13,7 @@ import { getDiscoverableRobotByName } from '/app/redux/discovery' import { UpdateRobotModal, RELEASE_NOTES_URL_BASE } from '../UpdateRobotModal' import { useIsRobotBusy } from '/app/redux-resources/robots' +import type { ComponentProps } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' @@ -21,14 +21,14 @@ vi.mock('/app/redux/robot-update') vi.mock('/app/redux/discovery') vi.mock('/app/redux-resources/robots') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('UpdateRobotModal', () => { - let props: React.ComponentProps + let props: ComponentProps let store: Store beforeEach(() => { store = createStore(vi.fn(), {}) diff --git a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/useRobotUpdateInfo.test.tsx b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/useRobotUpdateInfo.test.tsx index 2897110aacc..e81d61f1d5c 100644 --- a/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/useRobotUpdateInfo.test.tsx +++ b/app/src/organisms/Desktop/Devices/RobotSettings/UpdateBuildroot/__tests__/useRobotUpdateInfo.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { renderHook } from '@testing-library/react' import { createStore } from 'redux' import { I18nextProvider } from 'react-i18next' @@ -9,6 +8,7 @@ import { i18n } from '/app/i18n' import { useRobotUpdateInfo } from '../useRobotUpdateInfo' import { getRobotUpdateDownloadProgress } from '/app/redux/robot-update' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' import type { @@ -21,7 +21,7 @@ vi.mock('/app/redux/robot-update') describe('useRobotUpdateInfo', () => { let store: Store - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> const MOCK_ROBOT_NAME = 'testRobot' const mockRobotUpdateSession: RobotUpdateSession | null = { diff --git a/app/src/organisms/Desktop/Devices/RobotStatusHeader.tsx b/app/src/organisms/Desktop/Devices/RobotStatusHeader.tsx index 77cad0933ea..abeeceddc0b 100644 --- a/app/src/organisms/Desktop/Devices/RobotStatusHeader.tsx +++ b/app/src/organisms/Desktop/Devices/RobotStatusHeader.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { useSelector, useDispatch } from 'react-redux' import { Link, useNavigate } from 'react-router-dom' @@ -35,6 +34,7 @@ import { import { getNetworkInterfaces, fetchStatus } from '/app/redux/networking' import { useNotifyRunQuery, useCurrentRunId } from '/app/resources/runs' +import type { MouseEvent } from 'react' import type { IconName, StyleProps } from '@opentrons/components' import type { DiscoveredRobot } from '/app/redux/discovery/types' import type { Dispatch, State } from '/app/redux/types' @@ -79,7 +79,7 @@ export function RobotStatusHeader(props: RobotStatusHeaderProps): JSX.Element { currentRunId != null && currentRunStatus != null && displayName != null ? ( { + onClick={(e: MouseEvent) => { e.stopPropagation() }} > diff --git a/app/src/organisms/Desktop/Devices/RunPreview/index.tsx b/app/src/organisms/Desktop/Devices/RunPreview/index.tsx index c2a48aba59a..c217833593e 100644 --- a/app/src/organisms/Desktop/Devices/RunPreview/index.tsx +++ b/app/src/organisms/Desktop/Devices/RunPreview/index.tsx @@ -33,9 +33,11 @@ import { Divider } from '/app/atoms/structure' import { NAV_BAR_WIDTH } from '/app/App/constants' import { getLabwareDefinitionsFromCommands } from '/app/local-resources/labware' +import type { ForwardedRef } from 'react' +import type { ViewportListRef } from 'react-viewport-list' import type { RunStatus } from '@opentrons/api-client' import type { RobotType } from '@opentrons/shared-data' -import type { ViewportListRef } from 'react-viewport-list' + const COLOR_FADE_MS = 500 const LIVE_RUN_COMMANDS_POLL_MS = 3000 // arbitrary large number of commands @@ -49,7 +51,7 @@ interface RunPreviewProps { } export const RunPreviewComponent = ( { runId, jumpedIndex, makeHandleScrollToStep, robotType }: RunPreviewProps, - ref: React.ForwardedRef + ref: ForwardedRef ): JSX.Element | null => { const { t } = useTranslation(['run_details', 'protocol_setup']) const robotSideAnalysis = useMostRecentCompletedAnalysis(runId) diff --git a/app/src/organisms/Desktop/Devices/__tests__/CalibrationStatusBanner.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/CalibrationStatusBanner.test.tsx index fd50c5185ae..316315515b5 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/CalibrationStatusBanner.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/CalibrationStatusBanner.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -8,11 +7,11 @@ import { i18n } from '/app/i18n' import { CalibrationStatusBanner } from '../CalibrationStatusBanner' import { useCalibrationTaskList } from '../hooks' +import type { ComponentProps } from 'react' + vi.mock('../hooks') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -24,7 +23,7 @@ const render = ( } describe('CalibrationStatusBanner', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotName: 'otie' } }) diff --git a/app/src/organisms/Desktop/Devices/__tests__/ConnectionTroubleshootingModal.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/ConnectionTroubleshootingModal.test.tsx index d725929a081..a810d459f12 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/ConnectionTroubleshootingModal.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/ConnectionTroubleshootingModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -6,8 +5,10 @@ import { fireEvent, screen } from '@testing-library/react' import { i18n } from '/app/i18n' import { ConnectionTroubleshootingModal } from '../ConnectionTroubleshootingModal' +import type { ComponentProps } from 'react' + const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -15,7 +16,7 @@ const render = ( } describe('ConnectionTroubleshootingModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onClose: vi.fn(), diff --git a/app/src/organisms/Desktop/Devices/__tests__/EstopBanner.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/EstopBanner.test.tsx index 7d052568378..4e9f74b6310 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/EstopBanner.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/EstopBanner.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -6,11 +5,13 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { EstopBanner } from '../EstopBanner' -const render = (props: React.ComponentProps) => +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => renderWithProviders(, { i18nInstance: i18n }) describe('EstopBanner', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { status: 'physicallyEngaged', diff --git a/app/src/organisms/Desktop/Devices/__tests__/HistoricalProtocolRun.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/HistoricalProtocolRun.test.tsx index 070f1c614de..1b9ec0cde4e 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/HistoricalProtocolRun.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/HistoricalProtocolRun.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -10,6 +9,7 @@ import { useRunStatus, useRunTimestamps } from '/app/resources/runs' import { HistoricalProtocolRun } from '../HistoricalProtocolRun' import { HistoricalProtocolRunOverflowMenu } from '../HistoricalProtocolRunOverflowMenu' +import type { ComponentProps } from 'react' import type { RunStatus, RunData } from '@opentrons/api-client' import type { RunTimeParameter } from '@opentrons/shared-data' @@ -25,14 +25,14 @@ const run = { runTimeParameters: [] as RunTimeParameter[], } as RunData -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('RecentProtocolRuns', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx index 2b2706c9645..72148f614bf 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/HistoricalProtocolRunOverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { when } from 'vitest-when' @@ -23,6 +22,7 @@ import { useIsEstopNotDisengaged } from '/app/resources/devices' import { HistoricalProtocolRunOverflowMenu } from '../HistoricalProtocolRunOverflowMenu' import { useNotifyAllCommandsQuery } from '/app/resources/runs' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { CommandsData } from '@opentrons/api-client' @@ -40,7 +40,7 @@ vi.mock('/app/redux-resources/analytics') vi.mock('@opentrons/react-api-client') const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders( @@ -59,7 +59,7 @@ let mockTrackProtocolRunEvent: any const mockDownloadRunLog = vi.fn() describe('HistoricalProtocolRunOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { mockTrackEvent = vi.fn() vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent) diff --git a/app/src/organisms/Desktop/Devices/__tests__/RobotCard.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/RobotCard.test.tsx index 89d59c47cf8..f2814630b2b 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/RobotCard.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/RobotCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { when } from 'vitest-when' import { screen } from '@testing-library/react' @@ -37,6 +36,7 @@ import { useErrorRecoveryBanner, } from '../ErrorRecoveryBanner' +import type { ComponentProps } from 'react' import type { State } from '/app/redux/types' vi.mock('/app/redux/robot-update/selectors') @@ -94,7 +94,7 @@ const MOCK_STATE: State = { }, } as any -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -107,7 +107,7 @@ const render = (props: React.ComponentProps) => { } describe('RobotCard', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robot: mockConnectableRobot } diff --git a/app/src/organisms/Desktop/Devices/__tests__/RobotOverflowMenu.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/RobotOverflowMenu.test.tsx index b3735e72a77..7f8ce943211 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/RobotOverflowMenu.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/RobotOverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -16,6 +15,8 @@ import { mockConnectedRobot, } from '/app/redux/discovery/__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/robot-update/hooks') vi.mock('/app/resources/runs') vi.mock('/app/organisms/Desktop/ChooseProtocolSlideout') @@ -23,7 +24,7 @@ vi.mock('../hooks') vi.mock('/app/redux-resources/robots') vi.mock('/app/resources/devices/hooks/useIsEstopNotDisengaged') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -35,7 +36,7 @@ const render = (props: React.ComponentProps) => { } describe('RobotOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Devices/__tests__/RobotOverview.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/RobotOverview.test.tsx index 5d2513bec23..6f262e0a094 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/RobotOverview.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/RobotOverview.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { when } from 'vitest-when' import { screen, fireEvent } from '@testing-library/react' @@ -48,10 +47,11 @@ import { useErrorRecoveryBanner, } from '../ErrorRecoveryBanner' +import type { ComponentProps } from 'react' +import type * as ReactApiClient from '@opentrons/react-api-client' import type { Config } from '/app/redux/config/types' import type { DiscoveryClientRobotAddress } from '/app/redux/discovery/types' import type { State } from '/app/redux/types' -import type * as ReactApiClient from '@opentrons/react-api-client' vi.mock('@opentrons/react-api-client', async importOriginal => { const actual = await importOriginal() @@ -104,7 +104,7 @@ const MOCK_STATE: State = { const mockToggleLights = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -117,7 +117,7 @@ const render = (props: React.ComponentProps) => { } describe('RobotOverview', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotName: mockConnectableRobot.name } diff --git a/app/src/organisms/Desktop/Devices/__tests__/RobotOverviewOverflowMenu.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/RobotOverviewOverflowMenu.test.tsx index c4d384d0805..21d8e13b666 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/RobotOverviewOverflowMenu.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/RobotOverviewOverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { when } from 'vitest-when' @@ -24,6 +23,8 @@ import { handleUpdateBuildroot } from '../RobotSettings/UpdateBuildroot' import { useIsEstopNotDisengaged } from '/app/resources/devices/hooks/useIsEstopNotDisengaged' import { RobotOverviewOverflowMenu } from '../RobotOverviewOverflowMenu' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/robot-controls') vi.mock('/app/redux/robot-admin') vi.mock('../hooks') @@ -36,9 +37,7 @@ vi.mock('../RobotSettings/UpdateBuildroot') vi.mock('/app/resources/devices/hooks/useIsEstopNotDisengaged') vi.mock('/app/redux-resources/robots') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -50,7 +49,7 @@ const render = ( } describe('RobotOverviewOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps vi.useFakeTimers() beforeEach(() => { diff --git a/app/src/organisms/Desktop/Devices/__tests__/RobotStatusHeader.test.tsx b/app/src/organisms/Desktop/Devices/__tests__/RobotStatusHeader.test.tsx index 465c46cc566..af05d9ce131 100644 --- a/app/src/organisms/Desktop/Devices/__tests__/RobotStatusHeader.test.tsx +++ b/app/src/organisms/Desktop/Devices/__tests__/RobotStatusHeader.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { RUN_STATUS_RUNNING } from '@opentrons/api-client' import { when } from 'vitest-when' @@ -20,6 +19,7 @@ import { useIsFlex } from '/app/redux-resources/robots' import { RobotStatusHeader } from '../RobotStatusHeader' import { useNotifyRunQuery, useCurrentRunId } from '/app/resources/runs' +import type { ComponentProps } from 'react' import type { DiscoveryClientRobotAddress } from '/app/redux/discovery/types' import type { SimpleInterfaceStatus } from '/app/redux/networking/types' import type { State } from '/app/redux/types' @@ -45,7 +45,7 @@ const MOCK_BUZZ = { const WIFI_IP = 'wifi-ip' const ETHERNET_IP = 'ethernet-ip' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -56,7 +56,7 @@ const render = (props: React.ComponentProps) => { ) } describe('RobotStatusHeader', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = MOCK_OTIE diff --git a/app/src/organisms/Desktop/Devices/hooks/__tests__/useCalibrationTaskList.test.tsx b/app/src/organisms/Desktop/Devices/hooks/__tests__/useCalibrationTaskList.test.tsx index 9d675c77f7e..1ca9c1741c2 100644 --- a/app/src/organisms/Desktop/Devices/hooks/__tests__/useCalibrationTaskList.test.tsx +++ b/app/src/organisms/Desktop/Devices/hooks/__tests__/useCalibrationTaskList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { createStore } from 'redux' import { I18nextProvider } from 'react-i18next' import { Provider } from 'react-redux' @@ -30,6 +29,7 @@ import { } from '../__fixtures__/taskListFixtures' import { i18n } from '/app/i18n' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' @@ -41,7 +41,7 @@ const mockTipLengthCalLauncher = vi.fn() const mockDeckCalLauncher = vi.fn() describe('useCalibrationTaskList hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> let store: Store const mockDeleteCalibration = vi.fn() diff --git a/app/src/organisms/Desktop/Devices/hooks/__tests__/useDeckCalibrationData.test.tsx b/app/src/organisms/Desktop/Devices/hooks/__tests__/useDeckCalibrationData.test.tsx index c08f0e3e1e5..1aef843c8ef 100644 --- a/app/src/organisms/Desktop/Devices/hooks/__tests__/useDeckCalibrationData.test.tsx +++ b/app/src/organisms/Desktop/Devices/hooks/__tests__/useDeckCalibrationData.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { Provider } from 'react-redux' @@ -15,13 +14,13 @@ import { import { getDiscoverableRobotByName } from '/app/redux/discovery' import { mockDeckCalData } from '/app/redux/calibration/__fixtures__' import { useDispatchApiRequest } from '/app/redux/robot-api' +import { useDeckCalibrationData } from '..' +import { mockConnectableRobot } from '/app/redux/discovery/__fixtures__' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { DispatchApiRequestType } from '/app/redux/robot-api' -import { useDeckCalibrationData } from '..' -import { mockConnectableRobot } from '/app/redux/discovery/__fixtures__' - vi.mock('@opentrons/react-api-client') vi.mock('/app/redux/calibration') vi.mock('/app/redux/robot-api') @@ -31,7 +30,7 @@ const store: Store = createStore(vi.fn(), {}) describe('useDeckCalibrationData hook', () => { let dispatchApiRequest: DispatchApiRequestType - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { dispatchApiRequest = vi.fn() const queryClient = new QueryClient() diff --git a/app/src/organisms/Desktop/Devices/hooks/__tests__/usePipetteOffsetCalibration.test.tsx b/app/src/organisms/Desktop/Devices/hooks/__tests__/usePipetteOffsetCalibration.test.tsx index 2c23dcb0f61..50db68fe850 100644 --- a/app/src/organisms/Desktop/Devices/hooks/__tests__/usePipetteOffsetCalibration.test.tsx +++ b/app/src/organisms/Desktop/Devices/hooks/__tests__/usePipetteOffsetCalibration.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { Provider } from 'react-redux' @@ -15,6 +14,7 @@ import { useDispatchApiRequest } from '/app/redux/robot-api' import { useRobot } from '/app/redux-resources/robots' import { usePipetteOffsetCalibration } from '..' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { DiscoveredRobot } from '/app/redux/discovery/types' import type { DispatchApiRequestType } from '/app/redux/robot-api' @@ -32,7 +32,7 @@ const MOUNT = 'left' as Mount describe('usePipetteOffsetCalibration hook', () => { let dispatchApiRequest: DispatchApiRequestType - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { dispatchApiRequest = vi.fn() const queryClient = new QueryClient() diff --git a/app/src/organisms/Desktop/Devices/hooks/__tests__/usePipetteOffsetCalibrations.test.tsx b/app/src/organisms/Desktop/Devices/hooks/__tests__/usePipetteOffsetCalibrations.test.tsx index 9305b34af38..de390322d11 100644 --- a/app/src/organisms/Desktop/Devices/hooks/__tests__/usePipetteOffsetCalibrations.test.tsx +++ b/app/src/organisms/Desktop/Devices/hooks/__tests__/usePipetteOffsetCalibrations.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { renderHook } from '@testing-library/react' @@ -11,12 +10,14 @@ import { } from '/app/redux/calibration/pipette-offset/__fixtures__' import { usePipetteOffsetCalibrations } from '..' +import type { FunctionComponent, ReactNode } from 'react' + vi.mock('@opentrons/react-api-client') const CALIBRATION_DATA_POLL_MS = 5000 describe('usePipetteOffsetCalibrations hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() wrapper = ({ children }) => ( diff --git a/app/src/organisms/Desktop/Devices/hooks/__tests__/useSyncRobotClock.test.tsx b/app/src/organisms/Desktop/Devices/hooks/__tests__/useSyncRobotClock.test.tsx index 02b593fbaab..1ab5c1f55cb 100644 --- a/app/src/organisms/Desktop/Devices/hooks/__tests__/useSyncRobotClock.test.tsx +++ b/app/src/organisms/Desktop/Devices/hooks/__tests__/useSyncRobotClock.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { Provider } from 'react-redux' import { createStore } from 'redux' @@ -7,6 +6,8 @@ import { QueryClient, QueryClientProvider } from 'react-query' import { syncSystemTime } from '/app/redux/robot-admin' import { useSyncRobotClock } from '..' + +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' vi.mock('/app/redux/discovery') @@ -14,7 +15,7 @@ vi.mock('/app/redux/discovery') const store: Store = createStore(vi.fn(), {}) describe('useSyncRobotClock hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { store.dispatch = vi.fn() const queryClient = new QueryClient() diff --git a/app/src/organisms/Desktop/Devices/hooks/__tests__/useTipLengthCalibrations.test.tsx b/app/src/organisms/Desktop/Devices/hooks/__tests__/useTipLengthCalibrations.test.tsx index 7281b009b5c..d2cec816973 100644 --- a/app/src/organisms/Desktop/Devices/hooks/__tests__/useTipLengthCalibrations.test.tsx +++ b/app/src/organisms/Desktop/Devices/hooks/__tests__/useTipLengthCalibrations.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { QueryClient, QueryClientProvider } from 'react-query' @@ -11,12 +10,14 @@ import { } from '/app/redux/calibration/tip-length/__fixtures__' import { useTipLengthCalibrations } from '..' +import type { FunctionComponent, ReactNode } from 'react' + vi.mock('@opentrons/react-api-client') const CALIBRATIONS_FETCH_MS = 5000 describe('useTipLengthCalibrations hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() wrapper = ({ children }) => ( diff --git a/app/src/organisms/Desktop/Devices/hooks/__tests__/useTrackCreateProtocolRunEvent.test.tsx b/app/src/organisms/Desktop/Devices/hooks/__tests__/useTrackCreateProtocolRunEvent.test.tsx index b8cf7fd9896..1f7eb5041fa 100644 --- a/app/src/organisms/Desktop/Devices/hooks/__tests__/useTrackCreateProtocolRunEvent.test.tsx +++ b/app/src/organisms/Desktop/Devices/hooks/__tests__/useTrackCreateProtocolRunEvent.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { createStore } from 'redux' import { Provider } from 'react-redux' import { QueryClient, QueryClientProvider } from 'react-query' @@ -12,6 +11,7 @@ import { parseProtocolAnalysisOutput } from '/app/transformations/analysis' import { useTrackEvent } from '/app/redux/analytics' import { storedProtocolData } from '/app/redux/protocol-storage/__fixtures__' +import type { FunctionComponent, ReactNode } from 'react' import type { Mock } from 'vitest' import type { Store } from 'redux' import type { ProtocolAnalyticsData } from '/app/redux/analytics/types' @@ -28,7 +28,7 @@ const PROTOCOL_PROPERTIES = { protocolType: 'python' } as ProtocolAnalyticsData let mockTrackEvent: Mock let mockGetProtocolRunAnalyticsData: Mock -let wrapper: React.FunctionComponent<{ children: React.ReactNode }> +let wrapper: FunctionComponent<{ children: ReactNode }> let store: Store = createStore(vi.fn(), {}) describe('useTrackCreateProtocolRunEvent hook', () => { diff --git a/app/src/organisms/Desktop/HowCalibrationWorksModal/__tests__/HowCalibrationWorksModal.test.tsx b/app/src/organisms/Desktop/HowCalibrationWorksModal/__tests__/HowCalibrationWorksModal.test.tsx index 8eee1543c5a..dfa8ae09955 100644 --- a/app/src/organisms/Desktop/HowCalibrationWorksModal/__tests__/HowCalibrationWorksModal.test.tsx +++ b/app/src/organisms/Desktop/HowCalibrationWorksModal/__tests__/HowCalibrationWorksModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -6,16 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { HowCalibrationWorksModal } from '..' -const render = ( - props: React.ComponentProps -) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('HowCalibrationWorksModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onCloseClick: vi.fn() } }) diff --git a/app/src/organisms/Desktop/Labware/AddCustomLabwareSlideout/__tests__/AddCustomLabwareSlideout.test.tsx b/app/src/organisms/Desktop/Labware/AddCustomLabwareSlideout/__tests__/AddCustomLabwareSlideout.test.tsx index 1a4fef0be5c..9d67949e289 100644 --- a/app/src/organisms/Desktop/Labware/AddCustomLabwareSlideout/__tests__/AddCustomLabwareSlideout.test.tsx +++ b/app/src/organisms/Desktop/Labware/AddCustomLabwareSlideout/__tests__/AddCustomLabwareSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { describe, it, expect, vi, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -10,6 +9,8 @@ import { import { renderWithProviders } from '/app/__testing-utils__' import { AddCustomLabwareSlideout } from '..' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/custom-labware') vi.mock('/app/local-resources/labware') vi.mock('/app/redux/analytics') @@ -21,9 +22,7 @@ vi.mock('/app/redux/shell/remote', () => ({ let mockTrackEvent: any -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -35,7 +34,7 @@ const render = ( } describe('AddCustomLabwareSlideout', () => { - const props: React.ComponentProps = { + const props: ComponentProps = { isExpanded: true, onCloseClick: vi.fn(() => null), } diff --git a/app/src/organisms/Desktop/Labware/LabwareCard/__tests__/CustomLabwareOverflowMenu.test.tsx b/app/src/organisms/Desktop/Labware/LabwareCard/__tests__/CustomLabwareOverflowMenu.test.tsx index 588b47ecea3..5e47b3432f4 100644 --- a/app/src/organisms/Desktop/Labware/LabwareCard/__tests__/CustomLabwareOverflowMenu.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareCard/__tests__/CustomLabwareOverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest' @@ -9,6 +8,7 @@ import { i18n } from '/app/i18n' import { useTrackEvent } from '/app/redux/analytics' import { CustomLabwareOverflowMenu } from '../CustomLabwareOverflowMenu' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' import type * as OpentronsComponents from '@opentrons/components' @@ -31,7 +31,7 @@ vi.mock('@opentrons/components', async importOriginal => { }) const render = ( - props: React.ComponentProps + props: ComponentProps ): ReturnType => { return renderWithProviders(, { i18nInstance: i18n, @@ -39,7 +39,7 @@ const render = ( } describe('CustomLabwareOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/Labware/LabwareCard/__tests__/LabwareCard.test.tsx b/app/src/organisms/Desktop/Labware/LabwareCard/__tests__/LabwareCard.test.tsx index 76abb51c72f..2431472e274 100644 --- a/app/src/organisms/Desktop/Labware/LabwareCard/__tests__/LabwareCard.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareCard/__tests__/LabwareCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, vi, beforeEach } from 'vitest' import { renderWithProviders, nestedTextMatcher } from '/app/__testing-utils__' @@ -8,6 +7,7 @@ import { mockDefinition } from '/app/redux/custom-labware/__fixtures__' import { CustomLabwareOverflowMenu } from '../CustomLabwareOverflowMenu' import { LabwareCard } from '..' +import type { ComponentProps } from 'react' import type * as OpentronsComponents from '@opentrons/components' vi.mock('/app/local-resources/labware') @@ -21,14 +21,14 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('LabwareCard', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(CustomLabwareOverflowMenu).mockReturnValue(
        Mock CustomLabwareOverflowMenu
        diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/StyledComponents/__tests__/ExpandingTitle.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/StyledComponents/__tests__/ExpandingTitle.test.tsx index 6ee44619bc8..b7a872e9a1b 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/StyledComponents/__tests__/ExpandingTitle.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/StyledComponents/__tests__/ExpandingTitle.test.tsx @@ -1,11 +1,12 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, beforeEach } from 'vitest' import { getFootprintDiagram } from '@opentrons/components' import { renderWithProviders } from '/app/__testing-utils__' import { ExpandingTitle } from '../ExpandingTitle' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders() } @@ -13,7 +14,7 @@ const diagram = getFootprintDiagram({}) const DIAGRAM_TEST_ID = 'expanding_title_diagram' describe('ExpandingTitle', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { label: 'Title', diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/StyledComponents/__tests__/LabeledValue.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/StyledComponents/__tests__/LabeledValue.test.tsx index c3a73771f52..196d85cb707 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/StyledComponents/__tests__/LabeledValue.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/StyledComponents/__tests__/LabeledValue.test.tsx @@ -1,15 +1,16 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { LabeledValue } from '../LabeledValue' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders() } describe('LabeledValue', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { label: 'height', diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/Dimensions.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/Dimensions.test.tsx index 32de832214c..2afc92ad6cf 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/Dimensions.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/Dimensions.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -6,14 +5,16 @@ import { i18n } from '/app/i18n' import { mockDefinition } from '/app/redux/custom-labware/__fixtures__' import { Dimensions } from '../Dimensions' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Dimensions', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { definition: mockDefinition, diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/Gallery.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/Gallery.test.tsx index 344981336b0..5a61aa4684c 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/Gallery.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/Gallery.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -6,12 +5,14 @@ import { mockDefinition } from '/app/redux/custom-labware/__fixtures__' import { labwareImages } from '../labware-images' import { Gallery } from '../Gallery' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders() } describe('Gallery', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { labwareImages.mock_definition = ['image1'] props = { diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/LabwareDetails.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/LabwareDetails.test.tsx index 78c98c50a46..10602db9f9f 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/LabwareDetails.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/LabwareDetails.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, afterEach, vi, expect } from 'vitest' @@ -17,6 +16,8 @@ import { WellSpacing } from '../WellSpacing' import { LabwareDetails } from '..' +import type { ComponentProps } from 'react' + vi.mock('/app/local-resources/labware') vi.mock('../../LabwareCard/CustomLabwareOverflowMenu') vi.mock('../Dimensions') @@ -28,7 +29,7 @@ vi.mock('../WellDimensions') vi.mock('../WellSpacing') const render = ( - props: React.ComponentProps + props: ComponentProps ): ReturnType => { return renderWithProviders(, { i18nInstance: i18n, @@ -36,7 +37,7 @@ const render = ( } describe('LabwareDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(CustomLabwareOverflowMenu).mockReturnValue(
        Mock CustomLabwareOverflowMenu
        diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/ManufacturerDetails.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/ManufacturerDetails.test.tsx index 21dc8c8f1a2..9d50aa61500 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/ManufacturerDetails.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/ManufacturerDetails.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ManufacturerDetails } from '../ManufacturerDetails' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ManufacturerDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { brand: { brand: 'Opentrons' }, diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellCount.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellCount.test.tsx index f833c0c6e3f..6d94737a9c4 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellCount.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellCount.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { WellCount } from '../WellCount' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('WellCount', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { count: 1, diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellDimensions.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellDimensions.test.tsx index 3269c3ec241..78af2ac24b2 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellDimensions.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellDimensions.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -10,14 +9,16 @@ import { } from '/app/redux/custom-labware/__fixtures__' import { WellDimensions } from '../WellDimensions' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('WellDimensions', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { labwareParams: mockDefinition.parameters, diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellProperties.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellProperties.test.tsx index 3d21e01f4df..40b17b3b31f 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellProperties.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellProperties.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -6,14 +5,16 @@ import { i18n } from '/app/i18n' import { mockCircularLabwareWellGroupProperties } from '/app/redux/custom-labware/__fixtures__' import { WellProperties } from '../WellProperties' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('WellProperties', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { wellProperties: mockCircularLabwareWellGroupProperties, diff --git a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellSpacing.test.tsx b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellSpacing.test.tsx index 5496bbe33f1..59115886e02 100644 --- a/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellSpacing.test.tsx +++ b/app/src/organisms/Desktop/Labware/LabwareDetails/__tests__/WellSpacing.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -6,14 +5,16 @@ import { i18n } from '/app/i18n' import { mockCircularLabwareWellGroupProperties } from '/app/redux/custom-labware/__fixtures__' import { WellSpacing } from '../WellSpacing' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('WellSpacing', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { wellProperties: mockCircularLabwareWellGroupProperties, diff --git a/app/src/organisms/Desktop/ProtocolAnalysisFailure/ProtocolAnalysisStale.tsx b/app/src/organisms/Desktop/ProtocolAnalysisFailure/ProtocolAnalysisStale.tsx index 9b3a7c00d14..4daa1b24c80 100644 --- a/app/src/organisms/Desktop/ProtocolAnalysisFailure/ProtocolAnalysisStale.tsx +++ b/app/src/organisms/Desktop/ProtocolAnalysisFailure/ProtocolAnalysisStale.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useDispatch } from 'react-redux' import { useTranslation, Trans } from 'react-i18next' @@ -13,9 +12,11 @@ import { TYPOGRAPHY, WRAP_REVERSE, } from '@opentrons/components' +import { analyzeProtocol } from '/app/redux/protocol-storage' +import type { MouseEventHandler } from 'react' import type { Dispatch } from '/app/redux/types' -import { analyzeProtocol } from '/app/redux/protocol-storage' + interface ProtocolAnalysisStaleProps { protocolKey: string } @@ -27,7 +28,7 @@ export function ProtocolAnalysisStale( const { t } = useTranslation(['protocol_list', 'shared']) const dispatch = useDispatch() - const handleClickReanalyze: React.MouseEventHandler = e => { + const handleClickReanalyze: MouseEventHandler = e => { e.preventDefault() e.stopPropagation() dispatch(analyzeProtocol(protocolKey)) diff --git a/app/src/organisms/Desktop/ProtocolAnalysisFailure/__tests__/ProtocolAnalysisFailure.test.tsx b/app/src/organisms/Desktop/ProtocolAnalysisFailure/__tests__/ProtocolAnalysisFailure.test.tsx index fbdec0a45ec..becdabef509 100644 --- a/app/src/organisms/Desktop/ProtocolAnalysisFailure/__tests__/ProtocolAnalysisFailure.test.tsx +++ b/app/src/organisms/Desktop/ProtocolAnalysisFailure/__tests__/ProtocolAnalysisFailure.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect } from 'vitest' @@ -9,8 +8,10 @@ import { i18n } from '/app/i18n' import { ProtocolAnalysisFailure } from '..' import { analyzeProtocol } from '/app/redux/protocol-storage' +import type { ComponentProps } from 'react' + const render = ( - props: Partial> = {} + props: Partial> = {} ) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/ProtocolDetails/ProtocolParameters/__tests__/ProtocolParameters.test.tsx b/app/src/organisms/Desktop/ProtocolDetails/ProtocolParameters/__tests__/ProtocolParameters.test.tsx index e52803cc054..78f8184f7a1 100644 --- a/app/src/organisms/Desktop/ProtocolDetails/ProtocolParameters/__tests__/ProtocolParameters.test.tsx +++ b/app/src/organisms/Desktop/ProtocolDetails/ProtocolParameters/__tests__/ProtocolParameters.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' @@ -6,6 +5,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ProtocolParameters } from '..' +import type { ComponentProps } from 'react' import type { RunTimeParameter } from '@opentrons/shared-data' import type * as Components from '@opentrons/components' @@ -80,14 +80,14 @@ const mockRunTimeParameter: RunTimeParameter[] = [ }, ] -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ProtocolParameters', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolDetails.test.tsx b/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolDetails.test.tsx index f613c108b77..e9a75149199 100644 --- a/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolDetails.test.tsx +++ b/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolDetails.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { act, screen, waitFor } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { describe, it, beforeEach, vi, expect, afterEach } from 'vitest' @@ -26,6 +25,7 @@ import { import { storedProtocolData } from '/app/redux/protocol-storage/__fixtures__' import { ProtocolDetails } from '..' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' import type { ProtocolAnalysisOutput } from '@opentrons/shared-data' @@ -37,7 +37,7 @@ vi.mock('/app/organisms/Desktop/ChooseRobotToRunProtocolSlideout') vi.mock('/app/organisms/Desktop/SendProtocolToFlexSlideout') const render = ( - props: Partial> = {} + props: Partial> = {} ) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolLabwareDetails.test.tsx b/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolLabwareDetails.test.tsx index 93c215643cf..bc25bcb5569 100644 --- a/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolLabwareDetails.test.tsx +++ b/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolLabwareDetails.test.tsx @@ -1,10 +1,10 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach, vi } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ProtocolLabwareDetails } from '../ProtocolLabwareDetails' +import type { ComponentProps } from 'react' import type { LoadLabwareRunTimeCommand } from '@opentrons/shared-data' import type { InfoScreen } from '@opentrons/components' @@ -67,14 +67,14 @@ const mockRequiredLabwareDetails = [ } as LoadLabwareRunTimeCommand, ] -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ProtocolLabwareDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { requiredLabwareDetails: mockRequiredLabwareDetails, diff --git a/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolLiquidsDetails.test.tsx b/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolLiquidsDetails.test.tsx index 69e55cc1097..9a6f233b312 100644 --- a/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolLiquidsDetails.test.tsx +++ b/app/src/organisms/Desktop/ProtocolDetails/__tests__/ProtocolLiquidsDetails.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach, vi } from 'vitest' import { parseLiquidsInLoadOrder } from '@opentrons/shared-data' @@ -7,6 +6,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ProtocolLiquidsDetails } from '../ProtocolLiquidsDetails' +import type { ComponentProps } from 'react' import type * as SharedData from '@opentrons/shared-data' vi.mock('../../Desktop/Devices/ProtocolRun/SetupLiquids/SetupLiquidsList') @@ -18,14 +18,14 @@ vi.mock('@opentrons/shared-data', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ProtocolLiquidsDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { commands: [], diff --git a/app/src/organisms/Desktop/ProtocolDetails/__tests__/RobotConfigurationDetails.test.tsx b/app/src/organisms/Desktop/ProtocolDetails/__tests__/RobotConfigurationDetails.test.tsx index b823732ce95..65ce7d6d24d 100644 --- a/app/src/organisms/Desktop/ProtocolDetails/__tests__/RobotConfigurationDetails.test.tsx +++ b/app/src/organisms/Desktop/ProtocolDetails/__tests__/RobotConfigurationDetails.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, afterEach, vi } from 'vitest' import { screen } from '@testing-library/react' @@ -7,6 +6,8 @@ import { OT2_STANDARD_MODEL, FLEX_STANDARD_MODEL } from '@opentrons/shared-data' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { RobotConfigurationDetails } from '../RobotConfigurationDetails' + +import type { ComponentProps } from 'react' import type { LoadModuleRunTimeCommand } from '@opentrons/shared-data' const mockRequiredModuleDetails = [ @@ -57,16 +58,14 @@ const mockRequiredModuleDetails = [ } as LoadModuleRunTimeCommand, ] -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('RobotConfigurationDetails', () => { - let props: React.ComponentProps + let props: ComponentProps afterEach(() => { vi.clearAllMocks() diff --git a/app/src/organisms/Desktop/ProtocolsLanding/ConfirmDeleteProtocolModal.tsx b/app/src/organisms/Desktop/ProtocolsLanding/ConfirmDeleteProtocolModal.tsx index 14d75238bcc..439d48a9f0d 100644 --- a/app/src/organisms/Desktop/ProtocolsLanding/ConfirmDeleteProtocolModal.tsx +++ b/app/src/organisms/Desktop/ProtocolsLanding/ConfirmDeleteProtocolModal.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { AlertPrimaryButton, @@ -14,9 +13,11 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import type { MouseEventHandler } from 'react' + interface ConfirmDeleteProtocolModalProps { - cancelDeleteProtocol: React.MouseEventHandler | undefined - handleClickDelete: React.MouseEventHandler + cancelDeleteProtocol: MouseEventHandler | undefined + handleClickDelete: MouseEventHandler } export function ConfirmDeleteProtocolModal( diff --git a/app/src/organisms/Desktop/ProtocolsLanding/ProtocolOverflowMenu.tsx b/app/src/organisms/Desktop/ProtocolsLanding/ProtocolOverflowMenu.tsx index 0bcc71f8cc0..eacddbdaca3 100644 --- a/app/src/organisms/Desktop/ProtocolsLanding/ProtocolOverflowMenu.tsx +++ b/app/src/organisms/Desktop/ProtocolsLanding/ProtocolOverflowMenu.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' @@ -35,6 +34,7 @@ import { } from '/app/redux/protocol-storage' import { ConfirmDeleteProtocolModal } from './ConfirmDeleteProtocolModal' +import type { MouseEvent, MouseEventHandler } from 'react' import type { StyleProps } from '@opentrons/components' import type { StoredProtocolData } from '/app/redux/protocol-storage' import type { Dispatch } from '/app/redux/types' @@ -77,13 +77,13 @@ export function ProtocolOverflowMenu( const robotType = mostRecentAnalysis != null ? mostRecentAnalysis?.robotType ?? null : null - const handleClickShowInFolder: React.MouseEventHandler = e => { + const handleClickShowInFolder: MouseEventHandler = e => { e.preventDefault() e.stopPropagation() dispatch(viewProtocolSourceFolder(protocolKey)) setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) } - const handleClickRun: React.MouseEventHandler = e => { + const handleClickRun: MouseEventHandler = e => { e.preventDefault() e.stopPropagation() trackEvent({ @@ -93,25 +93,25 @@ export function ProtocolOverflowMenu( handleRunProtocol(storedProtocolData) setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) } - const handleClickSendToOT3: React.MouseEventHandler = e => { + const handleClickSendToOT3: MouseEventHandler = e => { e.preventDefault() e.stopPropagation() handleSendProtocolToFlex(storedProtocolData) setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) } - const handleClickDelete: React.MouseEventHandler = e => { + const handleClickDelete: MouseEventHandler = e => { e.preventDefault() e.stopPropagation() confirmDeleteProtocol() setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) } - const handleClickReanalyze: React.MouseEventHandler = e => { + const handleClickReanalyze: MouseEventHandler = e => { e.preventDefault() e.stopPropagation() dispatch(analyzeProtocol(protocolKey)) setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) } - const handleClickTimeline: React.MouseEventHandler = e => { + const handleClickTimeline: MouseEventHandler = e => { e.preventDefault() navigate(`/protocols/${protocolKey}/timeline`) setShowOverflowMenu(prevShowOverflowMenu => !prevShowOverflowMenu) @@ -121,7 +121,7 @@ export function ProtocolOverflowMenu( { + onClick={(e: MouseEvent) => { e.stopPropagation() }} > @@ -195,7 +195,7 @@ export function ProtocolOverflowMenu( {showDeleteConfirmation ? createPortal( { + cancelDeleteProtocol={(e: MouseEvent) => { e.preventDefault() e.stopPropagation() cancelDeleteProtocol() diff --git a/app/src/organisms/Desktop/ProtocolsLanding/__tests__/ConfirmDeleteProtocolModal.test.tsx b/app/src/organisms/Desktop/ProtocolsLanding/__tests__/ConfirmDeleteProtocolModal.test.tsx index dd19a80d133..8d040851a56 100644 --- a/app/src/organisms/Desktop/ProtocolsLanding/__tests__/ConfirmDeleteProtocolModal.test.tsx +++ b/app/src/organisms/Desktop/ProtocolsLanding/__tests__/ConfirmDeleteProtocolModal.test.tsx @@ -1,20 +1,19 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConfirmDeleteProtocolModal } from '../ConfirmDeleteProtocolModal' -const render = ( - props: React.ComponentProps -) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ConfirmDeleteProtocolModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/ProtocolsLanding/__tests__/ProtocolList.test.tsx b/app/src/organisms/Desktop/ProtocolsLanding/__tests__/ProtocolList.test.tsx index 59271bc279c..6f3190712c4 100644 --- a/app/src/organisms/Desktop/ProtocolsLanding/__tests__/ProtocolList.test.tsx +++ b/app/src/organisms/Desktop/ProtocolsLanding/__tests__/ProtocolList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { BrowserRouter } from 'react-router-dom' import { when } from 'vitest-when' import { fireEvent, screen } from '@testing-library/react' @@ -16,13 +15,15 @@ import { useSortedProtocols } from '../hooks' import { EmptyStateLinks } from '../EmptyStateLinks' import { ProtocolCard } from '../ProtocolCard' +import type { ComponentProps } from 'react' + vi.mock('../hooks') vi.mock('/app/redux/protocol-storage') vi.mock('/app/redux/config') vi.mock('../EmptyStateLinks') vi.mock('../ProtocolCard') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -34,7 +35,7 @@ const render = (props: React.ComponentProps) => { } describe('ProtocolList', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/ProtocolsLanding/__tests__/hooks.test.tsx b/app/src/organisms/Desktop/ProtocolsLanding/__tests__/hooks.test.tsx index 5d1485fe795..0c10c8fb1ef 100644 --- a/app/src/organisms/Desktop/ProtocolsLanding/__tests__/hooks.test.tsx +++ b/app/src/organisms/Desktop/ProtocolsLanding/__tests__/hooks.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Provider } from 'react-redux' import { createStore } from 'redux' import { renderHook } from '@testing-library/react' @@ -8,6 +7,7 @@ import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' import { useSortedProtocols } from '../hooks' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { ProtocolAnalysisOutput } from '@opentrons/shared-data' import type { StoredProtocolData } from '/app/redux/protocol-storage' @@ -294,7 +294,7 @@ describe('useSortedProtocols', () => { }) it('should return an object with protocols sorted alphabetically', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} @@ -318,7 +318,7 @@ describe('useSortedProtocols', () => { }) it('should return an object with protocols sorted reverse alphabetically', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} @@ -342,7 +342,7 @@ describe('useSortedProtocols', () => { }) it('should return an object with protocols sorted by most recent modified data', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} @@ -366,7 +366,7 @@ describe('useSortedProtocols', () => { }) it('should return an object with protocols sorted by oldest modified data', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} @@ -390,7 +390,7 @@ describe('useSortedProtocols', () => { }) it('should return an object with protocols sorted by flex then ot-2', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} @@ -413,7 +413,7 @@ describe('useSortedProtocols', () => { ) }) it('should return an object with protocols sorted by ot-2 then flex', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDataDownload.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDataDownload.tsx index 4731df09ae4..47058491684 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDataDownload.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDataDownload.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { saveAs } from 'file-saver' import { useTranslation, Trans } from 'react-i18next' @@ -17,6 +16,8 @@ import { useInstrumentsQuery, useModulesQuery, } from '@opentrons/react-api-client' +import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' + import { TertiaryButton } from '/app/atoms/buttons' import { useDeckCalibrationData, @@ -30,6 +31,8 @@ import { import { useRobot, useIsFlex } from '/app/redux-resources/robots' import { useIsEstopNotDisengaged } from '/app/resources/devices/hooks/useIsEstopNotDisengaged' +import type { MouseEventHandler } from 'react' + // TODO(bc, 2022-02-08): replace with support article when available const FLEX_CALIBRATION_SUPPORT_URL = 'https://support.opentrons.com' @@ -68,11 +71,13 @@ export function CalibrationDataDownload({ tipLengthCalibrations != null && tipLengthCalibrations.length > 0 - const onClickSaveAs: React.MouseEventHandler = e => { + const onClickSaveAs: MouseEventHandler = e => { e.preventDefault() doTrackEvent({ name: ANALYTICS_CALIBRATION_DATA_DOWNLOADED, - properties: {}, + properties: { + robotType: isFlex ? FLEX_ROBOT_TYPE : OT2_ROBOT_TYPE, + }, }) saveAs( new Blob([ diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/ModuleCalibrationItems.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/ModuleCalibrationItems.test.tsx index 8cb0dd62dc6..ee0529a1c4e 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/ModuleCalibrationItems.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/ModuleCalibrationItems.test.tsx @@ -1,6 +1,6 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' +import { ABSORBANCE_READER_TYPE } from '@opentrons/shared-data' import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { mockFetchModulesSuccessActionPayloadModules } from '/app/redux/modules/__fixtures__' @@ -8,8 +8,8 @@ import { ModuleCalibrationOverflowMenu } from '../ModuleCalibrationOverflowMenu' import { formatLastCalibrated } from '../utils' import { ModuleCalibrationItems } from '../ModuleCalibrationItems' +import type { ComponentProps } from 'react' import type { AttachedModule } from '@opentrons/api-client' -import { ABSORBANCE_READER_TYPE } from '@opentrons/shared-data' vi.mock('../ModuleCalibrationOverflowMenu') @@ -55,7 +55,7 @@ const mockCalibratedModule = { const ROBOT_NAME = 'mockRobot' const render = ( - props: React.ComponentProps + props: ComponentProps ): ReturnType => { return renderWithProviders(, { i18nInstance: i18n, @@ -63,7 +63,7 @@ const render = ( } describe('ModuleCalibrationItems', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/ModuleCalibrationOverflowMenu.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/ModuleCalibrationOverflowMenu.test.tsx index 47b5dbec249..2807269794b 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/ModuleCalibrationOverflowMenu.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/ModuleCalibrationOverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen, waitFor } from '@testing-library/react' import { when } from 'vitest-when' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -11,6 +10,7 @@ import { useIsEstopNotDisengaged } from '/app/resources/devices/hooks/useIsEstop import { ModuleCalibrationOverflowMenu } from '../ModuleCalibrationOverflowMenu' +import type { ComponentProps } from 'react' import type { Mount } from '@opentrons/components' vi.mock('@opentrons/react-api-client') @@ -87,7 +87,7 @@ const mockTCHeating = { } as any const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -97,7 +97,7 @@ const render = ( const ROBOT_NAME = 'mockRobot' describe('ModuleCalibrationOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps let mockChainLiveCommands = vi.fn() beforeEach(() => { diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/OverflowMenu.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/OverflowMenu.test.tsx index a0d2ff20096..cf106ccc6b3 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/OverflowMenu.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/OverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { when } from 'vitest-when' import '@testing-library/jest-dom/vitest' @@ -26,10 +25,11 @@ import { renderWithProviders } from '/app/__testing-utils__' import { useIsEstopNotDisengaged } from '/app/resources/devices' import { OverflowMenu } from '../OverflowMenu' +import type { ComponentProps } from 'react' import type { Mount } from '@opentrons/components' const render = ( - props: React.ComponentProps + props: ComponentProps ): ReturnType => { return renderWithProviders(, { i18nInstance: i18n, @@ -81,7 +81,7 @@ const RUN_STATUSES = { const mockUpdateRobotStatus = vi.fn() describe('OverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps const mockDeleteCalibration = vi.fn() beforeEach(() => { diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/PipetteOffsetCalibrationItems.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/PipetteOffsetCalibrationItems.test.tsx index 2a6cf82726f..bca259d41c5 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/PipetteOffsetCalibrationItems.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/PipetteOffsetCalibrationItems.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -19,11 +18,12 @@ import { PipetteOffsetCalibrationItems } from '../PipetteOffsetCalibrationItems' import { OverflowMenu } from '../OverflowMenu' import { formatLastCalibrated } from '../utils' +import type { ComponentProps } from 'react' import type { Mount } from '@opentrons/components' import type { AttachedPipettesByMount } from '/app/redux/pipettes/types' const render = ( - props: React.ComponentProps + props: ComponentProps ): ReturnType => { return renderWithProviders(, { i18nInstance: i18n, @@ -73,7 +73,7 @@ const mockAttachedPipettes: AttachedPipettesByMount = { const mockUpdateRobotStatus = vi.fn() describe('PipetteOffsetCalibrationItems', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(useAttachedPipettesFromInstrumentsQuery).mockReturnValue({ diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/TipLengthCalibrationItems.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/TipLengthCalibrationItems.test.tsx index 014ec28ee2c..19270615a06 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/TipLengthCalibrationItems.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/CalibrationDetails/__tests__/TipLengthCalibrationItems.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -6,6 +5,8 @@ import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { TipLengthCalibrationItems } from '../TipLengthCalibrationItems' import { OverflowMenu } from '../OverflowMenu' + +import type { ComponentProps } from 'react' import type { Mount } from '@opentrons/components' vi.mock('/app/redux/custom-labware/selectors') @@ -54,14 +55,14 @@ const mockTipLengthCalibrations = [ const mockUpdateRobotStatus = vi.fn() const render = ( - props: React.ComponentProps + props: ComponentProps ): ReturnType => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('TipLengthCalibrationItems', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(OverflowMenu).mockReturnValue(
        mock overflow menu
        ) diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/CalibrationDataDownload.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/CalibrationDataDownload.test.tsx index b84b25e2e16..b50a892ca73 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/CalibrationDataDownload.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/CalibrationDataDownload.test.tsx @@ -16,6 +16,7 @@ import { useModulesQuery, } from '@opentrons/react-api-client' import { instrumentsResponseFixture } from '@opentrons/api-client' +import { OT2_ROBOT_TYPE } from '@opentrons/shared-data' import { i18n } from '/app/i18n' import { @@ -145,7 +146,7 @@ describe('CalibrationDataDownload', () => { fireEvent.click(downloadButton) expect(mockTrackEvent).toHaveBeenCalledWith({ name: ANALYTICS_CALIBRATION_DATA_DOWNLOADED, - properties: {}, + properties: { robotType: OT2_ROBOT_TYPE }, }) }) diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/CalibrationHealthCheck.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/CalibrationHealthCheck.test.tsx index ee965c9d042..c434f6c1392 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/CalibrationHealthCheck.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/CalibrationHealthCheck.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import userEvent from '@testing-library/user-event' import { fireEvent, screen, waitFor } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -20,14 +19,13 @@ import { } from '/app/redux/calibration/tip-length/__fixtures__' import { mockAttachedPipette } from '/app/redux/pipettes/__fixtures__' import { useRunStatuses } from '/app/resources/runs' - import { useAttachedPipettes, useAttachedPipetteCalibrations, } from '/app/resources/instruments' - import { CalibrationHealthCheck } from '../CalibrationHealthCheck' +import type { ComponentProps } from 'react' import type { AttachedPipettesByMount, PipetteCalibrationsByMount, @@ -66,7 +64,7 @@ let mockTrackEvent: any const mockDispatchRequests = vi.fn() const render = ( - props?: Partial> + props?: Partial> ) => { return renderWithProviders( > + props?: Partial> ) => { return renderWithProviders( , diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsGripperCalibration.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsGripperCalibration.test.tsx index e4a4d48eea1..ff0570443d1 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsGripperCalibration.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsGripperCalibration.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -10,6 +9,7 @@ import { formatLastCalibrated } from '../CalibrationDetails/utils' import { useIsEstopNotDisengaged } from '/app/resources/devices/hooks/useIsEstopNotDisengaged' import { RobotSettingsGripperCalibration } from '../RobotSettingsGripperCalibration' +import type { ComponentProps } from 'react' import type { GripperData } from '@opentrons/api-client' vi.mock('/app/organisms/GripperWizardFlows') @@ -35,7 +35,7 @@ const mockNotCalibratedGripper = { const ROBOT_NAME = 'mockRobot' const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -43,7 +43,7 @@ const render = ( } describe('RobotSettingsGripperCalibration', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(formatLastCalibrated).mockReturnValue('last calibrated 1/2/3') vi.mocked(GripperWizardFlows).mockReturnValue(<>Mock Wizard Flow) diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsModuleCalibration.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsModuleCalibration.test.tsx index 95b71a450af..63d5014da79 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsModuleCalibration.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsModuleCalibration.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, vi, beforeEach } from 'vitest' import { i18n } from '/app/i18n' @@ -7,10 +6,12 @@ import { mockFetchModulesSuccessActionPayloadModules } from '/app/redux/modules/ import { RobotSettingsModuleCalibration } from '../RobotSettingsModuleCalibration' import { ModuleCalibrationItems } from '../CalibrationDetails/ModuleCalibrationItems' +import type { ComponentProps } from 'react' + vi.mock('../CalibrationDetails/ModuleCalibrationItems') const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -20,7 +21,7 @@ const render = ( const ROBOT_NAME = 'mockRobot' describe('RobotSettingsModuleCalibration', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsPipetteOffsetCalibration.test.tsx b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsPipetteOffsetCalibration.test.tsx index 0f628dddfef..6b883499831 100644 --- a/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsPipetteOffsetCalibration.test.tsx +++ b/app/src/organisms/Desktop/RobotSettingsCalibration/__tests__/RobotSettingsPipetteOffsetCalibration.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { describe, it, vi, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -18,6 +17,7 @@ import { useIsFlex } from '/app/redux-resources/robots' import { RobotSettingsPipetteOffsetCalibration } from '../RobotSettingsPipetteOffsetCalibration' import { PipetteOffsetCalibrationItems } from '../CalibrationDetails/PipetteOffsetCalibrationItems' +import type { ComponentProps } from 'react' import type { FormattedPipetteOffsetCalibration } from '..' vi.mock('/app/organisms/Desktop/Devices/hooks') @@ -29,9 +29,7 @@ const mockFormattedPipetteOffsetCalibrations: FormattedPipetteOffsetCalibration[ const mockUpdateRobotStatus = vi.fn() const render = ( - props?: Partial< - React.ComponentProps - > + props?: Partial> ) => { return renderWithProviders( ) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('InterventionTicks', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(Tick).mockImplementation(({ index }) => (
        MOCK TICK at index: {index}
        diff --git a/app/src/organisms/Desktop/RunProgressMeter/__tests__/RunProgressMeter.test.tsx b/app/src/organisms/Desktop/RunProgressMeter/__tests__/RunProgressMeter.test.tsx index 928bd2572a9..469c056c087 100644 --- a/app/src/organisms/Desktop/RunProgressMeter/__tests__/RunProgressMeter.test.tsx +++ b/app/src/organisms/Desktop/RunProgressMeter/__tests__/RunProgressMeter.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -37,6 +36,7 @@ import { RunProgressMeter } from '..' import { renderWithProviders } from '/app/__testing-utils__' import { useRunningStepCounts } from '/app/resources/protocols/hooks' +import type { ComponentProps } from 'react' import type { RunCommandSummary } from '@opentrons/api-client' import type * as ApiClient from '@opentrons/react-api-client' @@ -55,7 +55,7 @@ vi.mock('../../Devices/hooks') vi.mock('/app/resources/protocols/hooks') vi.mock('/app/redux-resources/robots') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -65,7 +65,7 @@ const NON_DETERMINISTIC_RUN_ID = 'nonDeterministicID' const ROBOT_NAME = 'otie' describe('RunProgressMeter', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(ProgressBar).mockReturnValue(
        MOCK PROGRESS BAR
        ) vi.mocked(InterventionModal).mockReturnValue( diff --git a/app/src/organisms/Desktop/RunProgressMeter/hooks/useRunProgressCopy.tsx b/app/src/organisms/Desktop/RunProgressMeter/hooks/useRunProgressCopy.tsx index 65e2f27d6b3..aaa90997167 100644 --- a/app/src/organisms/Desktop/RunProgressMeter/hooks/useRunProgressCopy.tsx +++ b/app/src/organisms/Desktop/RunProgressMeter/hooks/useRunProgressCopy.tsx @@ -4,7 +4,6 @@ import { RUN_STATUS_BLOCKED_BY_OPEN_DOOR, RUN_STATUS_IDLE, } from '@opentrons/api-client' -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { getCommandTextData } from '/app/local-resources/commands' @@ -13,6 +12,7 @@ import { LegacyStyledText } from '@opentrons/components' import { CommandText } from '/app/molecules/Command' import { TERMINAL_RUN_STATUSES } from '../constants' +import type { ReactNode } from 'react' import type { CommandDetail, RunStatus } from '@opentrons/api-client' import type { CompletedProtocolAnalysis, @@ -21,7 +21,7 @@ import type { } from '@opentrons/shared-data' interface UseRunProgressResult { - currentStepContents: React.ReactNode + currentStepContents: ReactNode stepCountStr: string | null progressPercentage: number } diff --git a/app/src/organisms/Desktop/RunProgressMeter/index.tsx b/app/src/organisms/Desktop/RunProgressMeter/index.tsx index fb158f5d686..ecf38cd31f6 100644 --- a/app/src/organisms/Desktop/RunProgressMeter/index.tsx +++ b/app/src/organisms/Desktop/RunProgressMeter/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' import { css } from 'styled-components' @@ -49,6 +48,8 @@ import { useRobotType } from '/app/redux-resources/robots' import { useRunningStepCounts } from '/app/resources/protocols/hooks' import { useRunProgressCopy } from './hooks' +import type { MouseEventHandler } from 'react' + interface RunProgressMeterProps { runId: string robotName: string @@ -92,7 +93,7 @@ export function RunProgressMeter(props: RunProgressMeterProps): JSX.Element { const { downloadRunLog } = useDownloadRunLog(robotName, runId) - const onDownloadClick: React.MouseEventHandler = e => { + const onDownloadClick: MouseEventHandler = e => { if (downloadIsDisabled) return false e.preventDefault() e.stopPropagation() diff --git a/app/src/organisms/Desktop/SendProtocolToFlexSlideout/__tests__/SendProtocolToFlexSlideout.test.tsx b/app/src/organisms/Desktop/SendProtocolToFlexSlideout/__tests__/SendProtocolToFlexSlideout.test.tsx index 5ed8f96fb1a..08e60f84729 100644 --- a/app/src/organisms/Desktop/SendProtocolToFlexSlideout/__tests__/SendProtocolToFlexSlideout.test.tsx +++ b/app/src/organisms/Desktop/SendProtocolToFlexSlideout/__tests__/SendProtocolToFlexSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -36,6 +35,7 @@ import { storedProtocolData as storedProtocolDataFixture } from '/app/redux/prot import { SendProtocolToFlexSlideout } from '..' import { useNotifyAllRunsQuery } from '/app/resources/runs' +import type { ComponentProps } from 'react' import type * as ApiClient from '@opentrons/react-api-client' vi.mock('@opentrons/react-api-client', async importOriginal => { @@ -53,9 +53,7 @@ vi.mock('/app/redux/custom-labware') vi.mock('/app/redux/protocol-storage/selectors') vi.mock('/app/resources/runs') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/Desktop/SystemLanguagePreferenceModal/__tests__/SystemLanguagePreferenceModal.test.tsx b/app/src/organisms/Desktop/SystemLanguagePreferenceModal/__tests__/SystemLanguagePreferenceModal.test.tsx index 104085fcb15..af4c4feb5d0 100644 --- a/app/src/organisms/Desktop/SystemLanguagePreferenceModal/__tests__/SystemLanguagePreferenceModal.test.tsx +++ b/app/src/organisms/Desktop/SystemLanguagePreferenceModal/__tests__/SystemLanguagePreferenceModal.test.tsx @@ -5,6 +5,10 @@ import { describe, it, vi, afterEach, beforeEach, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' +import { + ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL, + useTrackEvent, +} from '/app/redux/analytics' import { getAppLanguage, getStoredSystemLanguage, @@ -16,6 +20,7 @@ import { SystemLanguagePreferenceModal } from '..' vi.mock('react-router-dom') vi.mock('/app/redux/config') vi.mock('/app/redux/shell') +vi.mock('/app/redux/analytics') const render = () => { return renderWithProviders(, { @@ -24,6 +29,7 @@ const render = () => { } const mockNavigate = vi.fn() +const mockTrackEvent = vi.fn() const MOCK_DEFAULT_LANGUAGE = 'en-US' @@ -33,6 +39,7 @@ describe('SystemLanguagePreferenceModal', () => { vi.mocked(getSystemLanguage).mockReturnValue(MOCK_DEFAULT_LANGUAGE) vi.mocked(getStoredSystemLanguage).mockReturnValue(MOCK_DEFAULT_LANGUAGE) vi.mocked(useNavigate).mockReturnValue(mockNavigate) + vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent) }) afterEach(() => { vi.resetAllMocks() @@ -68,6 +75,14 @@ describe('SystemLanguagePreferenceModal', () => { 'language.systemLanguage', MOCK_DEFAULT_LANGUAGE ) + expect(mockTrackEvent).toBeCalledWith({ + name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL, + properties: { + language: MOCK_DEFAULT_LANGUAGE, + systemLanguage: MOCK_DEFAULT_LANGUAGE, + modalType: 'appBootModal', + }, + }) }) it('should default to English (US) if system language is unsupported', () => { @@ -90,6 +105,14 @@ describe('SystemLanguagePreferenceModal', () => { MOCK_DEFAULT_LANGUAGE ) expect(updateConfigValue).toBeCalledWith('language.systemLanguage', 'es-MX') + expect(mockTrackEvent).toBeCalledWith({ + name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL, + properties: { + language: MOCK_DEFAULT_LANGUAGE, + systemLanguage: 'es-MX', + modalType: 'appBootModal', + }, + }) }) it('should set a supported app language when system language is an unsupported locale of the same language', () => { @@ -112,6 +135,14 @@ describe('SystemLanguagePreferenceModal', () => { MOCK_DEFAULT_LANGUAGE ) expect(updateConfigValue).toBeCalledWith('language.systemLanguage', 'en-GB') + expect(mockTrackEvent).toBeCalledWith({ + name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL, + properties: { + language: MOCK_DEFAULT_LANGUAGE, + systemLanguage: 'en-GB', + modalType: 'appBootModal', + }, + }) }) it('should render the correct header, description, and buttons when system language changes', () => { @@ -139,6 +170,14 @@ describe('SystemLanguagePreferenceModal', () => { 'language.systemLanguage', 'zh-CN' ) + expect(mockTrackEvent).toBeCalledWith({ + name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL, + properties: { + language: 'zh-CN', + systemLanguage: 'zh-CN', + modalType: 'systemLanguageUpdateModal', + }, + }) fireEvent.click(secondaryButton) expect(updateConfigValue).toHaveBeenNthCalledWith( 3, @@ -168,6 +207,14 @@ describe('SystemLanguagePreferenceModal', () => { 'language.systemLanguage', 'zh-Hant' ) + expect(mockTrackEvent).toBeCalledWith({ + name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL, + properties: { + language: 'zh-CN', + systemLanguage: 'zh-Hant', + modalType: 'systemLanguageUpdateModal', + }, + }) fireEvent.click(secondaryButton) expect(updateConfigValue).toHaveBeenNthCalledWith( 3, diff --git a/app/src/organisms/Desktop/SystemLanguagePreferenceModal/index.tsx b/app/src/organisms/Desktop/SystemLanguagePreferenceModal/index.tsx index d3b04f19061..f135c0fe10a 100644 --- a/app/src/organisms/Desktop/SystemLanguagePreferenceModal/index.tsx +++ b/app/src/organisms/Desktop/SystemLanguagePreferenceModal/index.tsx @@ -16,6 +16,10 @@ import { } from '@opentrons/components' import { LANGUAGES } from '/app/i18n' +import { + ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL, + useTrackEvent, +} from '/app/redux/analytics' import { getAppLanguage, getStoredSystemLanguage, @@ -32,7 +36,7 @@ type ArrayElement< export function SystemLanguagePreferenceModal(): JSX.Element | null { const { i18n, t } = useTranslation(['app_settings', 'shared', 'branded']) - + const trackEvent = useTrackEvent() const [currentOption, setCurrentOption] = useState( LANGUAGES[0] ) @@ -66,6 +70,16 @@ export function SystemLanguagePreferenceModal(): JSX.Element | null { const handlePrimaryClick = (): void => { dispatch(updateConfigValue('language.appLanguage', currentOption.value)) dispatch(updateConfigValue('language.systemLanguage', systemLanguage)) + trackEvent({ + name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL, + properties: { + language: currentOption.value, + systemLanguage, + modalType: showUpdateModal + ? 'systemLanguageUpdateModal' + : 'appBootModal', + }, + }) } const handleDropdownClick = (value: string): void => { diff --git a/app/src/organisms/Desktop/UpdateAppModal/__tests__/UpdateAppModal.test.tsx b/app/src/organisms/Desktop/UpdateAppModal/__tests__/UpdateAppModal.test.tsx index 3131bee25a3..4469abc159a 100644 --- a/app/src/organisms/Desktop/UpdateAppModal/__tests__/UpdateAppModal.test.tsx +++ b/app/src/organisms/Desktop/UpdateAppModal/__tests__/UpdateAppModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen, fireEvent } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -9,10 +8,11 @@ import { renderWithProviders } from '/app/__testing-utils__' import { useRemoveActiveAppUpdateToast } from '../../Alerts' import { UpdateAppModal, RELEASE_NOTES_URL_BASE } from '..' +import type { ComponentProps } from 'react' +import type * as Dom from 'react-router-dom' import type { State } from '/app/redux/types' import type { ShellUpdateState } from '/app/redux/shell/types' import type * as ShellState from '/app/redux/shell' -import type * as Dom from 'react-router-dom' import type { UpdateAppModalProps } from '..' vi.mock('/app/redux/shell/update', async importOriginal => { @@ -35,7 +35,7 @@ vi.mock('../../Alerts') const getShellUpdateState = Shell.getShellUpdateState -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, initialState: { @@ -45,7 +45,7 @@ const render = (props: React.ComponentProps) => { } describe('UpdateAppModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/UpdateRobotBanner/__tests__/UpdateRobotBanner.test.tsx b/app/src/organisms/Desktop/UpdateRobotBanner/__tests__/UpdateRobotBanner.test.tsx index e7d90f70ea3..e1b67d7c9dc 100644 --- a/app/src/organisms/Desktop/UpdateRobotBanner/__tests__/UpdateRobotBanner.test.tsx +++ b/app/src/organisms/Desktop/UpdateRobotBanner/__tests__/UpdateRobotBanner.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -12,19 +11,21 @@ import { import { handleUpdateBuildroot } from '../../Devices/RobotSettings/UpdateBuildroot' import { UpdateRobotBanner } from '..' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/robot-update') vi.mock('../../Devices/RobotSettings/UpdateBuildroot') const getUpdateDisplayInfo = Buildroot.getRobotUpdateDisplayInfo -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('UpdateRobotBanner', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/Desktop/UpdateRobotBanner/index.tsx b/app/src/organisms/Desktop/UpdateRobotBanner/index.tsx index e5d7d2d0e85..390f4bc3ac1 100644 --- a/app/src/organisms/Desktop/UpdateRobotBanner/index.tsx +++ b/app/src/organisms/Desktop/UpdateRobotBanner/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { @@ -13,6 +12,7 @@ import { import { getRobotUpdateDisplayInfo } from '/app/redux/robot-update' import { handleUpdateBuildroot } from '../Devices/RobotSettings/UpdateBuildroot' +import type { MouseEvent } from 'react' import type { StyleProps } from '@opentrons/components' import type { State } from '/app/redux/types' import type { DiscoveredRobot } from '/app/redux/discovery/types' @@ -35,7 +35,7 @@ export function UpdateRobotBanner( robot !== null && robot.healthStatus === 'ok' ? ( { + onClick={(e: MouseEvent) => { e.stopPropagation() }} flexDirection={DIRECTION_COLUMN} diff --git a/app/src/organisms/DeviceDetailsDeckConfiguration/AddFixtureModal.tsx b/app/src/organisms/DeviceDetailsDeckConfiguration/AddFixtureModal.tsx index bf3be7e788e..b4b8f462d7f 100644 --- a/app/src/organisms/DeviceDetailsDeckConfiguration/AddFixtureModal.tsx +++ b/app/src/organisms/DeviceDetailsDeckConfiguration/AddFixtureModal.tsx @@ -369,8 +369,8 @@ export function AddFixtureModal({ }} aria-label="back" paddingX={SPACING.spacing16} - marginTop={'1.44rem'} - marginBottom={'0.56rem'} + marginTop="1.44rem" + marginBottom="0.56rem" > {t('shared:go_back')} diff --git a/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/AddFixtureModal.test.tsx b/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/AddFixtureModal.test.tsx index 450a64cc0e6..955c9e0f86b 100644 --- a/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/AddFixtureModal.test.tsx +++ b/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/AddFixtureModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect, afterEach } from 'vitest' @@ -16,6 +15,7 @@ import { i18n } from '/app/i18n' import { AddFixtureModal } from '../AddFixtureModal' import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { DeckConfiguration } from '@opentrons/shared-data' import type { Modules } from '@opentrons/api-client' @@ -26,14 +26,14 @@ vi.mock('/app/resources/deck_configuration') const mockCloseModal = vi.fn() const mockUpdateDeckConfiguration = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Touchscreen AddFixtureModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -88,7 +88,7 @@ describe('Touchscreen AddFixtureModal', () => { }) describe('Desktop AddFixtureModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeckConfigurationDiscardChangesModal.test.tsx b/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeckConfigurationDiscardChangesModal.test.tsx index fd0e56ffa4d..b0b213170dc 100644 --- a/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeckConfigurationDiscardChangesModal.test.tsx +++ b/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeckConfigurationDiscardChangesModal.test.tsx @@ -1,10 +1,11 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { DeckConfigurationDiscardChangesModal } from '../DeckConfigurationDiscardChangesModal' + +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockFunc = vi.fn() @@ -19,7 +20,7 @@ vi.mock('react-router-dom', async importOriginal => { }) const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders( , @@ -30,7 +31,7 @@ const render = ( } describe('DeckConfigurationDiscardChangesModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeckFixtureSetupInstructionsModal.test.tsx b/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeckFixtureSetupInstructionsModal.test.tsx index ddc9ff33194..bfd218f5e97 100644 --- a/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeckFixtureSetupInstructionsModal.test.tsx +++ b/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeckFixtureSetupInstructionsModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -6,12 +5,14 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { DeckFixtureSetupInstructionsModal } from '../DeckFixtureSetupInstructionsModal' +import type { ComponentProps } from 'react' + const mockFunc = vi.fn() const PNG_FILE_NAME = '/app/src/assets/images/on-device-display/deck_fixture_setup_qrcode.png' const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -19,7 +20,7 @@ const render = ( } describe('Touchscreen DeckFixtureSetupInstructionsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -49,7 +50,7 @@ describe('Touchscreen DeckFixtureSetupInstructionsModal', () => { }) describe('Desktop DeckFixtureSetupInstructionsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeviceDetailsDeckConfiguration.test.tsx b/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeviceDetailsDeckConfiguration.test.tsx index 16ef3db90a7..409f0d3f452 100644 --- a/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeviceDetailsDeckConfiguration.test.tsx +++ b/app/src/organisms/DeviceDetailsDeckConfiguration/__tests__/DeviceDetailsDeckConfiguration.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { when } from 'vitest-when' import { describe, it, beforeEach, vi, afterEach } from 'vitest' @@ -23,6 +22,7 @@ import { useNotifyDeckConfigurationQuery, } from '/app/resources/deck_configuration' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { MaintenanceRun } from '@opentrons/api-client' import type { DeckConfiguration } from '@opentrons/shared-data' @@ -62,7 +62,7 @@ const mockCurrnetMaintenanceRun = { } as MaintenanceRun const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -70,7 +70,7 @@ const render = ( } describe('DeviceDetailsDeckConfiguration', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/DropTipWizardFlows/__tests__/DropTipWizard.test.tsx b/app/src/organisms/DropTipWizardFlows/__tests__/DropTipWizard.test.tsx index d1108fc3c18..9ea6a6f2363 100644 --- a/app/src/organisms/DropTipWizardFlows/__tests__/DropTipWizard.test.tsx +++ b/app/src/organisms/DropTipWizardFlows/__tests__/DropTipWizard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -31,6 +30,8 @@ import { CONFIRM_POSITION, } from '../constants' +import type { ComponentProps } from 'react' + vi.mock('/app/molecules/InProgressModal') vi.mock('../ExitConfirmation') vi.mock('../steps') @@ -38,7 +39,7 @@ vi.mock('../ErrorInfo') vi.mock('../DropTipWizardHeader') const renderDropTipWizardContainer = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -46,7 +47,7 @@ const renderDropTipWizardContainer = ( } describe('DropTipWizardContainer', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = mockDropTipWizardContainerProps @@ -75,7 +76,7 @@ describe('DropTipWizardContainer', () => { }) const renderDropTipWizardContent = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -83,7 +84,7 @@ const renderDropTipWizardContent = ( } describe('DropTipWizardContent', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = mockDropTipWizardContainerProps diff --git a/app/src/organisms/DropTipWizardFlows/__tests__/DropTipWizardHeader.test.tsx b/app/src/organisms/DropTipWizardFlows/__tests__/DropTipWizardHeader.test.tsx index c5720adf4ab..74bb70f6a8e 100644 --- a/app/src/organisms/DropTipWizardFlows/__tests__/DropTipWizardHeader.test.tsx +++ b/app/src/organisms/DropTipWizardFlows/__tests__/DropTipWizardHeader.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { screen } from '@testing-library/react' @@ -10,17 +9,18 @@ import { DropTipWizardHeader, } from '../DropTipWizardHeader' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' import type { UseWizardExitHeaderProps } from '../DropTipWizardHeader' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('DropTipWizardHeader', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = mockDropTipWizardContainerProps diff --git a/app/src/organisms/EmergencyStop/__tests__/EstopMissingModal.test.tsx b/app/src/organisms/EmergencyStop/__tests__/EstopMissingModal.test.tsx index c2ce7cea0e1..c71c0a146ee 100644 --- a/app/src/organisms/EmergencyStop/__tests__/EstopMissingModal.test.tsx +++ b/app/src/organisms/EmergencyStop/__tests__/EstopMissingModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -8,16 +7,18 @@ import { i18n } from '/app/i18n' import { getIsOnDevice } from '/app/redux/config' import { EstopMissingModal } from '../EstopMissingModal' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('EstopMissingModal - Touchscreen', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -40,7 +41,7 @@ describe('EstopMissingModal - Touchscreen', () => { }) describe('EstopMissingModal - Desktop', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/EmergencyStop/__tests__/EstopPressedModal.test.tsx b/app/src/organisms/EmergencyStop/__tests__/EstopPressedModal.test.tsx index 067211c4c06..6d259bd6acb 100644 --- a/app/src/organisms/EmergencyStop/__tests__/EstopPressedModal.test.tsx +++ b/app/src/organisms/EmergencyStop/__tests__/EstopPressedModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' import { describe, it, vi, beforeEach, expect } from 'vitest' @@ -10,18 +9,20 @@ import { getIsOnDevice } from '/app/redux/config' import { EstopPressedModal } from '../EstopPressedModal' import { usePlacePlateReaderLid } from '/app/resources/modules' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') vi.mock('/app/redux/config') vi.mock('/app/resources/modules') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('EstopPressedModal - Touchscreen', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -85,7 +86,7 @@ describe('EstopPressedModal - Touchscreen', () => { }) describe('EstopPressedModal - Desktop', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/EmergencyStop/__tests__/EstopTakeover.test.tsx b/app/src/organisms/EmergencyStop/__tests__/EstopTakeover.test.tsx index 3ff0503dc69..4ffd822fb80 100644 --- a/app/src/organisms/EmergencyStop/__tests__/EstopTakeover.test.tsx +++ b/app/src/organisms/EmergencyStop/__tests__/EstopTakeover.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, expect, vi } from 'vitest' import { screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' @@ -19,6 +18,8 @@ import { getLocalRobot } from '/app/redux/discovery' import { mockConnectedRobot } from '/app/redux/discovery/__fixtures__' import { EstopTakeover } from '../EstopTakeover' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') vi.mock('../EstopMissingModal') vi.mock('../EstopPressedModal') @@ -33,14 +34,14 @@ const mockPressed = { }, } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('EstopTakeover', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/IgnoreErrorSkipStep.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/IgnoreErrorSkipStep.tsx index 16cb755d4da..2f8f7016773 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/IgnoreErrorSkipStep.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/IgnoreErrorSkipStep.tsx @@ -27,6 +27,7 @@ import { SkipStepInfo, } from '../shared' +import type { ChangeEvent } from 'react' import type { RecoveryContentProps } from '../types' export function IgnoreErrorSkipStep(props: RecoveryContentProps): JSX.Element { @@ -143,7 +144,7 @@ export function IgnoreErrorStepHome({ > ) => { + onChange={(e: ChangeEvent) => { setSelectedOption(e.currentTarget.value as IgnoreOption) }} options={IGNORE_OPTIONS_IN_ORDER.map(option => { diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/CancelRun.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/CancelRun.test.tsx index cbf8126e353..06923d65492 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/CancelRun.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/CancelRun.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { screen, waitFor } from '@testing-library/react' @@ -9,11 +8,13 @@ import { CancelRun } from '../CancelRun' import { RECOVERY_MAP } from '../../constants' import { SelectRecoveryOption } from '../SelectRecoveryOption' import { clickButtonLabeled } from '../../__tests__/util' + +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('../SelectRecoveryOption') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -21,7 +22,7 @@ const render = (props: React.ComponentProps) => { describe('RecoveryFooterButtons', () => { const { CANCEL_RUN, ROBOT_CANCELING, DROP_TIP_FLOWS } = RECOVERY_MAP - let props: React.ComponentProps + let props: ComponentProps let mockGoBackPrevStep: Mock let mockhandleMotionRouting: Mock let mockProceedToRouteAndStep: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/FillWellAndSkip.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/FillWellAndSkip.test.tsx index 3c7675ec21c..6f0145a9ef3 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/FillWellAndSkip.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/FillWellAndSkip.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { screen, waitFor } from '@testing-library/react' @@ -11,6 +10,7 @@ import { CancelRun } from '../CancelRun' import { SelectRecoveryOption } from '../SelectRecoveryOption' import { clickButtonLabeled } from '../../__tests__/util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('../../shared', async () => { @@ -32,28 +32,26 @@ vi.mock('../CancelRun') vi.mock('../SelectRecoveryOption') vi.mock('/app/molecules/Command') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } -const renderFillWell = (props: React.ComponentProps) => { +const renderFillWell = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } -const renderSkipToNextStep = ( - props: React.ComponentProps -) => { +const renderSkipToNextStep = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('FillWellAndSkip', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -118,7 +116,7 @@ describe('FillWellAndSkip', () => { }) describe('FillWell', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -136,7 +134,7 @@ describe('FillWell', () => { }) describe('SkipToNextStep', () => { - let props: React.ComponentProps + let props: ComponentProps let mockhandleMotionRouting: Mock let mockGoBackPrevStep: Mock let mockProceedToRouteAndStep: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/HomeAndRetry.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/HomeAndRetry.test.tsx index 3286041b7fb..aae543bc559 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/HomeAndRetry.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/HomeAndRetry.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' @@ -10,17 +9,19 @@ import { SelectRecoveryOption } from '../SelectRecoveryOption' import { HomeAndRetry } from '../HomeAndRetry' import { TipSelection } from '../../shared/TipSelection' +import type { ComponentProps } from 'react' + vi.mock('../SelectRecoveryOption') vi.mock('../../shared/TipSelection') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('HomeAndRetry', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { ...mockRecoveryContentProps, diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/IgnoreErrorSkipStep.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/IgnoreErrorSkipStep.test.tsx index 547334f77c4..a1655ae57b2 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/IgnoreErrorSkipStep.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/IgnoreErrorSkipStep.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { screen, fireEvent, waitFor } from '@testing-library/react' @@ -15,6 +14,7 @@ import { SelectRecoveryOption } from '../SelectRecoveryOption' import { clickButtonLabeled } from '../../__tests__/util' import { SkipStepInfo } from '/app/organisms/ErrorRecoveryFlows/shared' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/organisms/ErrorRecoveryFlows/shared', async () => { @@ -31,14 +31,14 @@ vi.mock('/app/organisms/ErrorRecoveryFlows/shared', async () => { }) vi.mock('../SelectRecoveryOption') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } const renderIgnoreErrorStepHome = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -46,7 +46,7 @@ const renderIgnoreErrorStepHome = ( } describe('IgnoreErrorSkipStep', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -98,7 +98,7 @@ describe('IgnoreErrorSkipStep', () => { }) describe('IgnoreErrorStepHome', () => { - let props: React.ComponentProps + let props: ComponentProps let mockIgnoreErrorKindThisRun: Mock let mockProceedToRouteAndStep: Mock let mockGoBackPrevStep: Mock @@ -184,7 +184,7 @@ describe('IgnoreErrorStepHome', () => { }) describe('IgnoreOptions', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManageTips.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManageTips.test.tsx index 941b19081c7..b047f7a463b 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManageTips.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManageTips.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { screen, @@ -18,6 +17,7 @@ import { DT_ROUTES } from '/app/organisms/DropTipWizardFlows/constants' import { SelectRecoveryOption } from '../SelectRecoveryOption' import { clickButtonLabeled } from '../../__tests__/util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' import type { PipetteModelSpecs } from '@opentrons/shared-data' @@ -34,14 +34,14 @@ const MOCK_ACTUAL_PIPETTE = { }, } as PipetteModelSpecs -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ManageTips', () => { - let props: React.ComponentProps + let props: ComponentProps let mockProceedNextStep: Mock let mockProceedToRouteAndStep: Mock let mockhandleMotionRouting: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManualMoveLwAndSkip.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManualMoveLwAndSkip.test.tsx index 48f8615cf81..8cc5285e31a 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManualMoveLwAndSkip.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManualMoveLwAndSkip.test.tsx @@ -6,7 +6,7 @@ import { i18n } from '/app/i18n' import { ManualMoveLwAndSkip } from '../ManualMoveLwAndSkip' import { RECOVERY_MAP } from '../../constants' -import type * as React from 'react' +import type { ComponentProps } from 'react' vi.mock('../../shared', () => ({ GripperIsHoldingLabware: vi.fn(() => ( @@ -23,7 +23,7 @@ vi.mock('../SelectRecoveryOption', () => ({ })) describe('ManualMoveLwAndSkip', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -34,7 +34,7 @@ describe('ManualMoveLwAndSkip', () => { } as any }) - const render = (props: React.ComponentProps) => { + const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManualReplaceLwAndRetry.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManualReplaceLwAndRetry.test.tsx index fb47ccb5f2f..0d25fcda029 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManualReplaceLwAndRetry.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/ManualReplaceLwAndRetry.test.tsx @@ -6,7 +6,7 @@ import { i18n } from '/app/i18n' import { ManualReplaceLwAndRetry } from '../ManualReplaceLwAndRetry' import { RECOVERY_MAP } from '../../constants' -import type * as React from 'react' +import type { ComponentProps } from 'react' vi.mock('../../shared', () => ({ GripperIsHoldingLabware: vi.fn(() => ( @@ -23,7 +23,7 @@ vi.mock('../SelectRecoveryOption', () => ({ })) describe('ManualReplaceLwAndRetry', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -35,9 +35,7 @@ describe('ManualReplaceLwAndRetry', () => { } as any }) - const render = ( - props: React.ComponentProps - ) => { + const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetryNewTips.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetryNewTips.test.tsx index dcee4c10c56..cfd55c45d77 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetryNewTips.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetryNewTips.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest' import { screen, waitFor } from '@testing-library/react' @@ -10,6 +9,7 @@ import { RECOVERY_MAP } from '../../constants' import { SelectRecoveryOption } from '../SelectRecoveryOption' import { clickButtonLabeled } from '../../__tests__/util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/molecules/Command') @@ -23,14 +23,14 @@ vi.mock('../../shared', async () => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } const renderRetryWithNewTips = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -38,7 +38,7 @@ const renderRetryWithNewTips = ( } describe('RetryNewTips', () => { - let props: React.ComponentProps + let props: ComponentProps let mockProceedToRouteAndStep: Mock beforeEach(() => { @@ -121,7 +121,7 @@ describe('RetryNewTips', () => { }) describe('RetryWithNewTips', () => { - let props: React.ComponentProps + let props: ComponentProps let mockhandleMotionRouting: Mock let mockRetryFailedCommand: Mock let mockResumeRun: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetrySameTips.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetrySameTips.test.tsx index 4514c9cc350..7457b50f824 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetrySameTips.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetrySameTips.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest' import { screen, waitFor } from '@testing-library/react' @@ -10,19 +9,20 @@ import { RECOVERY_MAP } from '../../constants' import { SelectRecoveryOption } from '../SelectRecoveryOption' import { clickButtonLabeled } from '../../__tests__/util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/molecules/Command') vi.mock('../SelectRecoveryOption') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } const renderRetrySameTipsInfo = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -30,7 +30,7 @@ const renderRetrySameTipsInfo = ( } describe('RetrySameTips', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -72,7 +72,7 @@ describe('RetrySameTips', () => { }) describe('RetrySameTipsInfo', () => { - let props: React.ComponentProps + let props: ComponentProps let mockhandleMotionRouting: Mock let mockRetryFailedCommand: Mock let mockResumeRun: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetryStep.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetryStep.test.tsx index ec39f5b7c18..f972275c224 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetryStep.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/RetryStep.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' @@ -9,17 +8,19 @@ import { RetryStep } from '../RetryStep' import { RECOVERY_MAP } from '../../constants' import { SelectRecoveryOption } from '../SelectRecoveryOption' +import type { ComponentProps } from 'react' + vi.mock('/app/molecules/Command') vi.mock('../SelectRecoveryOption') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('RetryStep', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SelectRecoveryOptions.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SelectRecoveryOptions.test.tsx index 62fe8eea3c8..caa67196866 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SelectRecoveryOptions.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SelectRecoveryOptions.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { screen, fireEvent } from '@testing-library/react' import { when } from 'vitest-when' @@ -23,10 +22,11 @@ import { import { RECOVERY_MAP, ERROR_KINDS } from '../../constants' import { clickButtonLabeled } from '../../__tests__/util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' const renderSelectRecoveryOption = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders( , @@ -37,7 +37,7 @@ const renderSelectRecoveryOption = ( } const renderRecoveryOptions = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -45,7 +45,7 @@ const renderRecoveryOptions = ( } describe('SelectRecoveryOption', () => { const { RETRY_STEP, RETRY_NEW_TIPS } = RECOVERY_MAP - let props: React.ComponentProps + let props: ComponentProps let mockProceedToRouteAndStep: Mock let mockSetSelectedRecoveryOption: Mock let mockGetRecoveryOptionCopy: Mock @@ -253,7 +253,7 @@ describe('SelectRecoveryOption', () => { }) }) describe('RecoveryOptions', () => { - let props: React.ComponentProps + let props: ComponentProps let mockSetSelectedRoute: Mock let mockGetRecoveryOptionCopy: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SkipStepNewTips.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SkipStepNewTips.test.tsx index 09afa086dca..a492184cbc7 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SkipStepNewTips.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SkipStepNewTips.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -9,6 +8,7 @@ import { SkipStepNewTips } from '../SkipStepNewTips' import { RECOVERY_MAP } from '../../constants' import { SelectRecoveryOption } from '../SelectRecoveryOption' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/molecules/Command') @@ -23,14 +23,14 @@ vi.mock('../../shared', async () => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('SkipStepNewTips', () => { - let props: React.ComponentProps + let props: ComponentProps let mockProceedToRouteAndStep: Mock beforeEach(() => { diff --git a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SkipStepSameTips.test.tsx b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SkipStepSameTips.test.tsx index 157825b3322..eb716694d68 100644 --- a/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SkipStepSameTips.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/RecoveryOptions/__tests__/SkipStepSameTips.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -10,18 +9,20 @@ import { RECOVERY_MAP } from '../../constants' import { SelectRecoveryOption } from '../SelectRecoveryOption' import { SkipStepInfo } from '/app/organisms/ErrorRecoveryFlows/shared' +import type { ComponentProps } from 'react' + vi.mock('/app/molecules/Command') vi.mock('/app/organisms/ErrorRecoveryFlows/shared') vi.mock('../SelectRecoveryOption') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('SkipStepSameTips', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryFlows.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryFlows.test.tsx index 2e425b73495..53bc8c15a8b 100644 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryFlows.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryFlows.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, expect, it, beforeEach } from 'vitest' import { screen, renderHook } from '@testing-library/react' @@ -24,6 +23,7 @@ import { useERWizard, ErrorRecoveryWizard } from '../ErrorRecoveryWizard' import { useRecoverySplash, RecoverySplash } from '../RecoverySplash' import { useRunLoadedLabwareDefinitionsByUri } from '/app/resources/runs' +import type { ComponentProps } from 'react' import type { RunStatus } from '@opentrons/api-client' vi.mock('../ErrorRecoveryWizard') @@ -128,14 +128,14 @@ describe('useErrorRecoveryFlows', () => { }) }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ErrorRecoveryFlows', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryWizard.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryWizard.test.tsx index d97072e45f3..1f358fc574c 100644 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryWizard.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/__tests__/ErrorRecoveryWizard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { renderHook, act, screen } from '@testing-library/react' @@ -35,6 +34,8 @@ import { RecoveryDoorOpenSpecial, } from '../shared' +import type { ComponentProps } from 'react' + vi.mock('../RecoveryOptions') vi.mock('../RecoveryInProgress') vi.mock('../RecoveryError') @@ -96,7 +97,7 @@ describe('useERWizard', () => { }) const renderRecoveryComponent = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -104,7 +105,7 @@ const renderRecoveryComponent = ( } describe('ErrorRecoveryComponent', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = mockRecoveryContentProps @@ -158,7 +159,7 @@ describe('ErrorRecoveryComponent', () => { }) const renderRecoveryContent = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -192,7 +193,7 @@ describe('ErrorRecoveryContent', () => { HOME_AND_RETRY, } = RECOVERY_MAP - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = mockRecoveryContentProps diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryDoorOpen.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryDoorOpen.test.tsx index a9fc92e1b84..57b0eff70a9 100644 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryDoorOpen.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryDoorOpen.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, vi, expect } from 'vitest' import { screen } from '@testing-library/react' @@ -10,16 +9,17 @@ import { i18n } from '/app/i18n' import { RecoveryDoorOpen } from '../RecoveryDoorOpen' import { clickButtonLabeled } from './util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('RecoveryDoorOpen', () => { - let props: React.ComponentProps + let props: ComponentProps let mockResumeRecovery: Mock let mockProceedToRouteAndStep: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryError.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryError.test.tsx index f46f3f949ba..9d19b32d788 100644 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryError.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryError.test.tsx @@ -1,5 +1,4 @@ /* eslint-disable testing-library/prefer-presence-queries */ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { screen, fireEvent, waitFor } from '@testing-library/react' @@ -9,9 +8,10 @@ import { i18n } from '/app/i18n' import { RecoveryError } from '../RecoveryError' import { RECOVERY_MAP } from '../constants' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -20,7 +20,7 @@ const render = (props: React.ComponentProps) => { const { ERROR_WHILE_RECOVERING } = RECOVERY_MAP describe('RecoveryError', () => { - let props: React.ComponentProps + let props: ComponentProps let proceedToRouteAndStepMock: Mock let getRecoverOptionCopyMock: Mock let handleMotionRoutingMock: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryInProgress.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryInProgress.test.tsx index 2dfa5711644..b9c6149a696 100644 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryInProgress.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryInProgress.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { beforeEach, describe, it, vi, afterEach, expect } from 'vitest' import { act, renderHook, screen } from '@testing-library/react' @@ -12,7 +11,9 @@ import { } from '../RecoveryInProgress' import { RECOVERY_MAP } from '../constants' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -28,7 +29,7 @@ describe('RecoveryInProgress', () => { ROBOT_SKIPPING_STEP, ROBOT_RELEASING_LABWARE, } = RECOVERY_MAP - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoverySplash.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoverySplash.test.tsx index 901ab22e158..17b7f9fd24d 100644 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoverySplash.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoverySplash.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { fireEvent, screen, waitFor, renderHook } from '@testing-library/react' @@ -21,6 +20,7 @@ import { StepInfo } from '../shared' import { useToaster } from '../../ToasterOven' import { clickButtonLabeled } from './util' +import type { ComponentProps, FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' vi.mock('/app/redux/config') @@ -30,7 +30,7 @@ vi.mock('../../ToasterOven') const store: Store = createStore(vi.fn(), {}) describe('useRunPausedSplash', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { vi.mocked(getIsOnDevice).mockReturnValue(true) const queryClient = new QueryClient() @@ -65,7 +65,7 @@ describe('useRunPausedSplash', () => { }) }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -77,7 +77,7 @@ const render = (props: React.ComponentProps) => { } describe('RecoverySplash', () => { - let props: React.ComponentProps + let props: ComponentProps const mockToggleERWiz = vi.fn(() => Promise.resolve()) const mockProceedToRouteAndStep = vi.fn() const mockHandleMotionRouting = vi.fn(() => Promise.resolve()) diff --git a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryTakeover.test.tsx b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryTakeover.test.tsx index 1eec7782713..6a71c84ba3c 100644 --- a/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryTakeover.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/__tests__/RecoveryTakeover.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -14,18 +13,19 @@ import { RecoveryTakeover, RecoveryTakeoverDesktop } from '../RecoveryTakeover' import { useUpdateClientDataRecovery } from '/app/resources/client_data' import { clickButtonLabeled } from './util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/resources/client_data') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('RecoveryTakeover', () => { - let props: React.ComponentProps + let props: ComponentProps let mockClearClientData: Mock beforeEach(() => { @@ -91,7 +91,7 @@ describe('RecoveryTakeover', () => { }) describe('RecoveryTakeoverDesktop', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryOptionCopy.test.tsx b/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryOptionCopy.test.tsx index 11e8a574246..0ec262c3f41 100644 --- a/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryOptionCopy.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryOptionCopy.test.tsx @@ -1,5 +1,3 @@ -import type * as React from 'react' - import { describe, it } from 'vitest' import { screen } from '@testing-library/react' @@ -10,6 +8,8 @@ import type { ErrorKind, RecoveryRoute } from '../../types' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' +import type { ComponentProps } from 'react' + function MockRenderCmpt({ route, errorKind, @@ -26,7 +26,7 @@ function MockRenderCmpt({ ) } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryToasts.test.tsx b/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryToasts.test.tsx index 085551f42e7..1ce852e86aa 100644 --- a/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryToasts.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryToasts.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { I18nextProvider } from 'react-i18next' import { i18n } from '/app/i18n' @@ -16,6 +15,7 @@ import { RECOVERY_MAP } from '../../constants' import { useToaster } from '../../../ToasterOven' import { useCommandTextString } from '/app/local-resources/commands' +import type { ReactElement } from 'react' import type { Mock } from 'vitest' import type { BuildToast } from '../useRecoveryToasts' @@ -42,7 +42,7 @@ const DEFAULT_PROPS: BuildToast = { } // Utility function for rendering with I18nextProvider -const renderWithI18n = (component: React.ReactElement) => { +const renderWithI18n = (component: ReactElement) => { return render({component}) } diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/LeftColumnLabwareInfo.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/LeftColumnLabwareInfo.tsx index 87cdac57255..c6fa3623cf4 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/LeftColumnLabwareInfo.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/LeftColumnLabwareInfo.tsx @@ -1,11 +1,11 @@ import { InterventionContent } from '/app/molecules/InterventionModal/InterventionContent' -import type * as React from 'react' +import type { ComponentProps } from 'react' import type { RecoveryContentProps } from '../types' type LeftColumnLabwareInfoProps = RecoveryContentProps & { title: string - type: React.ComponentProps['infoProps']['type'] + type: ComponentProps['infoProps']['type'] /* Renders a warning InlineNotification if provided. */ bannerText?: string } @@ -24,7 +24,7 @@ export function LeftColumnLabwareInfo({ } = failedLabwareUtils const { displayNameNewLoc, displayNameCurrentLoc } = failedLabwareLocations - const buildNewLocation = (): React.ComponentProps< + const buildNewLocation = (): ComponentProps< typeof InterventionContent >['infoProps']['newLocationProps'] => displayNameNewLoc != null diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryContentWrapper.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryContentWrapper.tsx index 9274079897f..d7c2abd900f 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryContentWrapper.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryContentWrapper.tsx @@ -1,7 +1,6 @@ // TODO: replace this by making these props true of interventionmodal content wrappers // once error recovery uses interventionmodal consistently -import type * as React from 'react' import { css } from 'styled-components' import { DIRECTION_COLUMN, @@ -18,19 +17,21 @@ import { } from '/app/molecules/InterventionModal' import { RecoveryFooterButtons } from './RecoveryFooterButtons' +import type { ComponentProps, ReactNode } from 'react' + interface SingleColumnContentWrapperProps { - children: React.ReactNode - footerDetails?: React.ComponentProps + children: ReactNode + footerDetails?: ComponentProps } interface TwoColumnContentWrapperProps { - children: [React.ReactNode, React.ReactNode] - footerDetails?: React.ComponentProps + children: [ReactNode, ReactNode] + footerDetails?: ComponentProps } interface OneOrTwoColumnContentWrapperProps { - children: [React.ReactNode, React.ReactNode] - footerDetails?: React.ComponentProps + children: [ReactNode, ReactNode] + footerDetails?: ComponentProps } // For flex-direction: column recovery content with one column only. export function RecoverySingleColumnContentWrapper({ diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryInterventionModal.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryInterventionModal.tsx index 82974023805..73ac8497deb 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryInterventionModal.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/RecoveryInterventionModal.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { createPortal } from 'react-dom' import { css } from 'styled-components' @@ -13,11 +12,12 @@ import { import { InterventionModal } from '/app/molecules/InterventionModal' import { getModalPortalEl, getTopPortalEl } from '/app/App/portal' +import type { ComponentProps } from 'react' import type { ModalType } from '/app/molecules/InterventionModal' import type { DesktopSizeType } from '../types' export type RecoveryInterventionModalProps = Omit< - React.ComponentProps, + ComponentProps, 'type' > & { /* If on desktop, specifies the hard-coded dimensions height of the modal. */ diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/StepInfo.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/StepInfo.tsx index bbc12ce0429..ba68ee88507 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/StepInfo.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/StepInfo.tsx @@ -1,11 +1,10 @@ -import type * as React from 'react' - import { useTranslation } from 'react-i18next' import { Flex, DISPLAY_INLINE, StyledText } from '@opentrons/components' import { CommandText } from '/app/molecules/Command' +import type { ComponentProps } from 'react' import type { StyleProps } from '@opentrons/components' import type { RecoveryContentProps } from '../types' @@ -15,8 +14,8 @@ interface StepInfoProps extends StyleProps { robotType: RecoveryContentProps['robotType'] protocolAnalysis: RecoveryContentProps['protocolAnalysis'] allRunDefs: RecoveryContentProps['allRunDefs'] - desktopStyle?: React.ComponentProps['desktopStyle'] - oddStyle?: React.ComponentProps['oddStyle'] + desktopStyle?: ComponentProps['desktopStyle'] + oddStyle?: ComponentProps['oddStyle'] } export function StepInfo({ diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/TwoColLwInfoAndDeck.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/TwoColLwInfoAndDeck.tsx index 9bf8f12bc22..ad7ac489d7c 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/TwoColLwInfoAndDeck.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/TwoColLwInfoAndDeck.tsx @@ -1,9 +1,9 @@ import { - Flex, - MoveLabwareOnDeck, COLORS, - Module, + Flex, LabwareRender, + Module, + MoveLabwareOnDeck, } from '@opentrons/components' import { inferModuleOrientationFromXCoordinate } from '@opentrons/shared-data' @@ -14,7 +14,7 @@ import { RecoveryFooterButtons } from './RecoveryFooterButtons' import { LeftColumnLabwareInfo } from './LeftColumnLabwareInfo' import { RECOVERY_MAP } from '../constants' -import type * as React from 'react' +import type { ComponentProps } from 'react' import type { RecoveryContentProps } from '../types' import type { InterventionContent } from '/app/molecules/InterventionModal/InterventionContent' @@ -100,7 +100,7 @@ export function TwoColLwInfoAndDeck( } } - const buildType = (): React.ComponentProps< + const buildType = (): ComponentProps< typeof InterventionContent >['infoProps']['type'] => { switch (selectedRecoveryOption) { diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/ErrorDetailsModal.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/ErrorDetailsModal.test.tsx index ce754df9cfa..da76c3e09d9 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/ErrorDetailsModal.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/ErrorDetailsModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, expect, vi } from 'vitest' import { screen, act, renderHook } from '@testing-library/react' @@ -17,6 +16,8 @@ import { StallErrorBanner, } from '../ErrorDetailsModal' +import type { ComponentProps } from 'react' + vi.mock('react-dom', () => ({ ...vi.importActual('react-dom'), createPortal: vi.fn((element, container) => element), @@ -47,14 +48,14 @@ describe('useErrorDetailsModal', () => { }) }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ErrorDetailsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/GripperIsHoldingLabware.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/GripperIsHoldingLabware.test.tsx index 95af112fa58..adedca04009 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/GripperIsHoldingLabware.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/GripperIsHoldingLabware.test.tsx @@ -10,13 +10,12 @@ import { GripperIsHoldingLabware, HOLDING_LABWARE_OPTIONS, } from '../GripperIsHoldingLabware' +import { RECOVERY_MAP } from '/app/organisms/ErrorRecoveryFlows/constants' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' -import { RECOVERY_MAP } from '/app/organisms/ErrorRecoveryFlows/constants' -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -26,7 +25,7 @@ let mockProceedToRouteAndStep: Mock let mockProceedNextStep: Mock describe('GripperIsHoldingLabware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { mockProceedToRouteAndStep = vi.fn(() => Promise.resolve()) mockProceedNextStep = vi.fn(() => Promise.resolve()) diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/GripperReleaseLabware.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/GripperReleaseLabware.test.tsx index 3bdd9f97819..91fdc3d6a9e 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/GripperReleaseLabware.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/GripperReleaseLabware.test.tsx @@ -7,20 +7,21 @@ import { i18n } from '/app/i18n' import { mockRecoveryContentProps } from '/app/organisms/ErrorRecoveryFlows/__fixtures__' import { clickButtonLabeled } from '/app/organisms/ErrorRecoveryFlows/__tests__/util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/assets/videos/error-recovery/Gripper_Release.webm', () => ({ default: 'mocked-animation-path.webm', })) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('GripperReleaseLabware', () => { - let props: React.ComponentProps + let props: ComponentProps let mockHandleMotionRouting: Mock beforeEach(() => { diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/LeftColumnLabwareInfo.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/LeftColumnLabwareInfo.test.tsx index f38e1e06922..c8ab8bc4ad6 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/LeftColumnLabwareInfo.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/LeftColumnLabwareInfo.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, expect, vi } from 'vitest' import { screen } from '@testing-library/react' @@ -8,16 +7,18 @@ import { i18n } from '/app/i18n' import { LeftColumnLabwareInfo } from '../LeftColumnLabwareInfo' import { InterventionContent } from '/app/molecules/InterventionModal/InterventionContent' +import type { ComponentProps } from 'react' + vi.mock('/app/molecules/InterventionModal/InterventionContent') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('LeftColumnLabwareInfo', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryDoorOpenSpecial.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryDoorOpenSpecial.test.tsx index 5cc4ae74b87..b54ccc649d7 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryDoorOpenSpecial.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryDoorOpenSpecial.test.tsx @@ -11,11 +11,11 @@ import { i18n } from '/app/i18n' import { RecoveryDoorOpenSpecial } from '../RecoveryDoorOpenSpecial' import { RECOVERY_MAP } from '../../constants' -import type * as React from 'react' +import type { ComponentProps } from 'react' import { clickButtonLabeled } from '/app/organisms/ErrorRecoveryFlows/__tests__/util' describe('RecoveryDoorOpenSpecial', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -39,9 +39,7 @@ describe('RecoveryDoorOpenSpecial', () => { } as any }) - const render = ( - props: React.ComponentProps - ) => { + const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx index 6381d2b579a..6643dcf8021 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RecoveryFooterButtons.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { screen, fireEvent } from '@testing-library/react' @@ -8,16 +7,17 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { RecoveryFooterButtons } from '../RecoveryFooterButtons' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('RecoveryFooterButtons', () => { - let props: React.ComponentProps + let props: ComponentProps let mockPrimaryBtnOnClick: Mock let mockSecondaryBtnOnClick: Mock let mockTertiaryBtnOnClick: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RetryStepInfo.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RetryStepInfo.test.tsx index 94f77910657..810cf1c00f1 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RetryStepInfo.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/RetryStepInfo.test.tsx @@ -7,10 +7,11 @@ import { RetryStepInfo } from '../RetryStepInfo' import { ERROR_KINDS, RECOVERY_MAP } from '../../constants' import { clickButtonLabeled } from '/app/organisms/ErrorRecoveryFlows/__tests__/util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' describe('RetryStepInfo', () => { - let props: React.ComponentProps + let props: ComponentProps let mockHandleMotionRouting: Mock let mockRetryFailedCommand: Mock let mockResumeRun: Mock @@ -33,7 +34,7 @@ describe('RetryStepInfo', () => { } as any }) - const render = (props: React.ComponentProps) => { + const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/SelectTips.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/SelectTips.test.tsx index 08db6269c4d..425bf68b264 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/SelectTips.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/SelectTips.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { screen, fireEvent, waitFor } from '@testing-library/react' @@ -9,19 +8,20 @@ import { SelectTips } from '../SelectTips' import { RECOVERY_MAP } from '../../constants' import { TipSelectionModal } from '../TipSelectionModal' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('../TipSelectionModal') vi.mock('../TipSelection') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('SelectTips', () => { - let props: React.ComponentProps + let props: ComponentProps let mockGoBackPrevStep: Mock let mockhandleMotionRouting: Mock let mockProceedNextStep: Mock diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/SkipStepInfo.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/SkipStepInfo.test.tsx index 28ef4177648..0bd7b1a5a72 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/SkipStepInfo.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/SkipStepInfo.test.tsx @@ -7,10 +7,11 @@ import { SkipStepInfo } from '../SkipStepInfo' import { RECOVERY_MAP } from '../../constants' import { clickButtonLabeled } from '/app/organisms/ErrorRecoveryFlows/__tests__/util' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' describe('SkipStepInfo', () => { - let props: React.ComponentProps + let props: ComponentProps let mockHandleMotionRouting: Mock let mockSkipFailedCommand: Mock @@ -32,7 +33,7 @@ describe('SkipStepInfo', () => { } as any }) - const render = (props: React.ComponentProps) => { + const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/StepInfo.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/StepInfo.test.tsx index d6fbb50c345..aefd143aee1 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/StepInfo.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/StepInfo.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, beforeEach, vi } from 'vitest' import { screen } from '@testing-library/react' @@ -8,16 +7,18 @@ import { i18n } from '/app/i18n' import { StepInfo } from '../StepInfo' import { CommandText } from '/app/molecules/Command' +import type { ComponentProps } from 'react' + vi.mock('/app/molecules/Command') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('StepInfo', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TipSelection.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TipSelection.test.tsx index 9df7f8e02ec..c6bee4f8d55 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TipSelection.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TipSelection.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, expect, beforeEach } from 'vitest' import { screen } from '@testing-library/react' @@ -8,16 +7,18 @@ import { i18n } from '/app/i18n' import { TipSelection } from '../TipSelection' import { WellSelection } from '../../../WellSelection' +import type { ComponentProps } from 'react' + vi.mock('../../../WellSelection') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TipSelection', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { ...mockRecoveryContentProps, diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TipSelectionModal.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TipSelectionModal.test.tsx index fed5a44d4ce..32711ec9762 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TipSelectionModal.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TipSelectionModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { screen } from '@testing-library/react' @@ -8,16 +7,18 @@ import { i18n } from '/app/i18n' import { TipSelectionModal } from '../TipSelectionModal' import { TipSelection } from '../TipSelection' +import type { ComponentProps } from 'react' + vi.mock('../TipSelection') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TipSelectionModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TwoColLwInfoAndDeck.test.tsx b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TwoColLwInfoAndDeck.test.tsx index 0629038f800..e151cb11d7a 100644 --- a/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TwoColLwInfoAndDeck.test.tsx +++ b/app/src/organisms/ErrorRecoveryFlows/shared/__tests__/TwoColLwInfoAndDeck.test.tsx @@ -11,7 +11,7 @@ import { RECOVERY_MAP } from '../../constants' import { LeftColumnLabwareInfo } from '../LeftColumnLabwareInfo' import { getSlotNameAndLwLocFrom } from '../../hooks/useDeckMapUtils' -import type * as React from 'react' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('@opentrons/components', async () => { @@ -26,14 +26,14 @@ vi.mock('../../hooks/useDeckMapUtils') let mockProceedNextStep: Mock -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TwoColLwInfoAndDeck', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { mockProceedNextStep = vi.fn() diff --git a/app/src/organisms/FirmwareUpdateModal/__tests__/FirmwareUpdateModal.test.tsx b/app/src/organisms/FirmwareUpdateModal/__tests__/FirmwareUpdateModal.test.tsx index bb205f3f852..16aa35ec87d 100644 --- a/app/src/organisms/FirmwareUpdateModal/__tests__/FirmwareUpdateModal.test.tsx +++ b/app/src/organisms/FirmwareUpdateModal/__tests__/FirmwareUpdateModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { act, screen, waitFor } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -10,6 +9,8 @@ import { } from '@opentrons/react-api-client' import { i18n } from '/app/i18n' import { FirmwareUpdateModal } from '..' + +import type { ComponentProps } from 'react' import type { BadPipette, PipetteData, @@ -18,14 +19,14 @@ import type { vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('FirmwareUpdateModal', () => { - let props: React.ComponentProps + let props: ComponentProps const refetch = vi.fn(() => Promise.resolve()) const updateSubsystem = vi.fn(() => Promise.resolve()) beforeEach(() => { diff --git a/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateInProgressModal.test.tsx b/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateInProgressModal.test.tsx index f2ce6047481..2e237468b3d 100644 --- a/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateInProgressModal.test.tsx +++ b/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateInProgressModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { UpdateInProgressModal } from '../UpdateInProgressModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('UpdateInProgressModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { subsystem: 'pipette_right', diff --git a/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateNeededModal.test.tsx b/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateNeededModal.test.tsx index 53c91223b47..eb8e1cdcc5b 100644 --- a/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateNeededModal.test.tsx +++ b/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateNeededModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -12,6 +11,7 @@ import { UpdateNeededModal } from '../UpdateNeededModal' import { UpdateInProgressModal } from '../UpdateInProgressModal' import { UpdateResultsModal } from '../UpdateResultsModal' +import type { ComponentProps } from 'react' import type { BadPipette, SubsystemUpdateProgressData, @@ -21,14 +21,14 @@ vi.mock('@opentrons/react-api-client') vi.mock('../UpdateInProgressModal') vi.mock('../UpdateResultsModal') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('UpdateNeededModal', () => { - let props: React.ComponentProps + let props: ComponentProps const refetch = vi.fn(() => Promise.resolve()) const updateSubsystem = vi.fn(() => Promise.resolve({ diff --git a/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateResultsModal.test.tsx b/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateResultsModal.test.tsx index 29c5233db45..5c4f3f02473 100644 --- a/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateResultsModal.test.tsx +++ b/app/src/organisms/FirmwareUpdateModal/__tests__/UpdateResultsModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { UpdateResultsModal } from '../UpdateResultsModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('UpdateResultsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { isSuccess: true, diff --git a/app/src/organisms/GripperWizardFlows/BeforeBeginning.tsx b/app/src/organisms/GripperWizardFlows/BeforeBeginning.tsx index 89395aeee10..316a3a5526a 100644 --- a/app/src/organisms/GripperWizardFlows/BeforeBeginning.tsx +++ b/app/src/organisms/GripperWizardFlows/BeforeBeginning.tsx @@ -13,6 +13,8 @@ import { SCREWDRIVER_LOADNAME, GRIPPER_LOADNAME, CAL_PIN_LOADNAME, + CALIBRATION_PIN_DISPLAY_NAME, + HEX_SCREWDRIVER_DISPLAY_NAME, } from './constants' import type { UseMutateFunction } from 'react-query' @@ -105,9 +107,9 @@ export const BeforeBeginning = ( const equipmentInfoByLoadName: { [loadName: string]: { displayName: string; subtitle?: string } } = { - calibration_pin: { displayName: t('calibration_pin') }, + calibration_pin: { displayName: CALIBRATION_PIN_DISPLAY_NAME }, hex_screwdriver: { - displayName: t('hex_screwdriver'), + displayName: HEX_SCREWDRIVER_DISPLAY_NAME, subtitle: t('provided_with_robot_use_right_size'), }, [GRIPPER_LOADNAME]: { displayName: t('branded:gripper') }, diff --git a/app/src/organisms/GripperWizardFlows/MovePin.tsx b/app/src/organisms/GripperWizardFlows/MovePin.tsx index 1cf5153eaa5..b4cb6ebcf25 100644 --- a/app/src/organisms/GripperWizardFlows/MovePin.tsx +++ b/app/src/organisms/GripperWizardFlows/MovePin.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation, Trans } from 'react-i18next' import { EXTENSION } from '@opentrons/shared-data' import { @@ -25,6 +24,7 @@ import movePinRearToStorage from '/app/assets/videos/gripper-wizards/PIN_FROM_RE import calibratingFrontJaw from '/app/assets/videos/gripper-wizards/CALIBRATING_FRONT_JAW.webm' import calibratingRearJaw from '/app/assets/videos/gripper-wizards/CALIBRATING_REAR_JAW.webm' +import type { ReactNode } from 'react' import type { Coordinates } from '@opentrons/shared-data' import type { CreateMaintenanceCommand } from '/app/resources/runs' import type { GripperWizardStepProps, MovePinStep } from './types' @@ -137,10 +137,10 @@ export const MovePin = (props: MovePinProps): JSX.Element | null => { [m in typeof movement]: { inProgressText: string header: string - body: React.ReactNode + body: ReactNode buttonText: string - prepImage: React.ReactNode - inProgressImage?: React.ReactNode + prepImage: ReactNode + inProgressImage?: ReactNode } } = { [MOVE_PIN_TO_FRONT_JAW]: { diff --git a/app/src/organisms/GripperWizardFlows/__tests__/BeforeBeginning.test.tsx b/app/src/organisms/GripperWizardFlows/__tests__/BeforeBeginning.test.tsx index 888e6ba30b2..da3b45eec62 100644 --- a/app/src/organisms/GripperWizardFlows/__tests__/BeforeBeginning.test.tsx +++ b/app/src/organisms/GripperWizardFlows/__tests__/BeforeBeginning.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen, waitFor } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -8,15 +7,17 @@ import { RUN_ID_1 } from '/app/resources/runs/__fixtures__' import { BeforeBeginning } from '../BeforeBeginning' import { GRIPPER_FLOW_TYPES } from '../constants' +import type { ComponentProps } from 'react' + vi.mock('/app/molecules/InProgressModal/InProgressModal') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('BeforeBeginning', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { goBack: vi.fn(), diff --git a/app/src/organisms/GripperWizardFlows/__tests__/ExitConfirmation.test.tsx b/app/src/organisms/GripperWizardFlows/__tests__/ExitConfirmation.test.tsx index 4b20f234bfb..871a58d1ba9 100644 --- a/app/src/organisms/GripperWizardFlows/__tests__/ExitConfirmation.test.tsx +++ b/app/src/organisms/GripperWizardFlows/__tests__/ExitConfirmation.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -7,12 +6,14 @@ import { i18n } from '/app/i18n' import { ExitConfirmation } from '../ExitConfirmation' import { GRIPPER_FLOW_TYPES } from '../constants' +import type { ComponentProps } from 'react' + describe('ExitConfirmation', () => { const mockBack = vi.fn() const mockExit = vi.fn() const render = ( - props: Partial> = {} + props: Partial> = {} ) => { return renderWithProviders( { let mockChainRunCommands: any let mockSetErrorMessage: any - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { return renderWithProviders( { @@ -25,9 +26,7 @@ describe('MovePin', () => { const mockSetFrontJawOffset = vi.fn() const mockRunId = 'fakeRunId' - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { return renderWithProviders( { const mockProceed = vi.fn() - const render = ( - props: Partial> = {} - ) => { + const render = (props: Partial> = {}) => { return renderWithProviders( { let mockChainRunCommands: any let mockSetErrorMessage: any const render = ( - props: Partial> = {} + props: Partial> = {} ) => { return renderWithProviders( { when(useIsFlex).calledWith('otie').thenReturn(isFlex) - return ( - props: React.ComponentProps - ) => { + return (props: ComponentProps) => { return renderWithProviders( , { @@ -26,7 +25,7 @@ const getRenderer = (isFlex: boolean) => { } describe('IncompatibleModuleDesktopModalBody', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { modules: [], diff --git a/app/src/organisms/IncompatibleModule/__tests__/IncompatibleModuleODDModalBody.test.tsx b/app/src/organisms/IncompatibleModule/__tests__/IncompatibleModuleODDModalBody.test.tsx index e2d4e1a2af0..9c59e9a821a 100644 --- a/app/src/organisms/IncompatibleModule/__tests__/IncompatibleModuleODDModalBody.test.tsx +++ b/app/src/organisms/IncompatibleModule/__tests__/IncompatibleModuleODDModalBody.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -7,8 +6,10 @@ import { i18n } from '/app/i18n' import { IncompatibleModuleODDModalBody } from '../IncompatibleModuleODDModalBody' import * as Fixtures from '../__fixtures__' +import type { ComponentProps } from 'react' + const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -16,7 +17,7 @@ const render = ( } describe('IncompatibleModuleODDModalBody', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { modules: [], diff --git a/app/src/organisms/IncompatibleModule/__tests__/IncompatibleModuleTakeover.test.tsx b/app/src/organisms/IncompatibleModule/__tests__/IncompatibleModuleTakeover.test.tsx index d3da5d17958..24e35265621 100644 --- a/app/src/organisms/IncompatibleModule/__tests__/IncompatibleModuleTakeover.test.tsx +++ b/app/src/organisms/IncompatibleModule/__tests__/IncompatibleModuleTakeover.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest' import { when } from 'vitest-when' @@ -18,6 +17,8 @@ import { } from '/app/App/portal' import * as Fixtures from '../__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('../hooks') vi.mock('../IncompatibleModuleODDModalBody') vi.mock('../IncompatibleModuleDesktopModalBody') @@ -32,7 +33,7 @@ const getRenderer = (incompatibleModules: AttachedModule[]) => { vi.mocked(IncompatibleModuleDesktopModalBody).mockReturnValue(
        TEST ELEMENT DESKTOP
        ) - return (props: React.ComponentProps) => { + return (props: ComponentProps) => { const [rendered] = renderWithProviders( <> @@ -55,7 +56,7 @@ const getRenderer = (incompatibleModules: AttachedModule[]) => { } describe('IncompatibleModuleTakeover', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { isOnDevice: true } }) diff --git a/app/src/organisms/IncompatibleModule/hooks/__tests__/useIncompatibleModulesAttached.test.tsx b/app/src/organisms/IncompatibleModule/hooks/__tests__/useIncompatibleModulesAttached.test.tsx index 7e1a7342db1..c58d75c7b6d 100644 --- a/app/src/organisms/IncompatibleModule/hooks/__tests__/useIncompatibleModulesAttached.test.tsx +++ b/app/src/organisms/IncompatibleModule/hooks/__tests__/useIncompatibleModulesAttached.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { QueryClient, QueryClientProvider } from 'react-query' import { vi, it, expect, describe, beforeEach } from 'vitest' @@ -8,16 +7,17 @@ import { useIncompatibleModulesAttached } from '..' import * as Fixtures from '../__fixtures__' +import type { FunctionComponent, ReactNode } from 'react' import type { Modules } from '@opentrons/api-client' import type { UseQueryResult } from 'react-query' vi.mock('@opentrons/react-api-client') describe('useIncompatibleModulesAttached', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() - const clientProvider: React.FunctionComponent<{ - children: React.ReactNode + const clientProvider: FunctionComponent<{ + children: ReactNode }> = ({ children }) => ( {children} ) diff --git a/app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx b/app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx index af561b6c15d..556c051d32d 100644 --- a/app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx +++ b/app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx @@ -191,7 +191,6 @@ export function MoveLabwareInterventionContent({ -) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('InterventionCommandMessage', () => { - let props: React.ComponentProps + let props: ComponentProps it('truncates command text greater than 220 characters long', () => { props = { commandMessage: longCommandMessage } diff --git a/app/src/organisms/InterventionModal/__tests__/InterventionCommandMessage.test.tsx b/app/src/organisms/InterventionModal/__tests__/InterventionCommandMessage.test.tsx index 6f3a688b808..a09994c929d 100644 --- a/app/src/organisms/InterventionModal/__tests__/InterventionCommandMessage.test.tsx +++ b/app/src/organisms/InterventionModal/__tests__/InterventionCommandMessage.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -10,16 +9,16 @@ import { truncatedCommandMessage, } from '../__fixtures__' -const render = ( - props: React.ComponentProps -) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('InterventionCommandMessage', () => { - let props: React.ComponentProps + let props: ComponentProps it('truncates command text greater than 220 characters long', () => { props = { commandMessage: longCommandMessage } diff --git a/app/src/organisms/InterventionModal/__tests__/InterventionModal.test.tsx b/app/src/organisms/InterventionModal/__tests__/InterventionModal.test.tsx index 59b8c659a1a..bb4799e1754 100644 --- a/app/src/organisms/InterventionModal/__tests__/InterventionModal.test.tsx +++ b/app/src/organisms/InterventionModal/__tests__/InterventionModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, renderHook, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -18,6 +17,7 @@ import { import { InterventionModal, useInterventionModal } from '..' import { useIsFlex } from '/app/redux-resources/robots' +import type { ComponentProps } from 'react' import type { CompletedProtocolAnalysis } from '@opentrons/shared-data' import type { RunData } from '@opentrons/api-client' @@ -90,14 +90,14 @@ describe('useInterventionModal', () => { }) }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('InterventionModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotName: ROBOT_NAME, diff --git a/app/src/organisms/LegacyApplyHistoricOffsets/__tests__/ApplyHistoricOffsets.test.tsx b/app/src/organisms/LegacyApplyHistoricOffsets/__tests__/ApplyHistoricOffsets.test.tsx index db70b1c096d..fb39565b161 100644 --- a/app/src/organisms/LegacyApplyHistoricOffsets/__tests__/ApplyHistoricOffsets.test.tsx +++ b/app/src/organisms/LegacyApplyHistoricOffsets/__tests__/ApplyHistoricOffsets.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' @@ -10,6 +9,7 @@ import { getIsLabwareOffsetCodeSnippetsOn } from '/app/redux/config' import { getLabwareDefinitionsFromCommands } from '/app/local-resources/labware' import { LegacyApplyHistoricOffsets } from '..' +import type { ComponentProps } from 'react' import type { LabwareDefinition2 } from '@opentrons/shared-data' import type { OffsetCandidate } from '../hooks/useOffsetCandidatesForAnalysis' @@ -64,11 +64,9 @@ const mockFourthCandidate: OffsetCandidate = { describe('ApplyHistoricOffsets', () => { const mockSetShouldApplyOffsets = vi.fn() const render = ( - props?: Partial> + props?: Partial> ) => - renderWithProviders< - React.ComponentProps - >( + renderWithProviders>( - renderWithProviders>( + renderWithProviders>( { ) it('returns historical run details with newest first', async () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) =>
        {children}
        const { result } = renderHook(useHistoricRunDetails, { wrapper }) @@ -57,7 +57,7 @@ describe('useHistoricRunDetails', () => { links: {}, }) ) - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) =>
        {children}
        const { result } = renderHook( diff --git a/app/src/organisms/LegacyApplyHistoricOffsets/hooks/__tests__/useOffsetCandidatesForAnalysis.test.tsx b/app/src/organisms/LegacyApplyHistoricOffsets/hooks/__tests__/useOffsetCandidatesForAnalysis.test.tsx index 832417cb9af..b4581abe15e 100644 --- a/app/src/organisms/LegacyApplyHistoricOffsets/hooks/__tests__/useOffsetCandidatesForAnalysis.test.tsx +++ b/app/src/organisms/LegacyApplyHistoricOffsets/hooks/__tests__/useOffsetCandidatesForAnalysis.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import { when } from 'vitest-when' import { renderHook, waitFor } from '@testing-library/react' @@ -13,6 +12,7 @@ import { getLabwareLocationCombos } from '../getLabwareLocationCombos' import { useOffsetCandidatesForAnalysis } from '../useOffsetCandidatesForAnalysis' import { storedProtocolData as storedProtocolDataFixture } from '/app/redux/protocol-storage/__fixtures__' +import type { FunctionComponent, ReactNode } from 'react' import type { LabwareDefinition2 } from '@opentrons/shared-data' import type { OffsetCandidate } from '../useOffsetCandidatesForAnalysis' @@ -102,7 +102,7 @@ describe('useOffsetCandidatesForAnalysis', () => { }) it('returns an empty array if robot ip but no analysis output', async () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) =>
        {children}
        const { result } = renderHook( @@ -115,7 +115,7 @@ describe('useOffsetCandidatesForAnalysis', () => { }) it('returns an empty array if no robot ip', async () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) =>
        {children}
        const { result } = renderHook( @@ -131,7 +131,7 @@ describe('useOffsetCandidatesForAnalysis', () => { }) }) it('returns candidates for each first match with newest first', async () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) =>
        {children}
        const { result } = renderHook( diff --git a/app/src/organisms/LegacyLabwarePositionCheck/PrepareSpace.tsx b/app/src/organisms/LegacyLabwarePositionCheck/PrepareSpace.tsx index 8820acfef33..f77973cbc1f 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/PrepareSpace.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/PrepareSpace.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import styled, { css } from 'styled-components' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' @@ -23,6 +22,7 @@ import { SmallButton } from '/app/atoms/buttons' import { NeedHelpLink } from '/app/molecules/OT2CalibrationNeedHelpLink' import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration' +import type { ReactNode } from 'react' import type { CompletedProtocolAnalysis, LabwareDefinition2, @@ -61,8 +61,8 @@ interface PrepareSpaceProps extends Omit { labwareDef: LabwareDefinition2 protocolData: CompletedProtocolAnalysis confirmPlacement: () => void - header: React.ReactNode - body: React.ReactNode + header: ReactNode + body: ReactNode robotType: RobotType } export const PrepareSpace = (props: PrepareSpaceProps): JSX.Element | null => { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/TwoUpTileLayout.tsx b/app/src/organisms/LegacyLabwarePositionCheck/TwoUpTileLayout.tsx index 7c6cd309bb4..396d49d5150 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/TwoUpTileLayout.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/TwoUpTileLayout.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import styled, { css } from 'styled-components' import { DIRECTION_COLUMN, @@ -12,6 +11,8 @@ import { DISPLAY_INLINE_BLOCK, } from '@opentrons/components' +import type { ReactNode } from 'react' + const Title = styled.h1` ${TYPOGRAPHY.h1Default}; margin-bottom: ${SPACING.spacing8}; @@ -36,11 +37,11 @@ export interface TwoUpTileLayoutProps { /** main header text on left half */ title: string /** paragraph text below title on left half */ - body: React.ReactNode + body: ReactNode /** entire contents of the right half */ - rightElement: React.ReactNode + rightElement: ReactNode /** footer underneath both halves of content */ - footer: React.ReactNode + footer: ReactNode } export function TwoUpTileLayout(props: TwoUpTileLayoutProps): JSX.Element { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/CheckItem.test.tsx b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/CheckItem.test.tsx index 17442dfc42b..5c2907c5276 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/CheckItem.test.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/CheckItem.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest' @@ -15,6 +14,7 @@ import { CheckItem } from '../CheckItem' import { SECTIONS } from '../constants' import { mockCompletedAnalysis, mockExistingOffsets } from '../__fixtures__' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/redux/config') @@ -23,14 +23,14 @@ vi.mock('../../Desktop/Devices/hooks') const mockStartPosition = { x: 10, y: 20, z: 30 } const mockEndPosition = { x: 9, y: 19, z: 29 } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('CheckItem', () => { - let props: React.ComponentProps + let props: ComponentProps let mockChainRunCommands: Mock beforeEach(() => { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ExitConfirmation.test.tsx b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ExitConfirmation.test.tsx index 6a93da71dc5..e710c991ccb 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ExitConfirmation.test.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ExitConfirmation.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest' import { ExitConfirmation } from '../ExitConfirmation' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ExitConfirmation', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/PickUpTip.test.tsx b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/PickUpTip.test.tsx index c23e1c1af2c..286e6f5f095 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/PickUpTip.test.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/PickUpTip.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen, waitFor } from '@testing-library/react' import { it, describe, beforeEach, vi, afterEach, expect } from 'vitest' import { FLEX_ROBOT_TYPE, HEATERSHAKER_MODULE_V1 } from '@opentrons/shared-data' @@ -8,23 +7,25 @@ import { getIsOnDevice } from '/app/redux/config' import { PickUpTip } from '../PickUpTip' import { SECTIONS } from '../constants' import { mockCompletedAnalysis, mockExistingOffsets } from '../__fixtures__' -import type { CommandData } from '@opentrons/api-client' import { nestedTextMatcher, renderWithProviders } from '/app/__testing-utils__' + +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' +import type { CommandData } from '@opentrons/api-client' vi.mock('/app/resources/protocols') vi.mock('/app/redux/config') const mockStartPosition = { x: 10, y: 20, z: 30 } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('PickUpTip', () => { - let props: React.ComponentProps + let props: ComponentProps let mockChainRunCommands: Mock beforeEach(() => { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ResultsSummary.test.tsx b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ResultsSummary.test.tsx index 24101904de4..30a29496aaf 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ResultsSummary.test.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ResultsSummary.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { i18n } from '/app/i18n' @@ -13,16 +12,18 @@ import { mockWorkingOffsets, } from '../__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ResultsSummary', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ReturnTip.test.tsx b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ReturnTip.test.tsx index 0af86097f9c..112be630a31 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ReturnTip.test.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/ReturnTip.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' @@ -12,17 +11,19 @@ import { useProtocolMetadata } from '/app/resources/protocols' import { getIsOnDevice } from '/app/redux/config' import { ReturnTip } from '../ReturnTip' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') vi.mock('/app/resources/protocols') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ReturnTip', () => { - let props: React.ComponentProps + let props: ComponentProps let mockChainRunCommands beforeEach(() => { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/TipConfirmation.test.tsx b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/TipConfirmation.test.tsx index 8f8878a7122..a262641fd84 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/TipConfirmation.test.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/TipConfirmation.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { TipConfirmation } from '../TipConfirmation' import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TipConfirmation', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/useLaunchLPC.test.tsx b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/useLaunchLPC.test.tsx index 070931ee998..83613b0d778 100644 --- a/app/src/organisms/LegacyLabwarePositionCheck/__tests__/useLaunchLPC.test.tsx +++ b/app/src/organisms/LegacyLabwarePositionCheck/__tests__/useLaunchLPC.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Provider } from 'react-redux' import configureStore from 'redux-mock-store' import { when } from 'vitest-when' @@ -27,6 +26,7 @@ import { import { useLaunchLegacyLPC } from '../useLaunchLegacyLPC' import { LegacyLabwarePositionCheck } from '..' +import type { FunctionComponent, ReactNode } from 'react' import type { Mock } from 'vitest' import type { LabwareOffset } from '@opentrons/api-client' import type { LabwareDefinition2 } from '@opentrons/shared-data' @@ -57,7 +57,7 @@ const mockCurrentOffsets: LabwareOffset[] = [ const mockLabwareDef = fixtureTiprack300ul as LabwareDefinition2 describe('useLaunchLPC hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> let mockCreateMaintenanceRun: Mock let mockCreateLabwareDefinition: Mock let mockDeleteMaintenanceRun: Mock diff --git a/app/src/organisms/LiquidsLabwareDetailsModal/LiquidDetailCard.tsx b/app/src/organisms/LiquidsLabwareDetailsModal/LiquidDetailCard.tsx index cb1591dc72f..7dc33069500 100644 --- a/app/src/organisms/LiquidsLabwareDetailsModal/LiquidDetailCard.tsx +++ b/app/src/organisms/LiquidsLabwareDetailsModal/LiquidDetailCard.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useSelector } from 'react-redux' import { css } from 'styled-components' import { @@ -27,6 +26,8 @@ import { import { getIsOnDevice } from '/app/redux/config' import { getWellRangeForLiquidLabwarePair } from '/app/transformations/analysis' +import type { Dispatch, SetStateAction } from 'react' + export const CARD_OUTLINE_BORDER_STYLE = css` border-style: ${BORDERS.styleSolid}; border-width: 1px; @@ -56,7 +57,7 @@ interface LiquidDetailCardProps { description: string | null displayColor: string volumeByWell: { [well: string]: number } - setSelectedValue: React.Dispatch> + setSelectedValue: Dispatch> selectedValue: string | undefined labwareWellOrdering: string[][] } diff --git a/app/src/organisms/LiquidsLabwareDetailsModal/__tests__/LiquidDetailCard.test.tsx b/app/src/organisms/LiquidsLabwareDetailsModal/__tests__/LiquidDetailCard.test.tsx index a96c8128897..522fae501b1 100644 --- a/app/src/organisms/LiquidsLabwareDetailsModal/__tests__/LiquidDetailCard.test.tsx +++ b/app/src/organisms/LiquidsLabwareDetailsModal/__tests__/LiquidDetailCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -12,12 +11,14 @@ import { } from '/app/redux/analytics' import { getIsOnDevice } from '/app/redux/config' import { LiquidDetailCard } from '../LiquidDetailCard' + +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('/app/redux/analytics') vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -25,7 +26,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEvent: Mock describe('LiquidDetailCard', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { mockTrackEvent = vi.fn() diff --git a/app/src/organisms/LiquidsLabwareDetailsModal/__tests__/LiquidsLabwareDetailsModal.test.tsx b/app/src/organisms/LiquidsLabwareDetailsModal/__tests__/LiquidsLabwareDetailsModal.test.tsx index 967a840ee75..e70756ed3f3 100644 --- a/app/src/organisms/LiquidsLabwareDetailsModal/__tests__/LiquidsLabwareDetailsModal.test.tsx +++ b/app/src/organisms/LiquidsLabwareDetailsModal/__tests__/LiquidsLabwareDetailsModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, vi, afterEach, expect } from 'vitest' import { screen } from '@testing-library/react' @@ -21,6 +20,7 @@ import { import { LiquidsLabwareDetailsModal } from '../LiquidsLabwareDetailsModal' import { LiquidDetailCard } from '../LiquidDetailCard' +import type { ComponentProps } from 'react' import type * as Components from '@opentrons/components' import type * as SharedData from '@opentrons/shared-data' @@ -44,16 +44,14 @@ vi.mock('/app/transformations/commands') vi.mock('/app/transformations/analysis') vi.mock('../LiquidDetailCard') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('LiquidsLabwareDetailsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { window.HTMLElement.prototype.scrollIntoView = function () {} props = { diff --git a/app/src/organisms/LocationConflictModal/__tests__/LocationConflictModal.test.tsx b/app/src/organisms/LocationConflictModal/__tests__/LocationConflictModal.test.tsx index 207caa02a1b..1d72ae1b858 100644 --- a/app/src/organisms/LocationConflictModal/__tests__/LocationConflictModal.test.tsx +++ b/app/src/organisms/LocationConflictModal/__tests__/LocationConflictModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { screen, fireEvent } from '@testing-library/react' import '@testing-library/jest-dom/vitest' @@ -21,6 +20,7 @@ import { useCloseCurrentRun } from '/app/resources/runs' import { LocationConflictModal } from '../LocationConflictModal' import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { DeckConfiguration } from '@opentrons/shared-data' @@ -33,7 +33,7 @@ const mockFixture = { cutoutFixtureId: STAGING_AREA_RIGHT_SLOT_FIXTURE, } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -45,7 +45,7 @@ const render = (props: React.ComponentProps) => { } describe('LocationConflictModal', () => { - let props: React.ComponentProps + let props: ComponentProps const mockUpdate = vi.fn() beforeEach(() => { props = { diff --git a/app/src/organisms/ModuleCard/Collapsible.tsx b/app/src/organisms/ModuleCard/Collapsible.tsx index cc15a88d4a0..00f032b52ef 100644 --- a/app/src/organisms/ModuleCard/Collapsible.tsx +++ b/app/src/organisms/ModuleCard/Collapsible.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { ALIGN_CENTER, @@ -13,13 +12,15 @@ import { } from '@opentrons/components' import type { IconName } from '@opentrons/components' +import type { ReactNode } from 'react' + interface CollapsibleProps { expanded: boolean - title: React.ReactNode + title: ReactNode expandedIcon?: IconName collapsedIcon?: IconName toggleExpanded: () => void - children: React.ReactNode + children: ReactNode } const EXPANDED_STYLE = css` diff --git a/app/src/organisms/ModuleCard/__tests__/AboutModuleSlideout.test.tsx b/app/src/organisms/ModuleCard/__tests__/AboutModuleSlideout.test.tsx index 35eb81ab169..e397eae0b50 100644 --- a/app/src/organisms/ModuleCard/__tests__/AboutModuleSlideout.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/AboutModuleSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -20,16 +19,18 @@ import { import { useCurrentRunStatus } from '/app/organisms/RunTimeControl' import { AboutModuleSlideout } from '../AboutModuleSlideout' +import type { ComponentProps } from 'react' + vi.mock('/app/organisms/RunTimeControl') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('AboutModuleSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { module: mockMagneticModule, diff --git a/app/src/organisms/ModuleCard/__tests__/Collapsible.test.tsx b/app/src/organisms/ModuleCard/__tests__/Collapsible.test.tsx index 3db479e3228..9ec5f2d0e1b 100644 --- a/app/src/organisms/ModuleCard/__tests__/Collapsible.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/Collapsible.test.tsx @@ -1,16 +1,17 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { Collapsible } from '../Collapsible' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('Collapsible', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { expanded: false, diff --git a/app/src/organisms/ModuleCard/__tests__/ConfirmAttachmentModal.test.tsx b/app/src/organisms/ModuleCard/__tests__/ConfirmAttachmentModal.test.tsx index ccc81bcb167..6c4d41973da 100644 --- a/app/src/organisms/ModuleCard/__tests__/ConfirmAttachmentModal.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/ConfirmAttachmentModal.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConfirmAttachmentModal } from '../ConfirmAttachmentModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ConfirmAttachmentBanner', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ModuleCard/__tests__/ErrorInfo.test.tsx b/app/src/organisms/ModuleCard/__tests__/ErrorInfo.test.tsx index bde80a0d7d2..2a93208d89e 100644 --- a/app/src/organisms/ModuleCard/__tests__/ErrorInfo.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/ErrorInfo.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -9,6 +8,8 @@ import { mockTemperatureModule, mockThermocycler, } from '/app/redux/modules/__fixtures__' + +import type { ComponentProps } from 'react' import type { HeaterShakerModule, ThermocyclerModule, @@ -71,14 +72,14 @@ const mockErrorHeaterShaker = { }, } as HeaterShakerModule -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ErrorInfo', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { attachedModule: mockTemperatureModule, diff --git a/app/src/organisms/ModuleCard/__tests__/FirmwareUpdateFailedModal.test.tsx b/app/src/organisms/ModuleCard/__tests__/FirmwareUpdateFailedModal.test.tsx index 13395bcff69..f3eaa7dc0ed 100644 --- a/app/src/organisms/ModuleCard/__tests__/FirmwareUpdateFailedModal.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/FirmwareUpdateFailedModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -6,16 +5,16 @@ import { i18n } from '/app/i18n' import { mockTemperatureModule } from '/app/redux/modules/__fixtures__' import { FirmwareUpdateFailedModal } from '../FirmwareUpdateFailedModal' -const render = ( - props: React.ComponentProps -) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('FirmwareUpdateFailedModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onCloseClick: vi.fn(), diff --git a/app/src/organisms/ModuleCard/__tests__/HeaterShakerModuleData.test.tsx b/app/src/organisms/ModuleCard/__tests__/HeaterShakerModuleData.test.tsx index 348fdb614d4..dab6f1fc7ad 100644 --- a/app/src/organisms/ModuleCard/__tests__/HeaterShakerModuleData.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/HeaterShakerModuleData.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -6,16 +5,18 @@ import { i18n } from '/app/i18n' import { StatusLabel } from '/app/atoms/StatusLabel' import { HeaterShakerModuleData } from '../HeaterShakerModuleData' +import type { ComponentProps } from 'react' + vi.mock('/app/atoms/StatusLabel') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('HeaterShakerModuleData', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { moduleData: { diff --git a/app/src/organisms/ModuleCard/__tests__/HeaterShakerSlideout.test.tsx b/app/src/organisms/ModuleCard/__tests__/HeaterShakerSlideout.test.tsx index 883d5b6bb7c..b02e5205f42 100644 --- a/app/src/organisms/ModuleCard/__tests__/HeaterShakerSlideout.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/HeaterShakerSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -9,16 +8,18 @@ import { i18n } from '/app/i18n' import { mockHeaterShaker } from '/app/redux/modules/__fixtures__' import { HeaterShakerSlideout } from '../HeaterShakerSlideout' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('HeaterShakerSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps let mockCreateLiveCommand = vi.fn() beforeEach(() => { diff --git a/app/src/organisms/ModuleCard/__tests__/MagneticModuleData.test.tsx b/app/src/organisms/ModuleCard/__tests__/MagneticModuleData.test.tsx index b6534d233e3..0440b16b251 100644 --- a/app/src/organisms/ModuleCard/__tests__/MagneticModuleData.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/MagneticModuleData.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { afterEach, beforeEach, describe, it, vi } from 'vitest' @@ -8,16 +7,18 @@ import { StatusLabel } from '/app/atoms/StatusLabel' import { MagneticModuleData } from '../MagneticModuleData' import { mockMagneticModule } from '/app/redux/modules/__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('/app/atoms/StatusLabel') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('MagneticModuleData', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { moduleHeight: mockMagneticModule.data.height, diff --git a/app/src/organisms/ModuleCard/__tests__/MagneticModuleSlideout.test.tsx b/app/src/organisms/ModuleCard/__tests__/MagneticModuleSlideout.test.tsx index fa10546af90..c52817fb517 100644 --- a/app/src/organisms/ModuleCard/__tests__/MagneticModuleSlideout.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/MagneticModuleSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { COLORS } from '@opentrons/components' @@ -13,15 +12,17 @@ import { mockMagneticModuleGen2, } from '/app/redux/modules/__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('MagneticModuleSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps let mockCreateLiveCommand = vi.fn() beforeEach(() => { mockCreateLiveCommand = vi.fn() diff --git a/app/src/organisms/ModuleCard/__tests__/ModuleCard.test.tsx b/app/src/organisms/ModuleCard/__tests__/ModuleCard.test.tsx index d30a885b759..078dfe12ada 100644 --- a/app/src/organisms/ModuleCard/__tests__/ModuleCard.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/ModuleCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -29,12 +28,13 @@ import { FirmwareUpdateFailedModal } from '../FirmwareUpdateFailedModal' import { ErrorInfo } from '../ErrorInfo' import { ModuleCard } from '..' +import type { ComponentProps } from 'react' +import type { Mock } from 'vitest' import type { HeaterShakerModule, MagneticModule, ThermocyclerModule, } from '/app/redux/modules/types' -import type { Mock } from 'vitest' vi.mock('../ErrorInfo') vi.mock('../MagneticModuleData') @@ -175,14 +175,14 @@ const mockEatToast = vi.fn() const MOCK_LATEST_REQUEST_ID = '1234' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ModuleCard', () => { - let props: React.ComponentProps + let props: ComponentProps let mockHandleModuleApiRequests: Mock beforeEach(() => { diff --git a/app/src/organisms/ModuleCard/__tests__/ModuleOverflowMenu.test.tsx b/app/src/organisms/ModuleCard/__tests__/ModuleOverflowMenu.test.tsx index 75701934e36..6e54dee83f9 100644 --- a/app/src/organisms/ModuleCard/__tests__/ModuleOverflowMenu.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/ModuleOverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -16,13 +15,14 @@ import { useIsFlex } from '/app/redux-resources/robots' import { useCurrentRunId, useRunStatuses } from '/app/resources/runs' import { ModuleOverflowMenu } from '../ModuleOverflowMenu' +import type { ComponentProps } from 'react' import type { TemperatureStatus } from '@opentrons/api-client' vi.mock('/app/resources/legacy_sessions') vi.mock('/app/redux-resources/robots') vi.mock('/app/resources/runs') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -161,7 +161,7 @@ const mockThermocyclerGen2LidClosed = { } as any describe('ModuleOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(useIsLegacySessionInProgress).mockReturnValue(false) vi.mocked(useRunStatuses).mockReturnValue({ diff --git a/app/src/organisms/ModuleCard/__tests__/ModuleSetupModal.test.tsx b/app/src/organisms/ModuleCard/__tests__/ModuleSetupModal.test.tsx index 87f340b2845..1b3abfab5ce 100644 --- a/app/src/organisms/ModuleCard/__tests__/ModuleSetupModal.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/ModuleSetupModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ModuleSetupModal } from '../ModuleSetupModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ModuleSetupModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { close: vi.fn(), moduleDisplayName: 'mockModuleDisplayName' } }) diff --git a/app/src/organisms/ModuleCard/__tests__/TemperatureModuleData.test.tsx b/app/src/organisms/ModuleCard/__tests__/TemperatureModuleData.test.tsx index 5b851ce5796..a9e2d88fb26 100644 --- a/app/src/organisms/ModuleCard/__tests__/TemperatureModuleData.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/TemperatureModuleData.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -8,16 +7,18 @@ import { StatusLabel } from '/app/atoms/StatusLabel' import { TemperatureModuleData } from '../TemperatureModuleData' import { mockTemperatureModuleGen2 } from '/app/redux/modules/__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('/app/atoms/StatusLabel') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TemperatureModuleData', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { moduleStatus: mockTemperatureModuleGen2.data.status, diff --git a/app/src/organisms/ModuleCard/__tests__/TemperatureModuleSlideout.test.tsx b/app/src/organisms/ModuleCard/__tests__/TemperatureModuleSlideout.test.tsx index ce65306741d..12ecc39f2f1 100644 --- a/app/src/organisms/ModuleCard/__tests__/TemperatureModuleSlideout.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/TemperatureModuleSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -12,18 +11,18 @@ import { } from '/app/redux/modules/__fixtures__' import { TemperatureModuleSlideout } from '../TemperatureModuleSlideout' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TemperatureModuleSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps let mockCreateLiveCommand = vi.fn() beforeEach(() => { diff --git a/app/src/organisms/ModuleCard/__tests__/TestShakeSlideout.test.tsx b/app/src/organisms/ModuleCard/__tests__/TestShakeSlideout.test.tsx index f11816df8b6..865c656b1ed 100644 --- a/app/src/organisms/ModuleCard/__tests__/TestShakeSlideout.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/TestShakeSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen, waitFor } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -12,12 +11,14 @@ import { useLatchControls } from '../hooks' import { TestShakeSlideout } from '../TestShakeSlideout' import { ModuleSetupModal } from '../ModuleSetupModal' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') vi.mock('@opentrons/react-api-client') vi.mock('../hooks') vi.mock('../ModuleSetupModal') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -90,7 +91,7 @@ const mockMovingHeaterShaker = { } as any describe('TestShakeSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps let mockCreateLiveCommand = vi.fn() const mockToggleLatch = vi.fn() beforeEach(() => { diff --git a/app/src/organisms/ModuleCard/__tests__/ThermocyclerModuleData.test.tsx b/app/src/organisms/ModuleCard/__tests__/ThermocyclerModuleData.test.tsx index 0885c74bb5d..fc2346cf9ba 100644 --- a/app/src/organisms/ModuleCard/__tests__/ThermocyclerModuleData.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/ThermocyclerModuleData.test.tsx @@ -1,6 +1,4 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' - import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -11,9 +9,10 @@ import { } from '/app/redux/modules/__fixtures__' import { ThermocyclerModuleData } from '../ThermocyclerModuleData' +import type { ComponentProps } from 'react' import type { ThermocyclerData } from '/app/redux/modules/api-types' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -49,7 +48,7 @@ const mockDataHeating = { } as ThermocyclerData describe('ThermocyclerModuleData', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { data: mockThermocycler.data, diff --git a/app/src/organisms/ModuleCard/__tests__/ThermocyclerModuleSlideout.test.tsx b/app/src/organisms/ModuleCard/__tests__/ThermocyclerModuleSlideout.test.tsx index d93a1b1f607..1557b821dd9 100644 --- a/app/src/organisms/ModuleCard/__tests__/ThermocyclerModuleSlideout.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/ThermocyclerModuleSlideout.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -9,18 +8,18 @@ import { i18n } from '/app/i18n' import { mockThermocycler } from '/app/redux/modules/__fixtures__' import { ThermocyclerModuleSlideout } from '../ThermocyclerModuleSlideout' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ThermocyclerModuleSlideout', () => { - let props: React.ComponentProps + let props: ComponentProps let mockCreateLiveCommand = vi.fn() beforeEach(() => { mockCreateLiveCommand = vi.fn() diff --git a/app/src/organisms/ModuleCard/__tests__/hooks.test.tsx b/app/src/organisms/ModuleCard/__tests__/hooks.test.tsx index ce0f0450179..2fe0c5502ef 100644 --- a/app/src/organisms/ModuleCard/__tests__/hooks.test.tsx +++ b/app/src/organisms/ModuleCard/__tests__/hooks.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Provider } from 'react-redux' import { when } from 'vitest-when' import { createStore } from 'redux' @@ -30,6 +29,7 @@ import { useIsHeaterShakerInProtocol, } from '../hooks' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' @@ -188,7 +188,7 @@ describe('useLatchControls', () => { }) it('should return latch is open and handle latch function and command to close latch', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -212,7 +212,7 @@ describe('useLatchControls', () => { }) }) it('should return if latch is closed and handle latch function opens latch', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -263,7 +263,7 @@ describe('useModuleOverflowMenu', () => { vi.restoreAllMocks() }) it('should return everything for menuItemsByModuleType and create deactive heater command', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -304,7 +304,7 @@ describe('useModuleOverflowMenu', () => { const mockAboutClick = vi.fn() const mockTestShakeClick = vi.fn() const mockHandleWizard = vi.fn() - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -336,7 +336,7 @@ describe('useModuleOverflowMenu', () => { it('should return only 1 menu button when module is a magnetic module and calls handleClick when module is disengaged', () => { const mockHandleClick = vi.fn() - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -366,7 +366,7 @@ describe('useModuleOverflowMenu', () => { }) it('should render magnetic module and create disengage command', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -404,7 +404,7 @@ describe('useModuleOverflowMenu', () => { it('should render temperature module and call handleClick when module is idle', () => { const mockHandleClick = vi.fn() - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -433,7 +433,7 @@ describe('useModuleOverflowMenu', () => { }) it('should render temp module and create deactivate temp command', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -470,7 +470,7 @@ describe('useModuleOverflowMenu', () => { it('should render TC module and call handleClick when module is idle', () => { const mockHandleClick = vi.fn() - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -499,7 +499,7 @@ describe('useModuleOverflowMenu', () => { }) it('should render TC module and create open lid command', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -537,7 +537,7 @@ describe('useModuleOverflowMenu', () => { }) it('should render TC module and create deactivate lid command', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -575,7 +575,7 @@ describe('useModuleOverflowMenu', () => { }) it('should render TC module gen 2 and create a close lid command', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -650,7 +650,7 @@ describe('useIsHeaterShakerInProtocol', () => { }) it('should return true when a heater shaker is in the protocol', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook(useIsHeaterShakerInProtocol, { wrapper }) @@ -674,7 +674,7 @@ describe('useIsHeaterShakerInProtocol', () => { id, })), } as any) - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook(useIsHeaterShakerInProtocol, { wrapper }) diff --git a/app/src/organisms/ODD/ChildNavigation/__tests__/ChildNavigation.test.tsx b/app/src/organisms/ODD/ChildNavigation/__tests__/ChildNavigation.test.tsx index 82a0dfb0b3c..44ff1414dd6 100644 --- a/app/src/organisms/ODD/ChildNavigation/__tests__/ChildNavigation.test.tsx +++ b/app/src/organisms/ODD/ChildNavigation/__tests__/ChildNavigation.test.tsx @@ -1,19 +1,20 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { ChildNavigation } from '..' + +import type { ComponentProps } from 'react' import type { SmallButton } from '/app/atoms/buttons' -const render = (props: React.ComponentProps) => +const render = (props: ComponentProps) => renderWithProviders() const mockOnClickBack = vi.fn() const mockOnClickButton = vi.fn() const mockOnClickSecondaryButton = vi.fn() -const mockSecondaryButtonProps: React.ComponentProps = { +const mockSecondaryButtonProps: ComponentProps = { onClick: mockOnClickSecondaryButton, buttonText: 'Setup Instructions', buttonType: 'tertiaryLowLight', @@ -22,7 +23,7 @@ const mockSecondaryButtonProps: React.ComponentProps = { } describe('ChildNavigation', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/ChildNavigation/index.tsx b/app/src/organisms/ODD/ChildNavigation/index.tsx index ea6c72f293b..ff1ebed1c95 100644 --- a/app/src/organisms/ODD/ChildNavigation/index.tsx +++ b/app/src/organisms/ODD/ChildNavigation/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import styled from 'styled-components' import { @@ -21,6 +20,7 @@ import { ODD_FOCUS_VISIBLE } from '/app/atoms/buttons/constants' import { SmallButton } from '/app/atoms/buttons' import { InlineNotification } from '/app/atoms/InlineNotification' +import type { ComponentProps, MouseEventHandler, ReactNode } from 'react' import type { IconName, StyleProps } from '@opentrons/components' import type { InlineNotificationProps } from '/app/atoms/InlineNotification' import type { @@ -30,15 +30,15 @@ import type { interface ChildNavigationProps extends StyleProps { header: string - onClickBack?: React.MouseEventHandler - buttonText?: React.ReactNode + onClickBack?: MouseEventHandler + buttonText?: ReactNode inlineNotification?: InlineNotificationProps - onClickButton?: React.MouseEventHandler + onClickButton?: MouseEventHandler buttonType?: SmallButtonTypes buttonIsDisabled?: boolean iconName?: IconName iconPlacement?: IconPlacement - secondaryButtonProps?: React.ComponentProps + secondaryButtonProps?: ComponentProps ariaDisabled?: boolean } diff --git a/app/src/organisms/ODD/InstrumentInfo/__tests__/InstrumentInfo.test.tsx b/app/src/organisms/ODD/InstrumentInfo/__tests__/InstrumentInfo.test.tsx index a478716483d..ec8b55bb17a 100644 --- a/app/src/organisms/ODD/InstrumentInfo/__tests__/InstrumentInfo.test.tsx +++ b/app/src/organisms/ODD/InstrumentInfo/__tests__/InstrumentInfo.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' @@ -8,22 +7,23 @@ import { PipetteWizardFlows } from '/app/organisms/PipetteWizardFlows' import { GripperWizardFlows } from '/app/organisms/GripperWizardFlows' import { InstrumentInfo } from '..' +import type { ComponentProps } from 'react' +import type * as ReactRouterDom from 'react-router-dom' import type { GripperData } from '@opentrons/api-client' -import type * as Dom from 'react-router-dom' const mockNavigate = vi.fn() vi.mock('/app/organisms/PipetteWizardFlows') vi.mock('/app/organisms/GripperWizardFlows') vi.mock('react-router-dom', async importOriginal => { - const reactRouterDom = await importOriginal() + const reactRouterDom = await importOriginal() return { ...reactRouterDom, useNavigate: () => mockNavigate, } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -65,7 +65,7 @@ const mockGripperDataWithCalData: GripperData = { } describe('InstrumentInfo', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(PipetteWizardFlows).mockReturnValue(
        mock PipetteWizardFlows
        diff --git a/app/src/organisms/ODD/InstrumentMountItem/LabeledMount.tsx b/app/src/organisms/ODD/InstrumentMountItem/LabeledMount.tsx index 20fe3941604..c909e4768f0 100644 --- a/app/src/organisms/ODD/InstrumentMountItem/LabeledMount.tsx +++ b/app/src/organisms/ODD/InstrumentMountItem/LabeledMount.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' import { @@ -15,6 +14,8 @@ import { TEXT_TRANSFORM_CAPITALIZE, TYPOGRAPHY, } from '@opentrons/components' + +import type { MouseEventHandler } from 'react' import type { Mount } from '/app/redux/pipettes/types' const MountButton = styled.button<{ isAttached: boolean }>` @@ -34,7 +35,7 @@ const MountButton = styled.button<{ isAttached: boolean }>` interface LabeledMountProps { mount: Mount | 'extension' instrumentName: string | null - handleClick: React.MouseEventHandler + handleClick: MouseEventHandler } export function LabeledMount(props: LabeledMountProps): JSX.Element { diff --git a/app/src/organisms/ODD/InstrumentMountItem/__tests__/ProtocolInstrumentMountItem.test.tsx b/app/src/organisms/ODD/InstrumentMountItem/__tests__/ProtocolInstrumentMountItem.test.tsx index 6c4c308b8d2..52c62382241 100644 --- a/app/src/organisms/ODD/InstrumentMountItem/__tests__/ProtocolInstrumentMountItem.test.tsx +++ b/app/src/organisms/ODD/InstrumentMountItem/__tests__/ProtocolInstrumentMountItem.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { LEFT } from '@opentrons/shared-data' @@ -8,6 +7,8 @@ import { PipetteWizardFlows } from '/app/organisms/PipetteWizardFlows' import { GripperWizardFlows } from '/app/organisms/GripperWizardFlows' import { ProtocolInstrumentMountItem } from '..' +import type { ComponentProps } from 'react' + vi.mock('/app/organisms/PipetteWizardFlows') vi.mock('/app/organisms/GripperWizardFlows') vi.mock('../../TakeoverModal') @@ -51,16 +52,14 @@ const mockLeftPipetteData = { ok: true, } -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ProtocolInstrumentMountItem', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { mount: LEFT, diff --git a/app/src/organisms/ODD/NameRobot/__tests__/ConfirmRobotName.test.tsx b/app/src/organisms/ODD/NameRobot/__tests__/ConfirmRobotName.test.tsx index d9e8521260e..21d34d3a59d 100644 --- a/app/src/organisms/ODD/NameRobot/__tests__/ConfirmRobotName.test.tsx +++ b/app/src/organisms/ODD/NameRobot/__tests__/ConfirmRobotName.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -7,6 +6,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConfirmRobotName } from '../ConfirmRobotName' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockNavigate = vi.fn() @@ -19,7 +19,7 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -31,7 +31,7 @@ const render = (props: React.ComponentProps) => { } describe('ConfirmRobotName', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotName: 'otie', diff --git a/app/src/organisms/ODD/Navigation/__tests__/Navigation.test.tsx b/app/src/organisms/ODD/Navigation/__tests__/Navigation.test.tsx index c86ba363d5c..1a212f83e46 100644 --- a/app/src/organisms/ODD/Navigation/__tests__/Navigation.test.tsx +++ b/app/src/organisms/ODD/Navigation/__tests__/Navigation.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -12,6 +11,8 @@ import { NavigationMenu } from '../NavigationMenu' import { Navigation } from '..' import { useScrollPosition } from '/app/local-resources/dom-utils' +import type { ComponentProps } from 'react' + vi.mock('/app/resources/networking/hooks/useNetworkConnection') vi.mock('/app/redux/discovery') vi.mock('../NavigationMenu') @@ -19,7 +20,7 @@ vi.mock('/app/local-resources/dom-utils') mockConnectedRobot.name = '12345678901234567' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -29,7 +30,7 @@ const render = (props: React.ComponentProps) => { } describe('Navigation', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = {} vi.mocked(getLocalRobot).mockReturnValue(mockConnectedRobot) diff --git a/app/src/organisms/ODD/Navigation/__tests__/NavigationMenu.test.tsx b/app/src/organisms/ODD/Navigation/__tests__/NavigationMenu.test.tsx index b40122cdd6b..b4b70528cda 100644 --- a/app/src/organisms/ODD/Navigation/__tests__/NavigationMenu.test.tsx +++ b/app/src/organisms/ODD/Navigation/__tests__/NavigationMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -9,6 +8,7 @@ import { useLights } from '/app/resources/devices' import { RestartRobotConfirmationModal } from '../RestartRobotConfirmationModal' import { NavigationMenu } from '../NavigationMenu' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' vi.mock('/app/redux/robot-admin') @@ -27,14 +27,14 @@ vi.mock('react-router-dom', async importOriginal => { const mockToggleLights = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('NavigationMenu', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { onClick: vi.fn(), diff --git a/app/src/organisms/ODD/Navigation/__tests__/RestartRobotConfirmationModal.test.tsx b/app/src/organisms/ODD/Navigation/__tests__/RestartRobotConfirmationModal.test.tsx index 8922a4225c2..5b3bc007567 100644 --- a/app/src/organisms/ODD/Navigation/__tests__/RestartRobotConfirmationModal.test.tsx +++ b/app/src/organisms/ODD/Navigation/__tests__/RestartRobotConfirmationModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -7,12 +6,14 @@ import { i18n } from '/app/i18n' import { restartRobot } from '/app/redux/robot-admin' import { RestartRobotConfirmationModal } from '../RestartRobotConfirmationModal' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/robot-admin') const mockFunc = vi.fn() const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -20,7 +21,7 @@ const render = ( } describe('RestartRobotConfirmationModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/NetworkSettings/FailedToConnect.tsx b/app/src/organisms/ODD/NetworkSettings/FailedToConnect.tsx index e3859e4ed29..22358b922bc 100644 --- a/app/src/organisms/ODD/NetworkSettings/FailedToConnect.tsx +++ b/app/src/organisms/ODD/NetworkSettings/FailedToConnect.tsx @@ -51,7 +51,7 @@ export function FailedToConnect({ name="ot-alert" size="3rem" color={COLORS.red50} - aria-label={'failed_to_connect_invalidPassword'} + aria-label="failed_to_connect_invalidPassword" /> diff --git a/app/src/organisms/ODD/NetworkSettings/__tests__/AlternativeSecurityTypeModal.test.tsx b/app/src/organisms/ODD/NetworkSettings/__tests__/AlternativeSecurityTypeModal.test.tsx index a01af9bba66..2707b07a8f5 100644 --- a/app/src/organisms/ODD/NetworkSettings/__tests__/AlternativeSecurityTypeModal.test.tsx +++ b/app/src/organisms/ODD/NetworkSettings/__tests__/AlternativeSecurityTypeModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -6,6 +5,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { AlternativeSecurityTypeModal } from '../AlternativeSecurityTypeModal' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockFunc = vi.fn() @@ -18,16 +18,14 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('AlternativeSecurityTypeModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/NetworkSettings/__tests__/ConnectingNetwork.test.tsx b/app/src/organisms/ODD/NetworkSettings/__tests__/ConnectingNetwork.test.tsx index e040bee4572..29444afea6d 100644 --- a/app/src/organisms/ODD/NetworkSettings/__tests__/ConnectingNetwork.test.tsx +++ b/app/src/organisms/ODD/NetworkSettings/__tests__/ConnectingNetwork.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { screen } from '@testing-library/react' import { beforeEach, describe, expect, it } from 'vitest' @@ -7,7 +6,9 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConnectingNetwork } from '../ConnectingNetwork' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders( @@ -19,7 +20,7 @@ const render = (props: React.ComponentProps) => { } describe('ConnectingNetwork', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/NetworkSettings/__tests__/DisplayWifiList.test.tsx b/app/src/organisms/ODD/NetworkSettings/__tests__/DisplayWifiList.test.tsx index e1449be3b9d..11e0ef16a9b 100644 --- a/app/src/organisms/ODD/NetworkSettings/__tests__/DisplayWifiList.test.tsx +++ b/app/src/organisms/ODD/NetworkSettings/__tests__/DisplayWifiList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -8,6 +7,7 @@ import * as Fixtures from '/app/redux/networking/__fixtures__' import { DisplaySearchNetwork } from '../DisplaySearchNetwork' import { DisplayWifiList } from '../DisplayWifiList' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockNavigate = vi.fn() @@ -31,14 +31,14 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('DisplayWifiList', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { list: mockWifiList, diff --git a/app/src/organisms/ODD/NetworkSettings/__tests__/FailedToConnect.test.tsx b/app/src/organisms/ODD/NetworkSettings/__tests__/FailedToConnect.test.tsx index 3dbf7bf1f46..74e11378af8 100644 --- a/app/src/organisms/ODD/NetworkSettings/__tests__/FailedToConnect.test.tsx +++ b/app/src/organisms/ODD/NetworkSettings/__tests__/FailedToConnect.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -7,9 +6,10 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { FailedToConnect } from '../FailedToConnect' +import type { ComponentProps } from 'react' import type { RequestState } from '/app/redux/robot-api/types' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -33,7 +33,7 @@ const failureState = { } as RequestState describe('ConnectedResult', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/NetworkSettings/__tests__/SelectAuthenticationType.test.tsx b/app/src/organisms/ODD/NetworkSettings/__tests__/SelectAuthenticationType.test.tsx index bfce05cc22d..2520e944ad7 100644 --- a/app/src/organisms/ODD/NetworkSettings/__tests__/SelectAuthenticationType.test.tsx +++ b/app/src/organisms/ODD/NetworkSettings/__tests__/SelectAuthenticationType.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { afterEach, beforeEach, describe, it, vi } from 'vitest' @@ -11,6 +10,7 @@ import { AlternativeSecurityTypeModal } from '../AlternativeSecurityTypeModal' import { SelectAuthenticationType } from '../SelectAuthenticationType' import { SetWifiCred } from '../SetWifiCred' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockNavigate = vi.fn() @@ -36,9 +36,7 @@ const initialMockWifi = { type: INTERFACE_WIFI, } -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -50,7 +48,7 @@ const render = ( } describe('SelectAuthenticationType', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { selectedAuthType: 'wpa-psk', diff --git a/app/src/organisms/ODD/NetworkSettings/__tests__/SetWifiCred.test.tsx b/app/src/organisms/ODD/NetworkSettings/__tests__/SetWifiCred.test.tsx index d1a25f069c9..85cc94d895e 100644 --- a/app/src/organisms/ODD/NetworkSettings/__tests__/SetWifiCred.test.tsx +++ b/app/src/organisms/ODD/NetworkSettings/__tests__/SetWifiCred.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -7,11 +6,13 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { SetWifiCred } from '../SetWifiCred' +import type { ComponentProps } from 'react' + const mockSetPassword = vi.fn() vi.mock('/app/redux/discovery') vi.mock('/app/redux/robot-api') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -23,7 +24,7 @@ const render = (props: React.ComponentProps) => { } describe('SetWifiCred', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { password: 'mock-password', diff --git a/app/src/organisms/ODD/NetworkSettings/__tests__/SetWifiSsid.test.tsx b/app/src/organisms/ODD/NetworkSettings/__tests__/SetWifiSsid.test.tsx index 11eab279c37..c5b0ff26ee5 100644 --- a/app/src/organisms/ODD/NetworkSettings/__tests__/SetWifiSsid.test.tsx +++ b/app/src/organisms/ODD/NetworkSettings/__tests__/SetWifiSsid.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -7,8 +6,10 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { SetWifiSsid } from '../SetWifiSsid' +import type { ComponentProps } from 'react' + const mockSetSelectedSsid = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -20,7 +21,7 @@ const render = (props: React.ComponentProps) => { } describe('SetWifiSsid', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { setInputSsid: mockSetSelectedSsid, diff --git a/app/src/organisms/ODD/NetworkSettings/__tests__/WifiConnectionDetails.test.tsx b/app/src/organisms/ODD/NetworkSettings/__tests__/WifiConnectionDetails.test.tsx index efcee37e0c6..76e63934328 100644 --- a/app/src/organisms/ODD/NetworkSettings/__tests__/WifiConnectionDetails.test.tsx +++ b/app/src/organisms/ODD/NetworkSettings/__tests__/WifiConnectionDetails.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -11,6 +10,7 @@ import * as Fixtures from '/app/redux/networking/__fixtures__' import { NetworkDetailsModal } from '../../RobotSettingsDashboard/NetworkSettings/NetworkDetailsModal' import { WifiConnectionDetails } from '../WifiConnectionDetails' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' vi.mock('/app/resources/networking/hooks') @@ -27,7 +27,7 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -51,7 +51,7 @@ const mockWifiList = [ ] describe('WifiConnectionDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { ssid: 'mockWifi', diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupDeckConfiguration/__tests__/ProtocolSetupDeckConfiguration.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupDeckConfiguration/__tests__/ProtocolSetupDeckConfiguration.test.tsx index 84a7fd2eb87..21b26365d81 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupDeckConfiguration/__tests__/ProtocolSetupDeckConfiguration.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupDeckConfiguration/__tests__/ProtocolSetupDeckConfiguration.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { fireEvent, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, afterEach } from 'vitest' @@ -15,6 +14,7 @@ import { useMostRecentCompletedAnalysis } from '/app/resources/runs' import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration' import { ProtocolSetupDeckConfiguration } from '..' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { CompletedProtocolAnalysis, @@ -47,7 +47,7 @@ vi.mock('@opentrons/components', async importOriginal => { }) const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -55,7 +55,7 @@ const render = ( } describe('ProtocolSetupDeckConfiguration', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupInstruments/ProtocolSetupInstruments.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupInstruments/ProtocolSetupInstruments.tsx index 1af859bc431..1826ea10339 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupInstruments/ProtocolSetupInstruments.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupInstruments/ProtocolSetupInstruments.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import styled from 'styled-components' import { useTranslation } from 'react-i18next' import { @@ -16,15 +15,16 @@ import { PipetteRecalibrationODDWarning } from '/app/organisms/ODD/PipetteRecali import { getShowPipetteCalibrationWarning } from '/app/transformations/instruments' import { useMostRecentCompletedAnalysis } from '/app/resources/runs' import { ProtocolInstrumentMountItem } from '/app/organisms/ODD/InstrumentMountItem' +import { isGripperInCommands } from '/app/resources/protocols/utils' +import type { Dispatch, SetStateAction } from 'react' import type { GripperData, PipetteData } from '@opentrons/api-client' import type { GripperModel } from '@opentrons/shared-data' import type { SetupScreens } from '../types' -import { isGripperInCommands } from '/app/resources/protocols/utils' export interface ProtocolSetupInstrumentsProps { runId: string - setSetupScreen: React.Dispatch> + setSetupScreen: Dispatch> } export function ProtocolSetupInstruments({ diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/__tests__/LabwareMapView.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/__tests__/LabwareMapView.test.tsx index 860d927578e..3f387db51f6 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/__tests__/LabwareMapView.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLabware/__tests__/LabwareMapView.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { when } from 'vitest-when' import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest' @@ -17,6 +16,7 @@ import { getStandardDeckViewLayerBlockList } from '/app/local-resources/deck_con import { mockProtocolModuleInfo } from '../__fixtures__' import { LabwareMapView } from '../LabwareMapView' +import type { ComponentProps } from 'react' import type { getSimplestDeckConfigForProtocol, CompletedProtocolAnalysis, @@ -50,7 +50,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLiquids/__tests__/LiquidDetails.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLiquids/__tests__/LiquidDetails.test.tsx index 720b6db7545..9d05000cf8f 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLiquids/__tests__/LiquidDetails.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLiquids/__tests__/LiquidDetails.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen, fireEvent } from '@testing-library/react' import { describe, it, beforeEach, vi } from 'vitest' @@ -13,20 +12,22 @@ import { MOCK_LABWARE_INFO_BY_LIQUID_ID, MOCK_PROTOCOL_ANALYSIS, } from '../fixtures' + +import type { ComponentProps } from 'react' import type { CompletedProtocolAnalysis } from '@opentrons/shared-data' vi.mock('/app/transformations/analysis') vi.mock('/app/transformations/commands') vi.mock('/app/organisms/LiquidsLabwareDetailsModal') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('LiquidDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { commands: (MOCK_PROTOCOL_ANALYSIS as CompletedProtocolAnalysis).commands, diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLiquids/__tests__/ProtocolSetupLiquids.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLiquids/__tests__/ProtocolSetupLiquids.test.tsx index 487fbbd0bce..eed74fae79d 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLiquids/__tests__/ProtocolSetupLiquids.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupLiquids/__tests__/ProtocolSetupLiquids.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, vi } from 'vitest' import { screen, fireEvent } from '@testing-library/react' @@ -20,6 +19,7 @@ import { } from '../fixtures' import { ProtocolSetupLiquids } from '..' +import type { ComponentProps } from 'react' import type * as SharedData from '@opentrons/shared-data' vi.mock('/app/transformations/analysis') @@ -41,13 +41,13 @@ describe('ProtocolSetupLiquids', () => { isConfirmed = confirmed }) - const render = (props: React.ComponentProps) => { + const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { runId: RUN_ID_1, diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/FixtureTable.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/FixtureTable.test.tsx index e6ca8735d77..b5336d9c535 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/FixtureTable.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/FixtureTable.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, afterEach, vi, expect } from 'vitest' @@ -18,6 +17,8 @@ import { FixtureTable } from '../FixtureTable' import { getLocalRobot } from '/app/redux/discovery' import { mockConnectedRobot } from '/app/redux/discovery/__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/discovery') vi.mock('/app/resources/deck_configuration/hooks') vi.mock('/app/organisms/LocationConflictModal') @@ -26,14 +27,14 @@ const mockSetSetupScreen = vi.fn() const mockSetCutoutId = vi.fn() const mockSetProvidedFixtureOptions = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('FixtureTable', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { mostRecentAnalysis: { commands: [], labware: [] } as any, diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/ModulesAndDeckMapView.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/ModulesAndDeckMapView.test.tsx index d31a0312d02..a06ad0118db 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/ModulesAndDeckMapView.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/ModulesAndDeckMapView.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' @@ -12,6 +11,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ModulesAndDeckMapView } from '../ModulesAndDeckMapView' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/components/src/hardware-sim/BaseDeck') vi.mock('@opentrons/api-client') vi.mock('@opentrons/shared-data/js/helpers/getSimplestFlexDeckConfig') @@ -99,14 +100,14 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ModulesAndDeckMapView', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/SetupInstructionsModal.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/SetupInstructionsModal.test.tsx index 8f6f4c01739..f4ac8d4fec1 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/SetupInstructionsModal.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupModulesAndDeck/__tests__/SetupInstructionsModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, beforeEach, vi } from 'vitest' @@ -7,18 +6,20 @@ import { i18n } from '/app/i18n' import { SetupInstructionsModal } from '../SetupInstructionsModal' +import type { ComponentProps } from 'react' + const mockSetShowSetupInstructionsModal = vi.fn() const QR_CODE_IMAGE_FILE = '/app/src/assets/images/on-device-display/setup_instructions_qr_code.png' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('SetupInstructionsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupOffsets/index.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupOffsets/index.tsx index 310c1dadbc4..85e9352d5fe 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupOffsets/index.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupOffsets/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { Chip, @@ -24,9 +23,11 @@ import { } from '/app/resources/runs' import { getLatestCurrentOffsets } from '/app/transformations/runs' +import type { Dispatch, SetStateAction } from 'react' + export interface ProtocolSetupOffsetsProps { runId: string - setSetupScreen: React.Dispatch> + setSetupScreen: Dispatch> lpcDisabledReason: string | null launchLPC: () => void LPCWizard: JSX.Element | null diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/ViewOnlyParameters.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/ViewOnlyParameters.tsx index 1946d122848..7f5b73db3b0 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/ViewOnlyParameters.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/ViewOnlyParameters.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { css } from 'styled-components' @@ -22,11 +21,12 @@ import { useMostRecentCompletedAnalysis } from '/app/resources/runs' import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation' import { useToaster } from '/app/organisms/ToasterOven' +import type { Dispatch, SetStateAction } from 'react' import type { SetupScreens } from '../types' export interface ViewOnlyParametersProps { runId: string - setSetupScreen: React.Dispatch> + setSetupScreen: Dispatch> } export function ViewOnlyParameters({ diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/AnalysisFailedModal.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/AnalysisFailedModal.test.tsx index ac43f26d621..dec3fc608e2 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/AnalysisFailedModal.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/AnalysisFailedModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { when } from 'vitest-when' import { fireEvent, screen } from '@testing-library/react' @@ -7,6 +6,8 @@ import { useDismissCurrentRunMutation } from '@opentrons/react-api-client' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { AnalysisFailedModal } from '../AnalysisFailedModal' + +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const PROTOCOL_ID = 'mockProtocolId' @@ -26,14 +27,14 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('AnalysisFailedModal', () => { - let props: React.ComponentProps + let props: ComponentProps when(vi.mocked(useDismissCurrentRunMutation)) .calledWith() diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseCsvFile.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseCsvFile.test.tsx index 2f365fa5fbc..560f9f9490a 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseCsvFile.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseCsvFile.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, vi, expect } from 'vitest' import { screen, fireEvent } from '@testing-library/react' import { when } from 'vitest-when' @@ -13,6 +12,7 @@ import { getShellUpdateDataFiles } from '/app/redux/shell' import { EmptyFile } from '../EmptyFile' import { ChooseCsvFile } from '../ChooseCsvFile' +import type { ComponentProps } from 'react' import type { CsvFileParameter } from '@opentrons/shared-data' vi.mock('@opentrons/react-api-client') @@ -47,14 +47,14 @@ const mockDataOnRobot = { }, } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ChooseCsvFile', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { protocolId: PROTOCOL_ID, diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseEnum.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseEnum.test.tsx index a65c760d544..613e17c9523 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseEnum.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseEnum.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { it, describe, beforeEach, vi, expect } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' @@ -7,14 +6,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ChooseEnum } from '../ChooseEnum' +import type { ComponentProps } from 'react' + vi.mocked('../../../../ToasterOven') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ChooseEnum', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseNumber.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseNumber.test.tsx index 611c0e124fc..89af3d22e4c 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseNumber.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ChooseNumber.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { it, describe, beforeEach, vi, expect } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom/vitest' @@ -8,6 +7,7 @@ import { useToaster } from '/app/organisms/ToasterOven' import { mockRunTimeParameterData } from '../../__fixtures__' import { ChooseNumber } from '../ChooseNumber' +import type { ComponentProps } from 'react' import type { NumberParameter } from '@opentrons/shared-data' vi.mock('/app/organisms/ToasterOven') @@ -18,14 +18,14 @@ const mockFloatNumberParameterData = mockRunTimeParameterData[6] as NumberParame const mockSetParameter = vi.fn() const mockMakeSnackbar = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ChooseNumber', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ProtocolSetupParameters.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ProtocolSetupParameters.test.tsx index 18a01391711..f32442f7fda 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ProtocolSetupParameters.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ProtocolSetupParameters.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { it, describe, beforeEach, vi, expect } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -19,6 +18,7 @@ import { mockRunTimeParameterData } from '../../__fixtures__' import { useToaster } from '/app/organisms/ToasterOven' import { ProtocolSetupParameters } from '..' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' import type { HostConfig } from '@opentrons/api-client' import type { CompletedProtocolAnalysis } from '@opentrons/shared-data' @@ -51,16 +51,14 @@ const mockMostRecentAnalysis = ({ } as unknown) as CompletedProtocolAnalysis const mockMakeSnackbar = vi.fn() -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ProtocolSetupParameters', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ResetValuesModal.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ResetValuesModal.test.tsx index 4e263f9984b..d777a44da04 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ResetValuesModal.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ResetValuesModal.test.tsx @@ -1,23 +1,24 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ResetValuesModal } from '../ResetValuesModal' + +import type { ComponentProps } from 'react' import type { RunTimeParameter } from '@opentrons/shared-data' const mockGoBack = vi.fn() const mockSetRunTimeParametersOverrides = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ResetValuesModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ViewOnlyParameters.test.tsx b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ViewOnlyParameters.test.tsx index aed74fea585..2c0827de156 100644 --- a/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ViewOnlyParameters.test.tsx +++ b/app/src/organisms/ODD/ProtocolSetup/ProtocolSetupParameters/__tests__/ViewOnlyParameters.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { it, describe, beforeEach, vi, expect } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -9,17 +8,19 @@ import { useToaster } from '/app/organisms/ToasterOven' import { mockRunTimeParameterData } from '../../__fixtures__' import { ViewOnlyParameters } from '../ViewOnlyParameters' +import type { ComponentProps } from 'react' + vi.mock('/app/resources/runs') vi.mock('/app/organisms/ToasterOven') const RUN_ID = 'mockId' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } const mockMakeSnackBar = vi.fn() describe('ViewOnlyParameters', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/CreateNewTransfer.tsx b/app/src/organisms/ODD/QuickTransferFlow/CreateNewTransfer.tsx index 10b036b9064..b74ecc7065e 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/CreateNewTransfer.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/CreateNewTransfer.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation, Trans } from 'react-i18next' import { @@ -12,11 +11,13 @@ import { import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation' import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration' + +import type { ComponentProps } from 'react' import type { SmallButton } from '/app/atoms/buttons' interface CreateNewTransferProps { onNext: () => void - exitButtonProps: React.ComponentProps + exitButtonProps: ComponentProps } export function CreateNewTransfer(props: CreateNewTransferProps): JSX.Element { diff --git a/app/src/organisms/ODD/QuickTransferFlow/NameQuickTransfer.tsx b/app/src/organisms/ODD/QuickTransferFlow/NameQuickTransfer.tsx index 8bff060ac38..2ef4592f568 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/NameQuickTransfer.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/NameQuickTransfer.tsx @@ -33,7 +33,6 @@ export function NameQuickTransfer(props: NameQuickTransferProps): JSX.Element { if (name.length > 60) { error = t('character_limit_error') } - // TODO add error handling for quick transfer name replication return createPortal( diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/ConfirmExitModal.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/ConfirmExitModal.test.tsx index 4b50c31ca29..18fdc854a85 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/ConfirmExitModal.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/ConfirmExitModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConfirmExitModal } from '../ConfirmExitModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ConfirmExitModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/CreateNewTransfer.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/CreateNewTransfer.test.tsx index 178086ae401..7d134e0e2be 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/CreateNewTransfer.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/CreateNewTransfer.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' import { DeckConfigurator } from '@opentrons/components' @@ -7,6 +6,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { CreateNewTransfer } from '../CreateNewTransfer' +import type { ComponentProps } from 'react' import type * as OpentronsComponents from '@opentrons/components' vi.mock('@opentrons/components', async importOriginal => { @@ -16,14 +16,14 @@ vi.mock('@opentrons/components', async importOriginal => { DeckConfigurator: vi.fn(), } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('CreateNewTransfer', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/NameQuickTransfer.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/NameQuickTransfer.test.tsx index 363c89cdc82..3b247ce2ef8 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/NameQuickTransfer.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/NameQuickTransfer.test.tsx @@ -1,10 +1,11 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { NameQuickTransfer } from '../NameQuickTransfer' + +import type { ComponentProps } from 'react' import type { InputField } from '@opentrons/components' vi.mock('../utils') @@ -17,14 +18,14 @@ vi.mock('/app/atoms/InputField', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('NameQuickTransfer', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/Overview.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/Overview.test.tsx index 242b0f58a92..192d378f97c 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/Overview.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/Overview.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, afterEach, vi, beforeEach } from 'vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { Overview } from '../Overview' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Overview', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/AirGap.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/AirGap.test.tsx index a4cc4a2879a..f1e6245389f 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/AirGap.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/AirGap.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -8,6 +7,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { AirGap } from '../../QuickTransferAdvancedSettings/AirGap' + +import type { ComponentProps } from 'react' import type { QuickTransferSummaryState } from '../../types' vi.mock('/app/redux-resources/analytics') @@ -21,7 +22,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -29,7 +30,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('AirGap', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/BlowOut.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/BlowOut.test.tsx index c75788ac8cd..6921e9daea7 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/BlowOut.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/BlowOut.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -7,13 +6,15 @@ import { i18n } from '/app/i18n' import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { BlowOut } from '../../QuickTransferAdvancedSettings/BlowOut' + +import type { ComponentProps } from 'react' import type { QuickTransferSummaryState } from '../../types' vi.mock('/app/resources/deck_configuration') vi.mock('/app/redux-resources/analytics') vi.mock('../utils') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -21,7 +22,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('BlowOut', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Delay.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Delay.test.tsx index 957f3eb6e62..32b26e4712f 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Delay.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Delay.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -8,6 +7,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { Delay } from '../../QuickTransferAdvancedSettings/Delay' + +import type { ComponentProps } from 'react' import type { QuickTransferSummaryState } from '../../types' vi.mock('/app/redux-resources/analytics') @@ -21,7 +22,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -29,7 +30,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('Delay', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/FlowRate.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/FlowRate.test.tsx index 4b01bb52ebe..413af34ce99 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/FlowRate.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/FlowRate.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -8,6 +7,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { FlowRateEntry } from '../../QuickTransferAdvancedSettings/FlowRate' + +import type { ComponentProps } from 'react' import type { QuickTransferSummaryState } from '../../types' vi.mock('/app/redux-resources/analytics') @@ -21,7 +22,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -29,7 +30,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('FlowRate', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Mix.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Mix.test.tsx index c4d1c170be3..298bd040f1c 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Mix.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/Mix.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -8,6 +7,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { Mix } from '../../QuickTransferAdvancedSettings/Mix' + +import type { ComponentProps } from 'react' import type { QuickTransferSummaryState } from '../../types' vi.mock('/app/redux-resources/analytics') @@ -21,7 +22,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -29,7 +30,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('Mix', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/PipettePath.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/PipettePath.test.tsx index e62571bdc6a..536c14bbbfd 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/PipettePath.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/PipettePath.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -9,6 +8,8 @@ import { i18n } from '/app/i18n' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { PipettePath } from '../../QuickTransferAdvancedSettings/PipettePath' import { useBlowOutLocationOptions } from '../../QuickTransferAdvancedSettings/BlowOut' + +import type { ComponentProps } from 'react' import type { QuickTransferSummaryState } from '../../types' vi.mock('/app/redux-resources/analytics') @@ -23,7 +24,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -31,7 +32,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('PipettePath', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/QuickTransferAdvancedSettings.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/QuickTransferAdvancedSettings.test.tsx index 64e400f5a10..aac998fc8dd 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/QuickTransferAdvancedSettings.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/QuickTransferAdvancedSettings.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -16,6 +15,8 @@ import { TouchTip } from '../../QuickTransferAdvancedSettings/TouchTip' import { AirGap } from '../../QuickTransferAdvancedSettings/AirGap' import { BlowOut } from '../../QuickTransferAdvancedSettings/BlowOut' +import type { ComponentProps } from 'react' + vi.mock('/app/redux-resources/analytics') vi.mock('/app/organisms/ToasterOven') vi.mock('../../QuickTransferAdvancedSettings/PipettePath') @@ -28,7 +29,7 @@ vi.mock('../../QuickTransferAdvancedSettings/AirGap') vi.mock('../../QuickTransferAdvancedSettings/BlowOut') const render = ( - props: React.ComponentProps + props: ComponentProps ): any => { return renderWithProviders(, { i18nInstance: i18n, @@ -38,7 +39,7 @@ let mockTrackEventWithRobotSerial: any let mockMakeSnackbar: any describe('QuickTransferAdvancedSettings', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/TipPosition.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/TipPosition.test.tsx index dc109c2c302..02e5022785c 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/TipPosition.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/TipPosition.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -8,6 +7,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { TipPositionEntry } from '../../QuickTransferAdvancedSettings/TipPosition' + +import type { ComponentProps } from 'react' import type { QuickTransferSummaryState } from '../../types' vi.mock('/app/redux-resources/analytics') @@ -21,7 +22,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -29,7 +30,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('TipPosition', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/TouchTip.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/TouchTip.test.tsx index cc30db0a54f..a5338c8aa71 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/TouchTip.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/QuickTransferAdvancedSettings/TouchTip.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -8,6 +7,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { TouchTip } from '../../QuickTransferAdvancedSettings/TouchTip' + +import type { ComponentProps } from 'react' import type { QuickTransferSummaryState } from '../../types' vi.mock('/app/redux-resources/analytics') @@ -21,7 +22,7 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -29,7 +30,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('TouchTip', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectDestLabware.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectDestLabware.test.tsx index 6448542c14e..38007f7cc58 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectDestLabware.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectDestLabware.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -6,15 +5,17 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { SelectDestLabware } from '../SelectDestLabware' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('SelectDestLabware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectPipette.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectPipette.test.tsx index e32f7645593..b614041b127 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectPipette.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectPipette.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' import { useInstrumentsQuery } from '@opentrons/react-api-client' @@ -8,17 +7,19 @@ import { i18n } from '/app/i18n' import { useIsOEMMode } from '/app/resources/robot-settings/hooks' import { SelectPipette } from '../SelectPipette' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') vi.mock('/app/resources/robot-settings/hooks') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('SelectPipette', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectSourceLabware.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectSourceLabware.test.tsx index 73121ea7b2d..99e53fab15b 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectSourceLabware.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectSourceLabware.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -6,15 +5,17 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { SelectSourceLabware } from '../SelectSourceLabware' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('SelectSourceLabware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectTipRack.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectTipRack.test.tsx index f4ee22bd2b9..1489e744ec6 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectTipRack.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SelectTipRack.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -6,15 +5,17 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { SelectTipRack } from '../SelectTipRack' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('SelectTipRack', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SummaryAndSettings.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SummaryAndSettings.test.tsx index 0f5f7c7742c..246fa343260 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/SummaryAndSettings.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/SummaryAndSettings.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -16,6 +15,8 @@ import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { SummaryAndSettings } from '../SummaryAndSettings' import { NameQuickTransfer } from '../NameQuickTransfer' import { Overview } from '../Overview' + +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockNavigate = vi.fn() @@ -41,7 +42,7 @@ vi.mock('../utils/createQuickTransferFile') vi.mock('@opentrons/react-api-client') vi.mock('/app/resources/deck_configuration') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -49,7 +50,7 @@ const render = (props: React.ComponentProps) => { let mockTrackEventWithRobotSerial: any describe('SummaryAndSettings', () => { - let props: React.ComponentProps + let props: ComponentProps const createProtocol = vi.fn() const createRun = vi.fn() diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/ChangeTip.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/ChangeTip.test.tsx index 213633678e5..cfe5b0a5086 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/ChangeTip.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/ChangeTip.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -8,9 +7,11 @@ import { ANALYTICS_QUICK_TRANSFER_SETTING_SAVED } from '/app/redux/analytics' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { ChangeTip } from '../../TipManagement/ChangeTip' +import type { ComponentProps } from 'react' + vi.mock('/app/redux-resources/analytics') -const render = (props: React.ComponentProps): any => { +const render = (props: ComponentProps): any => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -19,7 +20,7 @@ const render = (props: React.ComponentProps): any => { let mockTrackEventWithRobotSerial: any describe('ChangeTip', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/TipDropLocation.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/TipDropLocation.test.tsx index aed3d143b31..712e7e2217e 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/TipDropLocation.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/TipDropLocation.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -9,10 +8,12 @@ import { ANALYTICS_QUICK_TRANSFER_SETTING_SAVED } from '/app/redux/analytics' import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { TipDropLocation } from '../../TipManagement/TipDropLocation' +import type { ComponentProps } from 'react' + vi.mock('/app/resources/deck_configuration') vi.mock('/app/redux-resources/analytics') -const render = (props: React.ComponentProps): any => { +const render = (props: ComponentProps): any => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -20,7 +21,7 @@ const render = (props: React.ComponentProps): any => { let mockTrackEventWithRobotSerial: any describe('TipDropLocation', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/TipManagement.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/TipManagement.test.tsx index 618153a8b53..61f38149a99 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/TipManagement.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/TipManagement/TipManagement.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -10,11 +9,13 @@ import { ChangeTip } from '../../TipManagement/ChangeTip' import { TipDropLocation } from '../../TipManagement/TipDropLocation' import { TipManagement } from '../../TipManagement/' +import type { ComponentProps } from 'react' + vi.mock('../../TipManagement/ChangeTip') vi.mock('../../TipManagement/TipDropLocation') vi.mock('/app/redux-resources/analytics') -const render = (props: React.ComponentProps): any => { +const render = (props: ComponentProps): any => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -22,7 +23,7 @@ const render = (props: React.ComponentProps): any => { let mockTrackEventWithRobotSerial: any describe('TipManagement', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/QuickTransferFlow/__tests__/VolumeEntry.test.tsx b/app/src/organisms/ODD/QuickTransferFlow/__tests__/VolumeEntry.test.tsx index 8a14b9a5993..e96ea2515cd 100644 --- a/app/src/organisms/ODD/QuickTransferFlow/__tests__/VolumeEntry.test.tsx +++ b/app/src/organisms/ODD/QuickTransferFlow/__tests__/VolumeEntry.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest' @@ -10,6 +9,8 @@ import { NumericalKeyboard } from '/app/atoms/SoftwareKeyboard' import { getVolumeRange } from '../utils' import { VolumeEntry } from '../VolumeEntry' +import type { ComponentProps } from 'react' + vi.mock('/app/atoms/SoftwareKeyboard') vi.mock('../utils') @@ -21,14 +22,14 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('VolumeEntry', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx b/app/src/organisms/ODD/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx index 10ee119176e..cb1b541b39b 100644 --- a/app/src/organisms/ODD/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx +++ b/app/src/organisms/ODD/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { formatDistance } from 'date-fns' import { MemoryRouter } from 'react-router-dom' @@ -26,6 +25,7 @@ import { useCloneRun, useNotifyAllRunsQuery } from '/app/resources/runs' import { useRerunnableStatusText } from '../hooks' import { RecentRunProtocolCard } from '../' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' import type { ProtocolHardware } from '/app/transformations/commands' @@ -103,7 +103,7 @@ const mockBadRunData = { const mockCloneRun = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -120,7 +120,7 @@ const mockTrackProtocolRunEvent = vi.fn( ) describe('RecentRunProtocolCard', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx b/app/src/organisms/ODD/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx index 277edf80d87..31026884d3c 100644 --- a/app/src/organisms/ODD/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx +++ b/app/src/organisms/ODD/RobotDashboard/__tests__/RecentRunProtocolCarousel.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { beforeEach, describe, it, vi } from 'vitest' @@ -6,6 +5,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { useNotifyAllRunsQuery } from '/app/resources/runs' import { RecentRunProtocolCard, RecentRunProtocolCarousel } from '..' +import type { ComponentProps } from 'react' import type { RunData } from '@opentrons/api-client' vi.mock('@opentrons/react-api-client') @@ -30,14 +30,12 @@ const mockRun = { runTimeParameters: [], } -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders() } describe('RecentRunProtocolCarousel', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotDashboard/hooks/__tests__/useHardwareStatusText.test.tsx b/app/src/organisms/ODD/RobotDashboard/hooks/__tests__/useHardwareStatusText.test.tsx index f209e69f5ec..996e00d56c7 100644 --- a/app/src/organisms/ODD/RobotDashboard/hooks/__tests__/useHardwareStatusText.test.tsx +++ b/app/src/organisms/ODD/RobotDashboard/hooks/__tests__/useHardwareStatusText.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { I18nextProvider } from 'react-i18next' import { renderHook } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -7,10 +6,12 @@ import { i18n } from '/app/i18n' import { useFeatureFlag } from '/app/redux/config' import { useHardwareStatusText } from '..' +import type { FunctionComponent, ReactNode } from 'react' + vi.mock('/app/redux/config') describe('useHardwareStatusText', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { wrapper = ({ children }) => ( {children} diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx index 49f58e26993..50af850a44f 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx @@ -1,7 +1,8 @@ -import { Fragment } from 'react' +import { Fragment, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import styled from 'styled-components' +import uuidv1 from 'uuid/v4' import { BORDERS, @@ -14,6 +15,8 @@ import { } from '@opentrons/components' import { LANGUAGES } from '/app/i18n' +import { ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS } from '/app/redux/analytics' +import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation' import { getAppLanguage, updateConfigValue } from '/app/redux/config' @@ -42,16 +45,31 @@ interface LanguageSettingProps { setCurrentOption: SetSettingOption } +const uuid: () => string = uuidv1 + export function LanguageSetting({ setCurrentOption, }: LanguageSettingProps): JSX.Element { const { t } = useTranslation('app_settings') const dispatch = useDispatch() + const { trackEventWithRobotSerial } = useTrackEventWithRobotSerial() + + let transactionId = '' + useEffect(() => { + transactionId = uuid() + }, []) const appLanguage = useSelector(getAppLanguage) const handleChange = (event: ChangeEvent): void => { dispatch(updateConfigValue('language.appLanguage', event.target.value)) + trackEventWithRobotSerial({ + name: ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS, + properties: { + language: event.target.value, + transactionId, + }, + }) } return ( diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsSelectAuthenticationType.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsSelectAuthenticationType.tsx index f8ce5e6a205..8193a44b23b 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsSelectAuthenticationType.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsSelectAuthenticationType.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { DIRECTION_COLUMN, Flex } from '@opentrons/components' @@ -6,6 +5,7 @@ import { DIRECTION_COLUMN, Flex } from '@opentrons/components' import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation' import { SelectAuthenticationType } from '../../NetworkSettings' +import type { Dispatch, SetStateAction } from 'react' import type { WifiSecurityType } from '@opentrons/api-client' import type { SetSettingOption } from '../types' @@ -13,7 +13,7 @@ interface RobotSettingsSelectAuthenticationTypeProps { handleWifiConnect: () => void selectedAuthType: WifiSecurityType setCurrentOption: SetSettingOption - setSelectedAuthType: React.Dispatch> + setSelectedAuthType: Dispatch> } /** diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsSetWifiCred.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsSetWifiCred.tsx index 9204f22f5c4..aaafbf5d590 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsSetWifiCred.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsSetWifiCred.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { DIRECTION_COLUMN, Flex } from '@opentrons/components' @@ -6,13 +5,14 @@ import { DIRECTION_COLUMN, Flex } from '@opentrons/components' import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation' import { SetWifiCred } from '../../NetworkSettings/SetWifiCred' +import type { Dispatch, SetStateAction } from 'react' import type { SetSettingOption } from '../types' interface RobotSettingsSetWifiCredProps { handleConnect: () => void password: string setCurrentOption: SetSettingOption - setPassword: React.Dispatch> + setPassword: Dispatch> } /** diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsWifi.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsWifi.tsx index add3565fe74..67edb5249ff 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsWifi.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/RobotSettingsWifi.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { DIRECTION_COLUMN, Flex } from '@opentrons/components' @@ -6,11 +5,12 @@ import { DIRECTION_COLUMN, Flex } from '@opentrons/components' import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation' import { WifiConnectionDetails } from './WifiConnectionDetails' +import type { Dispatch, SetStateAction } from 'react' import type { WifiSecurityType } from '@opentrons/api-client' import type { SetSettingOption } from '../types' interface RobotSettingsWifiProps { - setSelectedSsid: React.Dispatch> + setSelectedSsid: Dispatch> setCurrentOption: SetSettingOption activeSsid?: string connectedWifiAuthType?: WifiSecurityType diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/EthernetConnectionDetails.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/EthernetConnectionDetails.test.tsx index ddefd80196d..d034b6b6e7c 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/EthernetConnectionDetails.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/EthernetConnectionDetails.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -11,13 +10,13 @@ import { getLocalRobot } from '/app/redux/discovery' import { mockConnectedRobot } from '/app/redux/discovery/__fixtures__' import { EthernetConnectionDetails } from '../EthernetConnectionDetails' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/discovery') vi.mock('/app/redux/discovery/selectors') vi.mock('/app/redux/networking/selectors') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) @@ -31,7 +30,7 @@ const mockEthernet = { } describe('EthernetConnectionDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { handleGoBack: vi.fn(), diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/NetworkDetailsModal.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/NetworkDetailsModal.test.tsx index 76b4c6f1be0..6b4048dbcfc 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/NetworkDetailsModal.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/NetworkDetailsModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -7,16 +6,18 @@ import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { NetworkDetailsModal } from '../NetworkDetailsModal' +import type { ComponentProps } from 'react' + const mockFn = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('NetworkDetailsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/NetworkSettings.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/NetworkSettings.test.tsx index 266778c0c81..37a5db9c3f2 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/NetworkSettings.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/NetworkSettings.test.tsx @@ -1,5 +1,4 @@ /* eslint-disable testing-library/no-node-access */ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -12,6 +11,7 @@ import { WifiConnectionDetails } from '../WifiConnectionDetails' import { EthernetConnectionDetails } from '../EthernetConnectionDetails' import { NetworkSettings } from '..' +import type { ComponentProps } from 'react' import type { DiscoveredRobot } from '/app/redux/discovery/types' import type { WifiNetwork } from '/app/redux/networking/types' @@ -22,14 +22,14 @@ vi.mock('../EthernetConnectionDetails') const mockSetCurrentOption = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('NetworkSettings', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/WifiConnectionDetails.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/WifiConnectionDetails.test.tsx index 9650a89b76c..c7311087fe4 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/WifiConnectionDetails.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/__tests__/WifiConnectionDetails.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { when } from 'vitest-when' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -10,6 +9,8 @@ import { getLocalRobot } from '/app/redux/discovery' import * as Networking from '/app/redux/networking' import { NetworkDetailsModal } from '../NetworkDetailsModal' import { WifiConnectionDetails } from '../WifiConnectionDetails' + +import type { ComponentProps } from 'react' import type * as Dom from 'react-router-dom' import type { State } from '/app/redux/types' @@ -36,14 +37,14 @@ const initialMockWifi = { type: Networking.INTERFACE_WIFI, } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('WifiConnectionDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { activeSsid: 'mock wifi ssid', diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/index.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/index.tsx index db73b89dae3..9afeeb15cc0 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/index.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/NetworkSettings/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { useTranslation } from 'react-i18next' @@ -19,6 +18,7 @@ import { import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation' +import type { ComponentProps } from 'react' import type { IconName, ChipType } from '@opentrons/components' import type { NetworkConnection } from '/app/resources/networking/hooks/useNetworkConnection' import type { SetSettingOption } from '../types' @@ -87,7 +87,7 @@ export function NetworkSettings({ ) } -interface NetworkSettingButtonProps extends React.ComponentProps { +interface NetworkSettingButtonProps extends ComponentProps { buttonTitle: string iconName: IconName chipType: ChipType diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/RobotSettingButton.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/RobotSettingButton.tsx index f777c9fcb77..1a47e3bbe4e 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/RobotSettingButton.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/RobotSettingButton.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { css } from 'styled-components' import { @@ -20,6 +19,7 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import type { MouseEventHandler, ReactNode } from 'react' import type { IconName } from '@opentrons/components' const SETTING_BUTTON_STYLE = css` @@ -36,10 +36,10 @@ const SETTING_BUTTON_STYLE = css` interface RobotSettingButtonProps { settingName: string - onClick: React.MouseEventHandler + onClick: MouseEventHandler iconName?: IconName settingInfo?: string - rightElement?: React.ReactNode + rightElement?: ReactNode dataTestId?: string } diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/DeviceReset.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/DeviceReset.test.tsx index 9f71205231e..3f273ab2df2 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/DeviceReset.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/DeviceReset.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -9,6 +8,7 @@ import { useDispatchApiRequest } from '/app/redux/robot-api' import { DeviceReset } from '../DeviceReset' +import type { ComponentProps } from 'react' import type { DispatchApiRequestType } from '/app/redux/robot-api' vi.mock('/app/redux/robot-admin') @@ -47,7 +47,7 @@ const mockResetConfigOptions = [ }, ] -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( , @@ -56,7 +56,7 @@ const render = (props: React.ComponentProps) => { } describe('DeviceReset', () => { - let props: React.ComponentProps + let props: ComponentProps let dispatchApiRequest: DispatchApiRequestType beforeEach(() => { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/LanguageSetting.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/LanguageSetting.test.tsx index 80d35ebea15..50ce6edc7e6 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/LanguageSetting.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/LanguageSetting.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -10,28 +9,37 @@ import { SIMPLIFIED_CHINESE_DISPLAY_NAME, SIMPLIFIED_CHINESE, } from '/app/i18n' +import { ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS } from '/app/redux/analytics' +import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { getAppLanguage, updateConfigValue } from '/app/redux/config' import { renderWithProviders } from '/app/__testing-utils__' import { LanguageSetting } from '../LanguageSetting' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') +vi.mock('/app/redux-resources/analytics') const mockSetCurrentOption = vi.fn() +const mockTrackEvent = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('LanguageSetting', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { setCurrentOption: mockSetCurrentOption, } vi.mocked(getAppLanguage).mockReturnValue(US_ENGLISH) + vi.mocked(useTrackEventWithRobotSerial).mockReturnValue({ + trackEventWithRobotSerial: mockTrackEvent, + }) }) it('should render text and buttons', () => { @@ -49,6 +57,13 @@ describe('LanguageSetting', () => { 'language.appLanguage', SIMPLIFIED_CHINESE ) + expect(mockTrackEvent).toHaveBeenCalledWith({ + name: ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS, + properties: { + language: SIMPLIFIED_CHINESE, + transactionId: expect.anything(), + }, + }) }) it('should call mock function when tapping back button', () => { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/Privacy.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/Privacy.test.tsx index 03f4a987462..d281ccecf6f 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/Privacy.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/Privacy.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { vi, describe, beforeEach, afterEach, expect, it } from 'vitest' @@ -9,17 +8,19 @@ import { getRobotSettings } from '/app/redux/robot-settings' import { Privacy } from '../Privacy' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/analytics') vi.mock('/app/redux/robot-settings') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Privacy', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { robotName: 'Otie', diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/RobotSystemVersion.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/RobotSystemVersion.test.tsx index ad30e2539fa..955161149d8 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/RobotSystemVersion.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/RobotSystemVersion.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -9,12 +8,14 @@ import { renderWithProviders } from '/app/__testing-utils__' import { RobotSystemVersion } from '../RobotSystemVersion' import { RobotSystemVersionModal } from '../RobotSystemVersionModal' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/shell') vi.mock('../RobotSystemVersionModal') const mockBack = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -26,7 +27,7 @@ const render = (props: React.ComponentProps) => { } describe('RobotSystemVersion', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/RobotSystemVersionModal.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/RobotSystemVersionModal.test.tsx index 0d7a125eb1f..0b8f891f1a5 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/RobotSystemVersionModal.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/RobotSystemVersionModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -6,6 +5,8 @@ import '@testing-library/jest-dom/vitest' import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { RobotSystemVersionModal } from '../RobotSystemVersionModal' + +import type { ComponentProps } from 'react' import type * as Dom from 'react-router-dom' const mockFn = vi.fn() @@ -19,16 +20,14 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('RobotSystemVersionModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TextSize.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TextSize.test.tsx index 703323c0d7e..3ca0124810a 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TextSize.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TextSize.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -6,15 +5,17 @@ import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { TextSize } from '../TextSize' +import type { ComponentProps } from 'react' + const mockFunc = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('TextSize', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TouchScreenSleep.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TouchScreenSleep.test.tsx index 990c6bcf436..6c75c584cfe 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TouchScreenSleep.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TouchScreenSleep.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import { i18n } from '/app/i18n' @@ -6,19 +5,21 @@ import { updateConfigValue } from '/app/redux/config' import { TouchScreenSleep } from '../TouchScreenSleep' import { renderWithProviders } from '/app/__testing-utils__' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') // Note (kj:06/28/2023) this line is to avoid causing errors for scrollIntoView window.HTMLElement.prototype.scrollIntoView = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('TouchScreenSleep', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TouchscreenBrightness.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TouchscreenBrightness.test.tsx index 76993c42300..0dd4c87331d 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TouchscreenBrightness.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/TouchscreenBrightness.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -10,18 +9,20 @@ import { import { renderWithProviders } from '/app/__testing-utils__' import { TouchscreenBrightness } from '../TouchscreenBrightness' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') const mockFunc = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('TouchscreenBrightness', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/UpdateChannel.test.tsx b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/UpdateChannel.test.tsx index c25e9582a4b..5cf5d34ffde 100644 --- a/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/UpdateChannel.test.tsx +++ b/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/UpdateChannel.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -13,6 +12,8 @@ import { renderWithProviders } from '/app/__testing-utils__' import { UpdateChannel } from '../UpdateChannel' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/config') const mockChannelOptions = [ @@ -26,14 +27,14 @@ const mockChannelOptions = [ const mockhandleBackPress = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('UpdateChannel', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { handleBackPress: mockhandleBackPress, diff --git a/app/src/organisms/ODD/RobotSetupHeader/index.tsx b/app/src/organisms/ODD/RobotSetupHeader/index.tsx index 6b7a3fa1049..3e828ddb061 100644 --- a/app/src/organisms/ODD/RobotSetupHeader/index.tsx +++ b/app/src/organisms/ODD/RobotSetupHeader/index.tsx @@ -1,5 +1,3 @@ -import type * as React from 'react' - import { ALIGN_CENTER, Btn, @@ -17,14 +15,15 @@ import { import { SmallButton } from '/app/atoms/buttons' import { InlineNotification } from '/app/atoms/InlineNotification' +import type { MouseEventHandler, ReactNode } from 'react' import type { InlineNotificationProps } from '/app/atoms/InlineNotification' interface RobotSetupHeaderProps { header: string - buttonText?: React.ReactNode + buttonText?: ReactNode inlineNotification?: InlineNotificationProps - onClickBack?: React.MouseEventHandler - onClickButton?: React.MouseEventHandler + onClickBack?: MouseEventHandler + onClickButton?: MouseEventHandler } export function RobotSetupHeader({ diff --git a/app/src/organisms/ODD/RunningProtocol/__tests__/ConfirmCancelRunModal.test.tsx b/app/src/organisms/ODD/RunningProtocol/__tests__/ConfirmCancelRunModal.test.tsx index 69f610d3ef8..bef530f1458 100644 --- a/app/src/organisms/ODD/RunningProtocol/__tests__/ConfirmCancelRunModal.test.tsx +++ b/app/src/organisms/ODD/RunningProtocol/__tests__/ConfirmCancelRunModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' @@ -21,6 +20,7 @@ import { mockConnectedRobot } from '/app/redux/discovery/__fixtures__' import { ConfirmCancelRunModal } from '../ConfirmCancelRunModal' import { CancelingRunModal } from '../CancelingRunModal' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' vi.mock('@opentrons/react-api-client') @@ -46,7 +46,7 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -63,7 +63,7 @@ const ROBOT_NAME = 'otie' const mockFn = vi.fn() describe('ConfirmCancelRunModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RunningProtocol/__tests__/CurrentRunningProtocolCommand.test.tsx b/app/src/organisms/ODD/RunningProtocol/__tests__/CurrentRunningProtocolCommand.test.tsx index 581df7c013c..ceda8df2e42 100644 --- a/app/src/organisms/ODD/RunningProtocol/__tests__/CurrentRunningProtocolCommand.test.tsx +++ b/app/src/organisms/ODD/RunningProtocol/__tests__/CurrentRunningProtocolCommand.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -12,6 +11,8 @@ import { useRunningStepCounts } from '/app/resources/protocols/hooks' import { useNotifyAllCommandsQuery } from '/app/resources/runs' import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' +import type { ComponentProps } from 'react' + vi.mock('/app/resources/runs') vi.mock('/app/resources/protocols/hooks') @@ -28,7 +29,7 @@ const mockRunTimer = { } const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -36,7 +37,7 @@ const render = ( } describe('CurrentRunningProtocolCommand', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RunningProtocol/__tests__/RunFailedModal.test.tsx b/app/src/organisms/ODD/RunningProtocol/__tests__/RunFailedModal.test.tsx index 8dcfd2e5b88..40e1366d324 100644 --- a/app/src/organisms/ODD/RunningProtocol/__tests__/RunFailedModal.test.tsx +++ b/app/src/organisms/ODD/RunningProtocol/__tests__/RunFailedModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -12,6 +11,8 @@ import { RunFailedModal } from '../RunFailedModal' import type { NavigateFunction } from 'react-router-dom' import { RUN_STATUS_FAILED } from '@opentrons/api-client' +import type { ComponentProps } from 'react' + vi.mock('@opentrons/react-api-client') const RUN_ID = 'mock_runID' @@ -82,7 +83,7 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -94,7 +95,7 @@ const render = (props: React.ComponentProps) => { } describe('RunFailedModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/ODD/RunningProtocol/__tests__/RunningProtocolCommandList.test.tsx b/app/src/organisms/ODD/RunningProtocol/__tests__/RunningProtocolCommandList.test.tsx index 199ae940c3b..5f2ceba8121 100644 --- a/app/src/organisms/ODD/RunningProtocol/__tests__/RunningProtocolCommandList.test.tsx +++ b/app/src/organisms/ODD/RunningProtocol/__tests__/RunningProtocolCommandList.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -10,20 +9,20 @@ import { i18n } from '/app/i18n' import { mockRobotSideAnalysis } from '/app/molecules/Command/__fixtures__' import { RunningProtocolCommandList } from '../RunningProtocolCommandList' +import type { ComponentProps } from 'react' + const mockPlayRun = vi.fn() const mockPauseRun = vi.fn() const mockShowModal = vi.fn() -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('RunningProtocolCommandList', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { runStatus: RUN_STATUS_RUNNING, diff --git a/app/src/organisms/ODD/RunningProtocol/__tests__/RunningProtocolSkeleton.test.tsx b/app/src/organisms/ODD/RunningProtocol/__tests__/RunningProtocolSkeleton.test.tsx index 656d4d250d1..fcd48819c49 100644 --- a/app/src/organisms/ODD/RunningProtocol/__tests__/RunningProtocolSkeleton.test.tsx +++ b/app/src/organisms/ODD/RunningProtocol/__tests__/RunningProtocolSkeleton.test.tsx @@ -1,18 +1,17 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { beforeEach, describe, expect, it } from 'vitest' import { renderWithProviders } from '/app/__testing-utils__' import { RunningProtocolSkeleton } from '../RunningProtocolSkeleton' -const render = ( - props: React.ComponentProps -) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders() } describe('RunningProtocolSkeleton', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/PipetteWizardFlows/BeforeBeginning.tsx b/app/src/organisms/PipetteWizardFlows/BeforeBeginning.tsx index ffcb72dae18..a69e095a674 100644 --- a/app/src/organisms/PipetteWizardFlows/BeforeBeginning.tsx +++ b/app/src/organisms/PipetteWizardFlows/BeforeBeginning.tsx @@ -107,6 +107,10 @@ export const BeforeBeginning = ( let equipmentList = [CALIBRATION_PROBE] const proceedButtonText = t('move_gantry_to_front') + const hexScrewdriverWithSubtitle = { + ...HEX_SCREWDRIVER, + subtitle: t('provided_with_robot'), + } let bodyTranslationKey: string = '' switch (flowType) { @@ -124,7 +128,7 @@ export const BeforeBeginning = ( equipmentList = [ { ...PIPETTE, displayName: displayName ?? PIPETTE.displayName }, CALIBRATION_PROBE, - HEX_SCREWDRIVER, + hexScrewdriverWithSubtitle, ] } else { equipmentList = [ @@ -133,7 +137,7 @@ export const BeforeBeginning = ( displayName: displayName ?? NINETY_SIX_CHANNEL_PIPETTE.displayName, }, CALIBRATION_PROBE, - HEX_SCREWDRIVER, + hexScrewdriverWithSubtitle, NINETY_SIX_CHANNEL_MOUNTING_PLATE, ] } @@ -148,19 +152,19 @@ export const BeforeBeginning = ( equipmentList = [ { ...NINETY_SIX_CHANNEL_PIPETTE, displayName }, CALIBRATION_PROBE, - HEX_SCREWDRIVER, + hexScrewdriverWithSubtitle, NINETY_SIX_CHANNEL_MOUNTING_PLATE, ] } else { equipmentList = [ { ...PIPETTE, displayName }, CALIBRATION_PROBE, - HEX_SCREWDRIVER, + hexScrewdriverWithSubtitle, ] } } else { bodyTranslationKey = 'get_started_detach' - equipmentList = [HEX_SCREWDRIVER] + equipmentList = [hexScrewdriverWithSubtitle] } break } diff --git a/app/src/organisms/PipetteWizardFlows/CheckPipetteButton.tsx b/app/src/organisms/PipetteWizardFlows/CheckPipetteButton.tsx index 2300204b65b..25438c1442c 100644 --- a/app/src/organisms/PipetteWizardFlows/CheckPipetteButton.tsx +++ b/app/src/organisms/PipetteWizardFlows/CheckPipetteButton.tsx @@ -1,11 +1,12 @@ -import type * as React from 'react' import { useInstrumentsQuery } from '@opentrons/react-api-client' import { PrimaryButton } from '@opentrons/components' import { SmallButton } from '/app/atoms/buttons' +import type { Dispatch, SetStateAction } from 'react' + interface CheckPipetteButtonProps { proceedButtonText: string - setFetching: React.Dispatch> + setFetching: Dispatch> isFetching: boolean isOnDevice: boolean | null proceed?: () => void diff --git a/app/src/organisms/PipetteWizardFlows/MountPipette.tsx b/app/src/organisms/PipetteWizardFlows/MountPipette.tsx index 9ba1b036785..b750aee0ad2 100644 --- a/app/src/organisms/PipetteWizardFlows/MountPipette.tsx +++ b/app/src/organisms/PipetteWizardFlows/MountPipette.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { SINGLE_MOUNT_PIPETTES, @@ -17,11 +16,13 @@ import { Skeleton } from '/app/atoms/Skeleton' import { CheckPipetteButton } from './CheckPipetteButton' import { BODY_STYLE, SECTIONS } from './constants' import { getPipetteAnimations, getPipetteAnimations96 } from './utils' + +import type { Dispatch, ReactNode, SetStateAction } from 'react' import type { PipetteWizardStepProps } from './types' interface MountPipetteProps extends PipetteWizardStepProps { isFetching: boolean - setFetching: React.Dispatch> + setFetching: Dispatch> } const BACKGROUND_SIZE = '47rem' @@ -47,7 +48,7 @@ export const MountPipette = (props: MountPipetteProps): JSX.Element => { backgroundSize={BACKGROUND_SIZE} /> ) - let bodyText: React.ReactNode =
        + let bodyText: ReactNode =
        if (isFetching) { bodyText = ( <> diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/AttachProbe.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/AttachProbe.test.tsx index 00120fe438a..0bbab02ebc5 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/AttachProbe.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/AttachProbe.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen, waitFor } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -16,7 +15,9 @@ import { FLOWS } from '../constants' import { AttachProbe } from '../AttachProbe' import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -24,7 +25,7 @@ const render = (props: React.ComponentProps) => { vi.mock('/app/resources/deck_configuration') describe('AttachProbe', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { mount: LEFT, diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/BeforeBeginning.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/BeforeBeginning.test.tsx index a75e8bfe97a..db8b03816c2 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/BeforeBeginning.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/BeforeBeginning.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, waitFor, screen } from '@testing-library/react' import { describe, it, vi, beforeEach, expect, afterEach } from 'vitest' @@ -19,19 +18,21 @@ import { BeforeBeginning } from '../BeforeBeginning' import { FLOWS } from '../constants' import { getIsGantryEmpty } from '../utils' +import type { ComponentProps } from 'react' + // TODO(jr, 11/3/22): uncomment out the get help link when we have // the correct URL to link it to vi.mock('/app/molecules/InProgressModal/InProgressModal') vi.mock('../utils') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('BeforeBeginning', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { selectedPipette: SINGLE_MOUNT_PIPETTES, diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/Carriage.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/Carriage.test.tsx index 17c8140ebe8..389fde2801f 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/Carriage.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/Carriage.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -11,14 +10,16 @@ import { RUN_ID_1 } from '/app/resources/runs/__fixtures__' import { FLOWS } from '../constants' import { Carriage } from '../Carriage' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('Carriage', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { mount: LEFT, diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/CheckPipetteButton.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/CheckPipetteButton.test.tsx index e5dfc5fe3de..76c60357b46 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/CheckPipetteButton.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/CheckPipetteButton.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, waitFor, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' @@ -7,14 +6,16 @@ import { useInstrumentsQuery } from '@opentrons/react-api-client' import { renderWithProviders } from '/app/__testing-utils__' import { CheckPipetteButton } from '../CheckPipetteButton' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders()[0] } vi.mock('@opentrons/react-api-client') describe('CheckPipetteButton', () => { - let props: React.ComponentProps + let props: ComponentProps const refetch = vi.fn(() => Promise.resolve()) beforeEach(() => { props = { diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/ChoosePipette.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/ChoosePipette.test.tsx index bda196f388c..ab14c846013 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/ChoosePipette.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/ChoosePipette.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { LEFT, NINETY_SIX_CHANNEL, @@ -17,18 +16,20 @@ import { useAttachedPipettesFromInstrumentsQuery } from '/app/resources/instrume import { ChoosePipette } from '../ChoosePipette' import { getIsGantryEmpty } from '../utils' +import type { ComponentProps } from 'react' + vi.mock('../utils') vi.mock('/app/resources/instruments') vi.mock('/app/redux/config') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ChoosePipette', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { vi.mocked(getIsOnDevice).mockReturnValue(false) vi.mocked(getIsGantryEmpty).mockReturnValue(true) diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/DetachPipette.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/DetachPipette.test.tsx index d7777ed368c..a8f85ef3d73 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/DetachPipette.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/DetachPipette.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -19,17 +18,19 @@ import { RUN_ID_1 } from '/app/resources/runs/__fixtures__' import { FLOWS } from '../constants' import { DetachPipette } from '../DetachPipette' +import type { ComponentProps } from 'react' + vi.mock('../CheckPipetteButton') vi.mock('/app/molecules/InProgressModal/InProgressModal') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('DetachPipette', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { selectedPipette: SINGLE_MOUNT_PIPETTES, diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/DetachProbe.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/DetachProbe.test.tsx index 059846aebb5..755c795e0d4 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/DetachProbe.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/DetachProbe.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -12,16 +11,18 @@ import { RUN_ID_1 } from '/app/resources/runs/__fixtures__' import { FLOWS } from '../constants' import { DetachProbe } from '../DetachProbe' +import type { ComponentProps } from 'react' + vi.mock('/app/molecules/InProgressModal/InProgressModal') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('DetachProbe', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { selectedPipette: SINGLE_MOUNT_PIPETTES, diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/ExitModal.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/ExitModal.test.tsx index a30407379a2..b09319fcb94 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/ExitModal.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/ExitModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, beforeEach, vi, expect } from 'vitest' @@ -7,14 +6,16 @@ import { i18n } from '/app/i18n' import { FLOWS } from '../constants' import { ExitModal } from '../ExitModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ExitModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/MountPipette.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/MountPipette.test.tsx index 4c5d9dda2e0..24e8bb926b8 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/MountPipette.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/MountPipette.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, beforeEach, vi } from 'vitest' @@ -16,16 +15,18 @@ import { FLOWS } from '../constants' import { CheckPipetteButton } from '../CheckPipetteButton' import { MountPipette } from '../MountPipette' +import type { ComponentProps } from 'react' + vi.mock('../CheckPipetteButton') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('MountPipette', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { selectedPipette: SINGLE_MOUNT_PIPETTES, diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/MountingPlate.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/MountingPlate.test.tsx index 38744ad1bab..2393d1080d5 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/MountingPlate.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/MountingPlate.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, waitFor, screen } from '@testing-library/react' import { describe, it, expect, beforeEach, vi } from 'vitest' @@ -10,14 +9,16 @@ import { RUN_ID_1 } from '/app/resources/runs/__fixtures__' import { FLOWS } from '../constants' import { MountingPlate } from '../MountingPlate' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('MountingPlate', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { mount: LEFT, diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/Results.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/Results.test.tsx index 3382ac401a0..df53d95cdb7 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/Results.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/Results.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { act, fireEvent, screen, waitFor } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' @@ -18,19 +17,20 @@ import { RUN_ID_1 } from '/app/resources/runs/__fixtures__' import { Results } from '../Results' import { FLOWS } from '../constants' +import type { ComponentProps } from 'react' import type { Mock } from 'vitest' vi.mock('@opentrons/react-api-client') vi.mock('/app/resources/robot-settings/hooks') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('Results', () => { - let props: React.ComponentProps + let props: ComponentProps let pipettePromise: Promise let mockRefetchInstruments: Mock beforeEach(() => { diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/UnskippableModal.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/UnskippableModal.test.tsx index bc738d0caf3..f2d51af9a5f 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/UnskippableModal.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/UnskippableModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { UnskippableModal } from '../UnskippableModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('UnskippableModal', () => { - let props: React.ComponentProps + let props: ComponentProps it('returns the correct information for unskippable modal, pressing return button calls goBack prop', () => { props = { goBack: vi.fn(), diff --git a/app/src/organisms/PipetteWizardFlows/__tests__/hooks.test.tsx b/app/src/organisms/PipetteWizardFlows/__tests__/hooks.test.tsx index f44bd96fc6e..996ec520af2 100644 --- a/app/src/organisms/PipetteWizardFlows/__tests__/hooks.test.tsx +++ b/app/src/organisms/PipetteWizardFlows/__tests__/hooks.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, vi, afterEach, expect } from 'vitest' import { I18nextProvider } from 'react-i18next' import { renderHook } from '@testing-library/react' @@ -16,6 +15,8 @@ import { import { FLOWS } from '../constants' import { usePipetteFlowWizardHeaderText } from '../hooks' +import type { FunctionComponent, ReactNode } from 'react' + const BASE_PROPS_FOR_RUN_SETUP = { flowType: FLOWS.CALIBRATE, selectedPipette: SINGLE_MOUNT_PIPETTES, @@ -23,7 +24,7 @@ const BASE_PROPS_FOR_RUN_SETUP = { } describe('usePipetteFlowWizardHeaderText', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { wrapper = ({ children }) => ( {children} diff --git a/app/src/organisms/PipetteWizardFlows/constants.ts b/app/src/organisms/PipetteWizardFlows/constants.ts index e4ddd762d95..1d0b94878c1 100644 --- a/app/src/organisms/PipetteWizardFlows/constants.ts +++ b/app/src/organisms/PipetteWizardFlows/constants.ts @@ -18,6 +18,8 @@ export const FLOWS = { DETACH: 'DETACH', CALIBRATE: 'CALIBRATE', } + +// note: we will not be translating these item titles to be consistent with manuals export const CALIBRATION_PROBE_DISPLAY_NAME = 'Calibration Probe' export const HEX_SCREWDRIVER_DISPLAY_NAME = '2.5 mm Hex Screwdriver' export const PIPETTE_DISPLAY_NAME = '1- or 8-Channel Pipette' @@ -33,9 +35,6 @@ export const CALIBRATION_PROBE = { export const HEX_SCREWDRIVER = { loadName: 'hex_screwdriver', displayName: HEX_SCREWDRIVER_DISPLAY_NAME, - // TODO(jr, 4/3/23): add this subtitle to i18n - subtitle: - 'Provided with the robot. Using another size can strip the instruments’s screws.', } export const PIPETTE = { loadName: 'flex_pipette', diff --git a/app/src/organisms/PipetteWizardFlows/types.ts b/app/src/organisms/PipetteWizardFlows/types.ts index a8785e8a31c..3870141ab6e 100644 --- a/app/src/organisms/PipetteWizardFlows/types.ts +++ b/app/src/organisms/PipetteWizardFlows/types.ts @@ -1,6 +1,7 @@ -import type { SECTIONS, FLOWS } from './constants' +import type { Dispatch, SetStateAction } from 'react' import type { useCreateCommandMutation } from '@opentrons/react-api-client' import type { PipetteMount, CreateCommand } from '@opentrons/shared-data' +import type { SECTIONS, FLOWS } from './constants' import type { AttachedPipettesFromInstrumentsQuery } from '/app/resources/instruments' export type PipetteWizardStep = @@ -78,7 +79,7 @@ export interface PipetteWizardStepProps { isRobotMoving: boolean maintenanceRunId?: string attachedPipettes: AttachedPipettesFromInstrumentsQuery - setShowErrorMessage: React.Dispatch> + setShowErrorMessage: Dispatch> errorMessage: string | null selectedPipette: SelectablePipettes isOnDevice: boolean | null diff --git a/app/src/organisms/TakeoverModal/TakeoverModal.tsx b/app/src/organisms/TakeoverModal/TakeoverModal.tsx index 8f6441124a7..34a0754609e 100644 --- a/app/src/organisms/TakeoverModal/TakeoverModal.tsx +++ b/app/src/organisms/TakeoverModal/TakeoverModal.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' @@ -18,12 +17,13 @@ import { getTopPortalEl } from '/app/App/portal' import { SmallButton } from '/app/atoms/buttons' import { OddModal } from '/app/molecules/OddModal' +import type { Dispatch, SetStateAction } from 'react' import type { OddModalHeaderBaseProps } from '/app/molecules/OddModal/types' interface TakeoverModalProps { title: string showConfirmTerminateModal: boolean - setShowConfirmTerminateModal: React.Dispatch> + setShowConfirmTerminateModal: Dispatch> confirmTerminate: () => void terminateInProgress: boolean } diff --git a/app/src/organisms/TakeoverModal/__tests__/MaintenanceRunTakeover.test.tsx b/app/src/organisms/TakeoverModal/__tests__/MaintenanceRunTakeover.test.tsx index 94a59aa903a..92c286e9bbd 100644 --- a/app/src/organisms/TakeoverModal/__tests__/MaintenanceRunTakeover.test.tsx +++ b/app/src/organisms/TakeoverModal/__tests__/MaintenanceRunTakeover.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -8,6 +7,7 @@ import { useMaintenanceRunTakeover } from '../useMaintenanceRunTakeover' import { MaintenanceRunTakeover } from '../MaintenanceRunTakeover' import { useNotifyCurrentMaintenanceRun } from '/app/resources/maintenance_runs' +import type { ComponentProps } from 'react' import type { MaintenanceRunStatus } from '../MaintenanceRunStatusProvider' vi.mock('../useMaintenanceRunTakeover') @@ -21,14 +21,14 @@ const MOCK_MAINTENANCE_RUN: MaintenanceRunStatus = { setOddRunIds: () => null, } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('MaintenanceRunTakeover', () => { - let props: React.ComponentProps + let props: ComponentProps const testComponent =
        {'Test Component'}
        beforeEach(() => { diff --git a/app/src/organisms/TakeoverModal/__tests__/TakeoverModal.test.tsx b/app/src/organisms/TakeoverModal/__tests__/TakeoverModal.test.tsx index a902544e4a0..4f3ffd78894 100644 --- a/app/src/organisms/TakeoverModal/__tests__/TakeoverModal.test.tsx +++ b/app/src/organisms/TakeoverModal/__tests__/TakeoverModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { fireEvent, screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -6,14 +5,16 @@ import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { TakeoverModal } from '../TakeoverModal' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TakeoverModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { showConfirmTerminateModal: false, diff --git a/app/src/organisms/UpdateRobotSoftware/ErrorUpdateSoftware.tsx b/app/src/organisms/UpdateRobotSoftware/ErrorUpdateSoftware.tsx index f1629e15b64..ab1b44991b6 100644 --- a/app/src/organisms/UpdateRobotSoftware/ErrorUpdateSoftware.tsx +++ b/app/src/organisms/UpdateRobotSoftware/ErrorUpdateSoftware.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { @@ -14,9 +13,11 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import type { ReactNode } from 'react' + interface ErrorUpdateSoftwareProps { errorMessage: string - children: React.ReactNode + children: ReactNode } export function ErrorUpdateSoftware({ errorMessage, diff --git a/app/src/organisms/UpdateRobotSoftware/__tests__/CompleteUpdateSoftware.test.tsx b/app/src/organisms/UpdateRobotSoftware/__tests__/CompleteUpdateSoftware.test.tsx index b6b91424b92..be6d9056cc0 100644 --- a/app/src/organisms/UpdateRobotSoftware/__tests__/CompleteUpdateSoftware.test.tsx +++ b/app/src/organisms/UpdateRobotSoftware/__tests__/CompleteUpdateSoftware.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -6,16 +5,18 @@ import { i18n } from '/app/i18n' import { renderWithProviders } from '/app/__testing-utils__' import { CompleteUpdateSoftware } from '../CompleteUpdateSoftware' +import type { ComponentProps } from 'react' + vi.mock('/app/redux/robot-admin') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('CompleteUpdateSoftware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/UpdateRobotSoftware/__tests__/ErrorUpdateSoftware.test.tsx b/app/src/organisms/UpdateRobotSoftware/__tests__/ErrorUpdateSoftware.test.tsx index d1706a4bf18..6f585d31db0 100644 --- a/app/src/organisms/UpdateRobotSoftware/__tests__/ErrorUpdateSoftware.test.tsx +++ b/app/src/organisms/UpdateRobotSoftware/__tests__/ErrorUpdateSoftware.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ErrorUpdateSoftware } from '../ErrorUpdateSoftware' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ErrorUpdateSoftware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/organisms/UpdateRobotSoftware/__tests__/UpdateSoftware.test.tsx b/app/src/organisms/UpdateRobotSoftware/__tests__/UpdateSoftware.test.tsx index 93deeb27956..76344df2c4c 100644 --- a/app/src/organisms/UpdateRobotSoftware/__tests__/UpdateSoftware.test.tsx +++ b/app/src/organisms/UpdateRobotSoftware/__tests__/UpdateSoftware.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' @@ -6,14 +5,16 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { UpdateSoftware } from '../UpdateSoftware' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('UpdateSoftware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { updateType: 'downloading', diff --git a/app/src/pages/Desktop/AppSettings/GeneralSettings.tsx b/app/src/pages/Desktop/AppSettings/GeneralSettings.tsx index 85b24816da6..eed4f5be96a 100644 --- a/app/src/pages/Desktop/AppSettings/GeneralSettings.tsx +++ b/app/src/pages/Desktop/AppSettings/GeneralSettings.tsx @@ -1,8 +1,9 @@ // app info card with version and updated -import { useState } from 'react' +import { useState, useEffect } from 'react' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' import { useSelector, useDispatch } from 'react-redux' +import uuidv1 from 'uuid/v4' import { ALIGN_CENTER, @@ -41,6 +42,7 @@ import { import { useTrackEvent, ANALYTICS_APP_UPDATE_NOTIFICATIONS_TOGGLED, + ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS, } from '/app/redux/analytics' import { getAppLanguage, updateConfigValue } from '/app/redux/config' import { UpdateAppModal } from '/app/organisms/Desktop/UpdateAppModal' @@ -55,6 +57,7 @@ const GITHUB_LINK = 'https://github.com/Opentrons/opentrons/blob/edge/app-shell/build/release-notes.md' const ENABLE_APP_UPDATE_NOTIFICATIONS = 'Enable app update notifications' +const uuid: () => string = uuidv1 export function GeneralSettings(): JSX.Element { const { t } = useTranslation(['app_settings', 'shared', 'branded']) @@ -68,9 +71,19 @@ export function GeneralSettings(): JSX.Element { const appLanguage = useSelector(getAppLanguage) const currentLanguageOption = LANGUAGES.find(lng => lng.value === appLanguage) - + let transactionId = '' + useEffect(() => { + transactionId = uuid() + }, []) const handleDropdownClick = (value: string): void => { dispatch(updateConfigValue('language.appLanguage', value)) + trackEvent({ + name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS, + properties: { + language: value, + transactionId, + }, + }) } const [showUpdateBanner, setShowUpdateBanner] = useState( diff --git a/app/src/pages/Desktop/AppSettings/__test__/GeneralSettings.test.tsx b/app/src/pages/Desktop/AppSettings/__test__/GeneralSettings.test.tsx index 43d17a4d2cb..a06f4204bd7 100644 --- a/app/src/pages/Desktop/AppSettings/__test__/GeneralSettings.test.tsx +++ b/app/src/pages/Desktop/AppSettings/__test__/GeneralSettings.test.tsx @@ -12,6 +12,10 @@ import { US_ENGLISH_DISPLAY_NAME, } from '/app/i18n' import { getAlertIsPermanentlyIgnored } from '/app/redux/alerts' +import { + ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS, + useTrackEvent, +} from '/app/redux/analytics' import { getAppLanguage, updateConfigValue } from '/app/redux/config' import * as Shell from '/app/redux/shell' import { GeneralSettings } from '../GeneralSettings' @@ -32,11 +36,14 @@ const render = (): ReturnType => { ) } +const mockTrackEvent = vi.fn() + describe('GeneralSettings', () => { beforeEach(() => { vi.mocked(Shell.getAvailableShellUpdate).mockReturnValue(null) vi.mocked(getAlertIsPermanentlyIgnored).mockReturnValue(false) vi.mocked(getAppLanguage).mockReturnValue(US_ENGLISH) + vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent) }) afterEach(() => { vi.resetAllMocks() @@ -118,5 +125,12 @@ describe('GeneralSettings', () => { 'language.appLanguage', SIMPLIFIED_CHINESE ) + expect(mockTrackEvent).toHaveBeenCalledWith({ + name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS, + properties: { + language: SIMPLIFIED_CHINESE, + transactionId: expect.anything(), + }, + }) }) }) diff --git a/app/src/pages/Desktop/Labware/__tests__/hooks.test.tsx b/app/src/pages/Desktop/Labware/__tests__/hooks.test.tsx index 5fe55c260dc..c2edcacd08a 100644 --- a/app/src/pages/Desktop/Labware/__tests__/hooks.test.tsx +++ b/app/src/pages/Desktop/Labware/__tests__/hooks.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Provider } from 'react-redux' import { createStore } from 'redux' import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest' @@ -20,6 +19,7 @@ import { import { useLabwareFailure, useNewLabwareName } from '../hooks' import { useAllLabware } from '/app/local-resources/labware' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' import type { FailedLabwareFile } from '/app/redux/custom-labware/types' @@ -39,7 +39,7 @@ describe('useAllLabware hook', () => { }) it('should return object with only definition and modified date', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook(() => useAllLabware('reverse', 'all'), { @@ -53,7 +53,7 @@ describe('useAllLabware hook', () => { expect(labware2.definition).toBe(mockValidLabware.definition) }) it('should return alphabetically sorted list', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook(() => useAllLabware('alphabetical', 'all'), { @@ -67,7 +67,7 @@ describe('useAllLabware hook', () => { expect(labware1.definition).toBe(mockValidLabware.definition) }) it('should return no labware if not the right filter', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook(() => useAllLabware('reverse', 'reservoir'), { @@ -80,7 +80,7 @@ describe('useAllLabware hook', () => { expect(labware2).toBe(undefined) }) it('should return labware with wellPlate filter', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook(() => useAllLabware('reverse', 'wellPlate'), { @@ -94,7 +94,7 @@ describe('useAllLabware hook', () => { expect(labware2.definition).toBe(mockValidLabware.definition) }) it('should return custom labware with customLabware filter', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook( @@ -127,7 +127,7 @@ describe('useLabwareFailure hook', () => { vi.restoreAllMocks() }) it('should return invalid labware definition', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -147,7 +147,7 @@ describe('useLabwareFailure hook', () => { errorMessage: null, }) - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -170,7 +170,7 @@ describe('useLabwareFailure hook', () => { errorMessage: null, }) - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -190,7 +190,7 @@ describe('useLabwareFailure hook', () => { errorMessage: 'error', }) - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( @@ -217,7 +217,7 @@ describe('useNewLabwareName hook', () => { }) it('should return filename as a string', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook(useNewLabwareName, { wrapper }) diff --git a/app/src/pages/Desktop/Labware/index.tsx b/app/src/pages/Desktop/Labware/index.tsx index 83f9dd94f3f..5d8959a5861 100644 --- a/app/src/pages/Desktop/Labware/index.tsx +++ b/app/src/pages/Desktop/Labware/index.tsx @@ -63,6 +63,7 @@ const labwareDisplayCategoryFilters: LabwareFilter[] = [ 'wellPlate', ] +// note: we've decided not to translate these categories const FILTER_OPTIONS: DropdownOption[] = labwareDisplayCategoryFilters.map( category => ({ name: startCase(category), diff --git a/app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx b/app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx index 8508a7b4d08..fa5c793e2d2 100644 --- a/app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx +++ b/app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx @@ -1,10 +1,12 @@ -import { vi, it, describe, expect } from 'vitest' +import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' -import { updateConfigValue } from '/app/redux/config' +import { ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW } from '/app/redux/analytics' +import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' +import { updateConfigValue, getAppLanguage } from '/app/redux/config' import { ChooseLanguage } from '..' import type { NavigateFunction } from 'react-router-dom' @@ -18,6 +20,9 @@ vi.mock('react-router-dom', async importOriginal => { } }) vi.mock('/app/redux/config') +vi.mock('/app/redux-resources/analytics') + +const mockTrackEvent = vi.fn() const render = () => { return renderWithProviders( @@ -31,6 +36,12 @@ const render = () => { } describe('ChooseLanguage', () => { + beforeEach(() => { + vi.mocked(useTrackEventWithRobotSerial).mockReturnValue({ + trackEventWithRobotSerial: mockTrackEvent, + }) + vi.mocked(getAppLanguage).mockReturnValue('en-US') + }) it('should render text, language options, and continue button', () => { render() screen.getByText('Choose your language') @@ -54,6 +65,12 @@ describe('ChooseLanguage', () => { it('should call mockNavigate when tapping continue', () => { render() fireEvent.click(screen.getByRole('button', { name: 'Continue' })) + expect(mockTrackEvent).toHaveBeenCalledWith({ + name: ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW, + properties: { + language: 'en-US', + }, + }) expect(mockNavigate).toHaveBeenCalledWith('/welcome') }) }) diff --git a/app/src/pages/ODD/ChooseLanguage/index.tsx b/app/src/pages/ODD/ChooseLanguage/index.tsx index d0110e68591..8ecb87451f7 100644 --- a/app/src/pages/ODD/ChooseLanguage/index.tsx +++ b/app/src/pages/ODD/ChooseLanguage/index.tsx @@ -13,6 +13,8 @@ import { TYPOGRAPHY, } from '@opentrons/components' +import { ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW } from '/app/redux/analytics' +import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics' import { MediumButton } from '/app/atoms/buttons' import { LANGUAGES, US_ENGLISH } from '/app/i18n' import { RobotSetupHeader } from '/app/organisms/ODD/RobotSetupHeader' @@ -24,6 +26,7 @@ export function ChooseLanguage(): JSX.Element { const { i18n, t } = useTranslation(['app_settings', 'shared']) const navigate = useNavigate() const dispatch = useDispatch() + const { trackEventWithRobotSerial } = useTrackEventWithRobotSerial() const appLanguage = useSelector(getAppLanguage) @@ -69,6 +72,12 @@ export function ChooseLanguage(): JSX.Element { { + trackEventWithRobotSerial({ + name: ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW, + properties: { + language: appLanguage, + }, + }) navigate('/welcome') }} width="100%" diff --git a/app/src/pages/ODD/ConnectViaEthernet/__tests__/DisplayConnectionStatus.test.tsx b/app/src/pages/ODD/ConnectViaEthernet/__tests__/DisplayConnectionStatus.test.tsx index 9efbfd5c3dc..0caa3a6ba10 100644 --- a/app/src/pages/ODD/ConnectViaEthernet/__tests__/DisplayConnectionStatus.test.tsx +++ b/app/src/pages/ODD/ConnectViaEthernet/__tests__/DisplayConnectionStatus.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -6,6 +5,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { DisplayConnectionStatus } from '../DisplayConnectionStatus' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockFunc = vi.fn() @@ -18,16 +18,14 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('DisplayConnectionStatus', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/pages/ODD/ConnectViaEthernet/__tests__/TitleHeader.test.tsx b/app/src/pages/ODD/ConnectViaEthernet/__tests__/TitleHeader.test.tsx index cffa8e9c63a..8c8667619b9 100644 --- a/app/src/pages/ODD/ConnectViaEthernet/__tests__/TitleHeader.test.tsx +++ b/app/src/pages/ODD/ConnectViaEthernet/__tests__/TitleHeader.test.tsx @@ -1,10 +1,10 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' import { TitleHeader } from '../TitleHeader' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockNavigate = vi.fn() @@ -16,12 +16,12 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders() } describe('TitleHeader', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/pages/ODD/ConnectViaWifi/SetWifiCred.tsx b/app/src/pages/ODD/ConnectViaWifi/SetWifiCred.tsx index e2716fc2282..2d81286d058 100644 --- a/app/src/pages/ODD/ConnectViaWifi/SetWifiCred.tsx +++ b/app/src/pages/ODD/ConnectViaWifi/SetWifiCred.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { Flex, DIRECTION_COLUMN } from '@opentrons/components' @@ -6,13 +5,14 @@ import { Flex, DIRECTION_COLUMN } from '@opentrons/components' import { SetWifiCred as SetWifiCredComponent } from '/app/organisms/ODD/NetworkSettings' import { RobotSetupHeader } from '/app/organisms/ODD/RobotSetupHeader' +import type { Dispatch, SetStateAction } from 'react' import type { WifiScreenOption } from './' interface SetWifiCredProps { handleConnect: () => void password: string setCurrentOption: (option: WifiScreenOption) => void - setPassword: React.Dispatch> + setPassword: Dispatch> } export function SetWifiCred({ diff --git a/app/src/pages/ODD/InitialLoadingScreen/index.tsx b/app/src/pages/ODD/InitialLoadingScreen/index.tsx index f35fad13b56..003d37e549e 100644 --- a/app/src/pages/ODD/InitialLoadingScreen/index.tsx +++ b/app/src/pages/ODD/InitialLoadingScreen/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useSelector } from 'react-redux' import { ALIGN_CENTER, @@ -12,10 +11,12 @@ import { import { useRobotSettingsQuery } from '@opentrons/react-api-client' import { getIsShellReady } from '/app/redux/shell' +import type { ReactNode } from 'react' + export function InitialLoadingScreen({ children, }: { - children?: React.ReactNode + children?: ReactNode }): JSX.Element { const isShellReady = useSelector(getIsShellReady) diff --git a/app/src/pages/ODD/InstrumentsDashboard/index.tsx b/app/src/pages/ODD/InstrumentsDashboard/index.tsx index acc88714979..49268c0ec51 100644 --- a/app/src/pages/ODD/InstrumentsDashboard/index.tsx +++ b/app/src/pages/ODD/InstrumentsDashboard/index.tsx @@ -7,6 +7,7 @@ import { AttachedInstrumentMountItem } from '/app/organisms/ODD/InstrumentMountI import { GripperWizardFlows } from '/app/organisms/GripperWizardFlows' import { getShowPipetteCalibrationWarning } from '/app/transformations/instruments' import { PipetteRecalibrationODDWarning } from '/app/organisms/ODD/PipetteRecalibrationODDWarning' +import type { ComponentProps } from 'react' import type { GripperData, PipetteData } from '@opentrons/api-client' const FETCH_PIPETTE_CAL_POLL = 10000 @@ -16,8 +17,8 @@ export const InstrumentsDashboard = (): JSX.Element => { refetchInterval: FETCH_PIPETTE_CAL_POLL, }) const [wizardProps, setWizardProps] = useState< - | React.ComponentProps - | React.ComponentProps + | ComponentProps + | ComponentProps | null >(null) diff --git a/app/src/pages/ODD/NameRobot/index.tsx b/app/src/pages/ODD/NameRobot/index.tsx index b0162041963..1417adbbf37 100644 --- a/app/src/pages/ODD/NameRobot/index.tsx +++ b/app/src/pages/ODD/NameRobot/index.tsx @@ -23,6 +23,7 @@ import { TYPOGRAPHY, } from '@opentrons/components' import { useUpdateRobotNameMutation } from '@opentrons/react-api-client' +import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' import { removeRobot, @@ -166,6 +167,7 @@ export function NameRobot(): JSX.Element { properties: { previousRobotName: previousName, newRobotName: newRobotName, + robotType: FLEX_ROBOT_TYPE, }, }) handleSubmit(onSubmit)() diff --git a/app/src/pages/ODD/ProtocolDashboard/PinnedProtocolCarousel.tsx b/app/src/pages/ODD/ProtocolDashboard/PinnedProtocolCarousel.tsx index 59e9fc5c117..267b77a5cea 100644 --- a/app/src/pages/ODD/ProtocolDashboard/PinnedProtocolCarousel.tsx +++ b/app/src/pages/ODD/ProtocolDashboard/PinnedProtocolCarousel.tsx @@ -1,4 +1,4 @@ -import type * as React from 'react' +import styled from 'styled-components' import { ALIGN_FLEX_START, DIRECTION_ROW, @@ -9,13 +9,13 @@ import { import { useNotifyAllRunsQuery } from '/app/resources/runs' import { PinnedProtocol } from './PinnedProtocol' +import type { Dispatch, SetStateAction } from 'react' import type { ProtocolResource } from '@opentrons/shared-data' import type { CardSizeType } from './PinnedProtocol' -import styled from 'styled-components' interface PinnedProtocolCarouselProps { pinnedProtocols: ProtocolResource[] - longPress: React.Dispatch> + longPress: Dispatch> setShowDeleteConfirmationModal: (showDeleteConfirmationModal: boolean) => void setTargetProtocolId: (targetProtocolId: string) => void isRequiredCSV?: boolean diff --git a/app/src/pages/ODD/ProtocolDashboard/ProtocolCard.tsx b/app/src/pages/ODD/ProtocolDashboard/ProtocolCard.tsx index 61a0bcb9061..6786205d889 100644 --- a/app/src/pages/ODD/ProtocolDashboard/ProtocolCard.tsx +++ b/app/src/pages/ODD/ProtocolDashboard/ProtocolCard.tsx @@ -35,6 +35,7 @@ import { LongPressModal } from './LongPressModal' import { formatTimeWithUtcLabel } from '/app/resources/runs' import { useUpdatedLastRunTime } from '/app/pages/ODD/ProtocolDashboard/hooks' +import type { Dispatch, SetStateAction } from 'react' import type { UseLongPressResult } from '@opentrons/components' import type { ProtocolResource } from '@opentrons/shared-data' import type { OddModalHeaderBaseProps } from '/app/molecules/OddModal/types' @@ -43,7 +44,7 @@ const REFETCH_INTERVAL = 5000 interface ProtocolCardProps { protocol: ProtocolResource - longPress: React.Dispatch> + longPress: Dispatch> setShowDeleteConfirmationModal: (showDeleteConfirmationModal: boolean) => void setTargetProtocolId: (targetProtocolId: string) => void lastRun?: string diff --git a/app/src/pages/ODD/ProtocolDashboard/__tests__/DeleteProtocolConfirmationModal.test.tsx b/app/src/pages/ODD/ProtocolDashboard/__tests__/DeleteProtocolConfirmationModal.test.tsx index 1c55405dbde..059e62f85e5 100644 --- a/app/src/pages/ODD/ProtocolDashboard/__tests__/DeleteProtocolConfirmationModal.test.tsx +++ b/app/src/pages/ODD/ProtocolDashboard/__tests__/DeleteProtocolConfirmationModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { act, fireEvent, screen } from '@testing-library/react' @@ -10,6 +9,8 @@ import { useHost, useProtocolQuery } from '@opentrons/react-api-client' import { i18n } from '/app/i18n' import { useToaster } from '/app/organisms/ToasterOven' import { DeleteProtocolConfirmationModal } from '../DeleteProtocolConfirmationModal' + +import type { ComponentProps } from 'react' import type { HostConfig } from '@opentrons/api-client' vi.mock('@opentrons/api-client') @@ -22,7 +23,7 @@ const mockMakeSnackbar = vi.fn() const MOCK_HOST_CONFIG = {} as HostConfig const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -30,7 +31,7 @@ const render = ( } describe('DeleteProtocolConfirmationModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/pages/ODD/ProtocolDashboard/__tests__/LongPressModal.test.tsx b/app/src/pages/ODD/ProtocolDashboard/__tests__/LongPressModal.test.tsx index 777e8d92a39..68ff6dcc9bf 100644 --- a/app/src/pages/ODD/ProtocolDashboard/__tests__/LongPressModal.test.tsx +++ b/app/src/pages/ODD/ProtocolDashboard/__tests__/LongPressModal.test.tsx @@ -26,7 +26,7 @@ const render = (longPress: UseLongPressResult) => { diff --git a/app/src/pages/ODD/ProtocolDashboard/__tests__/PinnedProtocol.test.tsx b/app/src/pages/ODD/ProtocolDashboard/__tests__/PinnedProtocol.test.tsx index f92904573d0..a98f0478d6d 100644 --- a/app/src/pages/ODD/ProtocolDashboard/__tests__/PinnedProtocol.test.tsx +++ b/app/src/pages/ODD/ProtocolDashboard/__tests__/PinnedProtocol.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { act, fireEvent, screen } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' @@ -10,9 +9,10 @@ import { i18n } from '/app/i18n' import { useFeatureFlag } from '/app/redux/config' import { PinnedProtocol } from '../PinnedProtocol' +import type { ComponentProps } from 'react' +import type { NavigateFunction } from 'react-router-dom' import type { Chip } from '@opentrons/components' import type { ProtocolResource } from '@opentrons/shared-data' -import type { NavigateFunction } from 'react-router-dom' const mockNavigate = vi.fn() @@ -50,7 +50,7 @@ const mockProtocol: ProtocolResource = { key: '26ed5a82-502f-4074-8981-57cdda1d066d', } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -62,7 +62,7 @@ const render = (props: React.ComponentProps) => { } describe('Pinned Protocol', () => { - let props: React.ComponentProps + let props: ComponentProps vi.useFakeTimers() beforeEach(() => { diff --git a/app/src/pages/ODD/ProtocolDashboard/__tests__/ProtocolCard.test.tsx b/app/src/pages/ODD/ProtocolDashboard/__tests__/ProtocolCard.test.tsx index c26b429e29e..5f1587242d8 100644 --- a/app/src/pages/ODD/ProtocolDashboard/__tests__/ProtocolCard.test.tsx +++ b/app/src/pages/ODD/ProtocolDashboard/__tests__/ProtocolCard.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { act, fireEvent, screen } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' @@ -14,6 +13,7 @@ import { i18n } from '/app/i18n' import { useFeatureFlag } from '/app/redux/config' import { ProtocolCard } from '../ProtocolCard' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' import type { UseQueryResult } from 'react-query' import type { @@ -77,7 +77,7 @@ const mockProtocolWithCSV: ProtocolResource = { key: '26ed5a82-502f-4074-8981-57cdda1d066d', } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders( @@ -89,7 +89,7 @@ const render = (props: React.ComponentProps) => { } describe('ProtocolCard', () => { - let props: React.ComponentProps + let props: ComponentProps vi.useFakeTimers() beforeEach(() => { diff --git a/app/src/pages/ODD/ProtocolDetails/__tests__/Deck.test.tsx b/app/src/pages/ODD/ProtocolDetails/__tests__/Deck.test.tsx index a99e2c3f82e..902cd1d3b8c 100644 --- a/app/src/pages/ODD/ProtocolDetails/__tests__/Deck.test.tsx +++ b/app/src/pages/ODD/ProtocolDetails/__tests__/Deck.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' import { when } from 'vitest-when' @@ -12,6 +11,7 @@ import { import { i18n } from '/app/i18n' import { Deck } from '../Deck' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { CompletedProtocolAnalysis } from '@opentrons/shared-data' import type { Protocol } from '@opentrons/api-client' @@ -141,14 +141,14 @@ const MOCK_PROTOCOL_ANALYSIS = { ], } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Deck', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { protocolId: MOCK_PROTOCOL_ID, diff --git a/app/src/pages/ODD/ProtocolDetails/__tests__/EmptySection.test.tsx b/app/src/pages/ODD/ProtocolDetails/__tests__/EmptySection.test.tsx index 32b388ef1f3..9c1832fc08f 100644 --- a/app/src/pages/ODD/ProtocolDetails/__tests__/EmptySection.test.tsx +++ b/app/src/pages/ODD/ProtocolDetails/__tests__/EmptySection.test.tsx @@ -1,18 +1,19 @@ -import type * as React from 'react' import { it, describe } from 'vitest' import { screen } from '@testing-library/react' import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { EmptySection } from '../EmptySection' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('EmptySection', () => { - let props: React.ComponentProps + let props: ComponentProps it('should render text for labware', () => { props = { diff --git a/app/src/pages/ODD/ProtocolDetails/__tests__/Hardware.test.tsx b/app/src/pages/ODD/ProtocolDetails/__tests__/Hardware.test.tsx index a8f78c8a121..7d03ce6ba3d 100644 --- a/app/src/pages/ODD/ProtocolDetails/__tests__/Hardware.test.tsx +++ b/app/src/pages/ODD/ProtocolDetails/__tests__/Hardware.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' import { when } from 'vitest-when' @@ -12,19 +11,21 @@ import { i18n } from '/app/i18n' import { useRequiredProtocolHardware } from '/app/resources/protocols' import { Hardware } from '../Hardware' +import type { ComponentProps } from 'react' + vi.mock('/app/resources/protocols') vi.mock('/app/redux/config') const MOCK_PROTOCOL_ID = 'mock_protocol_id' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Hardware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { protocolId: MOCK_PROTOCOL_ID, diff --git a/app/src/pages/ODD/ProtocolDetails/__tests__/Labware.test.tsx b/app/src/pages/ODD/ProtocolDetails/__tests__/Labware.test.tsx index 0c1aab61196..14568a6ceb3 100644 --- a/app/src/pages/ODD/ProtocolDetails/__tests__/Labware.test.tsx +++ b/app/src/pages/ODD/ProtocolDetails/__tests__/Labware.test.tsx @@ -1,10 +1,6 @@ -import type * as React from 'react' import { vi, it, describe, beforeEach, afterEach } from 'vitest' +import { screen } from '@testing-library/react' import { when } from 'vitest-when' -import { renderWithProviders } from '/app/__testing-utils__' -import { i18n } from '/app/i18n' -import { useRequiredProtocolLabware } from '/app/resources/protocols' -import { Labware } from '../Labware' import { fixtureTiprack10ul, @@ -12,21 +8,26 @@ import { fixture96Plate, } from '@opentrons/shared-data' +import { renderWithProviders } from '/app/__testing-utils__' +import { i18n } from '/app/i18n' +import { useRequiredProtocolLabware } from '/app/resources/protocols' +import { Labware } from '../Labware' + +import type { ComponentProps } from 'react' import type { LabwareDefinition2 } from '@opentrons/shared-data' -import { screen } from '@testing-library/react' vi.mock('/app/resources/protocols') const MOCK_PROTOCOL_ID = 'mock_protocol_id' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Labware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { protocolId: MOCK_PROTOCOL_ID, diff --git a/app/src/pages/ODD/ProtocolDetails/__tests__/Liquids.test.tsx b/app/src/pages/ODD/ProtocolDetails/__tests__/Liquids.test.tsx index be019dd6cf1..23233c6c50d 100644 --- a/app/src/pages/ODD/ProtocolDetails/__tests__/Liquids.test.tsx +++ b/app/src/pages/ODD/ProtocolDetails/__tests__/Liquids.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, beforeEach } from 'vitest' import { when } from 'vitest-when' import { screen } from '@testing-library/react' @@ -16,6 +15,7 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { Liquids } from '../Liquids' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { Protocol } from '@opentrons/api-client' import type * as SharedData from '@opentrons/shared-data' @@ -180,14 +180,14 @@ const MOCK_LABWARE_INFO_BY_LIQUID_ID = { ], } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Liquids', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { protocolId: MOCK_PROTOCOL_ID, diff --git a/app/src/pages/ODD/ProtocolDetails/__tests__/Parameters.test.tsx b/app/src/pages/ODD/ProtocolDetails/__tests__/Parameters.test.tsx index f41115cf638..b2d60c57c92 100644 --- a/app/src/pages/ODD/ProtocolDetails/__tests__/Parameters.test.tsx +++ b/app/src/pages/ODD/ProtocolDetails/__tests__/Parameters.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { it, describe, beforeEach, vi } from 'vitest' import { screen } from '@testing-library/react' @@ -9,17 +8,19 @@ import { useRunTimeParameters } from '/app/resources/protocols' import { Parameters } from '../Parameters' import { mockRunTimeParameterData } from '/app/organisms/ODD/ProtocolSetup/__fixtures__' +import type { ComponentProps } from 'react' + vi.mock('/app/organisms/ToasterOven') vi.mock('/app/resources/protocols') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } const MOCK_MAKE_SNACK_BAR = vi.fn() describe('Parameters', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/pages/ODD/ProtocolSetup/__tests__/ConfirmAttachedModal.test.tsx b/app/src/pages/ODD/ProtocolSetup/__tests__/ConfirmAttachedModal.test.tsx index f06e36df20d..5a9b2ea98bc 100644 --- a/app/src/pages/ODD/ProtocolSetup/__tests__/ConfirmAttachedModal.test.tsx +++ b/app/src/pages/ODD/ProtocolSetup/__tests__/ConfirmAttachedModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -7,17 +6,19 @@ import { renderWithProviders } from '/app/__testing-utils__' import { i18n } from '/app/i18n' import { ConfirmAttachedModal } from '../ConfirmAttachedModal' +import type { ComponentProps } from 'react' + const mockOnCloseClick = vi.fn() const mockOnConfirmClick = vi.fn() -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('ConfirmAttachedModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/pages/ODD/ProtocolSetup/__tests__/ProtocolSetup.test.tsx b/app/src/pages/ODD/ProtocolSetup/__tests__/ProtocolSetup.test.tsx index 5f7d1f8cfc8..5863d70ba93 100644 --- a/app/src/pages/ODD/ProtocolSetup/__tests__/ProtocolSetup.test.tsx +++ b/app/src/pages/ODD/ProtocolSetup/__tests__/ProtocolSetup.test.tsx @@ -576,7 +576,6 @@ describe('ProtocolSetup', () => { render(`/runs/${RUN_ID}/setup/`) fireEvent.click(screen.getByRole('button', { name: 'play' })) - expect(mockTrackProtocolRunEvent).toBeCalledTimes(1) expect(mockTrackProtocolRunEvent).toHaveBeenCalledWith({ name: ANALYTICS_PROTOCOL_RUN_ACTION.START, properties: {}, diff --git a/app/src/pages/ODD/ProtocolSetup/index.tsx b/app/src/pages/ODD/ProtocolSetup/index.tsx index 25c978e6717..1df659c633b 100644 --- a/app/src/pages/ODD/ProtocolSetup/index.tsx +++ b/app/src/pages/ODD/ProtocolSetup/index.tsx @@ -741,11 +741,19 @@ export function ProtocolSetup(): JSX.Element { robotType, protocolName ) + + const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId, robotName) + const robotAnalyticsData = useRobotAnalyticsData(robotName) + const handleProceedToRunClick = (): void => { trackEvent({ name: ANALYTICS_PROTOCOL_PROCEED_TO_RUN, properties: { robotSerialNumber }, }) + trackProtocolRunEvent({ + name: ANALYTICS_PROTOCOL_RUN_ACTION.START, + properties: robotAnalyticsData ?? {}, + }) play() } const configBypassHeaterShakerAttachmentConfirmation = useSelector( diff --git a/app/src/pages/ODD/QuickTransferDashboard/PinnedTransferCarousel.tsx b/app/src/pages/ODD/QuickTransferDashboard/PinnedTransferCarousel.tsx index 8b6554c3aa1..fc5b127b437 100644 --- a/app/src/pages/ODD/QuickTransferDashboard/PinnedTransferCarousel.tsx +++ b/app/src/pages/ODD/QuickTransferDashboard/PinnedTransferCarousel.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import styled from 'styled-components' import { ALIGN_FLEX_START, @@ -11,12 +10,13 @@ import { import { PinnedTransfer } from './PinnedTransfer' +import type { Dispatch, SetStateAction } from 'react' import type { ProtocolResource } from '@opentrons/shared-data' import type { CardSizeType } from './PinnedTransfer' export function PinnedTransferCarousel(props: { pinnedTransfers: ProtocolResource[] - longPress: React.Dispatch> + longPress: Dispatch> setShowDeleteConfirmationModal: (showDeleteConfirmationModal: boolean) => void setTargetTransferId: (targetTransferId: string) => void }): JSX.Element { diff --git a/app/src/pages/ODD/QuickTransferDashboard/__tests__/DeleteTransferConfirmationModal.test.tsx b/app/src/pages/ODD/QuickTransferDashboard/__tests__/DeleteTransferConfirmationModal.test.tsx index 1b42a6a5e3e..e7cb5ad92e6 100644 --- a/app/src/pages/ODD/QuickTransferDashboard/__tests__/DeleteTransferConfirmationModal.test.tsx +++ b/app/src/pages/ODD/QuickTransferDashboard/__tests__/DeleteTransferConfirmationModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { act, fireEvent, screen } from '@testing-library/react' @@ -11,6 +10,7 @@ import { i18n } from '/app/i18n' import { useToaster } from '/app/organisms/ToasterOven' import { DeleteTransferConfirmationModal } from '../DeleteTransferConfirmationModal' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' import type { HostConfig } from '@opentrons/api-client' @@ -33,7 +33,7 @@ const mockMakeSnackbar = vi.fn() const MOCK_HOST_CONFIG = {} as HostConfig const render = ( - props: React.ComponentProps + props: ComponentProps ) => { return renderWithProviders(, { i18nInstance: i18n, @@ -41,7 +41,7 @@ const render = ( } describe('DeleteTransferConfirmationModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/app/src/pages/ODD/QuickTransferDashboard/__tests__/LongPressModal.test.tsx b/app/src/pages/ODD/QuickTransferDashboard/__tests__/LongPressModal.test.tsx index 2c55020d32a..e7e743e19ae 100644 --- a/app/src/pages/ODD/QuickTransferDashboard/__tests__/LongPressModal.test.tsx +++ b/app/src/pages/ODD/QuickTransferDashboard/__tests__/LongPressModal.test.tsx @@ -26,7 +26,7 @@ const render = (longPress: UseLongPressResult) => { diff --git a/app/src/pages/ODD/QuickTransferDetails/__tests__/Deck.test.tsx b/app/src/pages/ODD/QuickTransferDetails/__tests__/Deck.test.tsx index e15296037d5..415a25fdc65 100644 --- a/app/src/pages/ODD/QuickTransferDetails/__tests__/Deck.test.tsx +++ b/app/src/pages/ODD/QuickTransferDetails/__tests__/Deck.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' import { when } from 'vitest-when' @@ -12,6 +11,7 @@ import { import { i18n } from '/app/i18n' import { Deck } from '../Deck' +import type { ComponentProps } from 'react' import type { UseQueryResult } from 'react-query' import type { CompletedProtocolAnalysis } from '@opentrons/shared-data' import type { Protocol } from '@opentrons/api-client' @@ -141,14 +141,14 @@ const MOCK_PROTOCOL_ANALYSIS = { ], } -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Deck', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { transferId: MOCK_PROTOCOL_ID, diff --git a/app/src/pages/ODD/QuickTransferDetails/__tests__/Hardware.test.tsx b/app/src/pages/ODD/QuickTransferDetails/__tests__/Hardware.test.tsx index 2b4af1a5178..a5f707fdf85 100644 --- a/app/src/pages/ODD/QuickTransferDetails/__tests__/Hardware.test.tsx +++ b/app/src/pages/ODD/QuickTransferDetails/__tests__/Hardware.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, beforeEach, afterEach } from 'vitest' import { screen } from '@testing-library/react' import { when } from 'vitest-when' @@ -12,20 +11,22 @@ import { i18n } from '/app/i18n' import { useRequiredProtocolHardware } from '/app/resources/protocols' import { Hardware } from '../Hardware' +import type { ComponentProps } from 'react' + vi.mock('/app/transformations/commands') vi.mock('/app/resources/protocols') vi.mock('/app/redux/config') const MOCK_PROTOCOL_ID = 'mock_protocol_id' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Hardware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { transferId: MOCK_PROTOCOL_ID, diff --git a/app/src/pages/ODD/QuickTransferDetails/__tests__/Labware.test.tsx b/app/src/pages/ODD/QuickTransferDetails/__tests__/Labware.test.tsx index 7dd49d4f279..09f09f73718 100644 --- a/app/src/pages/ODD/QuickTransferDetails/__tests__/Labware.test.tsx +++ b/app/src/pages/ODD/QuickTransferDetails/__tests__/Labware.test.tsx @@ -1,32 +1,31 @@ -import type * as React from 'react' import { vi, it, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' -import { renderWithProviders } from '/app/__testing-utils__' -import { i18n } from '/app/i18n' -import { useRequiredProtocolLabware } from '/app/resources/protocols' -import { Labware } from '../Labware' - +import { screen } from '@testing-library/react' import { fixtureTiprack10ul, fixtureTiprack300ul, fixture96Plate, } from '@opentrons/shared-data' +import { renderWithProviders } from '/app/__testing-utils__' +import { i18n } from '/app/i18n' +import { useRequiredProtocolLabware } from '/app/resources/protocols' +import { Labware } from '../Labware' +import type { ComponentProps } from 'react' import type { LabwareDefinition2 } from '@opentrons/shared-data' -import { screen } from '@testing-library/react' vi.mock('/app/resources/protocols') const MOCK_PROTOCOL_ID = 'mock_protocol_id' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('Labware', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { transferId: MOCK_PROTOCOL_ID, diff --git a/app/src/pages/ODD/RobotDashboard/__tests__/WelcomeModal.test.tsx b/app/src/pages/ODD/RobotDashboard/__tests__/WelcomeModal.test.tsx index 7fc9bf46ea2..8258f29f281 100644 --- a/app/src/pages/ODD/RobotDashboard/__tests__/WelcomeModal.test.tsx +++ b/app/src/pages/ODD/RobotDashboard/__tests__/WelcomeModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, describe, expect, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' @@ -9,6 +8,7 @@ import { i18n } from '/app/i18n' import { updateConfigValue } from '/app/redux/config' import { WelcomeModal } from '../WelcomeModal' +import type { ComponentProps } from 'react' import type { SetStatusBarCreateCommand } from '@opentrons/shared-data' vi.mock('/app/redux/config') @@ -17,14 +17,14 @@ vi.mock('@opentrons/react-api-client') const mockFunc = vi.fn() const WELCOME_MODAL_IMAGE_NAME = 'welcome_dashboard_modal.png' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('WelcomeModal', () => { - let props: React.ComponentProps + let props: ComponentProps let mockCreateLiveCommand = vi.fn() beforeEach(() => { diff --git a/app/src/redux-resources/analytics/hooks/__tests__/useProtocolRunAnalyticsData.test.tsx b/app/src/redux-resources/analytics/hooks/__tests__/useProtocolRunAnalyticsData.test.tsx index b99b66c8816..1f0501e0c96 100644 --- a/app/src/redux-resources/analytics/hooks/__tests__/useProtocolRunAnalyticsData.test.tsx +++ b/app/src/redux-resources/analytics/hooks/__tests__/useProtocolRunAnalyticsData.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { renderHook, waitFor } from '@testing-library/react' @@ -14,6 +13,8 @@ import { useStoredProtocolAnalysis } from '/app/resources/analysis' import { useProtocolMetadata } from '/app/resources/protocols' import { formatInterval } from '/app/transformations/commands' import { mockConnectableRobot } from '/app/redux/discovery/__fixtures__' + +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' vi.mock('/app/redux/analytics/hash') @@ -23,7 +24,7 @@ vi.mock('/app/resources/analysis') vi.mock('/app/resources/runs') vi.mock('/app/transformations/commands') -let wrapper: React.FunctionComponent<{ children: React.ReactNode }> +let wrapper: FunctionComponent<{ children: ReactNode }> let store: Store = createStore(vi.fn(), {}) const RUN_ID = '1' diff --git a/app/src/redux-resources/analytics/hooks/__tests__/useRobotAnalyticsData.test.tsx b/app/src/redux-resources/analytics/hooks/__tests__/useRobotAnalyticsData.test.tsx index a781d71c495..bdb7407f843 100644 --- a/app/src/redux-resources/analytics/hooks/__tests__/useRobotAnalyticsData.test.tsx +++ b/app/src/redux-resources/analytics/hooks/__tests__/useRobotAnalyticsData.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { renderHook } from '@testing-library/react' @@ -18,6 +17,7 @@ import { getRobotSerialNumber, } from '/app/redux/discovery' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { DiscoveredRobot } from '/app/redux/discovery/types' import type { AttachedPipettesByMount } from '/app/redux/pipettes/types' @@ -41,7 +41,7 @@ const ATTACHED_PIPETTES = { } const ROBOT_SERIAL_NUMBER = 'OT123' -let wrapper: React.FunctionComponent<{ children: React.ReactNode }> +let wrapper: FunctionComponent<{ children: ReactNode }> let store: Store = createStore(vi.fn(), {}) describe('useRobotAnalyticsData hook', () => { diff --git a/app/src/redux-resources/analytics/hooks/__tests__/useTrackProtocolRunEvent.test.tsx b/app/src/redux-resources/analytics/hooks/__tests__/useTrackProtocolRunEvent.test.tsx index 3172c8d1fbc..b387efa58fd 100644 --- a/app/src/redux-resources/analytics/hooks/__tests__/useTrackProtocolRunEvent.test.tsx +++ b/app/src/redux-resources/analytics/hooks/__tests__/useTrackProtocolRunEvent.test.tsx @@ -1,8 +1,7 @@ -import type * as React from 'react' import { createStore } from 'redux' import { Provider } from 'react-redux' import { QueryClient, QueryClientProvider } from 'react-query' -import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' +import { vi, it, expect, describe, beforeEach } from 'vitest' import { when } from 'vitest-when' import { waitFor, renderHook } from '@testing-library/react' @@ -12,9 +11,11 @@ import { useTrackEvent, ANALYTICS_PROTOCOL_RUN_ACTION, } from '/app/redux/analytics' +import { getAppLanguage } from '/app/redux/config' import { mockConnectableRobot } from '/app/redux/discovery/__fixtures__' import { useRobot } from '/app/redux-resources/robots' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { Mock } from 'vitest' @@ -23,6 +24,7 @@ vi.mock('../useProtocolRunAnalyticsData') vi.mock('/app/redux/discovery') vi.mock('/app/redux/pipettes') vi.mock('/app/redux/analytics') +vi.mock('/app/redux/config') vi.mock('/app/redux/robot-settings') const RUN_ID = 'runId' @@ -31,7 +33,7 @@ const PROTOCOL_PROPERTIES = { protocolType: 'python' } let mockTrackEvent: Mock let mockGetProtocolRunAnalyticsData: Mock -let wrapper: React.FunctionComponent<{ children: React.ReactNode }> +let wrapper: FunctionComponent<{ children: ReactNode }> let store: Store = createStore(vi.fn(), {}) describe('useTrackProtocolRunEvent hook', () => { @@ -55,6 +57,7 @@ describe('useTrackProtocolRunEvent hook', () => { ) vi.mocked(useRobot).mockReturnValue(mockConnectableRobot) vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent) + vi.mocked(getAppLanguage).mockReturnValue('en-US') when(vi.mocked(useProtocolRunAnalyticsData)) .calledWith(RUN_ID, mockConnectableRobot) @@ -63,10 +66,6 @@ describe('useTrackProtocolRunEvent hook', () => { }) }) - afterEach(() => { - vi.resetAllMocks() - }) - it('returns trackProtocolRunEvent function', () => { const { result } = renderHook( () => useTrackProtocolRunEvent(RUN_ID, ROBOT_NAME), @@ -92,7 +91,11 @@ describe('useTrackProtocolRunEvent hook', () => { ) expect(mockTrackEvent).toHaveBeenCalledWith({ name: ANALYTICS_PROTOCOL_RUN_ACTION.START, - properties: PROTOCOL_PROPERTIES, + properties: { + ...PROTOCOL_PROPERTIES, + transactionId: RUN_ID, + appLanguage: 'en-US', + }, }) }) diff --git a/app/src/redux-resources/analytics/hooks/useTrackProtocolRunEvent.ts b/app/src/redux-resources/analytics/hooks/useTrackProtocolRunEvent.ts index 2f9f085fd64..0603994d4b4 100644 --- a/app/src/redux-resources/analytics/hooks/useTrackProtocolRunEvent.ts +++ b/app/src/redux-resources/analytics/hooks/useTrackProtocolRunEvent.ts @@ -1,5 +1,7 @@ +import { useSelector } from 'react-redux' import { useTrackEvent } from '/app/redux/analytics' import { useProtocolRunAnalyticsData } from './useProtocolRunAnalyticsData' +import { getAppLanguage } from '/app/redux/config' import { useRobot } from '/app/redux-resources/robots' interface ProtocolRunAnalyticsEvent { @@ -21,7 +23,7 @@ export function useTrackProtocolRunEvent( runId, robot ) - + const appLanguage = useSelector(getAppLanguage) const trackProtocolRunEvent: TrackProtocolRunEvent = ({ name, properties = {}, @@ -34,6 +36,10 @@ export function useTrackProtocolRunEvent( ...properties, ...protocolRunAnalyticsData, runTime, + // It's sometimes unavoidable (namely on the desktop app) to prevent sending an event multiple times. + // In these circumstances, we need an idempotency key to accurately filter events in Mixpanel. + transactionId: runId, + appLanguage, }, }) }) diff --git a/app/src/redux-resources/config/__tests__/useIsUnboxingFlowOngoing.test.tsx b/app/src/redux-resources/config/__tests__/useIsUnboxingFlowOngoing.test.tsx index d7610096015..cc65ca15100 100644 --- a/app/src/redux-resources/config/__tests__/useIsUnboxingFlowOngoing.test.tsx +++ b/app/src/redux-resources/config/__tests__/useIsUnboxingFlowOngoing.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { renderHook } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import { Provider } from 'react-redux' @@ -7,6 +6,7 @@ import { createStore } from 'redux' import { getIsOnDevice, getOnDeviceDisplaySettings } from '/app/redux/config' import { useIsUnboxingFlowOngoing } from '../useIsUnboxingFlowOngoing' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' @@ -22,7 +22,7 @@ const mockDisplaySettings = { } describe('useIsUnboxingFlowOngoing', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { wrapper = ({ children }) => {children} vi.mocked(getOnDeviceDisplaySettings).mockReturnValue(mockDisplaySettings) diff --git a/app/src/redux-resources/robots/hooks/__tests__/useIsFlex.test.tsx b/app/src/redux-resources/robots/hooks/__tests__/useIsFlex.test.tsx index acb68840dfe..19ca25dff24 100644 --- a/app/src/redux-resources/robots/hooks/__tests__/useIsFlex.test.tsx +++ b/app/src/redux-resources/robots/hooks/__tests__/useIsFlex.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { Provider } from 'react-redux' @@ -9,6 +8,8 @@ import { QueryClient, QueryClientProvider } from 'react-query' import { getRobotModelByName } from '/app/redux/discovery' import { useIsFlex } from '..' + +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' vi.mock('/app/redux/discovery/selectors') @@ -16,7 +17,7 @@ vi.mock('/app/redux/discovery/selectors') const store: Store = createStore(vi.fn(), {}) describe('useIsFlex hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() wrapper = ({ children }) => ( diff --git a/app/src/redux-resources/robots/hooks/__tests__/useIsRobotViewable.test.tsx b/app/src/redux-resources/robots/hooks/__tests__/useIsRobotViewable.test.tsx index 8b659d25656..38e908b8452 100644 --- a/app/src/redux-resources/robots/hooks/__tests__/useIsRobotViewable.test.tsx +++ b/app/src/redux-resources/robots/hooks/__tests__/useIsRobotViewable.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { Provider } from 'react-redux' @@ -13,6 +12,8 @@ import { mockUnreachableRobot, } from '/app/redux/discovery/__fixtures__' import { useIsRobotViewable } from '../useIsRobotViewable' + +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' vi.mock('/app/redux/discovery') @@ -20,7 +21,7 @@ vi.mock('/app/redux/discovery') const store: Store = createStore(vi.fn(), {}) describe('useIsRobotViewable hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() wrapper = ({ children }) => ( diff --git a/app/src/redux-resources/robots/hooks/__tests__/useRobot.test.tsx b/app/src/redux-resources/robots/hooks/__tests__/useRobot.test.tsx index df7e05347dd..f99452c7994 100644 --- a/app/src/redux-resources/robots/hooks/__tests__/useRobot.test.tsx +++ b/app/src/redux-resources/robots/hooks/__tests__/useRobot.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { Provider } from 'react-redux' @@ -11,6 +10,7 @@ import { mockConnectableRobot } from '/app/redux/discovery/__fixtures__' import { useRobot } from '..' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' vi.mock('/app/redux/discovery') @@ -18,7 +18,7 @@ vi.mock('/app/redux/discovery') const store: Store = createStore(vi.fn(), {}) describe('useRobot hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() wrapper = ({ children }) => ( diff --git a/app/src/redux/analytics/__tests__/make-event.test.ts b/app/src/redux/analytics/__tests__/make-event.test.ts index 70506dc162a..8cc56e18950 100644 --- a/app/src/redux/analytics/__tests__/make-event.test.ts +++ b/app/src/redux/analytics/__tests__/make-event.test.ts @@ -1,5 +1,7 @@ import { vi, describe, it, expect, beforeEach } from 'vitest' +import { OT2_ROBOT_TYPE } from '@opentrons/shared-data' + import { makeEvent } from '../make-event' import * as selectors from '../selectors' @@ -49,6 +51,7 @@ describe('analytics events map', () => { name: 'pipetteOffsetCalibrationStarted', properties: { ...action.payload, + robotType: OT2_ROBOT_TYPE, }, }) }) @@ -65,6 +68,7 @@ describe('analytics events map', () => { name: 'tipLengthCalibrationStarted', properties: { ...action.payload, + robotType: OT2_ROBOT_TYPE, }, }) }) @@ -77,6 +81,7 @@ describe('analytics events map', () => { robotName: 'my-robot', sessionId: 'seshid', command: { command: 'calibration.exitSession' }, + robotType: OT2_ROBOT_TYPE, }, } as any vi.mocked(selectors.getAnalyticsSessionExitDetails).mockReturnValue({ @@ -86,7 +91,7 @@ describe('analytics events map', () => { return expect(makeEvent(action, state)).resolves.toEqual({ name: 'my-session-typeExit', - properties: { step: 'session-step' }, + properties: { step: 'session-step', robotType: OT2_ROBOT_TYPE }, }) }) @@ -117,12 +122,11 @@ describe('analytics events map', () => { properties: { pipetteModel: 'my-pipette-model', tipRackDisplayName: 'some display name', + robotType: OT2_ROBOT_TYPE, }, }) }) - }) - describe('events with calibration data', () => { it('analytics:RESOURCE_MONITOR_REPORT -> resourceMonitorReport event', () => { const state = {} as any const action = { diff --git a/app/src/redux/analytics/constants.ts b/app/src/redux/analytics/constants.ts index cde9b0a1d59..aadeb7c6696 100644 --- a/app/src/redux/analytics/constants.ts +++ b/app/src/redux/analytics/constants.ts @@ -103,3 +103,15 @@ export const ANALYTICS_QUICK_TRANSFER_RERUN = 'quickTransferReRunFromSummary' */ export const ANALYTICS_RESOURCE_MONITOR_REPORT: 'analytics:RESOURCE_MONITOR_REPORT' = 'analytics:RESOURCE_MONITOR_REPORT' + +/** + * Internationalization Analytics + */ +export const ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW: 'languageUpdatedOddUnboxingFlow' = + 'languageUpdatedOddUnboxingFlow' +export const ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS: 'languageUpdatedOddSettings' = + 'languageUpdatedOddSettings' +export const ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL: 'languageUpdatedDesktopAppModal' = + 'languageUpdatedDesktopAppModal' +export const ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS: 'languageUpdatedDesktopAppSettings' = + 'languageUpdatedDesktopAppSettings' diff --git a/app/src/redux/analytics/make-event.ts b/app/src/redux/analytics/make-event.ts index bc5c8955104..608915c4112 100644 --- a/app/src/redux/analytics/make-event.ts +++ b/app/src/redux/analytics/make-event.ts @@ -13,6 +13,7 @@ import { getAnalyticsSessionExitDetails, getSessionInstrumentAnalyticsData, } from './selectors' +import { OT2_ROBOT_TYPE } from '@opentrons/shared-data' import type { State, Action } from '../types' import type { AnalyticsEvent } from './types' @@ -180,6 +181,7 @@ export function makeEvent( name: `${sessionDetails.sessionType}Exit`, properties: { step: sessionDetails.step, + robotType: OT2_ROBOT_TYPE, }, } : null @@ -203,6 +205,7 @@ export function makeEvent( 'tiprackDefinition' in commandData ? commandData.tiprackDefinition.metadata.displayName : null, + robotType: OT2_ROBOT_TYPE, }, } : null @@ -234,6 +237,7 @@ export function makeEvent( name: 'pipetteOffsetCalibrationStarted', properties: { ...action.payload, + robotType: OT2_ROBOT_TYPE, }, }) } @@ -243,6 +247,7 @@ export function makeEvent( name: 'tipLengthCalibrationStarted', properties: { ...action.payload, + robotType: OT2_ROBOT_TYPE, }, }) } diff --git a/app/src/redux/analytics/mixpanel.ts b/app/src/redux/analytics/mixpanel.ts index aa5ad5a7893..5304bbbe563 100644 --- a/app/src/redux/analytics/mixpanel.ts +++ b/app/src/redux/analytics/mixpanel.ts @@ -1,9 +1,9 @@ -// mixpanel actions import mixpanel from 'mixpanel-browser' -import { createLogger } from '../../logger' +import { createLogger } from '/app/logger' import { CURRENT_VERSION } from '../shell' +import type { Config as MixpanelConfig } from 'mixpanel-browser' import type { AnalyticsEvent, AnalyticsConfig } from './types' const log = createLogger(new URL('', import.meta.url).pathname) @@ -11,7 +11,7 @@ const log = createLogger(new URL('', import.meta.url).pathname) // pulled in from environment at build time const MIXPANEL_ID = process.env.OT_APP_MIXPANEL_ID -const MIXPANEL_OPTS = { +const MIXPANEL_OPTS: Partial = { // opt out by default opt_out_tracking_by_default: true, // user details are persisted in our own config store @@ -27,9 +27,13 @@ export function initializeMixpanel( isOnDevice: boolean | null ): void { if (MIXPANEL_ID != null) { - initMixpanelInstanceOnce(config) - setMixpanelTracking(config, isOnDevice) - trackEvent({ name: 'appOpen', properties: {} }, config) + try { + initMixpanelInstanceOnce(config) + setMixpanelTracking(config, isOnDevice) + trackEvent({ name: 'appOpen', properties: {} }, config) + } catch (error) { + console.error('Failed to initialize Mixpanel:', error) + } } else { log.warn('MIXPANEL_ID not found; this is a bug if build is production') } @@ -43,11 +47,15 @@ export function trackEvent( log.debug('Trackable event', { event, optedIn }) if (MIXPANEL_ID != null && optedIn) { - if (event.superProperties != null) { - mixpanel.register(event.superProperties) - } - if ('name' in event && event.name != null) { - mixpanel.track(event.name, event.properties) + try { + if (event.superProperties != null) { + mixpanel.register(event.superProperties) + } + if ('name' in event && event.name != null) { + mixpanel.track(event.name, event.properties) + } + } catch (error) { + console.error('Failed to track event:', error) } } } @@ -58,19 +66,23 @@ export function setMixpanelTracking( ): void { if (MIXPANEL_ID != null) { initMixpanelInstanceOnce(config) - if (config.optedIn) { - log.debug('User has opted into analytics; tracking with Mixpanel') - mixpanel.identify(config.appId) - mixpanel.opt_in_tracking() - mixpanel.register({ - appVersion: CURRENT_VERSION, - appId: config.appId, - appMode: Boolean(isOnDevice) ? 'ODD' : 'Desktop', - }) - } else { - log.debug('User has opted out of analytics; stopping tracking') - mixpanel.opt_out_tracking?.() - mixpanel.reset?.() + try { + if (config.optedIn) { + log.debug('User has opted into analytics; tracking with Mixpanel') + mixpanel.identify(config.appId) + mixpanel.opt_in_tracking() + mixpanel.register({ + appVersion: CURRENT_VERSION, + appId: config.appId, + appMode: Boolean(isOnDevice) ? 'ODD' : 'Desktop', + }) + } else { + log.debug('User has opted out of analytics; stopping tracking') + mixpanel.opt_out_tracking() + mixpanel.reset() + } + } catch (error) { + console.error('Failed to set Mixpanel tracking:', error) } } } @@ -82,9 +94,13 @@ function initializeMixpanelInstanceOnce( return function (config: AnalyticsConfig): undefined { if (!hasBeenInitialized && MIXPANEL_ID != null) { - hasBeenInitialized = true - log.debug('Initializing Mixpanel', { config }) - mixpanel.init(MIXPANEL_ID, MIXPANEL_OPTS) + try { + hasBeenInitialized = true + log.debug('Initializing Mixpanel', { config }) + mixpanel.init(MIXPANEL_ID, MIXPANEL_OPTS) + } catch (error) { + console.error('Failed to initialize Mixpanel instance:', error) + } } } } diff --git a/app/src/redux/analytics/selectors.ts b/app/src/redux/analytics/selectors.ts index 4751a8eb99e..962f2c55832 100644 --- a/app/src/redux/analytics/selectors.ts +++ b/app/src/redux/analytics/selectors.ts @@ -1,24 +1,24 @@ import * as Sessions from '../sessions' -import { getViewableRobots, getRobotApiVersion } from '../discovery' +import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' +import { getViewableRobots, getRobotApiVersion } from '../discovery' import { getRobotUpdateVersion, getRobotUpdateRobot, getRobotUpdateSession, getRobotSystemType, } from '../robot-update' - import { getRobotSessionById } from '../sessions/selectors' import type { State } from '../types' - import type { AnalyticsConfig, BuildrootAnalyticsData, AnalyticsSessionExitDetails, SessionInstrumentAnalyticsData, } from './types' +import type { RobotType } from '@opentrons/shared-data' export function getBuildrootAnalyticsData( state: State, @@ -40,12 +40,29 @@ export function getBuildrootAnalyticsData( const currentVersion = getRobotApiVersion(robot) ?? 'unknown' const currentSystem = getRobotSystemType(robot) ?? 'unknown' + const getRobotType = (): RobotType | undefined => { + switch (currentSystem) { + case 'flex': + return FLEX_ROBOT_TYPE + case 'ot2-buildroot': + case 'ot2-balena': + return OT2_ROBOT_TYPE + case 'unknown': + return undefined + default: { + console.error('Unexpected system type: ', currentSystem) + return undefined + } + } + } + return { currentVersion, currentSystem, updateVersion: updateVersion ?? 'unknown', error: session != null && 'error' in session ? session.error : null, robotSerialNumber, + robotType: getRobotType(), } } diff --git a/app/src/redux/analytics/types.ts b/app/src/redux/analytics/types.ts index d27c2955fe2..95b320e88ab 100644 --- a/app/src/redux/analytics/types.ts +++ b/app/src/redux/analytics/types.ts @@ -1,4 +1,4 @@ -import type { PipetteMount as Mount } from '@opentrons/shared-data' +import type { PipetteMount as Mount, RobotType } from '@opentrons/shared-data' import type { CalibrationCheckComparisonsPerCalibration } from '../sessions/types' import type { DeckCalibrationStatus } from '../calibration/types' import type { Config } from '../config/types' @@ -42,6 +42,7 @@ export interface BuildrootAnalyticsData { updateVersion: string error: string | null robotSerialNumber: string | null + robotType: RobotType | undefined } export interface PipetteOffsetCalibrationAnalyticsData { diff --git a/app/src/redux/robot-update/__tests__/hooks.test.tsx b/app/src/redux/robot-update/__tests__/hooks.test.tsx index 7528bf290bc..bf9cee3afbc 100644 --- a/app/src/redux/robot-update/__tests__/hooks.test.tsx +++ b/app/src/redux/robot-update/__tests__/hooks.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, describe, it, expect, beforeEach } from 'vitest' import { createStore } from 'redux' import { renderHook } from '@testing-library/react' @@ -9,11 +8,12 @@ import { i18n } from '/app/i18n' import { useDispatchStartRobotUpdate } from '../hooks' import { startRobotUpdate, clearRobotUpdateSession } from '../actions' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '../../types' describe('useDispatchStartRobotUpdate', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> let store: Store const mockRobotName = 'robotName' const mockSystemFile = 'systemFile' diff --git a/app/src/redux/robot-update/__tests__/selectors.test.ts b/app/src/redux/robot-update/__tests__/selectors.test.ts index e4f3e8f8283..079244b5f32 100644 --- a/app/src/redux/robot-update/__tests__/selectors.test.ts +++ b/app/src/redux/robot-update/__tests__/selectors.test.ts @@ -474,10 +474,10 @@ describe('robot update selectors', () => { expect(result).toMatchObject({ autoUpdateDisabledReason: expect.stringMatching( - /update server is not responding/ + /update_server_unavailable/ ), updateFromFileDisabledReason: expect.stringMatching( - /update server is not responding/ + /update_server_unavailable/ ), }) }) @@ -495,10 +495,10 @@ describe('robot update selectors', () => { expect(result).toMatchObject({ autoUpdateDisabledReason: expect.stringMatching( - /update server is not responding/ + /update_server_unavailable/ ), updateFromFileDisabledReason: expect.stringMatching( - /update server is not responding/ + /update_server_unavailable/ ), }) }) @@ -526,11 +526,9 @@ describe('robot update selectors', () => { const result = selectors.getRobotUpdateDisplayInfo(state, robotName) expect(result).toMatchObject({ - autoUpdateDisabledReason: expect.stringMatching( - /updating a different robot/ - ), + autoUpdateDisabledReason: expect.stringMatching(/other_robot_updating/), updateFromFileDisabledReason: expect.stringMatching( - /updating a different robot/ + /other_robot_updating/ ), }) }) @@ -547,9 +545,7 @@ describe('robot update selectors', () => { expect(result).toEqual({ autoUpdateAction: expect.stringMatching(/unavailable/i), - autoUpdateDisabledReason: expect.stringMatching( - /unable to retrieve update/i - ), + autoUpdateDisabledReason: expect.stringMatching(/no_update_files/i), updateFromFileDisabledReason: null, }) }) diff --git a/app/src/redux/robot-update/selectors.ts b/app/src/redux/robot-update/selectors.ts index 0570a9dd2c8..a2427dfefb5 100644 --- a/app/src/redux/robot-update/selectors.ts +++ b/app/src/redux/robot-update/selectors.ts @@ -19,15 +19,6 @@ import type { RobotUpdateTarget, } from './types' -// TODO(mc, 2020-08-02): i18n -const UPDATE_SERVER_UNAVAILABLE = - "Unable to update because your robot's update server is not responding." -const OTHER_ROBOT_UPDATING = - 'Unable to update because the app is currently updating a different robot.' -const NO_UPDATE_FILES = - 'Unable to retrieve update for this robot. Ensure your computer is connected to the internet and try again later.' -const UNAVAILABLE = 'Update unavailable' - export const getRobotUpdateTarget: ( state: State, robotName: string @@ -198,6 +189,7 @@ export function getRobotUpdateAvailable( : getRobotUpdateType(currentVersion, updateVersion) } +// this util returns i18n keys in device_settings export const getRobotUpdateDisplayInfo: ( state: State, robotName: string @@ -212,21 +204,21 @@ export const getRobotUpdateDisplayInfo: ( (robot, currentUpdatingRobot, updateVersion) => { const robotVersion = robot ? getRobotApiVersion(robot) : null const autoUpdateType = getRobotUpdateType(robotVersion, updateVersion) - const autoUpdateAction = autoUpdateType ?? UNAVAILABLE + const autoUpdateAction = autoUpdateType ?? 'update_unavailable' let autoUpdateDisabledReason = null let updateFromFileDisabledReason = null if (robot?.serverHealthStatus !== HEALTH_STATUS_OK) { - autoUpdateDisabledReason = UPDATE_SERVER_UNAVAILABLE - updateFromFileDisabledReason = UPDATE_SERVER_UNAVAILABLE + autoUpdateDisabledReason = 'update_server_unavailable' + updateFromFileDisabledReason = 'update_server_unavailable' } else if ( currentUpdatingRobot !== null && currentUpdatingRobot.name !== robot?.name ) { - autoUpdateDisabledReason = OTHER_ROBOT_UPDATING - updateFromFileDisabledReason = OTHER_ROBOT_UPDATING + autoUpdateDisabledReason = 'other_robot_updating' + updateFromFileDisabledReason = 'other_robot_updating' } else if (autoUpdateType === null) { - autoUpdateDisabledReason = NO_UPDATE_FILES + autoUpdateDisabledReason = 'no_update_files' } return { diff --git a/app/src/redux/system-info/constants.ts b/app/src/redux/system-info/constants.ts index 1502d4bba07..a79b6ffb7bb 100644 --- a/app/src/redux/system-info/constants.ts +++ b/app/src/redux/system-info/constants.ts @@ -22,12 +22,3 @@ export const USB_DEVICE_REMOVED: 'systemInfo:USB_DEVICE_REMOVED' = export const NETWORK_INTERFACES_CHANGED: 'systemInfo:NETWORK_INTERFACES_CHANGED' = 'systemInfo:NETWORK_INTERFACES_CHANGED' - -// copy -// TODO(mc, 2020-05-11): i18n -export const U2E_DRIVER_OUTDATED_MESSAGE = - 'There is an updated Realtek USB-to-Ethernet adapter driver available for your computer.' -export const U2E_DRIVER_DESCRIPTION = - 'The OT-2 uses this adapter for its USB connection to the Opentrons App.' -export const U2E_DRIVER_OUTDATED_CTA = - "Please update your computer's driver to ensure a reliable connection to your OT-2." diff --git a/app/src/resources/analysis/hooks/__tests__/useStoredProtocolAnalysis.test.tsx b/app/src/resources/analysis/hooks/__tests__/useStoredProtocolAnalysis.test.tsx index 2437ed6e1a7..8bebe10c0e7 100644 --- a/app/src/resources/analysis/hooks/__tests__/useStoredProtocolAnalysis.test.tsx +++ b/app/src/resources/analysis/hooks/__tests__/useStoredProtocolAnalysis.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { QueryClient, QueryClientProvider } from 'react-query' @@ -25,6 +24,7 @@ import { } from '../__fixtures__/storedProtocolAnalysis' import { useNotifyRunQuery } from '/app/resources/runs' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { UseQueryResult } from 'react-query' import type { Protocol, Run } from '@opentrons/api-client' @@ -63,7 +63,7 @@ const PROTOCOL_ID = 'the_protocol_id' const PROTOCOL_KEY = 'the_protocol_key' describe('useStoredProtocolAnalysis hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() wrapper = ({ children }) => ( diff --git a/app/src/resources/calibration/__tests__/useDeckCalibrationStatus.test.tsx b/app/src/resources/calibration/__tests__/useDeckCalibrationStatus.test.tsx index 768c4152ff2..3cf71852f01 100644 --- a/app/src/resources/calibration/__tests__/useDeckCalibrationStatus.test.tsx +++ b/app/src/resources/calibration/__tests__/useDeckCalibrationStatus.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { Provider } from 'react-redux' @@ -12,6 +11,8 @@ import { getDiscoverableRobotByName } from '/app/redux/discovery' import { useDeckCalibrationStatus } from '..' import { mockConnectableRobot } from '/app/redux/discovery/__fixtures__' + +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' vi.mock('@opentrons/react-api-client') @@ -21,7 +22,7 @@ vi.mock('/app/redux/discovery') const store: Store = createStore(vi.fn(), {}) describe('useDeckCalibrationStatus hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() wrapper = ({ children }) => ( diff --git a/app/src/resources/devices/hooks/__tests__/useLights.test.tsx b/app/src/resources/devices/hooks/__tests__/useLights.test.tsx index 7485a61f757..e190902f24b 100644 --- a/app/src/resources/devices/hooks/__tests__/useLights.test.tsx +++ b/app/src/resources/devices/hooks/__tests__/useLights.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { Provider } from 'react-redux' import { createStore } from 'redux' @@ -11,6 +10,7 @@ import { import { useLights } from '../useLights' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { Mock } from 'vitest' @@ -19,7 +19,7 @@ vi.mock('@opentrons/react-api-client') const store: Store = createStore(vi.fn(), {}) describe('useLights hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> let setLights: Mock beforeEach(() => { diff --git a/app/src/resources/instruments/__tests__/useAttachedPipetteCalibrations.test.tsx b/app/src/resources/instruments/__tests__/useAttachedPipetteCalibrations.test.tsx index ef5309a52d5..02316923bd3 100644 --- a/app/src/resources/instruments/__tests__/useAttachedPipetteCalibrations.test.tsx +++ b/app/src/resources/instruments/__tests__/useAttachedPipetteCalibrations.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { vi, it, expect, describe, beforeEach } from 'vitest' import { Provider } from 'react-redux' @@ -21,6 +20,8 @@ import { } from '/app/redux/calibration/tip-length/__fixtures__' import { useAttachedPipetteCalibrations } from '..' + +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' @@ -43,7 +44,7 @@ const PIPETTE_CALIBRATIONS = { } describe('useAttachedPipetteCalibrations hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { const queryClient = new QueryClient() wrapper = ({ children }) => ( diff --git a/app/src/resources/instruments/__tests__/useAttachedPipettes.test.tsx b/app/src/resources/instruments/__tests__/useAttachedPipettes.test.tsx index 35dbd023839..30f9922a1a7 100644 --- a/app/src/resources/instruments/__tests__/useAttachedPipettes.test.tsx +++ b/app/src/resources/instruments/__tests__/useAttachedPipettes.test.tsx @@ -8,7 +8,8 @@ import { pipetteResponseFixtureLeft, pipetteResponseFixtureRight, } from '@opentrons/api-client' -import type * as React from 'react' + +import type { FunctionComponent, ReactNode } from 'react' import type { UseQueryResult } from 'react-query' import type { FetchPipettesResponseBody } from '@opentrons/api-client' import type { PipetteModelSpecs } from '@opentrons/shared-data' @@ -17,7 +18,7 @@ vi.mock('@opentrons/react-api-client') vi.mock('@opentrons/shared-data') describe('useAttachedPipettes hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { vi.mocked(getPipetteModelSpecs).mockReturnValue({ name: 'mockName', diff --git a/app/src/resources/instruments/__tests__/useAttachedPipettesFromInstrumentsQuery.test.ts b/app/src/resources/instruments/__tests__/useAttachedPipettesFromInstrumentsQuery.test.ts index a0ae4757582..cc2bdbdc081 100644 --- a/app/src/resources/instruments/__tests__/useAttachedPipettesFromInstrumentsQuery.test.ts +++ b/app/src/resources/instruments/__tests__/useAttachedPipettesFromInstrumentsQuery.test.ts @@ -7,7 +7,7 @@ import { } from '@opentrons/api-client' import { useIsOEMMode } from '/app/resources/robot-settings/hooks' import { useAttachedPipettesFromInstrumentsQuery } from '..' -import type * as React from 'react' +import type { FunctionComponent, ReactNode } from 'react' vi.mock('@opentrons/react-api-client') vi.mock('/app/resources/robot-settings/hooks') @@ -17,7 +17,7 @@ describe('useAttachedPipettesFromInstrumentsQuery hook', () => { vi.mocked(useIsOEMMode).mockReturnValue(false) }) - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> it('returns attached pipettes', () => { vi.mocked(useInstrumentsQuery).mockReturnValue({ data: { diff --git a/app/src/resources/modules/hooks/__tests__/useAttachedModules.test.tsx b/app/src/resources/modules/hooks/__tests__/useAttachedModules.test.tsx index 49fba0eeaa0..6eec9e0996f 100644 --- a/app/src/resources/modules/hooks/__tests__/useAttachedModules.test.tsx +++ b/app/src/resources/modules/hooks/__tests__/useAttachedModules.test.tsx @@ -4,13 +4,14 @@ import { mockModulesResponse } from '@opentrons/api-client' import { useModulesQuery } from '@opentrons/react-api-client' import { useAttachedModules } from '..' +import type { FunctionComponent, ReactNode } from 'react' import type { UseQueryResult } from 'react-query' import type { Modules } from '@opentrons/api-client' vi.mock('@opentrons/react-api-client') describe('useAttachedModules hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> it('returns attached modules', () => { vi.mocked(useModulesQuery).mockReturnValue({ diff --git a/app/src/resources/networking/hooks/__tests__/useCanDisconnect.test.tsx b/app/src/resources/networking/hooks/__tests__/useCanDisconnect.test.tsx index 3985dc36ca8..d8788bf7ee1 100644 --- a/app/src/resources/networking/hooks/__tests__/useCanDisconnect.test.tsx +++ b/app/src/resources/networking/hooks/__tests__/useCanDisconnect.test.tsx @@ -1,18 +1,18 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { describe, it, expect, vi, beforeEach } from 'vitest' import { createStore } from 'redux' import { Provider } from 'react-redux' -import { SECURITY_WPA_EAP } from '@opentrons/api-client' import { renderHook } from '@testing-library/react' +import { SECURITY_WPA_EAP } from '@opentrons/api-client' import { getRobotApiVersionByName } from '/app/redux/discovery' import { useIsFlex } from '/app/redux-resources/robots' import { useCanDisconnect } from '../useCanDisconnect' import { useWifiList } from '../useWifiList' -import type { WifiNetwork } from '@opentrons/api-client' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' +import type { WifiNetwork } from '@opentrons/api-client' import type { State } from '/app/redux/types' vi.mock('../useWifiList') @@ -21,9 +21,9 @@ vi.mock('/app/redux/discovery') const store: Store = createStore(state => state, {}) -const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ - children, -}) => {children} +const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children }) => ( + {children} +) const mockWifiNetwork: WifiNetwork = { ssid: 'linksys', diff --git a/app/src/resources/networking/hooks/__tests__/useNetworkConnection.test.tsx b/app/src/resources/networking/hooks/__tests__/useNetworkConnection.test.tsx index 2c0e6257e42..9335c7eafea 100644 --- a/app/src/resources/networking/hooks/__tests__/useNetworkConnection.test.tsx +++ b/app/src/resources/networking/hooks/__tests__/useNetworkConnection.test.tsx @@ -1,5 +1,3 @@ -import type * as React from 'react' - import { when } from 'vitest-when' import { describe, it, expect, vi, beforeEach } from 'vitest' import { Provider } from 'react-redux' @@ -14,6 +12,8 @@ import * as Fixtures from '/app/redux/networking/__fixtures__' import { getNetworkInterfaces } from '/app/redux/networking' import { useNetworkConnection } from '../useNetworkConnection' + +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' vi.mock('/app/redux/networking/selectors') @@ -48,7 +48,7 @@ const store: Store = createStore(vi.fn(), {}) // ToDo (kj:0202/2023) USB test cases will be added when USB is out describe('useNetworkConnection', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { wrapper = ({ children }) => ( diff --git a/app/src/resources/protocols/hooks/__tests__/useProtocolMetadata.test.tsx b/app/src/resources/protocols/hooks/__tests__/useProtocolMetadata.test.tsx index 48ced67d25f..bea57c80284 100644 --- a/app/src/resources/protocols/hooks/__tests__/useProtocolMetadata.test.tsx +++ b/app/src/resources/protocols/hooks/__tests__/useProtocolMetadata.test.tsx @@ -1,5 +1,4 @@ // tests for the HostConfig context and hook -import type * as React from 'react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' import { when } from 'vitest-when' import { Provider } from 'react-redux' @@ -8,6 +7,7 @@ import { renderHook } from '@testing-library/react' import { useCurrentProtocol } from '../useCurrentProtocol' import { useProtocolMetadata } from '../useProtocolMetadata' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type { State } from '/app/redux/types' @@ -39,7 +39,7 @@ describe('useProtocolMetadata', () => { }) it('should return author, lastUpdated, method, description, and robot type', () => { - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => {children} const { result } = renderHook(useProtocolMetadata, { wrapper }) diff --git a/app/src/resources/runs/__tests__/useCloneRun.test.tsx b/app/src/resources/runs/__tests__/useCloneRun.test.tsx index 9323bbf8073..cf9de675f67 100644 --- a/app/src/resources/runs/__tests__/useCloneRun.test.tsx +++ b/app/src/resources/runs/__tests__/useCloneRun.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { when } from 'vitest-when' import { renderHook } from '@testing-library/react' import { QueryClient, QueryClientProvider } from 'react-query' @@ -13,6 +12,7 @@ import { import { useCloneRun } from '../useCloneRun' import { useNotifyRunQuery } from '../useNotifyRunQuery' +import type { FunctionComponent, ReactNode } from 'react' import type { HostConfig } from '@opentrons/api-client' vi.mock('@opentrons/react-api-client') @@ -23,7 +23,7 @@ const RUN_ID_NO_RTP: string = 'run_id_no_rtp' const RUN_ID_RTP: string = 'run_id_rtp' describe('useCloneRun hook', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { when(vi.mocked(useHost)).calledWith().thenReturn(HOST_CONFIG) @@ -90,8 +90,8 @@ describe('useCloneRun hook', () => { } as any) const queryClient = new QueryClient() - const clientProvider: React.FunctionComponent<{ - children: React.ReactNode + const clientProvider: FunctionComponent<{ + children: ReactNode }> = ({ children }) => ( {children} ) diff --git a/app/src/resources/runs/__tests__/useLPCDisabledReason.test.tsx b/app/src/resources/runs/__tests__/useLPCDisabledReason.test.tsx index 0be19bc19d3..349804ac58b 100644 --- a/app/src/resources/runs/__tests__/useLPCDisabledReason.test.tsx +++ b/app/src/resources/runs/__tests__/useLPCDisabledReason.test.tsx @@ -1,13 +1,14 @@ -import type * as React from 'react' import { renderHook } from '@testing-library/react' import { Provider } from 'react-redux' import { I18nextProvider } from 'react-i18next' import { createStore } from 'redux' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' + import { getLoadedLabwareDefinitionsByUri, simple_v6 as _uncastedSimpleV6Protocol, } from '@opentrons/shared-data' + import { i18n } from '/app/i18n' import { RUN_ID_1 } from '..//__fixtures__' import { useStoredProtocolAnalysis } from '/app/resources/analysis' @@ -17,6 +18,7 @@ import { useRunCalibrationStatus } from '../useRunCalibrationStatus' import { useMostRecentCompletedAnalysis } from '../useMostRecentCompletedAnalysis' import { useRunHasStarted } from '../useRunHasStarted' +import type { FunctionComponent, ReactNode } from 'react' import type { Store } from 'redux' import type * as SharedData from '@opentrons/shared-data' import type { State } from '/app/redux/types' @@ -38,7 +40,7 @@ const simpleV6Protocol = (_uncastedSimpleV6Protocol as unknown) as SharedData.Pr describe('useLPCDisabledReason', () => { const store: Store = createStore(vi.fn(), {}) - const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ + const wrapper: FunctionComponent<{ children: ReactNode }> = ({ children, }) => ( diff --git a/app/src/resources/runs/__tests__/useModuleCalibrationStatus.test.tsx b/app/src/resources/runs/__tests__/useModuleCalibrationStatus.test.tsx index 5691230dbaf..fef1217b173 100644 --- a/app/src/resources/runs/__tests__/useModuleCalibrationStatus.test.tsx +++ b/app/src/resources/runs/__tests__/useModuleCalibrationStatus.test.tsx @@ -1,4 +1,5 @@ -import type * as React from 'react' +import { Provider } from 'react-redux' +import { createStore } from 'redux' import { QueryClient, QueryClientProvider } from 'react-query' import { renderHook } from '@testing-library/react' import { vi, it, expect, describe, beforeEach, afterEach } from 'vitest' @@ -10,15 +11,13 @@ import { useIsFlex } from '/app/redux-resources/robots' import { mockMagneticModuleGen2 } from '/app/redux/modules/__fixtures__' +import type { FunctionComponent, ReactNode } from 'react' import type { ModuleModel, ModuleType } from '@opentrons/shared-data' -import { Provider } from 'react-redux' -import { createStore } from 'redux' - vi.mock('/app/redux-resources/robots') vi.mock('../useModuleRenderInfoForProtocolById') -let wrapper: React.FunctionComponent<{ children: React.ReactNode }> +let wrapper: FunctionComponent<{ children: ReactNode }> const mockMagneticModuleDefinition = { moduleId: 'someMagneticModule', diff --git a/app/src/resources/runs/__tests__/useRunCalibrationStatus.test.tsx b/app/src/resources/runs/__tests__/useRunCalibrationStatus.test.tsx index d0a81ff3e50..a93a3fe5d7a 100644 --- a/app/src/resources/runs/__tests__/useRunCalibrationStatus.test.tsx +++ b/app/src/resources/runs/__tests__/useRunCalibrationStatus.test.tsx @@ -1,10 +1,11 @@ -import type * as React from 'react' +import { Provider } from 'react-redux' +import { createStore } from 'redux' import { QueryClient, QueryClientProvider } from 'react-query' import { renderHook } from '@testing-library/react' import { vi, it, expect, describe, beforeEach } from 'vitest' import { when } from 'vitest-when' -import { mockTipRackDefinition } from '/app/redux/custom-labware/__fixtures__' +import { mockTipRackDefinition } from '/app/redux/custom-labware/__fixtures__' import { useRunCalibrationStatus, useRunPipetteInfoByMount, @@ -13,9 +14,8 @@ import { import { useDeckCalibrationStatus } from '/app/resources/calibration' import { useIsFlex } from '/app/redux-resources/robots' +import type { FunctionComponent, ReactNode } from 'react' import type { PipetteInfo } from '/app/redux/pipettes' -import { Provider } from 'react-redux' -import { createStore } from 'redux' vi.mock('../useRunPipetteInfoByMount') vi.mock('../useNotifyRunQuery') @@ -23,7 +23,7 @@ vi.mock('/app/resources/calibration') vi.mock('/app/resources/analysis') vi.mock('/app/redux-resources/robots') -let wrapper: React.FunctionComponent<{ children: React.ReactNode }> +let wrapper: FunctionComponent<{ children: ReactNode }> describe('useRunCalibrationStatus hook', () => { beforeEach(() => { diff --git a/app/src/resources/runs/utils.ts b/app/src/resources/runs/utils.ts index d9b6781f4b2..1ab7c205158 100644 --- a/app/src/resources/runs/utils.ts +++ b/app/src/resources/runs/utils.ts @@ -1,6 +1,6 @@ import { format } from 'date-fns' -import type * as React from 'react' +import type { Dispatch, SetStateAction } from 'react' import type { UseMutateAsyncFunction } from 'react-query' import type { CommandData } from '@opentrons/api-client' import type { CreateCommand } from '@opentrons/shared-data' @@ -11,7 +11,7 @@ export const chainRunCommandsRecursive = ( commands: CreateCommand[], createRunCommand: CreateRunCommand, continuePastCommandFailure: boolean = true, - setIsLoading: React.Dispatch> + setIsLoading: Dispatch> ): Promise => { if (commands.length < 1) { return Promise.reject(new Error('no commands to execute')) @@ -57,7 +57,7 @@ export const chainLiveCommandsRecursive = ( CreateLiveCommandMutateParams >, continuePastCommandFailure: boolean = true, - setIsLoading: React.Dispatch> + setIsLoading: Dispatch> ): Promise => { if (commands.length < 1) { return Promise.reject(new Error('no commands to execute')) @@ -100,7 +100,7 @@ export const chainMaintenanceCommandsRecursive = ( commands: CreateCommand[], createMaintenanceCommand: CreateMaintenanceCommand, continuePastCommandFailure: boolean = true, - setIsLoading: React.Dispatch> + setIsLoading: Dispatch> ): Promise => { if (commands.length < 1) { return Promise.reject(new Error('no commands to execute')) diff --git a/app/src/transformations/commands/hooks/__tests__/useMissingProtocolHardware.test.tsx b/app/src/transformations/commands/hooks/__tests__/useMissingProtocolHardware.test.tsx index 99ce33a9de3..54fd39d607f 100644 --- a/app/src/transformations/commands/hooks/__tests__/useMissingProtocolHardware.test.tsx +++ b/app/src/transformations/commands/hooks/__tests__/useMissingProtocolHardware.test.tsx @@ -1,8 +1,7 @@ import omitBy from 'lodash/omitBy' import { vi, it, describe, expect, beforeEach, afterEach } from 'vitest' -import type { UseQueryResult } from 'react-query' import { renderHook } from '@testing-library/react' -import type { Protocol } from '@opentrons/api-client' + import { useProtocolQuery, useProtocolAnalysisAsDocumentQuery, @@ -14,14 +13,19 @@ import { WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE, fixtureTiprack300ul, } from '@opentrons/shared-data' + +import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration/useNotifyDeckConfigurationQuery' +import { useMissingProtocolHardware } from '../useMissingProtocolHardware' +import { mockHeaterShaker } from '/app/redux/modules/__fixtures__' + +import type { FunctionComponent, ReactNode } from 'react' +import type { UseQueryResult } from 'react-query' +import type { Protocol } from '@opentrons/api-client' import type { CompletedProtocolAnalysis, DeckConfiguration, LabwareDefinition2, } from '@opentrons/shared-data' -import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration/useNotifyDeckConfigurationQuery' -import { useMissingProtocolHardware } from '../useMissingProtocolHardware' -import { mockHeaterShaker } from '/app/redux/modules/__fixtures__' vi.mock('@opentrons/react-api-client') vi.mock('/app/resources/deck_configuration/useNotifyDeckConfigurationQuery') @@ -161,7 +165,7 @@ const PROTOCOL_ANALYSIS = { runTimeParameters: mockRTPData, } as any describe.only('useMissingProtocolHardware', () => { - let wrapper: React.FunctionComponent<{ children: React.ReactNode }> + let wrapper: FunctionComponent<{ children: ReactNode }> beforeEach(() => { vi.mocked(useInstrumentsQuery).mockReturnValue({ data: { data: [] }, diff --git a/app/tsconfig.json b/app/tsconfig.json index 92921aa2b1c..1a24f98b959 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -35,7 +35,7 @@ "outDir": "lib", "paths": { "/app/*": ["./src/*"] - } + }, }, "include": ["typings", "src"], "exclude": ["**/*.stories.tsx"] diff --git a/app/vite.config.mts b/app/vite.config.mts index f10fedf4f7e..4828283b107 100644 --- a/app/vite.config.mts +++ b/app/vite.config.mts @@ -19,6 +19,7 @@ export default defineConfig( build: { // Relative to the root outDir: 'dist', + sourcemap: true }, plugins: [ react({ diff --git a/components/src/controls/LabeledSelect.tsx b/components/src/controls/LabeledSelect.tsx deleted file mode 100644 index 6a99b5eab2c..00000000000 --- a/components/src/controls/LabeledSelect.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type * as React from 'react' - -import { DropdownField } from '../forms' -import { LabeledControl } from './LabeledControl' -import styles from './styles.module.css' - -import type { DropdownFieldProps } from '../forms' - -export interface LabeledSelectProps extends DropdownFieldProps { - label: string - children: React.ReactNode - /** optional data test id for the container */ - 'data-test'?: string -} - -export function LabeledSelect(props: LabeledSelectProps): JSX.Element { - const { label, value, options, onChange } = props - - return ( - - } - > - {props.children} - - ) -} diff --git a/components/src/controls/index.ts b/components/src/controls/index.ts index abb5b52f640..f1e416267cc 100644 --- a/components/src/controls/index.ts +++ b/components/src/controls/index.ts @@ -5,6 +5,5 @@ export * from './ToggleButton' export * from './LabeledToggle' export * from './LabeledButton' -export * from './LabeledSelect' export * from './LabeledCheckbox' export * from './LabeledRadioGroup' diff --git a/components/src/forms/DropdownField.stories.tsx b/components/src/forms/DropdownField.stories.tsx deleted file mode 100644 index fbb849caa9e..00000000000 --- a/components/src/forms/DropdownField.stories.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import * as React from 'react' - -import { DropdownField as DropdownFieldComponent } from './DropdownField' - -import type { Story, Meta } from '@storybook/react' - -export default { - title: 'Library/Molecules/Forms/Dropdown Field', - argTypes: { onChange: { action: 'clicked' } }, -} as Meta - -const Template: Story< - React.ComponentProps -> = args => { - const [selectedValue, setSelectedValue] = React.useState(null) - return ( - { - setSelectedValue(e.target.value) - args.onChange(e) - }} - value={selectedValue} - /> - ) -} -export const DropdownField = Template.bind({}) -DropdownField.parameters = { - docs: { - description: { - component: - '`DropdownField` is similar to `SelectField`, but more normal. It uses a ` element */ - id?: string - /** name of field in form */ - name?: string - /** Array of {name, value} data */ - options: Options - /** classes to apply */ - className?: string - /** optional caption. hidden when `error` is given */ - caption?: string - /** if included, DropdownField will use error style and display error instead of caption */ - error?: string | null | undefined - /** dropdown is disabled if value is true */ - disabled?: boolean - /** html tabindex property */ - tabIndex?: number - /** automatically focus field on render */ - autoFocus?: boolean - /** if true, render indeterminate unselectable option */ - isIndeterminate?: boolean -} - -const BLANK_OPTION: LegacyDropdownOption = { name: '', value: '' } -const INDETERMINATE_OPTION: LegacyDropdownOption = { - name: '-', - value: '', - disabled: true, -} - -export function DropdownField(props: DropdownFieldProps): JSX.Element { - let options = [] - // add in disabled, unselectable "-" mixed option when isIndeterminate is true - // add in "blank" option if there is no `value`, unless `options` already has a blank option - if (props.isIndeterminate) { - options = [INDETERMINATE_OPTION, ...props.options] - } else if (props.value || props.options.some(opt => opt.value === '')) { - options = props.options - } else { - options = [BLANK_OPTION, ...props.options] - } - - const error = props.error != null - const className = cx(props.className, { - [styles.error]: error, - [styles.dropdown_disabled]: props.disabled, - }) - - return ( -
        -
        - - -
        - -
        -
        -
        - {error ? props.error : props.caption} -
        -
        - ) -} diff --git a/components/src/forms/__tests__/DropdownField.test.tsx b/components/src/forms/__tests__/DropdownField.test.tsx deleted file mode 100644 index 7e39375e08e..00000000000 --- a/components/src/forms/__tests__/DropdownField.test.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { describe, it } from 'vitest' - -describe('DropdownField', () => { - it.todo('replace deprecated enzyme test') -}) diff --git a/components/src/forms/index.ts b/components/src/forms/index.ts index c8cd6e2b0f0..ba463a0892f 100644 --- a/components/src/forms/index.ts +++ b/components/src/forms/index.ts @@ -1,5 +1,4 @@ export * from './DeprecatedCheckboxField' -export * from './DropdownField' export * from './FormGroup' export * from './LegacyInputField' export * from './RadioGroup' diff --git a/components/src/hardware-sim/BaseDeck/WasteChuteFixture.tsx b/components/src/hardware-sim/BaseDeck/WasteChuteFixture.tsx index 2253e0f8726..cc977a4e8b6 100644 --- a/components/src/hardware-sim/BaseDeck/WasteChuteFixture.tsx +++ b/components/src/hardware-sim/BaseDeck/WasteChuteFixture.tsx @@ -8,6 +8,7 @@ import { JUSTIFY_CENTER, TEXT_ALIGN_CENTER, } from '../../styles' +import { DeckLabelSet } from '../../organisms' import { SPACING, TYPOGRAPHY } from '../../ui-style-constants' import { COLORS } from '../../helix-design-system' import { RobotCoordsForeignObject } from '../Deck/RobotCoordsForeignObject' @@ -17,7 +18,13 @@ import type { DeckDefinition, ModuleType, } from '@opentrons/shared-data' +import type { DeckLabelProps } from '../../molecules' +const WASTE_CHUTE_WIDTH = 130 +const WASTE_CHUTE_HEIGHT = 138 +const WASTE_CHUTE_X = 322 +const WASTE_CHUTE_Y = -51 +const TAG_HEIGHT = 28 interface WasteChuteFixtureProps extends React.SVGProps { cutoutId: typeof WASTE_CHUTE_CUTOUT deckDefinition: DeckDefinition @@ -25,6 +32,10 @@ interface WasteChuteFixtureProps extends React.SVGProps { fixtureBaseColor?: React.SVGProps['fill'] wasteChuteColor?: string showExtensions?: boolean + /** optional prop to highlight the border of the wasteChute */ + showHighlight?: boolean + /** optional tag info to display a tag below the waste */ + tagInfo?: DeckLabelProps[] } export function WasteChuteFixture( @@ -35,6 +46,8 @@ export function WasteChuteFixture( deckDefinition, fixtureBaseColor = COLORS.grey35, wasteChuteColor = COLORS.grey50, + showHighlight, + tagInfo, ...restProps } = props @@ -64,6 +77,8 @@ export function WasteChuteFixture( ) @@ -72,43 +87,57 @@ export function WasteChuteFixture( interface WasteChuteProps { wasteIconColor: string backgroundColor: string + showHighlight?: boolean + tagInfo?: DeckLabelProps[] } /** * a deck map foreign object representing the physical location of the waste chute connected to the deck */ export function WasteChute(props: WasteChuteProps): JSX.Element { - const { wasteIconColor, backgroundColor } = props + const { wasteIconColor, backgroundColor, showHighlight, tagInfo } = props return ( - - + - - - Waste chute - - - + + + Waste chute + +
        + + {tagInfo != null && tagInfo.length > 0 ? ( + + ) : null} + ) } diff --git a/components/src/hardware-sim/BaseDeck/WasteChuteStagingAreaFixture.tsx b/components/src/hardware-sim/BaseDeck/WasteChuteStagingAreaFixture.tsx index 0034439ce12..17539777257 100644 --- a/components/src/hardware-sim/BaseDeck/WasteChuteStagingAreaFixture.tsx +++ b/components/src/hardware-sim/BaseDeck/WasteChuteStagingAreaFixture.tsx @@ -8,6 +8,7 @@ import { SlotClip } from './SlotClip' import { WasteChute } from './WasteChuteFixture' import type { DeckDefinition, ModuleType } from '@opentrons/shared-data' +import type { DeckLabelProps } from '../../molecules' interface WasteChuteStagingAreaFixtureProps extends React.SVGProps { @@ -18,6 +19,8 @@ interface WasteChuteStagingAreaFixtureProps slotClipColor?: React.SVGProps['stroke'] wasteChuteColor?: string showExtensions?: boolean + showHighlight?: boolean + tagInfo?: DeckLabelProps[] } export function WasteChuteStagingAreaFixture( @@ -29,6 +32,8 @@ export function WasteChuteStagingAreaFixture( fixtureBaseColor = COLORS.grey35, slotClipColor = COLORS.grey60, wasteChuteColor = COLORS.grey50, + showHighlight, + tagInfo, ...restProps } = props @@ -62,6 +67,8 @@ export function WasteChuteStagingAreaFixture( ) diff --git a/components/src/hardware-sim/Deck/DeckFromLayers.tsx b/components/src/hardware-sim/Deck/DeckFromLayers.tsx index badd7e80ca1..14d5b5337f4 100644 --- a/components/src/hardware-sim/Deck/DeckFromLayers.tsx +++ b/components/src/hardware-sim/Deck/DeckFromLayers.tsx @@ -15,6 +15,8 @@ import { ALL_OT2_DECK_LAYERS } from './constants' import type { RobotType } from '@opentrons/shared-data' +export * from './OT2Layers' + export interface DeckFromLayersProps { robotType: RobotType layerBlocklist: string[] diff --git a/components/src/hardware-sim/Deck/FlexTrash.tsx b/components/src/hardware-sim/Deck/FlexTrash.tsx index 9b3c4c9fef9..00ae3fb2a1b 100644 --- a/components/src/hardware-sim/Deck/FlexTrash.tsx +++ b/components/src/hardware-sim/Deck/FlexTrash.tsx @@ -4,6 +4,7 @@ import { opentrons1Trash3200MlFixedV1 as trashLabwareDef, } from '@opentrons/shared-data' import { Icon } from '../../icons' +import { DeckLabelSet } from '../../organisms' import { Flex, Text } from '../../primitives' import { ALIGN_CENTER, JUSTIFY_CENTER } from '../../styles' import { SPACING, TYPOGRAPHY } from '../../ui-style-constants' @@ -11,6 +12,7 @@ import { COLORS, BORDERS } from '../../helix-design-system' import { RobotCoordsForeignObject } from './RobotCoordsForeignObject' import type { RobotType } from '@opentrons/shared-data' +import type { DeckLabelProps } from '../../molecules' // only allow edge cutout locations (columns 1 and 3) export type TrashCutoutId = @@ -23,11 +25,16 @@ export type TrashCutoutId = | 'cutoutC3' | 'cutoutD3' +const HEIGHT_OF_TAG = 28 interface FlexTrashProps { robotType: RobotType trashIconColor: string backgroundColor: string trashCutoutId?: TrashCutoutId + /** optional prop to highlight the border of the trashBin */ + showHighlight?: boolean + /** optional tag info to display a tag below the trash */ + tagInfo?: DeckLabelProps[] } /** @@ -40,6 +47,8 @@ export const FlexTrash = ({ trashIconColor, backgroundColor, trashCutoutId, + showHighlight, + tagInfo, }: FlexTrashProps): JSX.Element | null => { // be sure we don't try to render for an OT-2 if (robotType !== FLEX_ROBOT_TYPE) return null @@ -96,6 +105,7 @@ export const FlexTrash = ({ justifyContent={JUSTIFY_CENTER} gridGap={SPACING.spacing8} width="100%" + border={showHighlight ? `3px solid ${COLORS.blue50}` : 'none'} > {rotateDegrees === '180' ? ( + {tagInfo != null && tagInfo.length > 0 ? ( + + ) : null} ) : null } diff --git a/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx b/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx index 171beb0e597..debc81487ca 100644 --- a/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx +++ b/components/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx @@ -191,7 +191,10 @@ export function MoveLabwareOnDeck( loadedLabware, }) ?? offDeckPosition + const shouldReset = usePositionChangeReset(initialPosition, finalPosition) + const springProps = useSpring({ + reset: shouldReset, config: { duration: 1000, easing: easings.easeInOutSine }, from: { ...initialPosition, @@ -245,6 +248,37 @@ export function MoveLabwareOnDeck( ) } +function usePositionChangeReset( + initialPosition: { x: number; y: number }, + finalPosition: { x: number; y: number } +): boolean { + const [shouldReset, setShouldReset] = React.useState(false) + + React.useLayoutEffect(() => { + if (shouldReset) { + setShouldReset(false) + return + } + + const isNewPosition = + previousInitialRef.current?.x !== initialPosition.x || + previousInitialRef.current?.y !== initialPosition.y || + previousFinalRef.current?.x !== finalPosition.x || + previousFinalRef.current?.y !== finalPosition.y + + if (isNewPosition) { + setShouldReset(true) + } + + previousInitialRef.current = initialPosition + previousFinalRef.current = finalPosition + }, [initialPosition, finalPosition]) + + const previousInitialRef = React.useRef(initialPosition) + const previousFinalRef = React.useRef(finalPosition) + + return shouldReset +} /** * These animated components needs to be split out because react-spring and styled-components don't play nice * @see https://github.com/pmndrs/react-spring/issues/1515 */ diff --git a/components/src/hardware-sim/Deck/OT2Layers.tsx b/components/src/hardware-sim/Deck/OT2Layers.tsx index fe103ded268..cc5b3143aa1 100644 --- a/components/src/hardware-sim/Deck/OT2Layers.tsx +++ b/components/src/hardware-sim/Deck/OT2Layers.tsx @@ -1,3 +1,5 @@ +import { COLORS } from '../../helix-design-system' + export function FixedBase(): JSX.Element { return ( @@ -14,15 +16,20 @@ export function FixedBase(): JSX.Element { ) } -export function FixedTrash(): JSX.Element { +interface FixedTrashProps { + highlight?: boolean +} +export function FixedTrash(props: FixedTrashProps): JSX.Element { + const { highlight = false } = props return ( diff --git a/components/src/hardware-sim/DeckConfigurator/index.tsx b/components/src/hardware-sim/DeckConfigurator/index.tsx index 4a03ff6c866..65bb6968a45 100644 --- a/components/src/hardware-sim/DeckConfigurator/index.tsx +++ b/components/src/hardware-sim/DeckConfigurator/index.tsx @@ -38,6 +38,8 @@ import { MagneticBlockFixture } from './MagneticBlockFixture' import { ThermocyclerFixture } from './ThermocyclerFixture' import { AbsorbanceReaderFixture } from './AbsorbanceReaderFixture' +export * from './constants' + interface DeckConfiguratorProps { deckConfig: DeckConfiguration handleClickAdd: (cutoutId: CutoutId) => void diff --git a/components/src/modals/Modal.tsx b/components/src/modals/Modal.tsx index 7be1ca06340..2a622ad083f 100644 --- a/components/src/modals/Modal.tsx +++ b/components/src/modals/Modal.tsx @@ -24,6 +24,7 @@ export interface ModalProps extends StyleProps { zIndexOverlay?: number showOverlay?: boolean position?: Position + hasHeader?: boolean } /** @@ -43,6 +44,7 @@ export const Modal = (props: ModalProps): JSX.Element => { zIndexOverlay, position, showOverlay, + hasHeader = true, ...styleProps } = props @@ -83,7 +85,7 @@ export const Modal = (props: ModalProps): JSX.Element => { showOverlay={showOverlay} zIndexOverlay={zIndexOverlay} width={styleProps.width ?? '31.25rem'} - header={modalHeader} + header={hasHeader ? modalHeader : undefined} onOutsideClick={closeOnOutsideClick ?? false ? onClose : undefined} // center within viewport aside from nav marginLeft={styleProps.marginLeft ?? '5.656rem'} diff --git a/components/src/molecules/DeckLabel/__tests__/DeckLabel.test.tsx b/components/src/molecules/DeckLabel/__tests__/DeckLabel.test.tsx index 088536e706a..f041585b1cd 100644 --- a/components/src/molecules/DeckLabel/__tests__/DeckLabel.test.tsx +++ b/components/src/molecules/DeckLabel/__tests__/DeckLabel.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { screen } from '@testing-library/react' import { describe, it, beforeEach, expect } from 'vitest' @@ -20,6 +19,7 @@ describe('DeckLabel', () => { text: 'mock DeckLabel text', isSelected: false, isLast: true, + isZoomed: true, } }) diff --git a/components/src/molecules/DeckLabel/index.tsx b/components/src/molecules/DeckLabel/index.tsx index d3a8c02b975..67be739c7ab 100644 --- a/components/src/molecules/DeckLabel/index.tsx +++ b/components/src/molecules/DeckLabel/index.tsx @@ -11,6 +11,7 @@ import type { FlattenSimpleInterpolation } from 'styled-components' import type { ModuleModel } from '@opentrons/shared-data' export interface DeckLabelProps { + isZoomed: boolean text: string isSelected: boolean moduleModel?: ModuleModel @@ -26,6 +27,7 @@ export function DeckLabel({ moduleModel, maxWidth = FLEX_MAX_CONTENT, isLast = false, + isZoomed, }: DeckLabelProps): JSX.Element { const DECK_LABEL_BASE_STYLE = ( labelBorderRadius?: string @@ -59,7 +61,7 @@ export function DeckLabel({ return ( - {moduleModel != null ? ( + {moduleModel != null && isZoomed ? ( ) : null} diff --git a/components/src/molecules/DropdownMenu/index.tsx b/components/src/molecules/DropdownMenu/index.tsx index acfba246dc2..3b692c0b3dc 100644 --- a/components/src/molecules/DropdownMenu/index.tsx +++ b/components/src/molecules/DropdownMenu/index.tsx @@ -24,18 +24,17 @@ import { MenuItem } from '../../atoms/MenuList/MenuItem' import { Tooltip } from '../../atoms/Tooltip' import { StyledText } from '../../atoms/StyledText' import { LiquidIcon } from '../LiquidIcon' - -/** this is the max height to display 10 items */ -const MAX_HEIGHT = 316 - -/** this is for adjustment variable for the case that the space of the bottom and the space of the top are very close */ -const HEIGHT_ADJUSTMENT = 100 +import { DeckInfoLabel } from '../DeckInfoLabel' export interface DropdownOption { name: string value: string /** optional dropdown option for adding the liquid color icon */ liquidColor?: string + /** optional dropdown option for adding the deck label */ + deckLabel?: string + /** subtext below the name */ + subtext?: string disabled?: boolean tooltipText?: string } @@ -71,6 +70,8 @@ export interface DropdownMenuProps { disabled?: boolean /** optional placement of the menu */ menuPlacement?: 'auto' | 'top' | 'bottom' + onEnter?: (id: string) => void + onExit?: () => void } // TODO: (smb: 4/15/22) refactor this to use html select for accessibility @@ -90,6 +91,8 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element { disabled = false, onFocus, onBlur, + onEnter, + onExit, menuPlacement = 'auto', } = props const [targetProps, tooltipProps] = useHoverTooltip() @@ -115,34 +118,34 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element { const handlePositionCalculation = (): void => { const dropdownRect = dropDownMenuWrapperRef.current?.getBoundingClientRect() - if (dropdownRect != null) { - const parentElement = dropDownMenuWrapperRef?.current?.parentElement - const grandParentElement = parentElement?.parentElement?.parentElement - let availableHeight = window.innerHeight - let scrollOffset = 0 + if (!dropdownRect) return + + const parentElement = dropDownMenuWrapperRef.current?.parentElement + const grandParentElement = parentElement?.parentElement?.parentElement + + let availableHeight = window.innerHeight + let scrollOffset = 0 - if (grandParentElement != null) { - const grandParentRect = grandParentElement.getBoundingClientRect() - availableHeight = grandParentRect.bottom - grandParentRect.top - scrollOffset = grandParentRect.top - } else if (parentElement != null) { - const parentRect = parentElement.getBoundingClientRect() - availableHeight = parentRect.bottom - parentRect.top - scrollOffset = parentRect.top - } + if (grandParentElement) { + const grandParentRect = grandParentElement.getBoundingClientRect() + availableHeight = grandParentRect.bottom - grandParentRect.top + scrollOffset = grandParentRect.top + } else if (parentElement) { + const parentRect = parentElement.getBoundingClientRect() + availableHeight = parentRect.bottom - parentRect.top + scrollOffset = parentRect.top + } + + const dropdownHeight = filterOptions.length * 34 + 10 // note (kk:2024/12/06) need to modify the value since design uses different height in desktop and pd + const dropdownBottom = dropdownRect.bottom + dropdownHeight - scrollOffset - const downSpace = - filterOptions.length + 1 > 10 - ? MAX_HEIGHT - : (filterOptions.length + 1) * 34 - const dropdownBottom = dropdownRect.bottom + downSpace - scrollOffset + const fitsBelow = dropdownBottom <= availableHeight + const fitsAbove = dropdownRect.top - dropdownHeight >= scrollOffset - setDropdownPosition( - dropdownBottom > availableHeight && - Math.abs(dropdownBottom - availableHeight) > HEIGHT_ADJUSTMENT - ? 'top' - : 'bottom' - ) + if (menuPlacement === 'auto') { + setDropdownPosition(fitsBelow ? 'bottom' : fitsAbove ? 'top' : 'bottom') + } else { + setDropdownPosition(menuPlacement) } } @@ -250,7 +253,11 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element { {currentOption.liquidColor != null ? ( ) : null} + {currentOption.deckLabel != null ? ( + + ) : null} onEnter?.(option.value)} + onMouseLeave={onExit} > ) : null} - {option.name} + {option.deckLabel != null ? ( + + ) : null} + + + {option.name} + + + {option.subtext} + + {option.tooltipText != null ? ( diff --git a/components/src/molecules/Tabs/index.tsx b/components/src/molecules/Tabs/index.tsx index d9e6a7b349a..70811a3dd2a 100644 --- a/components/src/molecules/Tabs/index.tsx +++ b/components/src/molecules/Tabs/index.tsx @@ -1,8 +1,10 @@ import { css } from 'styled-components' -import { TYPOGRAPHY, SPACING, RESPONSIVENESS } from '../../ui-style-constants' +import { Tooltip } from '../../atoms' import { COLORS, BORDERS } from '../../helix-design-system' -import { POSITION_RELATIVE, DIRECTION_ROW } from '../../styles' import { Btn, Flex } from '../../primitives' +import { POSITION_RELATIVE, DIRECTION_ROW } from '../../styles' +import { useHoverTooltip } from '../../tooltips' +import { TYPOGRAPHY, SPACING, RESPONSIVENESS } from '../../ui-style-constants' const DEFAULT_TAB_STYLE = css` ${TYPOGRAPHY.pSemiBold} @@ -65,6 +67,7 @@ export interface TabProps { onClick: () => void isActive?: boolean disabled?: boolean + disabledReasonForTooltip?: string } export interface TabsProps { @@ -77,18 +80,36 @@ export function Tabs(props: TabsProps): JSX.Element { return ( {tabs.map((tab, index) => ( - { - tab.onClick() - }} - css={tab.isActive === true ? CURRENT_TAB_STYLE : DEFAULT_TAB_STYLE} - disabled={tab.disabled} - > - {tab.text} - + ))} ) } + +function Tab(props: TabProps): JSX.Element { + const { + text, + onClick, + isActive, + disabled = false, + disabledReasonForTooltip, + } = props + const [targetProps, tooltipProps] = useHoverTooltip() + return ( + <> + + {text} + + {disabled && disabledReasonForTooltip != null ? ( + + {disabledReasonForTooltip} + + ) : null} + + ) +} diff --git a/components/src/organisms/DeckLabelSet/__tests__/DeckLabelSet.test.tsx b/components/src/organisms/DeckLabelSet/__tests__/DeckLabelSet.test.tsx index fbb6c1b274d..359df2598de 100644 --- a/components/src/organisms/DeckLabelSet/__tests__/DeckLabelSet.test.tsx +++ b/components/src/organisms/DeckLabelSet/__tests__/DeckLabelSet.test.tsx @@ -14,11 +14,13 @@ const mockDeckLabels = [ text: 'Label', isSelected: false, labelBorderRadius: BORDERS.borderRadius4, + isZoomed: true, }, { text: 'Label', isSelected: false, labelBorderRadius: BORDERS.borderRadius4, + isZoomed: true, }, ] diff --git a/components/src/organisms/DeckLabelSet/index.tsx b/components/src/organisms/DeckLabelSet/index.tsx index 95ff6d2f2f3..e784955b89c 100644 --- a/components/src/organisms/DeckLabelSet/index.tsx +++ b/components/src/organisms/DeckLabelSet/index.tsx @@ -15,17 +15,31 @@ interface DeckLabelSetProps { y: number width: number height: number + invert?: boolean } const DeckLabelSetComponent = ( props: DeckLabelSetProps, ref: React.ForwardedRef ): JSX.Element => { - const { deckLabels, x, y, width, height } = props + const { deckLabels, x, y, width, height, invert = false } = props return ( - - + + 0 ? deckLabels[0].isZoomed : true} + /> {deckLabels.length > 0 ? deckLabels.map((deckLabel, index) => ( @@ -46,9 +60,14 @@ export const DeckLabelSet = React.forwardRef( DeckLabelSetComponent ) -const StyledBox = styled(Box)` +interface StyledBoxProps { + isZoomed: boolean +} + +const StyledBox = styled(Box)` border-radius: ${BORDERS.borderRadius4}; - border: 1.5px solid ${COLORS.blue50}; + border: ${({ isZoomed }) => + isZoomed ? `1.5px solid ${COLORS.blue50}` : `3px solid ${COLORS.blue50}`}; ` const LabelContainer = styled.div` diff --git a/g-code-testing/Pipfile b/g-code-testing/Pipfile index 0b39a9dd222..2eb3d56782a 100644 --- a/g-code-testing/Pipfile +++ b/g-code-testing/Pipfile @@ -11,7 +11,7 @@ opentrons-shared-data = { editable = true, path = "../shared-data/python" } opentrons_hardware = { editable = true, path = "../hardware" } g-code-testing = { editable = true, path = "." } anyio = "==3.7.1" -pydantic = "==1.10.12" +pydantic = "==2.6.4" # opentrons dependency on linux, spec'd here to force lockfile inclusion # https://github.com/pypa/pipenv/issues/4408#issuecomment-668324177 systemd-python = { version = "==234", markers="sys_platform=='linux'" } diff --git a/g-code-testing/Pipfile.lock b/g-code-testing/Pipfile.lock index c2ea7426d71..760db936cda 100644 --- a/g-code-testing/Pipfile.lock +++ b/g-code-testing/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "214f2b205b35dd2d385ec40836249499ecebefc1696107a12d48c58b13c6353d" + "sha256": "fa448b83f70b6937f46be6aeec3c0562410958165490aa6c5ede4f906364b751" }, "pipfile-spec": 6, "requires": { @@ -23,6 +23,14 @@ ], "version": "==0.3.1" }, + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "anyio": { "hashes": [ "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", @@ -58,11 +66,11 @@ }, "fastapi": { "hashes": [ - "sha256:976df7bab51ac7beda9f68c4513b8c4490b5c1135c72aafd0a5ee4023ec5282e", - "sha256:ac78f717cd80d657bd183f94d33b9bda84aa376a46a9dab513586b8eef1dc6fc" + "sha256:271662daf986da8fa98dc2b7c7f61c4abdfdccfb4786d79ed8b2878f172c6d5f", + "sha256:acb5f941ea8215663283c10018323ba7ea737c571b67fc7e88e9469c7eb1d12e" ], "markers": "python_version >= '3.7'", - "version": "==0.99.1" + "version": "==0.100.0" }, "g-code-testing": { "editable": true, @@ -86,11 +94,19 @@ }, "jsonschema": { "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" + "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7", + "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802" ], - "markers": "python_version >= '3.7'", - "version": "==4.17.3" + "markers": "python_version >= '3.8'", + "version": "==4.22.0" + }, + "jsonschema-specifications": { + "hashes": [ + "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", + "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + ], + "markers": "python_version >= '3.8'", + "version": "==2023.12.1" }, "numpy": { "hashes": [ @@ -156,84 +172,105 @@ }, "pydantic": { "hashes": [ - "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303", - "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe", - "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47", - "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494", - "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33", - "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86", - "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d", - "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c", - "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a", - "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565", - "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb", - "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62", - "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62", - "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0", - "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523", - "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d", - "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405", - "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f", - "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b", - "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718", - "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed", - "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb", - "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5", - "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc", - "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942", - "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe", - "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246", - "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350", - "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303", - "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09", - "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33", - "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8", - "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a", - "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1", - "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6", - "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d" + "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6", + "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.10.12" - }, - "pyrsistent": { - "hashes": [ - "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f", - "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e", - "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958", - "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34", - "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca", - "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d", - "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d", - "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", - "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714", - "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf", - "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee", - "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8", - "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224", - "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d", - "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054", - "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656", - "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7", - "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423", - "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce", - "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e", - "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3", - "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0", - "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f", - "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", - "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce", - "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a", - "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174", - "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86", - "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f", - "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b", - "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98", - "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022" + "markers": "python_version >= '3.8'", + "version": "==2.6.4" + }, + "pydantic-core": { + "hashes": [ + "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a", + "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed", + "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979", + "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff", + "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5", + "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45", + "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340", + "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad", + "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23", + "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6", + "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7", + "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241", + "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda", + "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187", + "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba", + "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c", + "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2", + "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c", + "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132", + "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf", + "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972", + "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db", + "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade", + "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4", + "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8", + "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f", + "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9", + "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48", + "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec", + "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d", + "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9", + "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb", + "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4", + "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89", + "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c", + "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9", + "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da", + "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac", + "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b", + "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf", + "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e", + "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137", + "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1", + "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b", + "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8", + "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e", + "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053", + "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01", + "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe", + "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd", + "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805", + "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183", + "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8", + "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99", + "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820", + "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074", + "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256", + "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8", + "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975", + "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad", + "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e", + "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca", + "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df", + "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b", + "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a", + "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a", + "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721", + "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a", + "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f", + "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2", + "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97", + "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6", + "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed", + "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc", + "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1", + "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe", + "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120", + "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f", + "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a" + ], + "markers": "python_version >= '3.8'", + "version": "==2.16.3" + }, + "pydantic-settings": { + "hashes": [ + "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed", + "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091" ], "markers": "python_version >= '3.8'", - "version": "==0.20.0" + "version": "==2.2.1" }, "pyserial": { "hashes": [ @@ -258,10 +295,123 @@ "markers": "python_version >= '3.7'", "version": "==0.0.6" }, + "referencing": { + "hashes": [ + "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", + "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de" + ], + "markers": "python_version >= '3.8'", + "version": "==0.35.1" + }, "robot-server": { "editable": true, "path": "./../robot-server" }, + "rpds-py": { + "hashes": [ + "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee", + "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc", + "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc", + "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944", + "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20", + "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7", + "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4", + "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6", + "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6", + "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93", + "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633", + "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0", + "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360", + "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8", + "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139", + "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7", + "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a", + "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9", + "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26", + "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724", + "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72", + "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b", + "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09", + "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100", + "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3", + "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261", + "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3", + "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9", + "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b", + "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3", + "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de", + "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d", + "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e", + "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8", + "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff", + "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5", + "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c", + "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e", + "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e", + "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4", + "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8", + "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922", + "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338", + "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d", + "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8", + "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2", + "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72", + "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80", + "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644", + "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae", + "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163", + "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104", + "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d", + "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60", + "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a", + "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d", + "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07", + "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49", + "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10", + "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f", + "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2", + "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8", + "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7", + "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88", + "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65", + "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0", + "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909", + "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8", + "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c", + "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184", + "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397", + "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a", + "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346", + "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590", + "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333", + "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb", + "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74", + "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e", + "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d", + "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa", + "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f", + "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53", + "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1", + "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac", + "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0", + "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd", + "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611", + "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f", + "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c", + "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5", + "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab", + "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc", + "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43", + "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da", + "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac", + "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843", + "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e", + "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89", + "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64" + ], + "markers": "python_version >= '3.8'", + "version": "==0.18.1" + }, "server-utils": { "editable": true, "path": "./../server-utils" @@ -343,11 +493,11 @@ }, "typing-extensions": { "hashes": [ - "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", - "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a" + "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8", + "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594" ], "markers": "python_version >= '3.8'", - "version": "==4.11.0" + "version": "==4.12.0" }, "uvicorn": { "hashes": [ @@ -980,11 +1130,11 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a", - "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f" + "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b", + "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268" ], "markers": "python_version >= '3.8'", - "version": "==0.23.6" + "version": "==0.23.7" }, "pytest-cov": { "hashes": [ @@ -1047,11 +1197,11 @@ }, "typing-extensions": { "hashes": [ - "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", - "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a" + "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8", + "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594" ], "markers": "python_version >= '3.8'", - "version": "==4.11.0" + "version": "==4.12.0" }, "yarl": { "hashes": [ diff --git a/g-code-testing/g_code_test_data/g_code_configuration.py b/g-code-testing/g_code_test_data/g_code_configuration.py index 488496e030a..ff7ccda0fb8 100644 --- a/g-code-testing/g_code_test_data/g_code_configuration.py +++ b/g-code-testing/g_code_test_data/g_code_configuration.py @@ -10,6 +10,7 @@ Union, ) from typing_extensions import ( + Annotated, Final, Literal, ) @@ -22,10 +23,11 @@ from opentrons.protocols.api_support.types import APIVersion from pydantic import ( + StringConstraints, + ConfigDict, BaseModel, Field, - constr, - validator, + model_validator, ) BUCKET_NAME = "g-code-comparison" @@ -41,20 +43,21 @@ def add_mark(self, user_mark: Mark) -> None: class ProtocolGCodeConfirmConfig(BaseModel, SharedFunctionsMixin): path: str - name: Optional[constr(regex=r'^[a-z0-9_]*$')] + name: Optional[Annotated[str, StringConstraints(pattern=r'^[a-z0-9_]*$')]] = None settings: Settings results_dir: ClassVar[str] = "protocols" driver: str = 'protocol' marks: List[Mark] = [pytest.mark.g_code_confirm] - versions: Set[Union[APIVersion,int]] = Field(..., min_items=1) + versions: Set[Union[APIVersion,int]] = Field(..., min_length=1) + model_config = ConfigDict(arbitrary_types_allowed=True) - class Config: - arbitrary_types_allowed = True - - @validator("name", pre=True, always=True) - def name_from_path(cls, name, values) -> str: - derived_name = os.path.splitext(os.path.basename(values["path"]))[0] - return derived_name if name is None else name + @model_validator(mode="after") + def _populate_name_from_path(self) -> "ProtocolGCodeConfirmConfig": + """If `.name` was not given, give it a default based on `.path`.""" + derived_name = os.path.splitext(os.path.basename(self.path))[0] + if self.name is None: + self.name = derived_name + return self def _get_full_path(self, version: APIVersion): return os.path.join( @@ -92,15 +95,13 @@ async def execute(self, version: APIVersion): class HTTPGCodeConfirmConfig(BaseModel, SharedFunctionsMixin): - name: constr(regex=r'^[a-z0-9_]*$') + name: Annotated[str, StringConstraints(pattern=r'^[a-z0-9_]*$')] executable: Callable settings: Settings results_dir: ClassVar[str] = "http" driver: str = 'http' marks: List[Mark] = [pytest.mark.g_code_confirm] - - class Config: - arbitrary_types_allowed = True + model_config = ConfigDict(arbitrary_types_allowed=True) def _get_full_path(self) -> str: return os.path.join(COMPARISON_FILES_FOLDER_PATH, self.get_comparison_file_path()) diff --git a/hardware-testing/Pipfile b/hardware-testing/Pipfile index e3c824f42b2..8182a96a66f 100644 --- a/hardware-testing/Pipfile +++ b/hardware-testing/Pipfile @@ -19,7 +19,7 @@ atomicwrites = "==1.4.1" colorama = "==0.4.4" pytest = "==7.1.1" pytest-cov = "==2.10.1" -mypy = "==0.990" +mypy = "==1.11.0" black = "==22.3.0" flake8 = "~=3.9.0" flake8-annotations = "~=2.6.2" diff --git a/hardware-testing/Pipfile.lock b/hardware-testing/Pipfile.lock index f8dcfd2f7df..5b88acf5b7a 100644 --- a/hardware-testing/Pipfile.lock +++ b/hardware-testing/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "bfa6574dcab4bd350d77135bd8ecefd5e12ad6479664f932a7c0c544ecdf4c47" + "sha256": "b99e29478d0dfb1309ba9c71f0b214bdaf682bb09eac44d9dad8bbbdc54fe12a" }, "pipfile-spec": 6, "requires": { @@ -28,6 +28,14 @@ "markers": "python_version >= '3.8'", "version": "==0.3.1" }, + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "anyio": { "hashes": [ "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", @@ -46,36 +54,34 @@ }, "bcrypt": { "hashes": [ - "sha256:096a15d26ed6ce37a14c1ac1e48119660f21b24cba457f160a4b830f3fe6b5cb", - "sha256:0da52759f7f30e83f1e30a888d9163a81353ef224d82dc58eb5bb52efcabc399", - "sha256:1bb429fedbe0249465cdd85a58e8376f31bb315e484f16e68ca4c786dcc04291", - "sha256:1d84cf6d877918620b687b8fd1bf7781d11e8a0998f576c7aa939776b512b98d", - "sha256:1ee38e858bf5d0287c39b7a1fc59eec64bbf880c7d504d3a06a96c16e14058e7", - "sha256:1ff39b78a52cf03fdf902635e4c81e544714861ba3f0efc56558979dd4f09170", - "sha256:27fe0f57bb5573104b5a6de5e4153c60814c711b29364c10a75a54bb6d7ff48d", - "sha256:3413bd60460f76097ee2e0a493ccebe4a7601918219c02f503984f0a7ee0aebe", - "sha256:3698393a1b1f1fd5714524193849d0c6d524d33523acca37cd28f02899285060", - "sha256:373db9abe198e8e2c70d12b479464e0d5092cc122b20ec504097b5f2297ed184", - "sha256:39e1d30c7233cfc54f5c3f2c825156fe044efdd3e0b9d309512cc514a263ec2a", - "sha256:3bbbfb2734f0e4f37c5136130405332640a1e46e6b23e000eeff2ba8d005da68", - "sha256:3d3a6d28cb2305b43feac298774b997e372e56c7c7afd90a12b3dc49b189151c", - "sha256:5a1e8aa9b28ae28020a3ac4b053117fb51c57a010b9f969603ed885f23841458", - "sha256:61ed14326ee023917ecd093ee6ef422a72f3aec6f07e21ea5f10622b735538a9", - "sha256:655ea221910bcac76ea08aaa76df427ef8625f92e55a8ee44fbf7753dbabb328", - "sha256:762a2c5fb35f89606a9fde5e51392dad0cd1ab7ae64149a8b935fe8d79dd5ed7", - "sha256:77800b7147c9dc905db1cba26abe31e504d8247ac73580b4aa179f98e6608f34", - "sha256:8ac68872c82f1add6a20bd489870c71b00ebacd2e9134a8aa3f98a0052ab4b0e", - "sha256:8d7bb9c42801035e61c109c345a28ed7e84426ae4865511eb82e913df18f58c2", - "sha256:8f6ede91359e5df88d1f5c1ef47428a4420136f3ce97763e31b86dd8280fbdf5", - "sha256:9c1c4ad86351339c5f320ca372dfba6cb6beb25e8efc659bedd918d921956bae", - "sha256:c02d944ca89d9b1922ceb8a46460dd17df1ba37ab66feac4870f6862a1533c00", - "sha256:c52aac18ea1f4a4f65963ea4f9530c306b56ccd0c6f8c8da0c06976e34a6e841", - "sha256:cb2a8ec2bc07d3553ccebf0746bbf3d19426d1c6d1adbd4fa48925f66af7b9e8", - "sha256:cf69eaf5185fd58f268f805b505ce31f9b9fc2d64b376642164e9244540c1221", - "sha256:f4f4acf526fcd1c34e7ce851147deedd4e26e6402369304220250598b26448db" + "sha256:041fa0155c9004eb98a232d54da05c0b41d4b8e66b6fc3cb71b4b3f6144ba837", + "sha256:04e56e3fe8308a88b77e0afd20bec516f74aecf391cdd6e374f15cbed32783d6", + "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17", + "sha256:533e7f3bcf2f07caee7ad98124fab7499cb3333ba2274f7a36cf1daee7409d99", + "sha256:6765386e3ab87f569b276988742039baab087b2cdb01e809d74e74503c2faafe", + "sha256:687cf30e6681eeda39548a93ce9bfbb300e48b4d445a43db4298d2474d2a1e54", + "sha256:76132c176a6d9953cdc83c296aeaed65e1a708485fd55abf163e0d9f8f16ce0e", + "sha256:76d3e352b32f4eeb34703370e370997065d28a561e4a18afe4fef07249cb4396", + "sha256:807261df60a8b1ccd13e6599c779014a362ae4e795f5c59747f60208daddd96d", + "sha256:89df2aea2c43be1e1fa066df5f86c8ce822ab70a30e4c210968669565c0f4685", + "sha256:8ad2f4528cbf0febe80e5a3a57d7a74e6635e41af1ea5675282a33d769fba413", + "sha256:8c458cd103e6c5d1d85cf600e546a639f234964d0228909d8f8dbeebff82d526", + "sha256:8dbd0747208912b1e4ce730c6725cb56c07ac734b3629b60d4398f082ea718ad", + "sha256:909faa1027900f2252a9ca5dfebd25fc0ef1417943824783d1c8418dd7d6df4a", + "sha256:aaa2e285be097050dba798d537b6efd9b698aa88eef52ec98d23dcd6d7cf6fea", + "sha256:adadd36274510a01f33e6dc08f5824b97c9580583bd4487c564fc4617b328005", + "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f", + "sha256:b588af02b89d9fad33e5f98f7838bf590d6d692df7153647724a7f20c186f6bf", + "sha256:b7703ede632dc945ed1172d6f24e9f30f27b1b1a067f32f68bf169c5f08d0425", + "sha256:c6f5fa3775966cca251848d4d5393ab016b3afed251163c1436fefdec3b02c84", + "sha256:cde78d385d5e93ece5479a0a87f73cd6fa26b171c786a884f955e165032b262c", + "sha256:cfdf3d7530c790432046c40cda41dfee8c83e29482e6a604f8930b9930e94139", + "sha256:e158009a54c4c8bc91d5e0da80920d048f918c61a581f0a63e4e93bb556d362f", + "sha256:e84e0e6f8e40a242b11bce56c313edc2be121cec3e0ec2d76fce01f6af33c07c", + "sha256:f85b1ffa09240c89aa2e1ae9f3b1c687104f7b2b9d2098da4e923f1b7082d331" ], "markers": "python_version >= '3.7'", - "version": "==4.2.0" + "version": "==4.2.1" }, "cffi": { "hashes": [ @@ -147,7 +153,7 @@ "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" ], - "markers": "platform_python_implementation != 'PyPy'", + "markers": "python_version >= '3.8'", "version": "==1.17.1" }, "click": { @@ -160,43 +166,43 @@ }, "cryptography": { "hashes": [ - "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", - "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", - "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", - "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", - "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", - "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", - "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", - "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", - "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", - "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", - "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", - "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", - "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", - "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", - "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", - "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", - "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", - "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", - "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", - "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", - "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", - "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", - "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", - "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", - "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", - "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", - "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7" + "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", + "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", + "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", + "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", + "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", + "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", + "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", + "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", + "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", + "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", + "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", + "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", + "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", + "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", + "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", + "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", + "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", + "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", + "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", + "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", + "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", + "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", + "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", + "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", + "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", + "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", + "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" ], - "markers": "python_version >= '3.7'", - "version": "==43.0.3" + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.0" }, "exceptiongroup": { "hashes": [ "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], - "markers": "python_version < '3.11'", + "markers": "python_version >= '3.7'", "version": "==1.2.2" }, "hardware-testing": { @@ -213,11 +219,19 @@ }, "jsonschema": { "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" + "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", + "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566" ], - "markers": "python_version >= '3.7'", - "version": "==4.17.3" + "markers": "python_version >= '3.8'", + "version": "==4.23.0" + }, + "jsonschema-specifications": { + "hashes": [ + "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", + "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.10.1" }, "msgpack": { "hashes": [ @@ -278,7 +292,7 @@ "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d", "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d" ], - "markers": "platform_system != 'Windows'", + "markers": "python_version >= '3.8'", "version": "==1.0.8" }, "numpy": { @@ -370,52 +384,125 @@ }, "pydantic": { "hashes": [ - "sha256:0399094464ae7f28482de22383e667625e38e1516d6b213176df1acdd0c477ea", - "sha256:076c49e24b73d346c45f9282d00dbfc16eef7ae27c970583d499f11110d9e5b0", - "sha256:07d00ca5ef0de65dd274005433ce2bb623730271d495a7d190a91c19c5679d34", - "sha256:0890fbd7fec9e151c7512941243d830b2d6076d5df159a2030952d480ab80a4e", - "sha256:0bfb5b378b78229119d66ced6adac2e933c67a0aa1d0a7adffbe432f3ec14ce4", - "sha256:0d32227ea9a3bf537a2273fd2fdb6d64ab4d9b83acd9e4e09310a777baaabb98", - "sha256:11965f421f7eb026439d4eb7464e9182fe6d69c3d4d416e464a4485d1ba61ab6", - "sha256:1fc8cc264afaf47ae6a9bcbd36c018d0c6b89293835d7fb0e5e1a95898062d59", - "sha256:2206a1752d9fac011e95ca83926a269fb0ef5536f7e053966d058316e24d929f", - "sha256:22a1794e01591884741be56c6fba157c4e99dcc9244beb5a87bd4aa54b84ea8b", - "sha256:4739c206bfb6bb2bdc78dcd40bfcebb2361add4ceac6d170e741bb914e9eff0f", - "sha256:4a5d5b877c7d3d9e17399571a8ab042081d22fe6904416a8b20f8af5909e6c8f", - "sha256:566bebdbe6bc0ac593fa0f67d62febbad9f8be5433f686dc56401ba4aab034e3", - "sha256:570ad0aeaf98b5e33ff41af75aba2ef6604ee25ce0431ecd734a28e74a208555", - "sha256:573254d844f3e64093f72fcd922561d9c5696821ff0900a0db989d8c06ab0c25", - "sha256:5d4320510682d5a6c88766b2a286d03b87bd3562bf8d78c73d63bab04b21e7b4", - "sha256:6d8a38a44bb6a15810084316ed69c854a7c06e0c99c5429f1d664ad52cec353c", - "sha256:6eb56074b11a696e0b66c7181da682e88c00e5cebe6570af8013fcae5e63e186", - "sha256:7e66aa0fa7f8aa9d0a620361834f6eb60d01d3e9cea23ca1a92cda99e6f61dac", - "sha256:7ea24e8614f541d69ea72759ff635df0e612b7dc9d264d43f51364df310081a3", - "sha256:7f31742c95e3f9443b8c6fa07c119623e61d76603be9c0d390bcf7e888acabcb", - "sha256:83ee8c9916689f8e6e7d90161e6663ac876be2efd32f61fdcfa3a15e87d4e413", - "sha256:8b2cf5e26da84f2d2dee3f60a3f1782adedcee785567a19b68d0af7e1534bd1f", - "sha256:945407f4d08cd12485757a281fca0e5b41408606228612f421aa4ea1b63a095d", - "sha256:9c46f58ef2df958ed2ea7437a8be0897d5efe9ee480818405338c7da88186fb3", - "sha256:9d7d48fbc5289efd23982a0d68e973a1f37d49064ccd36d86de4543aff21e086", - "sha256:9f28a81978e936136c44e6a70c65bde7548d87f3807260f73aeffbf76fb94c2f", - "sha256:a415b9e95fa602b10808113967f72b2da8722061265d6af69268c111c254832d", - "sha256:a82746c6d6e91ca17e75f7f333ed41d70fce93af520a8437821dec3ee52dfb10", - "sha256:ad57004e5d73aee36f1e25e4e73a4bc853b473a1c30f652dc8d86b0a987ffce3", - "sha256:c6444368b651a14c2ce2fb22145e1496f7ab23cbdb978590d47c8d34a7bc0289", - "sha256:d216f8d0484d88ab72ab45d699ac669fe031275e3fa6553e3804e69485449fa0", - "sha256:d3449633c207ec3d2d672eedb3edbe753e29bd4e22d2e42a37a2c1406564c20f", - "sha256:d5b5b7c6bafaef90cbb7dafcb225b763edd71d9e22489647ee7df49d6d341890", - "sha256:d7a8a1dd68bac29f08f0a3147de1885f4dccec35d4ea926e6e637fac03cdb4b3", - "sha256:d8d72553d2f3f57ce547de4fa7dc8e3859927784ab2c88343f1fc1360ff17a08", - "sha256:dce355fe7ae53e3090f7f5fa242423c3a7b53260747aa398b4b3aaf8b25f41c3", - "sha256:e351df83d1c9cffa53d4e779009a093be70f1d5c6bb7068584086f6a19042526", - "sha256:ec5c44e6e9eac5128a9bfd21610df3b8c6b17343285cc185105686888dc81206", - "sha256:f5bb81fcfc6d5bff62cd786cbd87480a11d23f16d5376ad2e057c02b3b44df96", - "sha256:fd34012691fbd4e67bdf4accb1f0682342101015b78327eaae3543583fcd451e", - "sha256:fea36c2065b7a1d28c6819cc2e93387b43dd5d3cf5a1e82d8132ee23f36d1f10", - "sha256:ff09600cebe957ecbb4a27496fe34c1d449e7957ed20a202d5029a71a8af2e35" + "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", + "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9" ], - "markers": "python_version >= '3.7'", - "version": "==1.10.19" + "markers": "python_version >= '3.8'", + "version": "==2.10.3" + }, + "pydantic-core": { + "hashes": [ + "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9", + "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b", + "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c", + "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529", + "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc", + "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854", + "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d", + "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278", + "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a", + "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c", + "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f", + "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27", + "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f", + "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac", + "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2", + "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97", + "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a", + "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919", + "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9", + "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4", + "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c", + "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131", + "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5", + "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd", + "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089", + "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107", + "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6", + "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60", + "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf", + "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5", + "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08", + "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05", + "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2", + "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e", + "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c", + "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17", + "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62", + "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23", + "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be", + "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067", + "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02", + "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f", + "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235", + "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840", + "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5", + "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807", + "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16", + "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c", + "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864", + "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e", + "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a", + "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35", + "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737", + "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a", + "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3", + "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52", + "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05", + "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31", + "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89", + "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de", + "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6", + "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36", + "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c", + "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154", + "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb", + "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e", + "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd", + "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3", + "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f", + "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78", + "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960", + "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618", + "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08", + "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4", + "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c", + "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c", + "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330", + "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8", + "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792", + "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025", + "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9", + "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f", + "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01", + "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337", + "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4", + "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f", + "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd", + "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51", + "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab", + "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc", + "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676", + "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381", + "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed", + "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb", + "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967", + "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073", + "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae", + "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c", + "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206", + "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b" + ], + "markers": "python_version >= '3.8'", + "version": "==2.27.1" + }, + "pydantic-settings": { + "hashes": [ + "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87", + "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0" + ], + "markers": "python_version >= '3.8'", + "version": "==2.6.1" }, "pynacl": { "hashes": [ @@ -433,44 +520,6 @@ "markers": "python_version >= '3.6'", "version": "==1.5.0" }, - "pyrsistent": { - "hashes": [ - "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f", - "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e", - "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958", - "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34", - "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca", - "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d", - "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d", - "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", - "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714", - "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf", - "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee", - "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8", - "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224", - "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d", - "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054", - "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656", - "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7", - "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423", - "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce", - "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e", - "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3", - "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0", - "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f", - "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", - "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce", - "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a", - "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174", - "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86", - "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f", - "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b", - "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98", - "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022" - ], - "markers": "python_version >= '3.8'", - "version": "==0.20.0" - }, "pyserial": { "hashes": [ "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", @@ -487,6 +536,14 @@ "markers": "python_version >= '3.7'", "version": "==4.2.2" }, + "python-dotenv": { + "hashes": [ + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" + ], + "markers": "python_version >= '3.8'", + "version": "==1.0.1" + }, "pyusb": { "hashes": [ "sha256:2b4c7cb86dbadf044dfb9d3a4ff69fd217013dbe78a792177a3feb172449ea36", @@ -495,13 +552,130 @@ "markers": "python_full_version >= '3.6.0'", "version": "==1.2.1" }, + "referencing": { + "hashes": [ + "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", + "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de" + ], + "markers": "python_version >= '3.8'", + "version": "==0.35.1" + }, + "rpds-py": { + "hashes": [ + "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518", + "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059", + "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61", + "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5", + "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9", + "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543", + "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2", + "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a", + "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d", + "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56", + "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d", + "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd", + "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b", + "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4", + "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99", + "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d", + "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd", + "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe", + "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1", + "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e", + "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f", + "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3", + "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca", + "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d", + "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e", + "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc", + "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea", + "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38", + "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b", + "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c", + "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff", + "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723", + "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e", + "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493", + "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6", + "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83", + "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091", + "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1", + "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627", + "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1", + "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728", + "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16", + "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c", + "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45", + "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7", + "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a", + "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730", + "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967", + "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25", + "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24", + "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055", + "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d", + "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0", + "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e", + "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7", + "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c", + "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f", + "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd", + "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652", + "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8", + "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11", + "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333", + "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96", + "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64", + "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b", + "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e", + "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c", + "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9", + "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec", + "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb", + "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37", + "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad", + "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9", + "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c", + "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf", + "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4", + "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f", + "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d", + "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09", + "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", + "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566", + "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74", + "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338", + "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15", + "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c", + "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648", + "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84", + "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3", + "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123", + "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520", + "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831", + "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e", + "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf", + "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b", + "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2", + "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3", + "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130", + "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b", + "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de", + "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5", + "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d", + "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00", + "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e" + ], + "markers": "python_version >= '3.9'", + "version": "==0.22.3" + }, "setuptools": { "hashes": [ - "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef", - "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829" + "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", + "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" ], "markers": "python_version >= '3.9'", - "version": "==75.5.0" + "version": "==75.6.0" }, "sniffio": { "hashes": [ @@ -539,79 +713,74 @@ }, "wrapt": { "hashes": [ - "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", - "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", - "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", - "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", - "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", - "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", - "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", - "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", - "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", - "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", - "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", - "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", - "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", - "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", - "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", - "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", - "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", - "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", - "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", - "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", - "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", - "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", - "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", - "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", - "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", - "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", - "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", - "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", - "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", - "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", - "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", - "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", - "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", - "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", - "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", - "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", - "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", - "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", - "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", - "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", - "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", - "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", - "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", - "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", - "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", - "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", - "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", - "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", - "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", - "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", - "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", - "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", - "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", - "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", - "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", - "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", - "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", - "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", - "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", - "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", - "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", - "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", - "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", - "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", - "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", - "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", - "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", - "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", - "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", - "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" + "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d", + "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301", + "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635", + "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a", + "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed", + "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721", + "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801", + "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b", + "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1", + "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88", + "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8", + "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0", + "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f", + "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578", + "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7", + "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045", + "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada", + "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d", + "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b", + "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a", + "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977", + "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea", + "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346", + "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13", + "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22", + "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339", + "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9", + "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181", + "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c", + "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90", + "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a", + "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489", + "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f", + "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504", + "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea", + "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569", + "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4", + "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce", + "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab", + "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a", + "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f", + "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c", + "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9", + "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf", + "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d", + "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627", + "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d", + "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4", + "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c", + "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d", + "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad", + "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b", + "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33", + "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371", + "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1", + "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393", + "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106", + "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df", + "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379", + "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451", + "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b", + "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575", + "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed", + "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb", + "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838" ], - "markers": "python_version >= '3.6'", - "version": "==1.16.0" + "markers": "python_version >= '3.8'", + "version": "==1.17.0" } }, "develop": { @@ -673,7 +842,7 @@ "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597", "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df" ], - "markers": "python_version >= '3'", + "markers": "python_full_version >= '3.5.0'", "version": "==2.0.12" }, "click": { @@ -695,71 +864,71 @@ }, "coverage": { "hashes": [ - "sha256:0266b62cbea568bd5e93a4da364d05de422110cbed5056d69339bd5af5685433", - "sha256:0573f5cbf39114270842d01872952d301027d2d6e2d84013f30966313cadb529", - "sha256:0ddcb70b3a3a57581b450571b31cb774f23eb9519c2aaa6176d3a84c9fc57671", - "sha256:108bb458827765d538abcbf8288599fee07d2743357bdd9b9dad456c287e121e", - "sha256:14045b8bfd5909196a90da145a37f9d335a5d988a83db34e80f41e965fb7cb42", - "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99", - "sha256:2bc3e45c16564cc72de09e37413262b9f99167803e5e48c6156bccdfb22c8327", - "sha256:2d608a7808793e3615e54e9267519351c3ae204a6d85764d8337bd95993581a8", - "sha256:34d23e28ccb26236718a3a78ba72744212aa383141961dd6825f6595005c8b06", - "sha256:37a15573f988b67f7348916077c6d8ad43adb75e478d0910957394df397d2874", - "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4", - "sha256:3c42ec2c522e3ddd683dec5cdce8e62817afb648caedad9da725001fa530d354", - "sha256:3c6b24007c4bcd0b19fac25763a7cac5035c735ae017e9a349b927cfc88f31c1", - "sha256:40cca284c7c310d622a1677f105e8507441d1bb7c226f41978ba7c86979609ab", - "sha256:46f21663e358beae6b368429ffadf14ed0a329996248a847a4322fb2e35d64d3", - "sha256:49ed5ee4109258973630c1f9d099c7e72c5c36605029f3a91fe9982c6076c82b", - "sha256:5c95e0fa3d1547cb6f021ab72f5c23402da2358beec0a8e6d19a368bd7b0fb37", - "sha256:5dd4e4a49d9c72a38d18d641135d2fb0bdf7b726ca60a103836b3d00a1182acd", - "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f", - "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b", - "sha256:623e6965dcf4e28a3debaa6fcf4b99ee06d27218f46d43befe4db1c70841551c", - "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b", - "sha256:6cf96ceaa275f071f1bea3067f8fd43bec184a25a962c754024c973af871e1b7", - "sha256:70a56a2ec1869e6e9fa69ef6b76b1a8a7ef709972b9cc473f9ce9d26b5997ce3", - "sha256:77256ad2345c29fe59ae861aa11cfc74579c88d4e8dbf121cbe46b8e32aec808", - "sha256:796c9b107d11d2d69e1849b2dfe41730134b526a49d3acb98ca02f4985eeff7a", - "sha256:7c07de0d2a110f02af30883cd7dddbe704887617d5c27cf373362667445a4c76", - "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469", - "sha256:82c809a62e953867cf57e0548c2b8464207f5f3a6ff0e1e961683e79b89f2c55", - "sha256:850cfd2d6fc26f8346f422920ac204e1d28814e32e3a58c19c91980fa74d8289", - "sha256:87ea64b9fa52bf395272e54020537990a28078478167ade6c61da7ac04dc14bc", - "sha256:90746521206c88bdb305a4bf3342b1b7316ab80f804d40c536fc7d329301ee13", - "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2", - "sha256:963e4a08cbb0af6623e61492c0ec4c0ec5c5cf74db5f6564f98248d27ee57d30", - "sha256:987a8e3da7da4eed10a20491cf790589a8e5e07656b6dc22d3814c4d88faf163", - "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d", - "sha256:a1ab9763d291a17b527ac6fd11d1a9a9c358280adb320e9c2672a97af346ac2c", - "sha256:a3b925300484a3294d1c70f6b2b810d6526f2929de954e5b6be2bf8caa1f12c1", - "sha256:acbb8af78f8f91b3b51f58f288c0994ba63c646bc1a8a22ad072e4e7e0a49f1c", - "sha256:ad32a981bcdedb8d2ace03b05e4fd8dace8901eec64a532b00b15217d3677dd2", - "sha256:aee9cf6b0134d6f932d219ce253ef0e624f4fa588ee64830fcba193269e4daa3", - "sha256:af05bbba896c4472a29408455fe31b3797b4d8648ed0a2ccac03e074a77e2314", - "sha256:b6cce5c76985f81da3769c52203ee94722cd5d5889731cd70d31fee939b74bf0", - "sha256:bb684694e99d0b791a43e9fc0fa58efc15ec357ac48d25b619f207c41f2fd384", - "sha256:c132b5a22821f9b143f87446805e13580b67c670a548b96da945a8f6b4f2efbb", - "sha256:c296263093f099da4f51b3dff1eff5d4959b527d4f2f419e16508c5da9e15e8c", - "sha256:c973b2fe4dc445cb865ab369df7521df9c27bf40715c837a113edaa2aa9faf45", - "sha256:cdd94501d65adc5c24f8a1a0eda110452ba62b3f4aeaba01e021c1ed9cb8f34a", - "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24", - "sha256:dbba8210f5067398b2c4d96b4e64d8fb943644d5eb70be0d989067c8ca40c0f8", - "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec", - "sha256:dfd14bcae0c94004baba5184d1c935ae0d1231b8409eb6c103a5fd75e8ecdc56", - "sha256:e25bacb53a8c7325e34d45dddd2f2fbae0dbc230d0e2642e264a64e17322a777", - "sha256:e2c8e3384c12dfa19fa9a52f23eb091a8fad93b5b81a41b14c17c78e23dd1d8b", - "sha256:e5f2a0f161d126ccc7038f1f3029184dbdf8f018230af17ef6fd6a707a5b881f", - "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a", - "sha256:ead9b9605c54d15be228687552916c89c9683c215370c4a44f1f217d2adcc34d", - "sha256:f07ff574986bc3edb80e2c36391678a271d555f91fd1d332a1e0f4b5ea4b6ea9", - "sha256:f2c7a045eef561e9544359a0bf5784b44e55cefc7261a20e730baa9220c83413", - "sha256:f3e8796434a8106b3ac025fd15417315d7a58ee3e600ad4dbcfddc3f4b14342c", - "sha256:f63e21ed474edd23f7501f89b53280014436e383a14b9bd77a648366c81dce7b", - "sha256:fd49c01e5057a451c30c9b892948976f5d38f2cbd04dc556a82743ba8e27ed8c" + "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", + "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", + "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f", + "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", + "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6", + "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae", + "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692", + "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4", + "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4", + "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", + "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d", + "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", + "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", + "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", + "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb", + "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", + "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08", + "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf", + "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b", + "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", + "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c", + "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae", + "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077", + "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00", + "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", + "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664", + "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014", + "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9", + "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6", + "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e", + "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", + "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", + "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", + "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", + "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", + "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", + "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030", + "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678", + "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", + "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902", + "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97", + "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845", + "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419", + "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464", + "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be", + "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", + "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", + "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be", + "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", + "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba", + "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", + "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", + "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4", + "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a", + "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a", + "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", + "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599", + "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", + "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", + "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", + "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1", + "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3" ], "markers": "python_version >= '3.9'", - "version": "==7.6.7" + "version": "==7.6.9" }, "flake8": { "hashes": [ @@ -821,40 +990,37 @@ }, "mypy": { "hashes": [ - "sha256:0680389c34284287fe00e82fc8bccdea9aff318f7e7d55b90d967a13a9606013", - "sha256:1767830da2d1afa4e62b684647af0ff79b401f004d7fa08bc5b0ce2d45bcd5ec", - "sha256:1ee5f99817ee70254e7eb5cf97c1b11dda29c6893d846c8b07bce449184e9466", - "sha256:262c543ef24deb10470a3c1c254bb986714e2b6b1a67d66daf836a548a9f316c", - "sha256:269f0dfb6463b8780333310ff4b5134425157ef0d2b1d614015adaf6d6a7eabd", - "sha256:2a3150d409609a775c8cb65dbe305c4edd7fe576c22ea79d77d1454acd9aeda8", - "sha256:2b6f85c2ad378e3224e017904a051b26660087b3b76490d533b7344f1546d3ff", - "sha256:3227f14fe943524f5794679156488f18bf8d34bfecd4623cf76bc55958d229c5", - "sha256:3ff201a0c6d3ea029d73b1648943387d75aa052491365b101f6edd5570d018ea", - "sha256:46897755f944176fbc504178422a5a2875bbf3f7436727374724842c0987b5af", - "sha256:47a9955214615108c3480a500cfda8513a0b1cd3c09a1ed42764ca0dd7b931dd", - "sha256:49082382f571c3186ce9ea0bd627cb1345d4da8d44a8377870f4442401f0a706", - "sha256:4a8a6c10f4c63fbf6ad6c03eba22c9331b3946a4cec97f008e9ffb4d3b31e8e2", - "sha256:6826d9c4d85bbf6d68cb279b561de6a4d8d778ca8e9ab2d00ee768ab501a9852", - "sha256:72382cb609142dba3f04140d016c94b4092bc7b4d98ca718740dc989e5271b8d", - "sha256:7da0005e47975287a92b43276e460ac1831af3d23032c34e67d003388a0ce8d0", - "sha256:8798c8ed83aa809f053abff08664bdca056038f5a02af3660de00b7290b64c47", - "sha256:8f1940325a8ed460ba03d19ab83742260fa9534804c317224e5d4e5aa588e2d6", - "sha256:8f694d6d09a460b117dccb6857dda269188e3437c880d7b60fa0014fa872d1e9", - "sha256:9b8f4a8213b1fd4b751e26b59ae0e0c12896568d7e805861035c7a15ed6dc9eb", - "sha256:9d851c09b981a65d9d283a8ccb5b1d0b698e580493416a10942ef1a04b19fd37", - "sha256:aaf1be63e0207d7d17be942dcf9a6b641745581fe6c64df9a38deb562a7dbafa", - "sha256:aba38e3dd66bdbafbbfe9c6e79637841928ea4c79b32e334099463c17b0d90ef", - "sha256:b08541a06eed35b543ae1a6b301590eb61826a1eb099417676ddc5a42aa151c5", - "sha256:be88d665e76b452c26fb2bdc3d54555c01226fba062b004ede780b190a50f9db", - "sha256:c76c769c46a1e6062a84837badcb2a7b0cdb153d68601a61f60739c37d41cc74", - "sha256:cc6019808580565040cd2a561b593d7c3c646badd7e580e07d875eb1bf35c695", - "sha256:cd2dd3730ba894ec2a2082cc703fbf3e95a08479f7be84912e3131fc68809d46", - "sha256:d555aa7f44cecb7ea3c0ac69d58b1a5afb92caa017285a8e9c4efbf0518b61b4", - "sha256:d847dd23540e2912d9667602271e5ebf25e5788e7da46da5ffd98e7872616e8e" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==0.990" + "markers": "python_version >= '3.8'", + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -972,11 +1138,41 @@ }, "tomli": { "hashes": [ - "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8", - "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391" + "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", + "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", + "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", + "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", + "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", + "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", + "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", + "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", + "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", + "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", + "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", + "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", + "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", + "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", + "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", + "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", + "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", + "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", + "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", + "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", + "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", + "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", + "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", + "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", + "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", + "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", + "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", + "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", + "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", + "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", + "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", + "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7" ], - "markers": "python_version < '3.11'", - "version": "==2.1.0" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "types-requests": { "hashes": [ diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index b68e8e98343..0905e1cdefd 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -6,7 +6,17 @@ from math import pi from subprocess import run, Popen from time import time -from typing import Callable, Coroutine, Dict, List, Optional, Tuple, Union, cast +from typing import ( + Callable, + Coroutine, + Dict, + List, + Optional, + Tuple, + Union, + cast, + Sequence, +) import atexit from opentrons_hardware.drivers.can_bus import DriverSettings, build, CanMessenger from opentrons_hardware.drivers.can_bus import settings as can_bus_settings @@ -146,7 +156,7 @@ def _create_attached_instruments_dict( async def update_firmware( - api: OT3API, force: bool = False, subsystems: Optional[List[SubSystem]] = None + api: OT3API, force: bool = False, subsystems: Optional[Sequence[SubSystem]] = None ) -> None: """Update firmware of OT3.""" if not api.is_simulator: diff --git a/hardware-testing/hardware_testing/opentrons_api/p1000_gen3_ul_per_mm.py b/hardware-testing/hardware_testing/opentrons_api/p1000_gen3_ul_per_mm.py index 9da7424eda4..ea6c7afef00 100644 --- a/hardware-testing/hardware_testing/opentrons_api/p1000_gen3_ul_per_mm.py +++ b/hardware-testing/hardware_testing/opentrons_api/p1000_gen3_ul_per_mm.py @@ -287,4 +287,4 @@ def overwrite_attached_pipette_ul_per_mm( pipette: Optional[Pipette] = api._pipette_handler._attached_instruments[mount] if pipette is None: raise RuntimeError(f"No pipette is attached to mount: {mount}") - pipette._config = replace(pipette._config, ul_per_mm=ul_per_mm) + pipette._config = replace(pipette._config, ul_per_mm=ul_per_mm) # type: ignore[type-var] diff --git a/hardware-testing/hardware_testing/production_qc/firmware_check.py b/hardware-testing/hardware_testing/production_qc/firmware_check.py index f84f5eb386c..097eab4dc76 100644 --- a/hardware-testing/hardware_testing/production_qc/firmware_check.py +++ b/hardware-testing/hardware_testing/production_qc/firmware_check.py @@ -1,6 +1,6 @@ """Firmware Check.""" from asyncio import run -from typing import List +from typing import Sequence from opentrons.hardware_control.ot3api import OT3API @@ -31,7 +31,7 @@ def _get_instrument_serial_number(api: OT3API, subsystem: SubSystem) -> str: return _id -async def _main(simulate: bool, subsystems: List[SubSystem]) -> None: +async def _main(simulate: bool, subsystems: Sequence[SubSystem]) -> None: api = await helpers_ot3.build_async_ot3_hardware_api(is_simulating=simulate) while True: for subsys, state in api.attached_subsystems.items(): diff --git a/hardware-testing/hardware_testing/production_qc/robot_assembly_qc_ot3/test_instruments.py b/hardware-testing/hardware_testing/production_qc/robot_assembly_qc_ot3/test_instruments.py index 45c1a7cc9c3..a9a86cc6d9b 100644 --- a/hardware-testing/hardware_testing/production_qc/robot_assembly_qc_ot3/test_instruments.py +++ b/hardware-testing/hardware_testing/production_qc/robot_assembly_qc_ot3/test_instruments.py @@ -58,7 +58,7 @@ def build_csv_lines() -> List[Union[CSVLine, CSVLineRepeating]]: for t, d in PIPETTE_TESTS.items(): for m in ["left", "right"]: tests.append(CSVLine(f"{m}-{t}", d)) # type: ignore[arg-type] - for t, d in GRIPPER_TESTS.items(): + for t, d in GRIPPER_TESTS.items(): # type: ignore[assignment] tests.append(CSVLine(f"gripper-{t}", d)) # type: ignore[arg-type] return tests diff --git a/hardware-testing/hardware_testing/protocols/__init__.py b/hardware-testing/hardware_testing/protocols/__init__.py index 0e3933fe74f..011cdaf9509 100644 --- a/hardware-testing/hardware_testing/protocols/__init__.py +++ b/hardware-testing/hardware_testing/protocols/__init__.py @@ -1 +1,131 @@ """Protocols.""" +from opentrons.protocol_api import ParameterContext + +from opentrons.protocols.labware import get_all_labware_definitions +from typing import List +from opentrons.protocols.parameters.types import ParameterChoice + + +def create_trials_parameter(parameters: ParameterContext) -> None: + """Create parameter for number of trials.""" + # NOTE: Place function inside def add_parameters(parameters) in protocol. + # NOTE: Copy ctx.params.num_of_trials, ctx.params.right_mount # type: ignore[attr-defined] + parameters.add_int( + variable_name="num_of_trials", + display_name="Number of Trials", + minimum=1, + maximum=100, + default=3, + ) + + +def create_pipette_parameters(parameters: ParameterContext) -> None: + """Create parameter for pipettes.""" + # NOTE: Place function inside def add_parameters(parameters) in protocol. + # NOTE: Copy ctx.params.left mount, ctx.params.right_mount # type: ignore[attr-defined] + # to get result + # Left Mount + parameters.add_str( + variable_name="left_mount", + display_name="Left Mount", + description="Pipette Type on Left Mount.", + choices=[ + {"display_name": "8ch 50ul", "value": "flex_8channel_50"}, + {"display_name": "8ch 1000ul", "value": "flex_8channel_1000"}, + {"display_name": "1ch 50ul", "value": "flex_1channel_50"}, + {"display_name": "1ch 1000ul", "value": "flex_1channel_1000"}, + {"display_name": "96ch 1000ul", "value": "flex_96channel_1000"}, + {"display_name": "None", "value": "none"}, + ], + default="flex_1channel_50", + ) + # Right Mount + parameters.add_str( + variable_name="right_mount", + display_name="Right Mount", + description="Pipette Type on Right Mount.", + choices=[ + {"display_name": "8ch 50ul", "value": "flex_8channel_50"}, + {"display_name": "8ch 1000ul", "value": "flex_8channel_1000"}, + {"display_name": "1ch 50ul", "value": "flex_1channel_50"}, + {"display_name": "1ch 1000ul", "value": "flex_1channel_1000"}, + {"display_name": "None", "value": "none"}, + ], + default="flex_1channel_1000", + ) + + +def create_labware_parameters(parameters: ParameterContext) -> None: + """Create parameters for Labware Type.""" + # NOTE: Place function inside def add_parameters(parameters) in protocol. + # NOTE: Copy ctx.params.labware_type # type: ignore[attr-defined] to get result + + labware_list = get_all_labware_definitions() + available_labware_choices: List[ParameterChoice] = [] + # Filter out labwaer containing the following strings + # TODO: change get_all_labware_definitions function to one + # that can filter labware by type and not by string + labware_filter_words = [ + "trash", + "adapter", + "tiprack", + "etips", + "lid", + "calibrationblock", + ] + filtered_labware = [ + labware + for labware in labware_list + if not any(filter_word in labware for filter_word in labware_filter_words) + ] + + # Add labware to choices list + filtered_labware.sort() + for labware in filtered_labware: + shorten_opentrons_str = labware.replace("opentrons", "opt") + available_labware_choices.append( + { + "display_name": shorten_opentrons_str.replace("_", "").title()[:30], + "value": labware, + } + ) + + # Create Parameter + parameters.add_str( + variable_name="labware_type", + display_name="Labware", + description="Labware to probe.", + choices=available_labware_choices, + default="opentrons_10_tuberack_nest_4x50ml_6x15ml_conical", + ) + + +def create_liquid_parameter(parameters: ParameterContext) -> None: + """Create parameter to specify liquid type.""" + # NOTE: Place function inside def add_parameters(parameters) in protocol. + # NOTE: Copy ctx.params.liquid_type # type: ignore[attr-defined] to get result + + parameters.add_str( + variable_name="liquid_type", + display_name="Liquid Type", + description="Liquid being tested.", + choices=[ + {"display_name": "Water", "value": "water"}, + {"display_name": "Ethanol", "value": "ethanol"}, + ], + default="water", + ) + + +def create_tube_volume_parameter(parameters: ParameterContext) -> None: + """Select if reagent tube should be 15 ml or 50 ml.""" + # NOTE: Place function inside def add_parameters(parameters) in protocol. + # NOTE: Copy ctx.params.tube_volume # type: ignore[attr-defined] to get result + parameters.add_int( + variable_name="tube_volume", + display_name="Tube Volume", + default=15, + minimum=15, + maximum=50, + unit="mL", + ) diff --git a/hardware-testing/hardware_testing/protocols/liquid_sense/lld_test_liquid_height.py b/hardware-testing/hardware_testing/protocols/liquid_sense/lld_test_liquid_height.py new file mode 100644 index 00000000000..ada7ce4e50b --- /dev/null +++ b/hardware-testing/hardware_testing/protocols/liquid_sense/lld_test_liquid_height.py @@ -0,0 +1,404 @@ +"""Measure Liquid Height.""" +from typing import List, Tuple, Optional +from opentrons.protocol_api import ( + ProtocolContext, + Labware, + Well, + InstrumentContext, + ParameterContext, +) +from opentrons.types import Point + + +########################################### +# VARIABLES - START +########################################### +# TODO: use runtime-variables instead of constants + +NUM_TRIALS = 12 + +ASPIRATE_MM_FROM_BOTTOM = 5 +RESERVOIR = "nest_1_reservoir_195ml" + +LIQUID_MOUNT = "right" +LIQUID_TIP_SIZE = 1000 +LIQUID_PIPETTE_SIZE = 1000 + +PROBING_MOUNT = "left" +PROBING_TIP_SIZE = 50 +PROBING_PIPETTE_SIZE = 50 + +SLOT_LIQUID_TIPRACK = "C3" +SLOT_PROBING_TIPRACK = "D3" +SLOT_LABWARE = "D1" +SLOT_RESERVOIR = "C1" +SLOT_DIAL = "B3" + + +def add_parameters(parameters: ParameterContext) -> None: + """Add parameters.""" + from hardware_testing import protocols + + protocols.create_liquid_parameter(parameters) + parameters.add_str( + variable_name="probe_yes_or_no", + display_name="Probe (Y/N)", + description="Find Liquid height?", + choices=[ + {"display_name": "Yes", "value": "yes"}, + {"display_name": "No", "value": "no"}, + ], + default="yes", + ) + protocols.create_labware_parameters(parameters) + + +########################################### +# VARIABLES - END +########################################### + +metadata = {"protocolName": "lld-test-liquid-height"} +requirements = {"robotType": "Flex", "apiLevel": "2.20"} + +_all_96_well_names = [f"{r}{c + 1}" for c in range(12) for r in "ABCDEFGH"] +_first_row_well_names = [f"A{c + 1}" for c in range(12)] +_tube_names = ["A3", "A4", "B3", "B4"] * 8 +TEST_WELLS = { + 1: { # channel count + "corning_96_wellplate_360ul_flat": _all_96_well_names, + "armadillo_96_wellplate_200ul_pcr_full_skirt": _all_96_well_names, + "nest_96_wellplate_2ml_deep": _all_96_well_names, + "corning_96_wellplate_360ul_flat": _all_96_well_names, + "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical": _tube_names, + }, + 8: {"nest_12_reservoir_15ml": _first_row_well_names}, +} + +DIAL_POS_WITHOUT_TIP: List[Optional[float]] = [None, None] +DIAL_PORT_NAME = "/dev/ttyUSB0" +DIAL_PORT = None +RUN_ID = "" +FILE_NAME = "" +CSV_HEADER = ["trial", "volume", "height", "tip-z-error", "corrected-height"] +CSV_SEPARATOR = "," +LABWARE_TYPE = "" +LIQUID_TYPE = "" +VOLUMES = [] + + +def _setup( + ctx: ProtocolContext, +) -> Tuple[ + InstrumentContext, + Labware, + InstrumentContext, + Labware, + Labware, + Labware, + Labware, +]: + global DIAL_PORT, RUN_ID, FILE_NAME, LABWARE_TYPE, VOLUMES, LIQUID_TYPE, NUM_TRIALS + # TODO: use runtime-variables instead of constants + ctx.load_trash_bin("A3") + LABWARE_TYPE = ctx.params.labware_type # type: ignore[attr-defined] + LIQUID_TYPE = ctx.params.liquid_type # type: ignore[attr-defined] + liquid_rack_name = f"opentrons_flex_96_tiprack_{LIQUID_TIP_SIZE}uL" + liquid_rack = ctx.load_labware(liquid_rack_name, SLOT_LIQUID_TIPRACK) + probing_rack_name = f"opentrons_flex_96_tiprack_{PROBING_TIP_SIZE}uL" + probing_rack = ctx.load_labware(probing_rack_name, SLOT_PROBING_TIPRACK) + if LABWARE_TYPE == "nest_12_reservoir_15ml": + liquid_pip_name = f"flex_8channel_{LIQUID_PIPETTE_SIZE}" + else: + liquid_pip_name = f"flex_1channel_{LIQUID_PIPETTE_SIZE}" + liquid_pipette = ctx.load_instrument(liquid_pip_name, LIQUID_MOUNT) + probing_pip_name = f"flex_1channel_{PROBING_PIPETTE_SIZE}" + probing_pipette = ctx.load_instrument(probing_pip_name, PROBING_MOUNT) + + reservoir = ctx.load_labware(RESERVOIR, SLOT_RESERVOIR) + labware = ctx.load_labware(LABWARE_TYPE, SLOT_LABWARE) + dial = ctx.load_labware("dial_indicator", SLOT_DIAL) + + # DETERMINE VOLUME LIST + if ( + LABWARE_TYPE == "armadillo_96_wellplate_200ul_pcr_full_skirt" + or LABWARE_TYPE == "corning_96_wellplate_360ul_flat" + ): + VOLUMES = [7, 10, 15, 25, 40, 60, 100, 200] + elif LABWARE_TYPE == "nest_96_wellplate_2ml_deep": + VOLUMES = [40, 50, 175, 200, 225, 275, 400, 1000] + elif LABWARE_TYPE == "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical": + VOLUMES = [250, 250, 500, 1000, 1000, 1000, 1000, 1000] + NUM_TRIALS = 4 + # Actual Volumes = 250, 500, 1000, 2000, 3000, 4000, 5000, 6000 + elif LABWARE_TYPE == "nest_12_reservoir_15ml": + VOLUMES = [200, 300, 400, 500, 600, 700, 800, 1000] + + if not ctx.is_simulating() and DIAL_PORT is None: + from hardware_testing.data import create_file_name, create_run_id + from hardware_testing.drivers.mitutoyo_digimatic_indicator import ( + Mitutoyo_Digimatic_Indicator, + ) + + DIAL_PORT = Mitutoyo_Digimatic_Indicator(port=DIAL_PORT_NAME) + DIAL_PORT.connect() + RUN_ID = create_run_id() + FILE_NAME = create_file_name( + metadata["protocolName"], RUN_ID, f"{liquid_pip_name}-{liquid_rack_name}" + ) + _write_line_to_csv(ctx, [RUN_ID]) + _write_line_to_csv(ctx, [liquid_pip_name]) + _write_line_to_csv(ctx, [liquid_rack_name]) + _write_line_to_csv(ctx, [LABWARE_TYPE]) + + return ( + liquid_pipette, + liquid_rack, + probing_pipette, + probing_rack, + labware, + reservoir, + dial, + ) + + +def _write_line_to_csv(ctx: ProtocolContext, line: List[str]) -> None: + if ctx.is_simulating(): + return + from hardware_testing.data import append_data_to_file + + line_str = f"{CSV_SEPARATOR.join(line)}\n" + append_data_to_file(metadata["protocolName"], RUN_ID, FILE_NAME, line_str) + + +def _get_test_wells(labware: Labware, channels: int) -> List[Well]: + return [labware[w] for w in TEST_WELLS[channels][labware.load_name]] + + +def _get_test_tips(rack: Labware, channels: int) -> List[Well]: + if channels == 96: + test_tips = [rack["A1"]] + elif channels == 8: + test_tips = rack.rows()[0] + else: + test_tips = rack.wells() + return test_tips + + +def _read_dial_indicator( + ctx: ProtocolContext, + pipette: InstrumentContext, + dial: Labware, + front_channel: bool = False, +) -> float: + target = dial["A1"].top() + if front_channel: + target = target.move(Point(y=9 * 7)) + if pipette.channels == 96: + target = target.move(Point(x=9 * -11)) + pipette.move_to(target.move(Point(z=10))) + pipette.move_to(target) + ctx.delay(seconds=2) + if ctx.is_simulating(): + return 0.0 + dial_port = DIAL_PORT.read() # type: ignore[union-attr] + pipette.move_to(target.move(Point(z=10))) + return dial_port + + +def _store_dial_baseline( + ctx: ProtocolContext, + pipette: InstrumentContext, + dial: Labware, + front_channel: bool = False, +) -> None: + global DIAL_POS_WITHOUT_TIP + idx = 0 if not front_channel else 1 + if DIAL_POS_WITHOUT_TIP[idx] is not None: + return + DIAL_POS_WITHOUT_TIP[idx] = _read_dial_indicator(ctx, pipette, dial, front_channel) + tag = f"DIAL-BASELINE-{idx}" + _write_line_to_csv(ctx, [tag, str(DIAL_POS_WITHOUT_TIP[idx])]) + + +def _get_tip_z_error( + ctx: ProtocolContext, + pipette: InstrumentContext, + dial: Labware, + front_channel: bool = False, +) -> float: + idx = 0 if not front_channel else 1 + dial_baseline_for_this_channel = DIAL_POS_WITHOUT_TIP[idx] + assert dial_baseline_for_this_channel is not None + new_val = _read_dial_indicator(ctx, pipette, dial, front_channel) + z_error = new_val - dial_baseline_for_this_channel + # NOTE: dial-indicators are upside-down, so we need to flip the values + return z_error * -1.0 + + +def _get_height_of_liquid_in_well( + pipette: InstrumentContext, + well: Well, +) -> float: + if pipette.detect_liquid_presence(well): + height = pipette.measure_liquid_height(well) - well.bottom().point.z + else: + height = 0.0 + return height + + +def _test_for_finding_liquid_height( + ctx: ProtocolContext, + volume: float, + liquid_pipette: InstrumentContext, + probing_pipette: InstrumentContext, + dial: Labware, + liquid_tips: List[Well], + probing_tips: List[Well], + src_well: Well, + wells: List[Well], + dispense_from_bottom: float, + probe_yes_or_no: str, +) -> None: + assert len(liquid_tips) == len( + probing_tips + ), f"{len(liquid_tips)},{len(probing_tips)}" + assert len(liquid_tips) == len(wells), f"{len(liquid_tips)},{len(wells)}" + trial_counter = 0 + if probe_yes_or_no == "yes": + _store_dial_baseline(ctx, probing_pipette, dial) + _write_line_to_csv(ctx, CSV_HEADER) + all_corrected_heights = [] + corrected_height_list = [] + volume_to_record = volume + if LABWARE_TYPE == "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical": + prev_vol = 0.0 + for liq_tip, probe_tip, well in zip(liquid_tips, probing_tips, wells): + trial_counter += 1 + if trial_counter == 1: + corrected_height_list.append(str(RUN_ID)) + if ( + LABWARE_TYPE == "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical" + and prev_vol == 0.0 + ): + prev_vol = volume + elif ( + LABWARE_TYPE == "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical" + and prev_vol > 0.0 + ): + volume_to_record += prev_vol + corrected_height_list.append(str(volume_to_record)) + # pickup probing tip, then measure Z-error + if probe_yes_or_no == "yes": + probing_pipette.pick_up_tip(probe_tip) + tip_z_error = _get_tip_z_error(ctx, probing_pipette, dial) + else: + tip_z_error = 0 + # pickup liquid tip, then immediately transfer liquid + liquid_pipette.pick_up_tip(liq_tip) + if volume < 1000: + liquid_pipette.aspirate(volume, src_well.bottom(ASPIRATE_MM_FROM_BOTTOM)) + liquid_pipette.dispense(volume, well.bottom(dispense_from_bottom)) + else: + volume_divide_by_2 = volume / 2 + liquid_pipette.aspirate( + volume_divide_by_2, src_well.bottom(ASPIRATE_MM_FROM_BOTTOM) + ) + liquid_pipette.dispense( + volume_divide_by_2, well.bottom(dispense_from_bottom) + ) + liquid_pipette.aspirate( + volume_divide_by_2, src_well.bottom(ASPIRATE_MM_FROM_BOTTOM) + ) + liquid_pipette.dispense( + volume_divide_by_2, well.bottom(dispense_from_bottom) + ) + + liquid_pipette.blow_out(well.top(z=-9)).prepare_to_aspirate() + # get height of liquid + if probe_yes_or_no == "yes": + height = _get_height_of_liquid_in_well(probing_pipette, well) + else: + height = 0 + corrected_height = height + tip_z_error + all_corrected_heights.append(corrected_height) + # drop all tips + liquid_pipette.return_tip() + if probe_yes_or_no == "yes": + probing_pipette.return_tip() + # save data + trial_data = [ + trial_counter, + volume_to_record, + height, + tip_z_error, + corrected_height, + ] + corrected_height_list.append(corrected_height) # type: ignore[arg-type] + _write_line_to_csv(ctx, [str(d) for d in trial_data]) + + avg = sum(all_corrected_heights) / len(all_corrected_heights) + error_mm = (max(all_corrected_heights) - min(all_corrected_heights)) * 0.5 + corrected_height_list.append(avg) # type: ignore[arg-type] + corrected_height_list.append(error_mm) # type: ignore[arg-type] + error_percent = error_mm / avg if avg else 0.0 + corrected_height_list.append(str(error_percent * 100)) + _write_line_to_csv(ctx, ["average", str(round(avg, 3))]) + _write_line_to_csv(ctx, ["error (mm)", str(round(error_mm, 3))]) + _write_line_to_csv(ctx, ["error (%)", str(round(error_percent * 100, 1))]) + _write_line_to_csv(ctx, ["Liquid Type", LIQUID_TYPE]) + + +def run(ctx: ProtocolContext) -> None: + """Run.""" + ( + liq_pipette, + liq_rack, + probe_pipette, + probe_rack, + labware, + reservoir, + dial, + ) = _setup(ctx) + # Parameters + liq_pipette.flow_rate.blow_out = 800 + probe_yes_or_no = ctx.params.probe_yes_or_no # type: ignore[attr-defined] + dispense_from_bottom = 2 + test_wells = _get_test_wells(labware, channels=8) + test_tips_liquid = _get_test_tips(liq_rack, channels=8) + test_tips_probe = _get_test_tips(probe_rack, channels=8) + stuff_lengths = len(test_tips_liquid), len(test_tips_probe), len(test_wells) + assert min(stuff_lengths) >= NUM_TRIALS * len(VOLUMES), f"{stuff_lengths}" + + for _vol in VOLUMES: + if LABWARE_TYPE == "nest_12_reservoir_15ml": + _test_for_finding_liquid_height( + ctx, + _vol, + liq_pipette, + probe_pipette, + dial, + liquid_tips=test_tips_liquid[:NUM_TRIALS], + probing_tips=test_tips_probe[:NUM_TRIALS], + src_well=reservoir["A1"], + wells=test_wells[:NUM_TRIALS], + dispense_from_bottom=dispense_from_bottom, + probe_yes_or_no=probe_yes_or_no, + ) + ctx.pause("Reset labware and tipracks.") + else: + _test_for_finding_liquid_height( + ctx, + _vol, + liq_pipette, + probe_pipette, + dial, + liquid_tips=test_tips_liquid[:NUM_TRIALS], + probing_tips=test_tips_probe[:NUM_TRIALS], + src_well=reservoir["A1"], + wells=test_wells[:NUM_TRIALS], + dispense_from_bottom=dispense_from_bottom, + probe_yes_or_no=probe_yes_or_no, + ) + test_wells = test_wells[NUM_TRIALS:] + test_tips_liquid = test_tips_liquid[NUM_TRIALS:] + test_tips_probe = test_tips_probe[NUM_TRIALS:] diff --git a/hardware-testing/hardware_testing/protocols/liquid_sense/lld_test_liquid_height_3mm.py b/hardware-testing/hardware_testing/protocols/liquid_sense/lld_test_liquid_height_3mm.py new file mode 100644 index 00000000000..d4f8ab7ecb4 --- /dev/null +++ b/hardware-testing/hardware_testing/protocols/liquid_sense/lld_test_liquid_height_3mm.py @@ -0,0 +1,540 @@ +"""Measure Liquid Height 3mm.""" +import math +from typing import List, Tuple, Optional +from opentrons.protocol_api import ( + ProtocolContext, + Labware, + Well, + InstrumentContext, + ParameterContext, +) +from opentrons.types import Point + + +########################################### +# VARIABLES - START +########################################### +# TODO: use runtime-variables instead of constants + +# NOTE: The volumes below were calculated using Solidworks +# models, they are the nominal volume inside the well +# at both 3mm from bottom and 3mm from top. +# FIXME: replace this with actual Opentrons API software +# volume estimations. No need for us to include Solidworks +# in this testing loop. + +# Default tube volumes are for 50 ml +VOLUMES_3MM_TOP_BOTTOM = { + "corning_96_wellplate_360ul_flat": [257.1, 97.2, 0.0], + "nest_96_wellplate_200ul_flat": [259.8, 96.3, 0.0], + "opentrons_96_wellplate_200ul_pcr_full_skirt": [150.2, 14.3, 0.0], + "nest_96_wellplate_2ml_deep": [2060.4, 118.3, 0.0], + "nest_12_reservoir_15ml": [13236.1, 1219.0, 0.0], + "nest_96_wellplate_100ul_pcr_full_skirt": [150.8, 15.5, 0.0], + "appliedbiosystemsmicroamp_384_wellplate_40ul": [26.2, 7.44, 0.0], + "thermoscientificnunc_96_wellplate_1300ul": [1155.1, 73.5, 0.0], + "thermoscientificnunc_96_wellplate_2000ul": [1768.0, 73.5, 0.0], + "biorad_96_wellplate_200ul_pcr": [161.2, 71.32, 17.9, 0.0], + "nest_1_reservoir_290ml": [16570.4, 271690.5, 0.0], + "corning_12_wellplate_6.9ml_flat": [5654.8, 1156.3, 0.0], + "corning_24_wellplate_3.4ml_flat": [2853.4, 1701.37, 579.0, 0.0], + "corning_6_wellplate_16.8ml_flat": [13901.9, 2862.1, 0.0], + "corning_48_wellplate_1.6ml_flat": [1327.0, 790.63, 268.9, 0.0], + "opentrons_24_tuberack_nest_0.5ml_screwcap": [795.4, 21.95, 0.0], + "opentrons_24_tuberack_nest_1.5ml_screwcap": [19.5, 735.89, 1750.8, 0.0], + "opentrons_24_tuberack_nest_1.5ml_snapcap": [28.7, 1739.7, 658.2, 0.0], + "opentrons_24_tuberack_nest_2ml_screwcap": [2104.9, 66.6, 0.0], + "opentrons_24_tuberack_nest_2ml_snapcap": [2148.5, 69.6, 0.0], + "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical": [115.0, 26117.4, 56110.3, 0.0], + "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical": [169.5, 57720.5, 0.0], + "nest_1_reservoir_195ml": [14513.1, 178181.9, 0.0], + "axygen_1_reservoir_90ml": [23136.9, 72854.8, 0.0], + "agilent_1_reservoir_290ml": [15652.9, 141945.59, 268813.8], + "opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap": [26.6, 593.7, 1629.9], + "corning_384_wellplate_112ul_flat": [23.2, 50.1, 80.0], + "biorad_384_wellplate_50ul": [28.7, 8.0, 0.0], + "usascientific_12_reservoir_22ml": [68.4, 11356.9, 19797.4], + "usascientific_96_wellplate_2.4ml_deep": [74.7, 1151.9, 2317.8], + "opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap": [63.0, 2237.8, 0.0], +} + +SAME_TIP = True # this is fine when using Ethanol (b/c it evaporates) +RETURN_TIP = False +DISPENSE_MM_FROM_MENISCUS = -0.5 + +ASPIRATE_MM_FROM_MENISCUS = -2.0 + +LIQUID_MOUNT = "right" +LIQUID_PIPETTE_SIZE = 1000 +LIQUID_CHANNELS = 1 + +PROBING_MOUNT = "left" +PROBING_TIP_SIZE = 50 +PROBING_PIPETTE_SIZE = 50 + +SLOT_LIQUID_TIPRACK = "C3" +SLOT_PROBING_TIPRACK = "D3" +SLOT_LABWARE = "D1" +SLOT_RESERVOIR = "C1" +SLOT_DIAL = "B3" + +########################################### +# VARIABLES - END +########################################### + + +metadata = {"protocolName": "lld-test-liquid-height-3mm-06dec"} +requirements = {"robotType": "Flex", "apiLevel": "2.20"} + + +def add_parameters(parameters: ParameterContext) -> None: + """Add parameters.""" + from hardware_testing import protocols + + protocols.create_pipette_parameters(parameters) + protocols.create_labware_parameters(parameters) + protocols.create_tube_volume_parameter(parameters) + protocols.create_trials_parameter(parameters) + + +_src_meniscus_height: Optional[float] = None +_first_row_well_names = [f"A{c + 1}" for c in range(12)] +_50_ml_tubes = ["A3", "B3", "A4", "B4"] + +TEST_WELLS = { + 1: { # channel count + "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical": _50_ml_tubes, + "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical": _50_ml_tubes, + "nest_12_reservoir_15ml": _first_row_well_names, + }, + 8: { + "nest_1_reservoir_290ml": ["A1"] * 9, + "nest_1_reservoir_195ml": ["A1"] * 9, + }, +} + +DIAL_POS_WITHOUT_TIP: List[Optional[float]] = [None, None] +DIAL_PORT_NAME = "/dev/ttyUSB0" +DIAL_PORT = None +RUN_ID = "" +FILE_NAME = "" +CSV_HEADER = ["trial", "volume", "height", "tip-z-error", "corrected-height"] +CSV_SEPARATOR = "," + + +def _setup( + ctx: ProtocolContext, +) -> Tuple[ + InstrumentContext, + Labware, + InstrumentContext, + Labware, + Labware, + Labware, + Labware, + int, + int, +]: + global DIAL_PORT, RUN_ID, FILE_NAME + # TODO: use runtime-variables instead of constants + # Variables + # Pipette Types + left_mount = ctx.params.left_mount # type: ignore[attr-defined] + right_mount = ctx.params.right_mount # type: ignore[attr-defined] + num_trials = ctx.params.num_of_trials # type: ignore[attr-defined] + if left_mount != "None": + probing_pipette = ctx.load_instrument(left_mount, "left") + if right_mount != "None": + liquid_pipette = ctx.load_instrument(right_mount, "right") + liquid_pip_name = right_mount + + ctx.load_trash_bin("A3") + + probing_rack_name = f"opentrons_flex_96_tiprack_{PROBING_TIP_SIZE}uL" + probing_rack = ctx.load_labware(probing_rack_name, SLOT_PROBING_TIPRACK) + + LABWARE = ctx.params.labware_type # type: ignore[attr-defined] + tube_volume = ctx.params.tube_volume # type: ignore[attr-defined] + + labware: Labware = ctx.load_labware(LABWARE, SLOT_LABWARE) + labware_max_volume = labware["A1"].max_volume + print(f"Labware max volume: {labware_max_volume}") + if labware_max_volume < 50: + LIQUID_TIP_SIZE = 50 + else: + LIQUID_TIP_SIZE = 1000 + liquid_pip_channels = liquid_pipette.channels + + if tube_volume == 15: + # Replace volumes with 15 ml volumes + VOLUMES_3MM_TOP_BOTTOM["opentrons_10_tuberack_nest_4x50ml_6x15ml_conical"] = [ + 17.3, + 7090.6, + 16077.5, + 0.0, + ] + VOLUMES_3MM_TOP_BOTTOM["opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical"] = [ + 42.2, + 15956.6, + 0.0, + ] + volumes = VOLUMES_3MM_TOP_BOTTOM[labware.load_name] + total_volume_to_aspirate = 0.0 + for one_vols in volumes: + total_volume_to_aspirate += one_vols * num_trials + if liquid_pip_channels == 1 and total_volume_to_aspirate < 1000: + RESERVOIR = "opentrons_15_tuberack_nest_15ml_conical" + else: + RESERVOIR = "nest_1_reservoir_195ml" + + reservoir = ctx.load_labware(RESERVOIR, SLOT_RESERVOIR) + if len(labware.wells()) > 96: + LIQUID_TIP_SIZE = 50 + liquid_rack_name = f"opentrons_flex_96_tiprack_{LIQUID_TIP_SIZE}uL" + liquid_rack = ctx.load_labware(liquid_rack_name, SLOT_LIQUID_TIPRACK) + + dial = ctx.load_labware("dial_indicator", SLOT_DIAL) + + if not ctx.is_simulating() and DIAL_PORT is None: + from hardware_testing.data import create_file_name, create_run_id + from hardware_testing.drivers.mitutoyo_digimatic_indicator import ( + Mitutoyo_Digimatic_Indicator, + ) + + DIAL_PORT = Mitutoyo_Digimatic_Indicator(port=DIAL_PORT_NAME) + DIAL_PORT.connect() + RUN_ID = create_run_id() + FILE_NAME = create_file_name( + metadata["protocolName"], RUN_ID, f"{liquid_pip_name}-{liquid_rack_name}" + ) + _write_line_to_csv(ctx, [RUN_ID]) + _write_line_to_csv(ctx, [liquid_pip_name]) + _write_line_to_csv(ctx, [liquid_rack_name]) + _write_line_to_csv(ctx, [LABWARE]) + _write_line_to_csv(ctx, ["depth", str(labware["A1"].depth)]) + return ( + liquid_pipette, + liquid_rack, + probing_pipette, + probing_rack, + labware, + reservoir, + dial, + tube_volume, + num_trials, + ) + + +def _write_line_to_csv(ctx: ProtocolContext, line: List[str]) -> None: + if ctx.is_simulating(): + return + from hardware_testing.data import append_data_to_file + + line_str = f"{CSV_SEPARATOR.join(line)}\n" + append_data_to_file(metadata["protocolName"], RUN_ID, FILE_NAME, line_str) + + +def _get_test_wells( + labware: Labware, channels: int, tube_volume: int, total_test_wells: int +) -> List[Well]: + well_names = [] + try: + if tube_volume == 15: + print("cHANGING LABWARE WELLS") + + TEST_WELLS[channels]["opentrons_10_tuberack_nest_4x50ml_6x15ml_conical"] = [ + "A1", + "B1", + "C1", + "A2", + "B2", + "C2", + ] + TEST_WELLS[channels][ + "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical" + ] = ["A1", "B1", "C1", "A2", "B2", "C2"] + well_names = TEST_WELLS[channels][labware.load_name] + print(TEST_WELLS[channels][labware.load_name]) + else: + well_names = TEST_WELLS[channels][labware.load_name] + except KeyError: + well_names = [ + str(well_name).split(" ")[0].replace(" ", "") + for well_name in labware.wells() + ] + print(len(well_names)) + print(well_names) + amount_of_well_names = len(well_names) + if amount_of_well_names < total_test_wells: + wells_needed = total_test_wells - amount_of_well_names + repeat_times = ( + wells_needed // len(well_names) + ) + 1 # Calculate how many times to repeat + # Extend well_names by repeating it, then trim any excess elements + well_names.extend(well_names * repeat_times) + diff_after_extension = len(well_names) - total_test_wells + well_names = well_names[:-diff_after_extension] + return [labware[w] for w in well_names] + + +def _get_test_tips(rack: Labware, channels: int) -> List[Well]: + if channels == 96: + test_tips = [rack["A1"]] + elif channels == 8: + test_tips = rack.rows()[0] + else: + test_tips = rack.wells() + return test_tips + + +def _read_dial_indicator( + ctx: ProtocolContext, + pipette: InstrumentContext, + dial: Labware, + front_channel: bool = False, +) -> float: + target = dial["A1"].top() + if front_channel: + target = target.move(Point(y=9 * 7)) + if pipette.channels == 96: + target = target.move(Point(x=9 * -11)) + pipette.move_to(target.move(Point(z=10))) + pipette.move_to(target) + ctx.delay(seconds=2) + if ctx.is_simulating(): + return 0.0 + dial_port = DIAL_PORT.read() # type: ignore[union-attr] + pipette.move_to(target.move(Point(z=10))) + return dial_port + + +def _store_dial_baseline( + ctx: ProtocolContext, + pipette: InstrumentContext, + dial: Labware, + front_channel: bool = False, +) -> None: + global DIAL_POS_WITHOUT_TIP + idx = 0 if not front_channel else 1 + if DIAL_POS_WITHOUT_TIP[idx] is not None: + return + DIAL_POS_WITHOUT_TIP[idx] = _read_dial_indicator(ctx, pipette, dial, front_channel) + tag = f"DIAL-BASELINE-{idx}" + _write_line_to_csv(ctx, [tag, str(DIAL_POS_WITHOUT_TIP[idx])]) + + +def _get_tip_z_error( + ctx: ProtocolContext, + pipette: InstrumentContext, + dial: Labware, + front_channel: bool = False, +) -> float: + idx = 0 if not front_channel else 1 + dial_baseline_for_this_channel = DIAL_POS_WITHOUT_TIP[idx] + assert dial_baseline_for_this_channel is not None + new_val = _read_dial_indicator(ctx, pipette, dial, front_channel) + z_error = new_val - dial_baseline_for_this_channel + # NOTE: dial-indicators are upside-down, so we need to flip the values + return z_error * -1.0 + + +def _test_for_finding_liquid_height( # noqa: C901 + ctx: ProtocolContext, + volume: float, + liquid_pipette: InstrumentContext, + probing_pipette: InstrumentContext, + dial: Labware, + liquid_tips: List[Well], + probing_tips: List[Well], + src_well: Well, + wells: List[Well], +) -> None: + global _src_meniscus_height + assert len(liquid_tips) == len( + probing_tips + ), f"{len(liquid_tips)},{len(probing_tips)}" + assert len(liquid_tips) == len(wells), f"{len(liquid_tips)},{len(wells)}" + trial_counter = 0 + _store_dial_baseline(ctx, probing_pipette, dial) + _write_line_to_csv(ctx, CSV_HEADER) + all_corrected_heights: List[float] = [] + for liq_tip, probe_tip, well in zip(liquid_tips, probing_tips, wells): + trial_counter += 1 + # pickup probing tip, then measure Z-error + if not probing_pipette.has_tip: + probing_pipette.pick_up_tip(probe_tip) + else: + # try and get any remaining droplets out of the way + probing_pipette.aspirate().dispense().prepare_to_aspirate() + tip_z_error = _get_tip_z_error(ctx, probing_pipette, dial) + total_vol_in_tube = 0.0 + if volume: + # transfer over and over until all volume is moved + if volume < 15650: + need_to_transfer_per_ch = volume / liquid_pipette.channels + # set flow-rates + liquid_pipette.flow_rate.aspirate = min( + max(min(liquid_pipette.max_volume, need_to_transfer_per_ch), 10), + 200, + ) + liquid_pipette.flow_rate.dispense = min( + liquid_pipette.flow_rate.aspirate, 50 + ) + liquid_pipette.flow_rate.blow_out = 100 + if _src_meniscus_height is None: + _src_meniscus_height = src_well.depth - 1.0 + if src_well.diameter: + src_well_z_ul_per_mm = math.pi * math.pow( + src_well.diameter * 0.5, 2 + ) + elif src_well.width is not None and src_well.length is not None: + src_well_z_ul_per_mm = src_well.width * src_well.length + else: + src_well_z_ul_per_mm = 0.0 + + while need_to_transfer_per_ch > 0.001: + transfer_vol = min( + liquid_pipette.max_volume * 0.9, need_to_transfer_per_ch + ) + if not liquid_pipette.has_tip: + liquid_pipette.pick_up_tip(liq_tip) + # NOTE: only use new, dry tips to probe + if not ctx.is_simulating(): + _src_meniscus_height = ( + liquid_pipette.measure_liquid_height(src_well) + - src_well.bottom().point.z + ) + else: + # try and get any remaining droplets out of the way + liquid_pipette.move_to(src_well.top(10)) + liquid_pipette.aspirate().blow_out().prepare_to_aspirate() + # aspirate + meniscus_shift_mm = transfer_vol / src_well_z_ul_per_mm + draft_multiplier = 1.2 if src_well.diameter else 1.5 + _src_meniscus_height -= draft_multiplier * meniscus_shift_mm + asp_mm = max(_src_meniscus_height + -2, 2) + liquid_pipette.aspirate(transfer_vol, src_well.bottom(asp_mm)) + need_to_transfer_per_ch -= transfer_vol + ctx.comment( + f"Aspirated {round(transfer_vol, 2)} from src, " + f"removed {round(meniscus_shift_mm, 2)} mm, " + f"now is {round(_src_meniscus_height, 2)} mm tall," + f"aspirating from {round(asp_mm, 2)} from bottom." + ) + liquid_pipette.move_to(src_well.bottom(_src_meniscus_height + 5)) + ctx.delay(seconds=1.5) + liquid_pipette.touch_tip(src_well, speed=30) + did_air_gap = False + if transfer_vol <= liquid_pipette.max_volume - 5: + liquid_pipette.aspirate(5, src_well.top(2)) + did_air_gap = True + # dispense + if did_air_gap: + liquid_pipette.dispense(5, well.top(5)) + # default will be to dispense from top + if volume > well.max_volume * 0.5: + dispense_loc = well.top(-3 + DISPENSE_MM_FROM_MENISCUS) + else: + dispense_loc = well.bottom(3 + DISPENSE_MM_FROM_MENISCUS) + liquid_pipette.dispense(transfer_vol, dispense_loc) + total_vol_in_tube += transfer_vol + ctx.delay(seconds=1.5) + liquid_pipette.move_to(well.top()) + ctx.delay(seconds=1.5) + liquid_pipette.blow_out(well.top()) + ctx.delay(seconds=1.5) + liquid_pipette.prepare_to_aspirate() + # get height of liquid + else: + ctx.pause("Fill well.") + height = probing_pipette.measure_liquid_height(well) - well.bottom().point.z + else: + is_empty = not probing_pipette.detect_liquid_presence(well) + height = ( + 0.0 if is_empty else -9999 + ) # some obviously fake number so we know it failed + corrected_height = height + tip_z_error + all_corrected_heights.append(corrected_height) + ctx.pause("CHECK LABWARE") + # drop tips + if not SAME_TIP: + if liquid_pipette.has_tip: + if RETURN_TIP: + liquid_pipette.return_tip() + else: + liquid_pipette.drop_tip() + # NOTE: always return probing tip, b/c it must be dry + if RETURN_TIP: + probing_pipette.return_tip() + else: + probing_pipette.drop_tip() + # save data + trial_data = [trial_counter, volume, height, tip_z_error, corrected_height] + _write_line_to_csv(ctx, [str(d) for d in trial_data]) + + avg = sum(all_corrected_heights) / len(all_corrected_heights) + error_mm = (max(all_corrected_heights) - min(all_corrected_heights)) * 0.5 + error_percent = error_mm / avg if avg else 0.0 + _write_line_to_csv(ctx, ["average", str(round(avg, 3))]) + _write_line_to_csv(ctx, ["error (mm)", str(round(error_mm, 3))]) + _write_line_to_csv(ctx, ["error (%)", str(round(error_percent * 100, 1))]) + + +def run(ctx: ProtocolContext) -> None: + """Run.""" + ( + liq_pipette, + liq_rack, + probe_pipette, + probe_rack, + labware, + reservoir, + dial, + tube_volume, + num_trials, + ) = _setup(ctx) + channels_liquid = liq_pipette.channels + channels_probe = probe_pipette.channels + test_tips_liquid = _get_test_tips(liq_rack, channels=channels_liquid) + test_tips_probe = _get_test_tips(probe_rack, channels=channels_probe) + # FIXME: calculate nominal volumes at +3mm from bottom and -3mm from top + # using Opentrons API (not Solidworks) + try: + if tube_volume == 15: + # Replace volumes with 15 ml volumes + VOLUMES_3MM_TOP_BOTTOM[ + "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical" + ] = [17.3, 7090.6, 16077.5, 0.0] + VOLUMES_3MM_TOP_BOTTOM[ + "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical" + ] = [42.2, 15956.6, 0.0] + volumes = VOLUMES_3MM_TOP_BOTTOM[labware.load_name] + except KeyError: + volumes = [0.0, 0.0, 0.0] + ctx.comment(f"No volumes loaded for labware {labware.load_name}") + total_test_wells = len(volumes) * num_trials + test_wells = _get_test_wells( + labware, channels=1, tube_volume=tube_volume, total_test_wells=total_test_wells + ) + stuff_lengths = len(test_tips_liquid), len(test_tips_probe), len(test_wells) + + assert min(stuff_lengths) >= num_trials * len(volumes), f"{stuff_lengths}" + for _vol in volumes: + _test_for_finding_liquid_height( + ctx, + _vol, + liq_pipette, + probe_pipette, + dial, + liquid_tips=test_tips_liquid[:num_trials], + probing_tips=test_tips_probe[:num_trials], + src_well=reservoir["A1"], + wells=test_wells[:num_trials], + ) + test_wells = test_wells[num_trials:] + test_tips_liquid = test_tips_liquid[num_trials:] + test_tips_probe = test_tips_probe[num_trials:] + if liq_pipette.has_tip: + liq_pipette.return_tip() if RETURN_TIP else liq_pipette.drop_tip() + if probe_pipette.has_tip: + probe_pipette.return_tip() if RETURN_TIP else probe_pipette.drop_tip() diff --git a/hardware-testing/mypy.ini b/hardware-testing/mypy.ini index eeb271520a5..30f61a87de7 100644 --- a/hardware-testing/mypy.ini +++ b/hardware-testing/mypy.ini @@ -1,5 +1,6 @@ [mypy] show_error_codes = True +plugins = pydantic.mypy strict = False [mypy-can.*] diff --git a/hardware/Pipfile b/hardware/Pipfile index b02e50c7c51..aeb7992ccf7 100644 --- a/hardware/Pipfile +++ b/hardware/Pipfile @@ -8,13 +8,14 @@ python-can = "==4.2.2" pyserial = "==3.5" typing-extensions = ">=4.0.0,<5" numpy = "==1.22.3" -pydantic = "==1.9.2" +pydantic = "==2.9.0" +pydantic-settings = "==2.4.0" [dev-packages] pytest = "==7.4.4" pytest-lazy-fixture = "==0.6.3" pytest-cov = "==4.1.0" -mypy = "==1.8.0" +mypy = "==1.11.0" black = "==22.3.0" flake8 = "==7.0.0" flake8-annotations = "~=3.0.1" diff --git a/hardware/Pipfile.lock b/hardware/Pipfile.lock index ccab8884999..aa21398c5f2 100644 --- a/hardware/Pipfile.lock +++ b/hardware/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "04ae6d52e739cf67e21d13822316f7dc2030d272976c0a9cfd0f7d35db743301" + "sha256": "570a23b85a17fe0749ea5bc3d8f0e9c52b88608d795c507ce5adaf65744d8587" }, "pipfile-spec": 6, "requires": { @@ -16,67 +16,75 @@ ] }, "default": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "msgpack": { "hashes": [ - "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", - "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d", - "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3", - "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", - "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0", - "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", - "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", - "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", - "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524", - "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819", - "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc", - "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc", - "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", - "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", - "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81", - "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", - "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", - "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2", - "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", - "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", - "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", - "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", - "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95", - "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f", - "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", - "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", - "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", - "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61", - "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", - "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", - "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d", - "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c", - "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", - "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", - "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", - "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", - "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", - "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", - "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", - "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f", - "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7", - "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", - "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", - "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", - "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf", - "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c", - "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", - "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", - "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", - "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", - "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", - "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad", - "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd", - "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7", - "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", - "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc" + "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982", + "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3", + "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40", + "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee", + "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693", + "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950", + "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151", + "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24", + "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305", + "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b", + "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c", + "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659", + "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d", + "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18", + "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746", + "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868", + "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2", + "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba", + "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228", + "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2", + "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273", + "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c", + "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653", + "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a", + "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596", + "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd", + "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8", + "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa", + "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85", + "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc", + "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836", + "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3", + "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58", + "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128", + "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db", + "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f", + "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77", + "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad", + "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13", + "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8", + "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b", + "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a", + "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543", + "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b", + "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce", + "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d", + "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a", + "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c", + "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f", + "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e", + "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011", + "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04", + "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480", + "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a", + "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d", + "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d" ], "markers": "platform_system != 'Windows'", - "version": "==1.0.7" + "version": "==1.0.8" }, "numpy": { "hashes": [ @@ -107,53 +115,124 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", + "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==24.1" }, "pydantic": { "hashes": [ - "sha256:1061c6ee6204f4f5a27133126854948e3b3d51fcc16ead2e5d04378c199b2f44", - "sha256:19b5686387ea0d1ea52ecc4cffb71abb21702c5e5b2ac626fd4dbaa0834aa49d", - "sha256:2bd446bdb7755c3a94e56d7bdfd3ee92396070efa8ef3a34fab9579fe6aa1d84", - "sha256:328558c9f2eed77bd8fffad3cef39dbbe3edc7044517f4625a769d45d4cf7555", - "sha256:32e0b4fb13ad4db4058a7c3c80e2569adbd810c25e6ca3bbd8b2a9cc2cc871d7", - "sha256:3ee0d69b2a5b341fc7927e92cae7ddcfd95e624dfc4870b32a85568bd65e6131", - "sha256:4aafd4e55e8ad5bd1b19572ea2df546ccace7945853832bb99422a79c70ce9b8", - "sha256:4b3946f87e5cef3ba2e7bd3a4eb5a20385fe36521d6cc1ebf3c08a6697c6cfb3", - "sha256:4de71c718c9756d679420c69f216776c2e977459f77e8f679a4a961dc7304a56", - "sha256:5565a49effe38d51882cb7bac18bda013cdb34d80ac336428e8908f0b72499b0", - "sha256:5803ad846cdd1ed0d97eb00292b870c29c1f03732a010e66908ff48a762f20e4", - "sha256:5da164119602212a3fe7e3bc08911a89db4710ae51444b4224c2382fd09ad453", - "sha256:615661bfc37e82ac677543704437ff737418e4ea04bef9cf11c6d27346606044", - "sha256:78a4d6bdfd116a559aeec9a4cfe77dda62acc6233f8b56a716edad2651023e5e", - "sha256:7d0f183b305629765910eaad707800d2f47c6ac5bcfb8c6397abdc30b69eeb15", - "sha256:7ead3cd020d526f75b4188e0a8d71c0dbbe1b4b6b5dc0ea775a93aca16256aeb", - "sha256:84d76ecc908d917f4684b354a39fd885d69dd0491be175f3465fe4b59811c001", - "sha256:8cb0bc509bfb71305d7a59d00163d5f9fc4530f0881ea32c74ff4f74c85f3d3d", - "sha256:91089b2e281713f3893cd01d8e576771cd5bfdfbff5d0ed95969f47ef6d676c3", - "sha256:9c9e04a6cdb7a363d7cb3ccf0efea51e0abb48e180c0d31dca8d247967d85c6e", - "sha256:a8c5360a0297a713b4123608a7909e6869e1b56d0e96eb0d792c27585d40757f", - "sha256:afacf6d2a41ed91fc631bade88b1d319c51ab5418870802cedb590b709c5ae3c", - "sha256:b34ba24f3e2d0b39b43f0ca62008f7ba962cff51efa56e64ee25c4af6eed987b", - "sha256:bd67cb2c2d9602ad159389c29e4ca964b86fa2f35c2faef54c3eb28b4efd36c8", - "sha256:c0f5e142ef8217019e3eef6ae1b6b55f09a7a15972958d44fbd228214cede567", - "sha256:cdb4272678db803ddf94caa4f94f8672e9a46bae4a44f167095e4d06fec12979", - "sha256:d70916235d478404a3fa8c997b003b5f33aeac4686ac1baa767234a0f8ac2326", - "sha256:d8ce3fb0841763a89322ea0432f1f59a2d3feae07a63ea2c958b2315e1ae8adb", - "sha256:e0b214e57623a535936005797567231a12d0da0c29711eb3514bc2b3cd008d0f", - "sha256:e631c70c9280e3129f071635b81207cad85e6c08e253539467e4ead0e5b219aa", - "sha256:e78578f0c7481c850d1c969aca9a65405887003484d24f6110458fb02cca7747", - "sha256:f0ca86b525264daa5f6b192f216a0d1e860b7383e3da1c65a1908f9c02f42801", - "sha256:f1a68f4f65a9ee64b6ccccb5bf7e17db07caebd2730109cb8a95863cfa9c4e55", - "sha256:fafe841be1103f340a24977f61dee76172e4ae5f647ab9e7fd1e1fca51524f08", - "sha256:ff68fc85355532ea77559ede81f35fff79a6a5543477e168ab3a381887caea76" + "sha256:c7a8a9fdf7d100afa49647eae340e2d23efa382466a8d177efcd1381e9be5598", + "sha256:f66a7073abd93214a20c5f7b32d56843137a7a2e70d02111f3be287035c45370" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.9.0" + }, + "pydantic-core": { + "hashes": [ + "sha256:0102e49ac7d2df3379ef8d658d3bc59d3d769b0bdb17da189b75efa861fc07b4", + "sha256:0123655fedacf035ab10c23450163c2f65a4174f2bb034b188240a6cf06bb123", + "sha256:043ef8469f72609c4c3a5e06a07a1f713d53df4d53112c6d49207c0bd3c3bd9b", + "sha256:0448b81c3dfcde439551bb04a9f41d7627f676b12701865c8a2574bcea034437", + "sha256:05b366fb8fe3d8683b11ac35fa08947d7b92be78ec64e3277d03bd7f9b7cda79", + "sha256:07049ec9306ec64e955b2e7c40c8d77dd78ea89adb97a2013d0b6e055c5ee4c5", + "sha256:084414ffe9a85a52940b49631321d636dadf3576c30259607b75516d131fecd0", + "sha256:086c5db95157dc84c63ff9d96ebb8856f47ce113c86b61065a066f8efbe80acf", + "sha256:12625e69b1199e94b0ae1c9a95d000484ce9f0182f9965a26572f054b1537e44", + "sha256:16b25a4a120a2bb7dab51b81e3d9f3cde4f9a4456566c403ed29ac81bf49744f", + "sha256:19f1352fe4b248cae22a89268720fc74e83f008057a652894f08fa931e77dced", + "sha256:1a2ab4f410f4b886de53b6bddf5dd6f337915a29dd9f22f20f3099659536b2f6", + "sha256:1c7b81beaf7c7ebde978377dc53679c6cba0e946426fc7ade54251dfe24a7604", + "sha256:1cf842265a3a820ebc6388b963ead065f5ce8f2068ac4e1c713ef77a67b71f7c", + "sha256:1eb37f7d6a8001c0f86dc8ff2ee8d08291a536d76e49e78cda8587bb54d8b329", + "sha256:23af245b8f2f4ee9e2c99cb3f93d0e22fb5c16df3f2f643f5a8da5caff12a653", + "sha256:257d6a410a0d8aeb50b4283dea39bb79b14303e0fab0f2b9d617701331ed1515", + "sha256:276ae78153a94b664e700ac362587c73b84399bd1145e135287513442e7dfbc7", + "sha256:2b1a195efd347ede8bcf723e932300292eb13a9d2a3c1f84eb8f37cbbc905b7f", + "sha256:329a721253c7e4cbd7aad4a377745fbcc0607f9d72a3cc2102dd40519be75ed2", + "sha256:358331e21a897151e54d58e08d0219acf98ebb14c567267a87e971f3d2a3be59", + "sha256:3649bd3ae6a8ebea7dc381afb7f3c6db237fc7cebd05c8ac36ca8a4187b03b30", + "sha256:3713dc093d5048bfaedbba7a8dbc53e74c44a140d45ede020dc347dda18daf3f", + "sha256:3ef71ec876fcc4d3bbf2ae81961959e8d62f8d74a83d116668409c224012e3af", + "sha256:41ae8537ad371ec018e3c5da0eb3f3e40ee1011eb9be1da7f965357c4623c501", + "sha256:4a801c5e1e13272e0909c520708122496647d1279d252c9e6e07dac216accc41", + "sha256:4c83c64d05ffbbe12d4e8498ab72bdb05bcc1026340a4a597dc647a13c1605ec", + "sha256:4cebb9794f67266d65e7e4cbe5dcf063e29fc7b81c79dc9475bd476d9534150e", + "sha256:5668b3173bb0b2e65020b60d83f5910a7224027232c9f5dc05a71a1deac9f960", + "sha256:56e6a12ec8d7679f41b3750ffa426d22b44ef97be226a9bab00a03365f217b2b", + "sha256:582871902e1902b3c8e9b2c347f32a792a07094110c1bca6c2ea89b90150caac", + "sha256:5c8aa40f6ca803f95b1c1c5aeaee6237b9e879e4dfb46ad713229a63651a95fb", + "sha256:5d813fd871b3d5c3005157622ee102e8908ad6011ec915a18bd8fde673c4360e", + "sha256:5dd0ec5f514ed40e49bf961d49cf1bc2c72e9b50f29a163b2cc9030c6742aa73", + "sha256:5f3cf3721eaf8741cffaf092487f1ca80831202ce91672776b02b875580e174a", + "sha256:6294907eaaccf71c076abdd1c7954e272efa39bb043161b4b8aa1cd76a16ce43", + "sha256:64d094ea1aa97c6ded4748d40886076a931a8bf6f61b6e43e4a1041769c39dd2", + "sha256:6650a7bbe17a2717167e3e23c186849bae5cef35d38949549f1c116031b2b3aa", + "sha256:67b6655311b00581914aba481729971b88bb8bc7996206590700a3ac85e457b8", + "sha256:6b06c5d4e8701ac2ba99a2ef835e4e1b187d41095a9c619c5b185c9068ed2a49", + "sha256:6ce883906810b4c3bd90e0ada1f9e808d9ecf1c5f0b60c6b8831d6100bcc7dd6", + "sha256:6db09153d8438425e98cdc9a289c5fade04a5d2128faff8f227c459da21b9703", + "sha256:6f80fba4af0cb1d2344869d56430e304a51396b70d46b91a55ed4959993c0589", + "sha256:743e5811b0c377eb830150d675b0847a74a44d4ad5ab8845923d5b3a756d8100", + "sha256:753294d42fb072aa1775bfe1a2ba1012427376718fa4c72de52005a3d2a22178", + "sha256:7568f682c06f10f30ef643a1e8eec4afeecdafde5c4af1b574c6df079e96f96c", + "sha256:7706e15cdbf42f8fab1e6425247dfa98f4a6f8c63746c995d6a2017f78e619ae", + "sha256:785e7f517ebb9890813d31cb5d328fa5eda825bb205065cde760b3150e4de1f7", + "sha256:7a05c0240f6c711eb381ac392de987ee974fa9336071fb697768dfdb151345ce", + "sha256:7ce7eaf9a98680b4312b7cebcdd9352531c43db00fca586115845df388f3c465", + "sha256:7ce8e26b86a91e305858e018afc7a6e932f17428b1eaa60154bd1f7ee888b5f8", + "sha256:7d0324a35ab436c9d768753cbc3c47a865a2cbc0757066cb864747baa61f6ece", + "sha256:7e9b24cca4037a561422bf5dc52b38d390fb61f7bfff64053ce1b72f6938e6b2", + "sha256:810ca06cca91de9107718dc83d9ac4d2e86efd6c02cba49a190abcaf33fb0472", + "sha256:820f6ee5c06bc868335e3b6e42d7ef41f50dfb3ea32fbd523ab679d10d8741c0", + "sha256:82764c0bd697159fe9947ad59b6db6d7329e88505c8f98990eb07e84cc0a5d81", + "sha256:8ae65fdfb8a841556b52935dfd4c3f79132dc5253b12c0061b96415208f4d622", + "sha256:8d5b0ff3218858859910295df6953d7bafac3a48d5cd18f4e3ed9999efd2245f", + "sha256:95d6bf449a1ac81de562d65d180af5d8c19672793c81877a2eda8fde5d08f2fd", + "sha256:964c7aa318da542cdcc60d4a648377ffe1a2ef0eb1e996026c7f74507b720a78", + "sha256:96ef39add33ff58cd4c112cbac076726b96b98bb8f1e7f7595288dcfb2f10b57", + "sha256:a6612c2a844043e4d10a8324c54cdff0042c558eef30bd705770793d70b224aa", + "sha256:a8031074a397a5925d06b590121f8339d34a5a74cfe6970f8a1124eb8b83f4ac", + "sha256:aab9e522efff3993a9e98ab14263d4e20211e62da088298089a03056980a3e69", + "sha256:ae579143826c6f05a361d9546446c432a165ecf1c0b720bbfd81152645cb897d", + "sha256:ae90b9e50fe1bd115b24785e962b51130340408156d34d67b5f8f3fa6540938e", + "sha256:b18cf68255a476b927910c6873d9ed00da692bb293c5b10b282bd48a0afe3ae2", + "sha256:b7efb12e5071ad8d5b547487bdad489fbd4a5a35a0fc36a1941517a6ad7f23e0", + "sha256:c4d9f15ffe68bcd3898b0ad7233af01b15c57d91cd1667f8d868e0eacbfe3f87", + "sha256:c53100c8ee5a1e102766abde2158077d8c374bee0639201f11d3032e3555dfbc", + "sha256:c57e493a0faea1e4c38f860d6862ba6832723396c884fbf938ff5e9b224200e2", + "sha256:c8319e0bd6a7b45ad76166cc3d5d6a36c97d0c82a196f478c3ee5346566eebfd", + "sha256:caffda619099cfd4f63d48462f6aadbecee3ad9603b4b88b60cb821c1b258576", + "sha256:cc0c316fba3ce72ac3ab7902a888b9dc4979162d320823679da270c2d9ad0cad", + "sha256:cdd02a08205dc90238669f082747612cb3c82bd2c717adc60f9b9ecadb540f80", + "sha256:d50ac34835c6a4a0d456b5db559b82047403c4317b3bc73b3455fefdbdc54b0a", + "sha256:d6b9dd6aa03c812017411734e496c44fef29b43dba1e3dd1fa7361bbacfc1354", + "sha256:da3131ef2b940b99106f29dfbc30d9505643f766704e14c5d5e504e6a480c35e", + "sha256:da43cbe593e3c87d07108d0ebd73771dc414488f1f91ed2e204b0370b94b37ac", + "sha256:dd59638025160056687d598b054b64a79183f8065eae0d3f5ca523cde9943940", + "sha256:e1895e949f8849bc2757c0dbac28422a04be031204df46a56ab34bcf98507342", + "sha256:e1a79ad49f346aa1a2921f31e8dbbab4d64484823e813a002679eaa46cba39e1", + "sha256:e460475719721d59cd54a350c1f71c797c763212c836bf48585478c5514d2854", + "sha256:e64ffaf8f6e17ca15eb48344d86a7a741454526f3a3fa56bc493ad9d7ec63936", + "sha256:e6e3ccebdbd6e53474b0bb7ab8b88e83c0cfe91484b25e058e581348ee5a01a5", + "sha256:e758d271ed0286d146cf7c04c539a5169a888dd0b57026be621547e756af55bc", + "sha256:f087879f1ffde024dd2788a30d55acd67959dcf6c431e9d3682d1c491a0eb474", + "sha256:f477d26183e94eaafc60b983ab25af2a809a1b48ce4debb57b343f671b7a90b6", + "sha256:fc535cb898ef88333cf317777ecdfe0faac1c2a3187ef7eb061b6f7ecf7e6bae" + ], + "markers": "python_version >= '3.8'", + "version": "==2.23.2" + }, + "pydantic-settings": { + "hashes": [ + "sha256:bb6849dc067f1687574c12a639e231f3a6feeed0a12d710c1382045c5db1c315", + "sha256:ed81c3a0f46392b4d7c0a565c05884e6e54b3456e6f0fe4d8814981172dc9a88" ], "index": "pypi", - "markers": "python_full_version >= '3.6.1'", - "version": "==1.9.2" + "markers": "python_version >= '3.8'", + "version": "==2.4.0" }, "pyserial": { "hashes": [ @@ -172,22 +251,38 @@ "markers": "python_version >= '3.7'", "version": "==4.2.2" }, + "python-dotenv": { + "hashes": [ + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" + ], + "markers": "python_version >= '3.8'", + "version": "==1.0.1" + }, "setuptools": { "hashes": [ - "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05", - "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78" + "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308", + "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6" ], "markers": "python_version >= '3.8'", - "version": "==69.0.3" + "version": "==74.1.2" }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.12.2" + }, + "tzdata": { + "hashes": [ + "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", + "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.1" }, "wrapt": { "hashes": [ @@ -267,13 +362,21 @@ } }, "develop": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "black": { "hashes": [ @@ -315,114 +418,134 @@ }, "contourpy": { "hashes": [ - "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8", - "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956", - "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5", - "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063", - "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286", - "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a", - "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686", - "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9", - "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f", - "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4", - "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e", - "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0", - "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e", - "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488", - "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399", - "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431", - "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779", - "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9", - "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab", - "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0", - "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd", - "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e", - "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc", - "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6", - "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316", - "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808", - "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0", - "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f", - "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843", - "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9", - "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95", - "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9", - "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de", - "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4", - "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4", - "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa", - "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8", - "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776", - "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41", - "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108", - "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e", - "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8", - "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727", - "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a" + "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2", + "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9", + "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9", + "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4", + "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce", + "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7", + "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f", + "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922", + "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4", + "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e", + "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b", + "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619", + "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205", + "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480", + "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965", + "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c", + "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd", + "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5", + "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f", + "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc", + "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec", + "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd", + "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b", + "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9", + "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe", + "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce", + "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609", + "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8", + "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0", + "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f", + "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8", + "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b", + "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364", + "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040", + "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f", + "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083", + "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df", + "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba", + "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445", + "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da", + "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3", + "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72", + "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02", + "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985" ], "markers": "python_version >= '3.9'", - "version": "==1.2.0" + "version": "==1.2.1" }, "coverage": { "extras": [ "toml" ], "hashes": [ - "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca", - "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471", - "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a", - "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058", - "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85", - "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143", - "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446", - "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590", - "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a", - "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105", - "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9", - "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a", - "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac", - "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25", - "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2", - "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450", - "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932", - "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba", - "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137", - "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae", - "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614", - "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70", - "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e", - "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505", - "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870", - "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc", - "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451", - "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7", - "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e", - "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566", - "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5", - "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26", - "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2", - "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42", - "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555", - "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43", - "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed", - "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa", - "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516", - "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952", - "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd", - "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09", - "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c", - "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f", - "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6", - "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1", - "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0", - "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e", - "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9", - "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9", - "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e", - "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06" + "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", + "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", + "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", + "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989", + "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", + "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", + "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", + "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", + "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", + "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", + "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", + "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", + "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", + "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", + "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", + "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8", + "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", + "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", + "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", + "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", + "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", + "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", + "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", + "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", + "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", + "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0", + "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", + "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", + "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", + "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", + "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", + "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", + "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", + "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", + "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de", + "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", + "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", + "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569", + "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", + "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", + "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", + "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", + "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", + "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6", + "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", + "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", + "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", + "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", + "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", + "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b", + "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", + "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", + "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3", + "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", + "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", + "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", + "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", + "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", + "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", + "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", + "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", + "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", + "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7", + "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", + "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", + "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", + "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", + "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", + "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a", + "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", + "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", + "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc" ], "markers": "python_version >= '3.8'", - "version": "==7.4.0" + "version": "==7.6.1" }, "cycler": { "hashes": [ @@ -434,11 +557,11 @@ }, "exceptiongroup": { "hashes": [ - "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", - "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.0" + "version": "==1.2.2" }, "flake8": { "hashes": [ @@ -478,60 +601,60 @@ }, "fonttools": { "hashes": [ - "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e", - "sha256:0a00bd0e68e88987dcc047ea31c26d40a3c61185153b03457956a87e39d43c37", - "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac", - "sha256:0f750037e02beb8b3569fbff701a572e62a685d2a0e840d75816592280e5feae", - "sha256:13819db8445a0cec8c3ff5f243af6418ab19175072a9a92f6cc8ca7d1452754b", - "sha256:254d9a6f7be00212bf0c3159e0a420eb19c63793b2c05e049eb337f3023c5ecc", - "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b", - "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07", - "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70", - "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71", - "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df", - "sha256:3d71606c9321f6701642bd4746f99b6089e53d7e9817fc6b964e90d9c5f0ecc6", - "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1", - "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670", - "sha256:4c811d3c73b6abac275babb8aa439206288f56fdb2c6f8835e3d7b70de8937a7", - "sha256:4e743935139aa485fe3253fc33fe467eab6ea42583fa681223ea3f1a93dd01e6", - "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635", - "sha256:5465df494f20a7d01712b072ae3ee9ad2887004701b95cb2cc6dcb9c2c97a899", - "sha256:5b60e3afa9635e3dfd3ace2757039593e3bd3cf128be0ddb7a1ff4ac45fa5a50", - "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9", - "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085", - "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb", - "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c", - "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3", - "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184", - "sha256:7ee48bd9d6b7e8f66866c9090807e3a4a56cf43ffad48962725a190e0dd774c8", - "sha256:86e0427864c6c91cf77f16d1fb9bf1bbf7453e824589e8fb8461b6ee1144f506", - "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c", - "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c", - "sha256:94208ea750e3f96e267f394d5588579bb64cc628e321dbb1d4243ffbc291b18b", - "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0", - "sha256:a5d77479fb885ef38a16a253a2f4096bc3d14e63a56d6246bfdb56365a12b20c", - "sha256:a86a5ab2873ed2575d0fcdf1828143cfc6b977ac448e3dc616bb1e3d20efbafa", - "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f", - "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4", - "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c", - "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1", - "sha256:d49ce3ea7b7173faebc5664872243b40cf88814ca3eb135c4a3cdff66af71946", - "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d", - "sha256:eabae77a07c41ae0b35184894202305c3ad211a93b2eb53837c2a1143c8bc952", - "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703", - "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8" + "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122", + "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397", + "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f", + "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d", + "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60", + "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169", + "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8", + "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31", + "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923", + "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2", + "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb", + "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab", + "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb", + "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a", + "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670", + "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8", + "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407", + "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671", + "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88", + "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f", + "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f", + "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0", + "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb", + "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2", + "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d", + "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c", + "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3", + "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719", + "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749", + "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4", + "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f", + "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02", + "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58", + "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1", + "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41", + "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4", + "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb", + "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb", + "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3", + "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d", + "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d", + "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2" ], "markers": "python_version >= '3.8'", - "version": "==4.47.2" + "version": "==4.53.1" }, "hypothesis": { "hashes": [ - "sha256:848ea0952f0bdfd02eac59e41b03f1cbba8fa2cffeffa8db328bbd6cfe159974", - "sha256:955a57e56be4607c81c17ca53e594af54aadeed91e07b88bb7f84e8208ea7739" + "sha256:2beb7a148e95a2067563bcca017d71cc286805c792e43ec5cb155ed6d0a1990d", + "sha256:3b0d080bfd3b303e91388507ac7edebd7039ffcc96ac2cfcdc3c45806352c09f" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==6.96.1" + "version": "==6.96.4" }, "iniconfig": { "hashes": [ @@ -543,155 +666,174 @@ }, "jsonschema": { "hashes": [ - "sha256:5f9c0a719ca2ce14c5de2fd350a64fd2d13e8539db29836a86adc990bb1a068f", - "sha256:8d4a2b7b6c2237e0199c8ea1a6d3e05bf118e289ae2b9d7ba444182a2959560d" + "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", + "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566" ], - "version": "==3.0.2" + "markers": "python_version >= '3.8'", + "version": "==4.23.0" + }, + "jsonschema-specifications": { + "hashes": [ + "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", + "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + ], + "markers": "python_version >= '3.8'", + "version": "==2023.12.1" }, "kiwisolver": { "hashes": [ - "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf", - "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", - "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", - "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", - "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046", - "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", - "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", - "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71", - "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee", - "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", - "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9", - "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", - "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985", - "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea", - "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", - "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89", - "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", - "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", - "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712", - "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342", - "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", - "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958", - "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d", - "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", - "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130", - "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", - "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898", - "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b", - "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f", - "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265", - "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93", - "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929", - "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635", - "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709", - "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", - "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb", - "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a", - "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920", - "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e", - "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544", - "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", - "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390", - "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77", - "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", - "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff", - "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", - "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", - "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", - "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c", - "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", - "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", - "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", - "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc", - "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a", - "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901", - "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", - "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", - "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", - "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad", - "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", - "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29", - "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", - "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250", - "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d", - "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3", - "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54", - "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f", - "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", - "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da", - "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", - "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", - "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523", - "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", - "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205", - "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3", - "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4", - "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", - "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", - "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb", - "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced", - "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd", - "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0", - "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", - "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18", - "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", - "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", - "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333", - "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b", - "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", - "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126", - "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", - "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09", - "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", - "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", - "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7", - "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", - "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9", - "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", - "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", - "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", - "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6", - "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", - "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892", - "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f" + "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", + "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95", + "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5", + "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0", + "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d", + "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18", + "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b", + "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258", + "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95", + "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e", + "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383", + "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02", + "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b", + "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523", + "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee", + "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88", + "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd", + "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb", + "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4", + "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e", + "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c", + "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935", + "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee", + "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e", + "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038", + "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d", + "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b", + "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5", + "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107", + "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f", + "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2", + "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17", + "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb", + "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674", + "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706", + "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327", + "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3", + "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a", + "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2", + "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f", + "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948", + "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3", + "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e", + "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545", + "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc", + "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f", + "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", + "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a", + "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8", + "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750", + "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b", + "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34", + "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225", + "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51", + "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c", + "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3", + "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde", + "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599", + "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c", + "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", + "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6", + "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39", + "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9", + "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933", + "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad", + "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520", + "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1", + "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503", + "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b", + "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36", + "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a", + "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643", + "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", + "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483", + "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf", + "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d", + "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6", + "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644", + "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2", + "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9", + "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2", + "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640", + "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade", + "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a", + "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c", + "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6", + "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00", + "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27", + "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2", + "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4", + "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379", + "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54", + "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09", + "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a", + "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c", + "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89", + "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407", + "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904", + "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376", + "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583", + "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278", + "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a", + "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d", + "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935", + "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb", + "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895", + "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b", + "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417", + "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608", + "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", + "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05", + "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a", + "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d", + "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052" ], - "markers": "python_version >= '3.7'", - "version": "==1.4.5" + "markers": "python_version >= '3.8'", + "version": "==1.4.7" }, "matplotlib": { "hashes": [ - "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1", - "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0", - "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4", - "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7", - "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630", - "sha256:172f4d0fbac3383d39164c6caafd3255ce6fa58f08fc392513a0b1d3b89c4f89", - "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d", - "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717", - "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a", - "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627", - "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31", - "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213", - "sha256:5864bdd7da445e4e5e011b199bb67168cdad10b501750367c496420f2ad00843", - "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788", - "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367", - "sha256:7c48d9e221b637c017232e3760ed30b4e8d5dfd081daf327e829bf2a72c731b4", - "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a", - "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8", - "sha256:aa11b3c6928a1e496c1a79917d51d4cd5d04f8a2e75f21df4949eeefdf697f4b", - "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18", - "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6", - "sha256:bddfb1db89bfaa855912261c805bd0e10218923cc262b9159a49c29a7a1c1afa", - "sha256:c7d36c2209d9136cd8e02fab1c0ddc185ce79bc914c45054a9f514e44c787917", - "sha256:d1095fecf99eeb7384dabad4bf44b965f929a5f6079654b681193edf7169ec20", - "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331", - "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63", - "sha256:deaed9ad4da0b1aea77fe0aa0cebb9ef611c70b3177be936a95e5d01fa05094f", - "sha256:ef8345b48e95cee45ff25192ed1f4857273117917a4dcd48e3905619bcd9c9b8" + "sha256:1c13f041a7178f9780fb61cc3a2b10423d5e125480e4be51beaf62b172413b67", + "sha256:232ce322bfd020a434caaffbd9a95333f7c2491e59cfc014041d95e38ab90d1c", + "sha256:493e9f6aa5819156b58fce42b296ea31969f2aab71c5b680b4ea7a3cb5c07d94", + "sha256:50bac6e4d77e4262c4340d7a985c30912054745ec99756ce213bfbc3cb3808eb", + "sha256:606e3b90897554c989b1e38a258c626d46c873523de432b1462f295db13de6f9", + "sha256:6209e5c9aaccc056e63b547a8152661324404dd92340a6e479b3a7f24b42a5d0", + "sha256:6485ac1f2e84676cff22e693eaa4fbed50ef5dc37173ce1f023daef4687df616", + "sha256:6addbd5b488aedb7f9bc19f91cd87ea476206f45d7116fcfe3d31416702a82fa", + "sha256:72f9322712e4562e792b2961971891b9fbbb0e525011e09ea0d1f416c4645661", + "sha256:7a6769f58ce51791b4cb8b4d7642489df347697cd3e23d88266aaaee93b41d9a", + "sha256:8080d5081a86e690d7688ffa542532e87f224c38a6ed71f8fbed34dd1d9fedae", + "sha256:843cbde2f0946dadd8c5c11c6d91847abd18ec76859dc319362a0964493f0ba6", + "sha256:8aac397d5e9ec158960e31c381c5ffc52ddd52bd9a47717e2a694038167dffea", + "sha256:8f65c9f002d281a6e904976007b2d46a1ee2bcea3a68a8c12dda24709ddc9106", + "sha256:90df07db7b599fe7035d2f74ab7e438b656528c68ba6bb59b7dc46af39ee48ef", + "sha256:9bb0189011785ea794ee827b68777db3ca3f93f3e339ea4d920315a0e5a78d54", + "sha256:a0e47eda4eb2614300fc7bb4657fced3e83d6334d03da2173b09e447418d499f", + "sha256:abc9d838f93583650c35eca41cfcec65b2e7cb50fd486da6f0c49b5e1ed23014", + "sha256:ac24233e8f2939ac4fd2919eed1e9c0871eac8057666070e94cbf0b33dd9c338", + "sha256:b12ba985837e4899b762b81f5b2845bd1a28f4fdd1a126d9ace64e9c4eb2fb25", + "sha256:b7a2a253d3b36d90c8993b4620183b55665a429da8357a4f621e78cd48b2b30b", + "sha256:c7064120a59ce6f64103c9cefba8ffe6fba87f2c61d67c401186423c9a20fd35", + "sha256:c89ee9314ef48c72fe92ce55c4e95f2f39d70208f9f1d9db4e64079420d8d732", + "sha256:cc4ccdc64e3039fc303defd119658148f2349239871db72cd74e2eeaa9b80b71", + "sha256:ce1edd9f5383b504dbc26eeea404ed0a00656c526638129028b758fd43fc5f10", + "sha256:ecd79298550cba13a43c340581a3ec9c707bd895a6a061a78fa2524660482fc0", + "sha256:f51c4c869d4b60d769f7b4406eec39596648d9d70246428745a681c327a8ad30", + "sha256:fb44f53af0a62dc80bba4443d9b27f2fde6acfdac281d95bc872dc148a6509cc" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==3.8.2" + "version": "==3.8.4" }, "mccabe": { "hashes": [ @@ -712,37 +854,37 @@ }, "mypy": { "hashes": [ - "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", - "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", - "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", - "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", - "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", - "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", - "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", - "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", - "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", - "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", - "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", - "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", - "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", - "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", - "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", - "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", - "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", - "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", - "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", - "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", - "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", - "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", - "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", - "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", - "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", - "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", - "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.8.0" + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -781,16 +923,16 @@ }, "opentrons-shared-data": { "editable": true, - "markers": "python_version >= '3.7'", + "markers": "python_version >= '3.10'", "path": "../shared-data/python" }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", + "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==24.1" }, "pathspec": { "hashes": [ @@ -802,93 +944,105 @@ }, "pillow": { "hashes": [ - "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8", - "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39", - "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac", - "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869", - "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e", - "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04", - "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9", - "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e", - "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe", - "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef", - "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56", - "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa", - "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f", - "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f", - "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e", - "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a", - "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2", - "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2", - "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5", - "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a", - "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2", - "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213", - "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563", - "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591", - "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c", - "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2", - "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb", - "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757", - "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0", - "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452", - "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad", - "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01", - "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f", - "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5", - "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61", - "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e", - "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b", - "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068", - "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9", - "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588", - "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483", - "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f", - "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67", - "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7", - "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311", - "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6", - "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72", - "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6", - "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129", - "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13", - "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67", - "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c", - "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516", - "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e", - "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e", - "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364", - "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023", - "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1", - "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04", - "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d", - "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a", - "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7", - "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb", - "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4", - "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e", - "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1", - "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48", - "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868" + "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", + "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", + "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df", + "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", + "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", + "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d", + "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd", + "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", + "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908", + "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", + "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", + "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", + "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b", + "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", + "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a", + "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e", + "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", + "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", + "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b", + "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", + "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", + "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab", + "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", + "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", + "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", + "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", + "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", + "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", + "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", + "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", + "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", + "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", + "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", + "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0", + "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", + "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", + "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", + "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef", + "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680", + "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b", + "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", + "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", + "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", + "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", + "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8", + "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", + "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736", + "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", + "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126", + "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd", + "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5", + "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b", + "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", + "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b", + "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", + "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", + "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2", + "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c", + "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", + "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", + "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", + "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", + "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", + "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b", + "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", + "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3", + "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84", + "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1", + "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", + "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", + "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", + "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", + "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", + "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e", + "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", + "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", + "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", + "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27", + "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", + "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1" ], "markers": "python_version >= '3.8'", - "version": "==10.2.0" + "version": "==10.4.0" }, "platformdirs": { "hashes": [ - "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380", - "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420" + "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", + "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" ], "markers": "python_version >= '3.8'", - "version": "==4.1.0" + "version": "==4.2.2" }, "pluggy": { "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" ], "markers": "python_version >= '3.8'", - "version": "==1.3.0" + "version": "==1.5.0" }, "pycodestyle": { "hashes": [ @@ -900,45 +1054,107 @@ }, "pydantic": { "hashes": [ - "sha256:1061c6ee6204f4f5a27133126854948e3b3d51fcc16ead2e5d04378c199b2f44", - "sha256:19b5686387ea0d1ea52ecc4cffb71abb21702c5e5b2ac626fd4dbaa0834aa49d", - "sha256:2bd446bdb7755c3a94e56d7bdfd3ee92396070efa8ef3a34fab9579fe6aa1d84", - "sha256:328558c9f2eed77bd8fffad3cef39dbbe3edc7044517f4625a769d45d4cf7555", - "sha256:32e0b4fb13ad4db4058a7c3c80e2569adbd810c25e6ca3bbd8b2a9cc2cc871d7", - "sha256:3ee0d69b2a5b341fc7927e92cae7ddcfd95e624dfc4870b32a85568bd65e6131", - "sha256:4aafd4e55e8ad5bd1b19572ea2df546ccace7945853832bb99422a79c70ce9b8", - "sha256:4b3946f87e5cef3ba2e7bd3a4eb5a20385fe36521d6cc1ebf3c08a6697c6cfb3", - "sha256:4de71c718c9756d679420c69f216776c2e977459f77e8f679a4a961dc7304a56", - "sha256:5565a49effe38d51882cb7bac18bda013cdb34d80ac336428e8908f0b72499b0", - "sha256:5803ad846cdd1ed0d97eb00292b870c29c1f03732a010e66908ff48a762f20e4", - "sha256:5da164119602212a3fe7e3bc08911a89db4710ae51444b4224c2382fd09ad453", - "sha256:615661bfc37e82ac677543704437ff737418e4ea04bef9cf11c6d27346606044", - "sha256:78a4d6bdfd116a559aeec9a4cfe77dda62acc6233f8b56a716edad2651023e5e", - "sha256:7d0f183b305629765910eaad707800d2f47c6ac5bcfb8c6397abdc30b69eeb15", - "sha256:7ead3cd020d526f75b4188e0a8d71c0dbbe1b4b6b5dc0ea775a93aca16256aeb", - "sha256:84d76ecc908d917f4684b354a39fd885d69dd0491be175f3465fe4b59811c001", - "sha256:8cb0bc509bfb71305d7a59d00163d5f9fc4530f0881ea32c74ff4f74c85f3d3d", - "sha256:91089b2e281713f3893cd01d8e576771cd5bfdfbff5d0ed95969f47ef6d676c3", - "sha256:9c9e04a6cdb7a363d7cb3ccf0efea51e0abb48e180c0d31dca8d247967d85c6e", - "sha256:a8c5360a0297a713b4123608a7909e6869e1b56d0e96eb0d792c27585d40757f", - "sha256:afacf6d2a41ed91fc631bade88b1d319c51ab5418870802cedb590b709c5ae3c", - "sha256:b34ba24f3e2d0b39b43f0ca62008f7ba962cff51efa56e64ee25c4af6eed987b", - "sha256:bd67cb2c2d9602ad159389c29e4ca964b86fa2f35c2faef54c3eb28b4efd36c8", - "sha256:c0f5e142ef8217019e3eef6ae1b6b55f09a7a15972958d44fbd228214cede567", - "sha256:cdb4272678db803ddf94caa4f94f8672e9a46bae4a44f167095e4d06fec12979", - "sha256:d70916235d478404a3fa8c997b003b5f33aeac4686ac1baa767234a0f8ac2326", - "sha256:d8ce3fb0841763a89322ea0432f1f59a2d3feae07a63ea2c958b2315e1ae8adb", - "sha256:e0b214e57623a535936005797567231a12d0da0c29711eb3514bc2b3cd008d0f", - "sha256:e631c70c9280e3129f071635b81207cad85e6c08e253539467e4ead0e5b219aa", - "sha256:e78578f0c7481c850d1c969aca9a65405887003484d24f6110458fb02cca7747", - "sha256:f0ca86b525264daa5f6b192f216a0d1e860b7383e3da1c65a1908f9c02f42801", - "sha256:f1a68f4f65a9ee64b6ccccb5bf7e17db07caebd2730109cb8a95863cfa9c4e55", - "sha256:fafe841be1103f340a24977f61dee76172e4ae5f647ab9e7fd1e1fca51524f08", - "sha256:ff68fc85355532ea77559ede81f35fff79a6a5543477e168ab3a381887caea76" + "sha256:c7a8a9fdf7d100afa49647eae340e2d23efa382466a8d177efcd1381e9be5598", + "sha256:f66a7073abd93214a20c5f7b32d56843137a7a2e70d02111f3be287035c45370" ], "index": "pypi", - "markers": "python_full_version >= '3.6.1'", - "version": "==1.9.2" + "markers": "python_version >= '3.8'", + "version": "==2.9.0" + }, + "pydantic-core": { + "hashes": [ + "sha256:0102e49ac7d2df3379ef8d658d3bc59d3d769b0bdb17da189b75efa861fc07b4", + "sha256:0123655fedacf035ab10c23450163c2f65a4174f2bb034b188240a6cf06bb123", + "sha256:043ef8469f72609c4c3a5e06a07a1f713d53df4d53112c6d49207c0bd3c3bd9b", + "sha256:0448b81c3dfcde439551bb04a9f41d7627f676b12701865c8a2574bcea034437", + "sha256:05b366fb8fe3d8683b11ac35fa08947d7b92be78ec64e3277d03bd7f9b7cda79", + "sha256:07049ec9306ec64e955b2e7c40c8d77dd78ea89adb97a2013d0b6e055c5ee4c5", + "sha256:084414ffe9a85a52940b49631321d636dadf3576c30259607b75516d131fecd0", + "sha256:086c5db95157dc84c63ff9d96ebb8856f47ce113c86b61065a066f8efbe80acf", + "sha256:12625e69b1199e94b0ae1c9a95d000484ce9f0182f9965a26572f054b1537e44", + "sha256:16b25a4a120a2bb7dab51b81e3d9f3cde4f9a4456566c403ed29ac81bf49744f", + "sha256:19f1352fe4b248cae22a89268720fc74e83f008057a652894f08fa931e77dced", + "sha256:1a2ab4f410f4b886de53b6bddf5dd6f337915a29dd9f22f20f3099659536b2f6", + "sha256:1c7b81beaf7c7ebde978377dc53679c6cba0e946426fc7ade54251dfe24a7604", + "sha256:1cf842265a3a820ebc6388b963ead065f5ce8f2068ac4e1c713ef77a67b71f7c", + "sha256:1eb37f7d6a8001c0f86dc8ff2ee8d08291a536d76e49e78cda8587bb54d8b329", + "sha256:23af245b8f2f4ee9e2c99cb3f93d0e22fb5c16df3f2f643f5a8da5caff12a653", + "sha256:257d6a410a0d8aeb50b4283dea39bb79b14303e0fab0f2b9d617701331ed1515", + "sha256:276ae78153a94b664e700ac362587c73b84399bd1145e135287513442e7dfbc7", + "sha256:2b1a195efd347ede8bcf723e932300292eb13a9d2a3c1f84eb8f37cbbc905b7f", + "sha256:329a721253c7e4cbd7aad4a377745fbcc0607f9d72a3cc2102dd40519be75ed2", + "sha256:358331e21a897151e54d58e08d0219acf98ebb14c567267a87e971f3d2a3be59", + "sha256:3649bd3ae6a8ebea7dc381afb7f3c6db237fc7cebd05c8ac36ca8a4187b03b30", + "sha256:3713dc093d5048bfaedbba7a8dbc53e74c44a140d45ede020dc347dda18daf3f", + "sha256:3ef71ec876fcc4d3bbf2ae81961959e8d62f8d74a83d116668409c224012e3af", + "sha256:41ae8537ad371ec018e3c5da0eb3f3e40ee1011eb9be1da7f965357c4623c501", + "sha256:4a801c5e1e13272e0909c520708122496647d1279d252c9e6e07dac216accc41", + "sha256:4c83c64d05ffbbe12d4e8498ab72bdb05bcc1026340a4a597dc647a13c1605ec", + "sha256:4cebb9794f67266d65e7e4cbe5dcf063e29fc7b81c79dc9475bd476d9534150e", + "sha256:5668b3173bb0b2e65020b60d83f5910a7224027232c9f5dc05a71a1deac9f960", + "sha256:56e6a12ec8d7679f41b3750ffa426d22b44ef97be226a9bab00a03365f217b2b", + "sha256:582871902e1902b3c8e9b2c347f32a792a07094110c1bca6c2ea89b90150caac", + "sha256:5c8aa40f6ca803f95b1c1c5aeaee6237b9e879e4dfb46ad713229a63651a95fb", + "sha256:5d813fd871b3d5c3005157622ee102e8908ad6011ec915a18bd8fde673c4360e", + "sha256:5dd0ec5f514ed40e49bf961d49cf1bc2c72e9b50f29a163b2cc9030c6742aa73", + "sha256:5f3cf3721eaf8741cffaf092487f1ca80831202ce91672776b02b875580e174a", + "sha256:6294907eaaccf71c076abdd1c7954e272efa39bb043161b4b8aa1cd76a16ce43", + "sha256:64d094ea1aa97c6ded4748d40886076a931a8bf6f61b6e43e4a1041769c39dd2", + "sha256:6650a7bbe17a2717167e3e23c186849bae5cef35d38949549f1c116031b2b3aa", + "sha256:67b6655311b00581914aba481729971b88bb8bc7996206590700a3ac85e457b8", + "sha256:6b06c5d4e8701ac2ba99a2ef835e4e1b187d41095a9c619c5b185c9068ed2a49", + "sha256:6ce883906810b4c3bd90e0ada1f9e808d9ecf1c5f0b60c6b8831d6100bcc7dd6", + "sha256:6db09153d8438425e98cdc9a289c5fade04a5d2128faff8f227c459da21b9703", + "sha256:6f80fba4af0cb1d2344869d56430e304a51396b70d46b91a55ed4959993c0589", + "sha256:743e5811b0c377eb830150d675b0847a74a44d4ad5ab8845923d5b3a756d8100", + "sha256:753294d42fb072aa1775bfe1a2ba1012427376718fa4c72de52005a3d2a22178", + "sha256:7568f682c06f10f30ef643a1e8eec4afeecdafde5c4af1b574c6df079e96f96c", + "sha256:7706e15cdbf42f8fab1e6425247dfa98f4a6f8c63746c995d6a2017f78e619ae", + "sha256:785e7f517ebb9890813d31cb5d328fa5eda825bb205065cde760b3150e4de1f7", + "sha256:7a05c0240f6c711eb381ac392de987ee974fa9336071fb697768dfdb151345ce", + "sha256:7ce7eaf9a98680b4312b7cebcdd9352531c43db00fca586115845df388f3c465", + "sha256:7ce8e26b86a91e305858e018afc7a6e932f17428b1eaa60154bd1f7ee888b5f8", + "sha256:7d0324a35ab436c9d768753cbc3c47a865a2cbc0757066cb864747baa61f6ece", + "sha256:7e9b24cca4037a561422bf5dc52b38d390fb61f7bfff64053ce1b72f6938e6b2", + "sha256:810ca06cca91de9107718dc83d9ac4d2e86efd6c02cba49a190abcaf33fb0472", + "sha256:820f6ee5c06bc868335e3b6e42d7ef41f50dfb3ea32fbd523ab679d10d8741c0", + "sha256:82764c0bd697159fe9947ad59b6db6d7329e88505c8f98990eb07e84cc0a5d81", + "sha256:8ae65fdfb8a841556b52935dfd4c3f79132dc5253b12c0061b96415208f4d622", + "sha256:8d5b0ff3218858859910295df6953d7bafac3a48d5cd18f4e3ed9999efd2245f", + "sha256:95d6bf449a1ac81de562d65d180af5d8c19672793c81877a2eda8fde5d08f2fd", + "sha256:964c7aa318da542cdcc60d4a648377ffe1a2ef0eb1e996026c7f74507b720a78", + "sha256:96ef39add33ff58cd4c112cbac076726b96b98bb8f1e7f7595288dcfb2f10b57", + "sha256:a6612c2a844043e4d10a8324c54cdff0042c558eef30bd705770793d70b224aa", + "sha256:a8031074a397a5925d06b590121f8339d34a5a74cfe6970f8a1124eb8b83f4ac", + "sha256:aab9e522efff3993a9e98ab14263d4e20211e62da088298089a03056980a3e69", + "sha256:ae579143826c6f05a361d9546446c432a165ecf1c0b720bbfd81152645cb897d", + "sha256:ae90b9e50fe1bd115b24785e962b51130340408156d34d67b5f8f3fa6540938e", + "sha256:b18cf68255a476b927910c6873d9ed00da692bb293c5b10b282bd48a0afe3ae2", + "sha256:b7efb12e5071ad8d5b547487bdad489fbd4a5a35a0fc36a1941517a6ad7f23e0", + "sha256:c4d9f15ffe68bcd3898b0ad7233af01b15c57d91cd1667f8d868e0eacbfe3f87", + "sha256:c53100c8ee5a1e102766abde2158077d8c374bee0639201f11d3032e3555dfbc", + "sha256:c57e493a0faea1e4c38f860d6862ba6832723396c884fbf938ff5e9b224200e2", + "sha256:c8319e0bd6a7b45ad76166cc3d5d6a36c97d0c82a196f478c3ee5346566eebfd", + "sha256:caffda619099cfd4f63d48462f6aadbecee3ad9603b4b88b60cb821c1b258576", + "sha256:cc0c316fba3ce72ac3ab7902a888b9dc4979162d320823679da270c2d9ad0cad", + "sha256:cdd02a08205dc90238669f082747612cb3c82bd2c717adc60f9b9ecadb540f80", + "sha256:d50ac34835c6a4a0d456b5db559b82047403c4317b3bc73b3455fefdbdc54b0a", + "sha256:d6b9dd6aa03c812017411734e496c44fef29b43dba1e3dd1fa7361bbacfc1354", + "sha256:da3131ef2b940b99106f29dfbc30d9505643f766704e14c5d5e504e6a480c35e", + "sha256:da43cbe593e3c87d07108d0ebd73771dc414488f1f91ed2e204b0370b94b37ac", + "sha256:dd59638025160056687d598b054b64a79183f8065eae0d3f5ca523cde9943940", + "sha256:e1895e949f8849bc2757c0dbac28422a04be031204df46a56ab34bcf98507342", + "sha256:e1a79ad49f346aa1a2921f31e8dbbab4d64484823e813a002679eaa46cba39e1", + "sha256:e460475719721d59cd54a350c1f71c797c763212c836bf48585478c5514d2854", + "sha256:e64ffaf8f6e17ca15eb48344d86a7a741454526f3a3fa56bc493ad9d7ec63936", + "sha256:e6e3ccebdbd6e53474b0bb7ab8b88e83c0cfe91484b25e058e581348ee5a01a5", + "sha256:e758d271ed0286d146cf7c04c539a5169a888dd0b57026be621547e756af55bc", + "sha256:f087879f1ffde024dd2788a30d55acd67959dcf6c431e9d3682d1c491a0eb474", + "sha256:f477d26183e94eaafc60b983ab25af2a809a1b48ce4debb57b343f671b7a90b6", + "sha256:fc535cb898ef88333cf317777ecdfe0faac1c2a3187ef7eb061b6f7ecf7e6bae" + ], + "markers": "python_version >= '3.8'", + "version": "==2.23.2" }, "pydocstyle": { "hashes": [ @@ -958,49 +1174,11 @@ }, "pyparsing": { "hashes": [ - "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", - "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db" + "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c", + "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032" ], "markers": "python_full_version >= '3.6.8'", - "version": "==3.1.1" - }, - "pyrsistent": { - "hashes": [ - "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f", - "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e", - "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958", - "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34", - "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca", - "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d", - "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d", - "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", - "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714", - "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf", - "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee", - "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8", - "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224", - "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d", - "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054", - "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656", - "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7", - "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423", - "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce", - "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e", - "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3", - "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0", - "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f", - "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", - "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce", - "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a", - "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174", - "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86", - "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f", - "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b", - "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98", - "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022" - ], - "markers": "python_version >= '3.8'", - "version": "==0.20.0" + "version": "==3.1.4" }, "pytest": { "hashes": [ @@ -1013,12 +1191,12 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:37a9d912e8338ee7b4a3e917381d1c95bfc8682048cb0fbc35baba316ec1faba", - "sha256:af313ce900a62fbe2b1aed18e37ad757f1ef9940c6b6a88e2954de38d6b1fb9f" + "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", + "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.23.3" + "version": "==0.23.8" }, "pytest-cov": { "hashes": [ @@ -1039,19 +1217,128 @@ }, "python-dateutil": { "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" + "version": "==2.9.0.post0" }, - "setuptools": { + "referencing": { "hashes": [ - "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05", - "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78" + "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", + "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de" ], "markers": "python_version >= '3.8'", - "version": "==69.0.3" + "version": "==0.35.1" + }, + "rpds-py": { + "hashes": [ + "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c", + "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585", + "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5", + "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6", + "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef", + "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2", + "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29", + "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318", + "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b", + "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399", + "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739", + "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee", + "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174", + "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a", + "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344", + "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2", + "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03", + "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5", + "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22", + "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e", + "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96", + "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91", + "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752", + "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075", + "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253", + "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee", + "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad", + "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5", + "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce", + "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7", + "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b", + "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8", + "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57", + "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3", + "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec", + "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209", + "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921", + "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045", + "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074", + "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580", + "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7", + "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5", + "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3", + "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0", + "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24", + "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139", + "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db", + "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc", + "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789", + "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f", + "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2", + "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c", + "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232", + "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6", + "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c", + "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29", + "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489", + "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94", + "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751", + "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2", + "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda", + "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9", + "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51", + "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c", + "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8", + "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989", + "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511", + "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1", + "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2", + "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150", + "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c", + "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965", + "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f", + "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58", + "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b", + "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f", + "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d", + "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821", + "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de", + "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121", + "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855", + "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272", + "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60", + "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02", + "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1", + "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140", + "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879", + "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940", + "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364", + "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4", + "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e", + "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420", + "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5", + "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24", + "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c", + "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf", + "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f", + "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e", + "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab", + "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08", + "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92", + "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a", + "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8" + ], + "markers": "python_version >= '3.8'", + "version": "==0.20.0" }, "six": { "hashes": [ @@ -1085,21 +1372,29 @@ }, "types-mock": { "hashes": [ - "sha256:13ca379d5710ccb3f18f69ade5b08881874cb83383d8fb49b1d4dac9d5c5d090", - "sha256:3d116955495935b0bcba14954b38d97e507cd43eca3e3700fc1b8e4f5c6bf2c7" + "sha256:5281a645d72e827d70043e3cc144fe33b1c003db084f789dc203aa90e812a5a4", + "sha256:d586a01d39ad919d3ddcd73de6cde73ca7f3c69707219f722d1b8d7733641ad7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==5.1.0.20240106" + "version": "==5.1.0.20240425" }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.12.2" + }, + "tzdata": { + "hashes": [ + "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", + "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.1" } } } diff --git a/hardware/mypy.ini b/hardware/mypy.ini index 0fd2feea554..e92fb180b4f 100644 --- a/hardware/mypy.ini +++ b/hardware/mypy.ini @@ -2,6 +2,3 @@ show_error_codes = True warn_unused_configs = True strict = True - -[mypy-can.*] -ignore_missing_imports = True diff --git a/hardware/opentrons_hardware/drivers/can_bus/settings.py b/hardware/opentrons_hardware/drivers/can_bus/settings.py index c36bd791fb9..21ab27cda2d 100644 --- a/hardware/opentrons_hardware/drivers/can_bus/settings.py +++ b/hardware/opentrons_hardware/drivers/can_bus/settings.py @@ -1,11 +1,12 @@ """Driver settings.""" from typing_extensions import Final, TypedDict from typing import Optional -from pydantic import BaseSettings, Field +from pydantic import Field from opentrons_shared_data.errors.exceptions import CANBusConfigurationError from math import floor +from pydantic_settings import BaseSettings, SettingsConfigDict DEFAULT_INTERFACE: Final = "socketcan" @@ -50,24 +51,22 @@ class DriverSettings(BaseSettings): """Settings for driver building.""" interface: str = Field( - DEFAULT_INTERFACE, + default=DEFAULT_INTERFACE, description=f"Can either be {OPENTRONS_INTERFACE} for simple socket " f"or a python can interface.", ) bit_rate: int = Field( - DEFAULT_BITRATE, + default=DEFAULT_BITRATE, description=f"Bit rate. Not applicable to {OPENTRONS_INTERFACE} interface.", ) - channel: str = Field(DEFAULT_CHANNEL, description="The SocketCan channel.") - - host: str = Field(DEFAULT_HOST, description=f"{OPENTRONS_INTERFACE} only.") - port: int = Field(DEFAULT_PORT, description=f"{OPENTRONS_INTERFACE} only.") - fcan_clock: int = Field(DEFAULT_FDCAN_CLK, description="pcan only.") - sample_rate: float = Field(DEFAULT_SAMPLE_RATE, description="pcan only.") - jump_width: int = Field(DEFAULT_JUMP_WIDTH_SEG, descript="pcan only.") - - class Config: # noqa: D106 - env_prefix = "OT3_CAN_DRIVER_" + channel: str = Field(default=DEFAULT_CHANNEL, description="The SocketCan channel.") + + host: str = Field(default=DEFAULT_HOST, description=f"{OPENTRONS_INTERFACE} only.") + port: int = Field(default=DEFAULT_PORT, description=f"{OPENTRONS_INTERFACE} only.") + fcan_clock: int = Field(default=DEFAULT_FDCAN_CLK, description="pcan only.") + sample_rate: float = Field(default=DEFAULT_SAMPLE_RATE, description="pcan only.") + jump_width: int = Field(default=DEFAULT_JUMP_WIDTH_SEG, description="pcan only.") + model_config = SettingsConfigDict(env_prefix="OT3_CAN_DRIVER_") def _check_calculated_bit_timing_values( diff --git a/hardware/opentrons_hardware/hardware_control/tool_sensors.py b/hardware/opentrons_hardware/hardware_control/tool_sensors.py index e9475de8b95..9df634f11b1 100644 --- a/hardware/opentrons_hardware/hardware_control/tool_sensors.py +++ b/hardware/opentrons_hardware/hardware_control/tool_sensors.py @@ -269,6 +269,7 @@ async def liquid_probe( threshold_pascals: float, plunger_impulse_time: float, num_baseline_reads: int, + z_offset_for_plunger_prep: float, sensor_id: SensorId = SensorId.S0, force_both_sensors: bool = False, emplace_data: Optional[ @@ -331,8 +332,9 @@ async def liquid_probe( ) sensor_runner = MoveGroupRunner(move_groups=[[lower_plunger], [sensor_group]]) + # Only raise the z a little so we don't do a huge slow travel raise_z = create_step( - distance={head_node: float64(max_z_distance)}, + distance={head_node: float64(z_offset_for_plunger_prep)}, velocity={head_node: float64(-1 * mount_speed)}, acceleration={}, duration=float64(max_z_distance / mount_speed), diff --git a/hardware/pytest.ini b/hardware/pytest.ini index 9337cd62ee1..2c36b03cf37 100644 --- a/hardware/pytest.ini +++ b/hardware/pytest.ini @@ -5,3 +5,9 @@ markers = requires_emulator: mark test as requiring emulator can_filter_func: can message filtering function asyncio_mode = auto + +filterwarnings = + # Pydantic's shims for its legacy v1 methods (e.g. `BaseModel.construct()`) + # are not type-checked properly. Forbid them, so we're forced to use their newer + # v2 replacements which are type-checked (e.g. ``BaseModel.model_construct()`) + error::pydantic.PydanticDeprecatedSince20 diff --git a/hardware/setup.py b/hardware/setup.py index b463a3fbd20..8d0f621e67c 100644 --- a/hardware/setup.py +++ b/hardware/setup.py @@ -48,6 +48,8 @@ def get_version() -> str: PACKAGES = find_packages(where=".", exclude=["tests.*", "tests"]) INSTALL_REQUIRES = [ "pyserial==3.5", + "pydantic>=2,<3", + "pydantic-settings>=2,<3", f"opentrons_shared_data=={VERSION}", ] diff --git a/hardware/tests/opentrons_hardware/hardware_control/test_tool_sensors.py b/hardware/tests/opentrons_hardware/hardware_control/test_tool_sensors.py index 0c53b81057a..ebcfcc5f05a 100644 --- a/hardware/tests/opentrons_hardware/hardware_control/test_tool_sensors.py +++ b/hardware/tests/opentrons_hardware/hardware_control/test_tool_sensors.py @@ -237,6 +237,7 @@ def move_responder( threshold_pascals=threshold_pascals, plunger_impulse_time=0.2, num_baseline_reads=20, + z_offset_for_plunger_prep=2.0, sensor_id=SensorId.S0, ) assert position[motor_node].positions_only()[0] == 14 diff --git a/opentrons-ai-client/package.json b/opentrons-ai-client/package.json index 9f636be46d9..a75473bd680 100644 --- a/opentrons-ai-client/package.json +++ b/opentrons-ai-client/package.json @@ -22,6 +22,7 @@ "@auth0/auth0-react": "2.2.4", "@fontsource/public-sans": "5.0.3", "@opentrons/components": "link:../components", + "@opentrons/shared-data": "link:../shared-data", "axios": "^0.21.1", "i18next": "^19.8.3", "jotai": "2.8.0", diff --git a/opentrons-ai-client/src/molecules/Accordion/index.tsx b/opentrons-ai-client/src/molecules/Accordion/index.tsx index 4bb611d618d..f45294faa70 100644 --- a/opentrons-ai-client/src/molecules/Accordion/index.tsx +++ b/opentrons-ai-client/src/molecules/Accordion/index.tsx @@ -79,7 +79,7 @@ export function Accordion({ )} diff --git a/opentrons-ai-client/src/molecules/ChatDisplay/index.tsx b/opentrons-ai-client/src/molecules/ChatDisplay/index.tsx index b1b1e9df98a..833596829f8 100644 --- a/opentrons-ai-client/src/molecules/ChatDisplay/index.tsx +++ b/opentrons-ai-client/src/molecules/ChatDisplay/index.tsx @@ -205,14 +205,14 @@ export function ChatDisplay({ chat, chatId }: ChatDisplayProps): JSX.Element { setInputFieldToCorrespondingRequest() }} > - + { setShowFeedbackModal(true) }} > - + { @@ -229,7 +229,7 @@ export function ChatDisplay({ chat, chatId }: ChatDisplayProps): JSX.Element { handleFileDownload() }} > - + ) : null} diff --git a/opentrons-ai-client/src/molecules/ControlledRadioButtonGroup/index.tsx b/opentrons-ai-client/src/molecules/ControlledRadioButtonGroup/index.tsx index 9245c3a1a1a..0b28a6721f4 100644 --- a/opentrons-ai-client/src/molecules/ControlledRadioButtonGroup/index.tsx +++ b/opentrons-ai-client/src/molecules/ControlledRadioButtonGroup/index.tsx @@ -48,7 +48,7 @@ export function ControlledRadioButtonGroup({ { field.onChange(e) diff --git a/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx b/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx index 79e7b822dcc..233af454bc5 100644 --- a/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreview/PromptPreview.stories.tsx @@ -13,7 +13,7 @@ const meta: Meta = { diff --git a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx index 34c986a865d..25b20037064 100644 --- a/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx +++ b/opentrons-ai-client/src/molecules/PromptPreviewSection/index.tsx @@ -72,7 +72,7 @@ export function PromptPreviewSection({ key={`item-tag-${index}`} itemMaxWidth={itemMaxWidth} > - + ) )} diff --git a/opentrons-ai-client/src/organisms/LabwareLiquidsSection/index.tsx b/opentrons-ai-client/src/organisms/LabwareLiquidsSection/index.tsx index 24d50e32f21..a3bc15abbbb 100644 --- a/opentrons-ai-client/src/organisms/LabwareLiquidsSection/index.tsx +++ b/opentrons-ai-client/src/organisms/LabwareLiquidsSection/index.tsx @@ -48,7 +48,7 @@ export function LabwareLiquidsSection(): JSX.Element | null { setDisplayLabwareModal(true) }} text={t('add_opentrons_labware')} - textAlignment={'left'} + textAlignment="left" iconName="plus" /> @@ -77,7 +77,7 @@ export function LabwareLiquidsSection(): JSX.Element | null { setValue(LIQUIDS_FIELD_NAME, [...liquids, '']) }} text={t('add_opentrons_liquid')} - textAlignment={'left'} + textAlignment="left" iconName="plus" /> diff --git a/opentrons-ai-client/src/organisms/LabwareModal/index.tsx b/opentrons-ai-client/src/organisms/LabwareModal/index.tsx index 0fcb6b17de1..d677f6fc14e 100644 --- a/opentrons-ai-client/src/organisms/LabwareModal/index.tsx +++ b/opentrons-ai-client/src/organisms/LabwareModal/index.tsx @@ -144,12 +144,12 @@ export function LabwareModal({ { handleCategoryClick(category) }} > - + diff --git a/opentrons-ai-client/src/pages/UpdateProtocol/index.tsx b/opentrons-ai-client/src/pages/UpdateProtocol/index.tsx index 6d12c3f700a..4538e8b063c 100644 --- a/opentrons-ai-client/src/pages/UpdateProtocol/index.tsx +++ b/opentrons-ai-client/src/pages/UpdateProtocol/index.tsx @@ -285,7 +285,7 @@ export function UpdateProtocol(): JSX.Element { ), }} @@ -304,7 +304,7 @@ export function UpdateProtocol(): JSX.Element { - + + +param ( + [string]$TestKey, + [string]$ExpectedOutput, + [string]$Venv = "venv", + [string]$ResultDir = "results" +) + +# Ensure the result directory exists +if (-not (Test-Path -Path $ResultDir)) { + New-Item -ItemType Directory -Path $ResultDir | Out-Null +} + +$resultFile = Join-Path -Path $ResultDir -ChildPath "$TestKey.txt" + +Write-Output "Activating virtual environment $Venv..." +$venvActivate = Join-Path -Path $Venv -ChildPath "Scripts/Activate.ps1" +if (-not (Test-Path -Path $venvActivate)) { + Write-Error "FAIL: Virtual environment not found at $venv" + exit 1 +} + +# Source the virtual environment +& $venvActivate + +Write-Output "Validating opentrons_simulate --help for test: $TestKey..." + +# Run the command and capture the output and return code +$output = & opentrons_simulate --help 2>&1 +$returnCode = $LASTEXITCODE + +if ($returnCode -ne 0) { + Write-Output "FAIL: Return code is $returnCode, expected 0" | Tee-Object -FilePath $resultFile + Write-Output "Output was:" | Add-Content -Path $resultFile + $output | Add-Content -Path $resultFile + Rename-Item -Path $resultFile -NewName "${resultFile.Substring(0, $resultFile.Length - 4)}_FAIL.txt" + exit 1 +} + +Write-Output "PASS: Return code is $returnCode, expected 0" | Tee-Object -FilePath $resultFile +Write-Output "Output was:" | Add-Content -Path $resultFile +$output | Add-Content -Path $resultFile + +if ($output -match [regex]::Escape($ExpectedOutput)) { + Write-Output "PASS: Output contains expected string" | Tee-Object -FilePath $resultFile -Append + Write-Output "PASS: Test $TestKey completed successfully." | Tee-Object -FilePath $resultFile -Append + exit 0 +} + +Write-Output "FAIL: Output does not contain expected string" | Tee-Object -FilePath $resultFile -Append +exit 1 diff --git a/package-testing/help.sh b/package-testing/help.sh new file mode 100755 index 00000000000..1d0db6f8f99 --- /dev/null +++ b/package-testing/help.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Function to validate `opentrons_simulate --help` +# Arguments: +# 1. Test Key (required) +# 2. Virtual Environment Directory (optional, default: "venv") +# 3. Result Directory (optional, default: "results") +test_opentrons_simulate_help() { + local test_key="$1" + local expected_output="${2}" + local venv="${3:-"venv"}" + local result_dir="${4:-"results"}" + mkdir -p "$result_dir" + local result_file="$result_dir/$test_key.txt" + + echo "Activating virtual environment $venv..." + # shellcheck disable=SC1091 + source "$venv/bin/activate" + + echo "Validating opentrons_simulate --help for test: $test_key..." + + local output + local return_code + output=$(opentrons_simulate --help 2>&1) + return_code=$? + + if [ $return_code -ne 0 ]; then + echo "FAIL: Return code is $return_code, expected 0" | tee "$result_file" + echo "Output was:" >> "$result_file" + echo "$output" >> "$result_file" + mv "$result_file" "${result_file%.txt}_FAIL.txt" + return 1 + fi + + echo "PASS: Return code is $return_code, expected 0" | tee "$result_file" + echo "Output was:" >> "$result_file" + echo "$output" >> "$result_file" + + if echo "$output" | grep -q "$expected_output"; then + echo "PASS: Output contains expected string" | tee -a "$result_file" + echo "PASS: Test $test_key completed successfully." | tee -a "$result_file" + return 0 + fi + echo "FAIL: Output does not contain expected string" | tee -a "$result_file" + return 1 +} + +test_opentrons_simulate_help "$@" \ No newline at end of file diff --git a/package-testing/run_tests.py b/package-testing/run_tests.py new file mode 100644 index 00000000000..ce67266df78 --- /dev/null +++ b/package-testing/run_tests.py @@ -0,0 +1,154 @@ +from dataclasses import dataclass +import os +import platform +import subprocess +import sys +from typing import List, Union + + +@dataclass +class TestConfig: + test_key: str + test_helper: str + + +@dataclass +class TestHelp(TestConfig): + expected_output: str + + +@dataclass +class TestSimulate(TestConfig): + protocol_path: str + expected_return_code: str + + +tests: List[Union[TestHelp, TestSimulate]] = [ + TestHelp( + test_key="help", + test_helper="help", + expected_output="Simulate a protocol for an Opentrons robot" + ), + TestSimulate( + test_key="Flex_v2_19_expect_success", + test_helper="simulate", + protocol_path="../analyses-snapshot-testing/files/protocols/Flex_S_v2_19_Illumina_DNA_Prep_48x.py", + expected_return_code="0", + ), + TestSimulate( + test_key="OT2_v2_20_expect_success", + test_helper="simulate", + protocol_path="../analyses-snapshot-testing/files/protocols/OT2_S_v2_20_8_None_SINGLE_HappyPath.py", + expected_return_code="0", + ), + TestSimulate( + test_key="Flex_v2_16_expect_error", + test_helper="simulate", + protocol_path="../analyses-snapshot-testing/files/protocols/Flex_X_v2_16_P1000_96_TM_ModuleAndWasteChuteConflict.py", + expected_return_code="1", + ), + TestSimulate( + test_key="OT2_v6PD_expect_error", + test_helper="simulate", + protocol_path="../analyses-snapshot-testing/files/protocols/OT2_X_v6_P300M_P20S_HS_MM_TM_TC_AllMods.json", + expected_return_code="1", + ), +] + + +def get_os_type() -> str: + """Determine the current operating system type.""" + return "windows_nt" if "windows" in platform.system().lower() else "unix" + + +def build_command(test: TestConfig, script_ext: str, venv_dir: str, results_dir: str) -> List[str]: + """ + Build the command to run a test based on the test type. + + Args: + test: The test object (either TestHelp or TestSimulate). + script_ext: The script file extension (e.g., "ps1", "sh"). + venv_dir: The virtual environment directory. + results_dir: The directory for storing test results. + + Returns: + A list of strings representing the command to execute. + + Raises: + ValueError: If the test type is unknown. + """ + prefix = ["pwsh", "-File"] if get_os_type() == "windows_nt" else [] + + # Build command based on test type + if isinstance(test, TestHelp): + return prefix + [ + f"./{test.test_helper}.{script_ext}", + test.test_key, + test.expected_output, + venv_dir, + results_dir, + ] + elif isinstance(test, TestSimulate): + return prefix + [ + f"./{test.test_helper}.{script_ext}", + test.test_key, + test.protocol_path, + test.expected_return_code, + venv_dir, + results_dir, + ] + else: + raise ValueError(f"Unknown test type: {type(test)}") + + +def run_test(test: TestConfig, script_ext: str, venv_dir: str, results_dir: str): + """ + Run a single test. + + Args: + test: The test object to run. + script_ext: The script extension (e.g., "ps1", "sh"). + venv_dir: The virtual environment directory. + results_dir: The directory to store test results. + """ + # Ensure results directory exists + os.makedirs(results_dir, exist_ok=True) + + # Build and execute command + command = build_command(test, script_ext, venv_dir, results_dir) + print(f"Running {test.test_key} using {test.test_helper}.{script_ext}...") + try: + subprocess.run(command, check=True, shell=False) + print(f"Test {test.test_key} passed.") + except subprocess.CalledProcessError as e: + print(f"Test {test.test_key} failed: {e}") + sys.exit(1) + + +def main(): + if len(sys.argv) < 4: + print("Usage: python run_tests.py ") + sys.exit(1) + + venv_dir, results_dir, tests_arg = sys.argv[1:4] + os_type = get_os_type() + script_ext = "ps1" if os_type == "windows_nt" else "sh" + + # Filter tests based on user input + if tests_arg.lower() == "all": + selected_tests = tests + else: + requested_keys = tests_arg.split(",") + selected_tests = [test for test in tests if test.test_key in requested_keys] + + if not selected_tests: + print(f"No matching tests found for the provided test keys: {tests_arg}") + sys.exit(1) + + # Run selected tests + for test in selected_tests: + run_test(test, script_ext, venv_dir, results_dir) + + +if __name__ == "__main__": + main() diff --git a/package-testing/setup.ps1 b/package-testing/setup.ps1 new file mode 100644 index 00000000000..afb2258d2b8 --- /dev/null +++ b/package-testing/setup.ps1 @@ -0,0 +1,42 @@ +# Exit immediately on any errors +$ErrorActionPreference = "Stop" + +$VENV_DIR = $null -ne $env:VENV_DIR ? $env:VENV_DIR : "venv" + + +if (Test-Path -Path $VENV_DIR) { + Write-Output "Removing existing virtual environment..." + Remove-Item -Recurse -Force -Path $VENV_DIR +} + +Write-Output "Creating virtual environment in $VENV_DIR..." +python -m venv $VENV_DIR + +Write-Output "Activating virtual environment..." +if ($IsWindows) { + . "$VENV_DIR\Scripts\Activate.ps1" +} else { + . "$VENV_DIR/bin/activate" +} + +Write-Output "Installing packages..." +pip install -U ../shared-data/python ../api + +Write-Output "Validating that opentrons-hardware is not installed..." +$pipList = pip list 2>&1 +if ($pipList -match "opentrons-hardware") { + Write-Output "FAIL: opentrons-hardware is installed" + exit 1 +} else { + Write-Output "PASS: opentrons-hardware is not installed" +} + +Write-Output "Packages installed successfully." +pip list + +Write-Output "To activate the virtual environment, run:" +if ($IsWindows) { + Write-Output ".\$VENV_DIR\Scripts\Activate.ps1" +} else { + Write-Output "source $VENV_DIR/bin/activate" +} diff --git a/package-testing/setup.sh b/package-testing/setup.sh new file mode 100755 index 00000000000..9eefa990cde --- /dev/null +++ b/package-testing/setup.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status +set -e + +VENV_DIR=${VENV_DIR:-"venv"} + +if [ -d "$VENV_DIR" ]; then + echo "Removing existing virtual environment..." + rm -rf "$VENV_DIR" +fi + +echo "Creating virtual environment in $VENV_DIR..." +python -m venv "$VENV_DIR" + +echo "Activating virtual environment..." +# shellcheck disable=SC1091 +source "$VENV_DIR/bin/activate" + +echo "Installing packages..." +pip install -U ../shared-data/python ../api # add ../hardware here to validate the below check + +echo "Validate opentrons-hardware is not installed..." +if pip list 2>/dev/null | grep -q "opentrons-hardware"; then + echo "FAIL: opentrons-hardware is installed" + exit 1 +else + echo "PASS: opentrons-hardware is not installed" +fi + +echo "Packages installed successfully." +pip list + +echo "To activate the virtual environment, run:" +echo "source $VENV_DIR/bin/activate" diff --git a/package-testing/simulate.ps1 b/package-testing/simulate.ps1 new file mode 100644 index 00000000000..ecbb3289b02 --- /dev/null +++ b/package-testing/simulate.ps1 @@ -0,0 +1,45 @@ +param ( + [Parameter(Mandatory = $true)][string]$TestKey, + [Parameter(Mandatory = $true)][string]$ProtocolFilePath, + [Parameter(Mandatory = $true)][int]$ExpectedReturnCode, + [string]$Venv = "venv", + [string]$ResultDir = "results" +) + +# Ensure result directory exists +if (-Not (Test-Path -Path $ResultDir)) { + Write-Output "Creating result directory at $ResultDir..." + New-Item -ItemType Directory -Path $ResultDir | Out-Null +} + +# Construct result file path +$ResultFile = Join-Path -Path $ResultDir -ChildPath "$TestKey.txt" + +Write-Output "Activating virtual environment $Venv..." +if (Test-Path -Path "$Venv/Scripts/Activate.ps1") { + . "$Venv/Scripts/Activate.ps1" +} else { + Write-Error "Virtual environment not found at $Venv." + exit 1 +} + +Write-Output "Running opentrons_simulate for protocol:" +Write-Output $ProtocolFilePath + +# Run opentrons_simulate and capture output +$Output = & opentrons_simulate $ProtocolFilePath 2>&1 +$ReturnCode = $LASTEXITCODE + +# Validate return code +if ($ReturnCode -ne $ExpectedReturnCode) { + Write-Output "FAIL: Return code is $ReturnCode, expected $ExpectedReturnCode" | Tee-Object -FilePath $ResultFile + Add-Content -Path $ResultFile -Value "Output was:" + Add-Content -Path $ResultFile -Value $Output + Rename-Item -Path $ResultFile -NewName "$($ResultFile -replace '\.txt$', '_FAIL.txt')" + exit 1 +} else { + Write-Output "PASS: Return code is $ReturnCode, expected $ExpectedReturnCode" | Tee-Object -FilePath $ResultFile + Add-Content -Path $ResultFile -Value "Output was:" + Add-Content -Path $ResultFile -Value $Output + exit 0 +} diff --git a/package-testing/simulate.sh b/package-testing/simulate.sh new file mode 100755 index 00000000000..6eb7039e06c --- /dev/null +++ b/package-testing/simulate.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Function to test `opentrons_simulate` with a given protocol file +# Arguments: +# 1. Test Key (required) +# 2. Protocol File Path (required) +# 3. Expected return code (required) +# 4. Virtual Environment Directory (optional) +# 5. Result Directory (optional) +simulate_protocol() { + local test_key="$1" + local protocol_file_path="$2" + local expected_return_code="$3" + local venv="${4:-"venv"}" + local result_dir="${5:-"results"}" + mkdir -p "$result_dir" + local result_file="$result_dir/$test_key.txt" + + # echo "test_key: $test_key, protocol_file_path: $protocol_file_path, expected_return_code: $expected_return_code, venv: $venv, result_dir:$result_dir" + # Fail fast if protocol file does not exist + if [ ! -f "$protocol_file_path" ]; then + echo "FAIL: Protocol file not found: $protocol_file_path" + exit 1 + fi + echo "Activating virtual environment $venv ..." + # shellcheck disable=SC1091 + source "$venv/bin/activate" + + printf "Running opentrons_simulate for protocol:\n %s\n" "$protocol_file_path" + + output=$(opentrons_simulate "$protocol_file_path" 2>&1) + return_code=$? + + if [ $return_code -ne "$expected_return_code" ]; then + echo "FAIL: Return code is $return_code, expected $expected_return_code" | tee "$result_file" + echo "Output was:" >> "$result_file" + echo "$output" >> "$result_file" + mv "$result_file" "${result_file%.txt}_FAIL.txt" + exit 1 + else + echo "PASS: Return code is $return_code, expected $expected_return_code" | tee "$result_file" + echo "Output was:" >> "$result_file" + echo "$output" >> "$result_file" + exit 0 + fi +} + +simulate_protocol "$@" diff --git a/package.json b/package.json index 17ee30cc3e4..654f12137fc 100755 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "@vitejs/plugin-react": "4.2.0", - "@vitest/coverage-v8": "1.3.0", + "@vitest/coverage-v8": "2.1.8", "ajv": "6.12.3", "aws-sdk": "^2.493.0", "babel-loader": "^8.2.2", @@ -151,8 +151,8 @@ "typescript": "5.3.3", "url-loader": "^2.1.0", "vite": "5.3.2", - "vitest": "1.2.2", - "vitest-when": "0.3.1", + "vitest": "2.1.8", + "vitest-when": "0.5.0", "wait-on": "^4.0.2", "webpack": "^4.41.6", "webpack-bundle-analyzer": "^3.6.0", diff --git a/performance-metrics/Pipfile b/performance-metrics/Pipfile index b57cd339814..79a023f1dcf 100644 --- a/performance-metrics/Pipfile +++ b/performance-metrics/Pipfile @@ -6,7 +6,7 @@ name = "pypi" [packages] performance-metrics = {file = ".", editable = true} psutil = "==6.0.0" -systemd-python = "234" +systemd-python = {version="==234", markers="sys_platform=='linux'" } [dev-packages] pytest = "==7.4.4" diff --git a/performance-metrics/Pipfile.lock b/performance-metrics/Pipfile.lock index 9bb77d23330..3a70b502d5a 100644 --- a/performance-metrics/Pipfile.lock +++ b/performance-metrics/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "a123325c3bebd1451774ebfadc532a4ca78a92897b80cf1d20ded030e145bac2" + "sha256": "259baa6d557d8069c945cca566e64d0135576284244f14461c67e044366535b5" }, "pipfile-spec": 6, "requires": { @@ -48,18 +48,18 @@ "hashes": [ "sha256:fd0e44bf70eadae45aadc292cb0a7eb5b0b6372cd1b391228047d33895db83e7" ], - "index": "pypi", + "markers": "sys_platform == 'linux'", "version": "==234" } }, "develop": { "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "black": { "hashes": [ @@ -101,11 +101,11 @@ }, "exceptiongroup": { "hashes": [ - "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", - "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.1" + "version": "==1.2.2" }, "flake8": { "hashes": [ @@ -203,11 +203,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pathspec": { "hashes": [ @@ -219,11 +219,11 @@ }, "platformdirs": { "hashes": [ - "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", - "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" ], "markers": "python_version >= '3.8'", - "version": "==4.2.2" + "version": "==4.3.6" }, "pluggy": { "hashes": [ @@ -268,12 +268,12 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b", - "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268" + "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", + "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.23.7" + "version": "==0.23.8" }, "snowballstemmer": { "hashes": [ @@ -284,11 +284,41 @@ }, "tomli": { "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", + "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", + "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", + "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", + "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", + "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", + "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", + "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", + "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", + "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", + "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", + "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", + "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", + "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", + "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", + "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", + "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", + "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", + "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", + "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", + "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", + "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", + "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", + "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", + "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", + "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", + "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", + "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", + "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", + "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", + "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", + "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7" ], "markers": "python_version < '3.11'", - "version": "==2.0.1" + "version": "==2.2.1" }, "types-psutil": { "hashes": [ diff --git a/performance-metrics/src/performance_metrics/system_resource_tracker/__main__.py b/performance-metrics/src/performance_metrics/system_resource_tracker/__main__.py index e810175bfb0..37c15337eba 100644 --- a/performance-metrics/src/performance_metrics/system_resource_tracker/__main__.py +++ b/performance-metrics/src/performance_metrics/system_resource_tracker/__main__.py @@ -2,7 +2,7 @@ import logging import time -import systemd.daemon # type: ignore [import-untyped] +import systemd.daemon # type: ignore [import-untyped,import-not-found,unused-ignore] from .._logging_config import log_init, LOGGER_NAME from ._config import SystemResourceTrackerConfiguration from ._system_resource_tracker import SystemResourceTracker diff --git a/protocol-designer/cypress/e2e/createNew.cy.ts b/protocol-designer/cypress/e2e/createNew.cy.ts index be578415bee..fb1c70470b3 100644 --- a/protocol-designer/cypress/e2e/createNew.cy.ts +++ b/protocol-designer/cypress/e2e/createNew.cy.ts @@ -12,6 +12,7 @@ describe('The Redesigned Create Protocol Landing Page', () => { }) it('content and step 1 flow works', () => { + cy.closeAnalyticsModal() cy.clickCreateNew() cy.verifyCreateNewHeader() verifyCreateProtocolPage() diff --git a/protocol-designer/cypress/e2e/import.cy.ts b/protocol-designer/cypress/e2e/import.cy.ts index 83ddaf0577d..8001f44f7d6 100644 --- a/protocol-designer/cypress/e2e/import.cy.ts +++ b/protocol-designer/cypress/e2e/import.cy.ts @@ -7,6 +7,7 @@ import { describe('The Import Page', () => { beforeEach(() => { cy.visit('/') + cy.closeAnalyticsModal() }) it('successfully loads a protocol exported on a previous version', () => { diff --git a/protocol-designer/cypress/e2e/migrations.cy.ts b/protocol-designer/cypress/e2e/migrations.cy.ts index 61470962bad..eb93db7ed51 100644 --- a/protocol-designer/cypress/e2e/migrations.cy.ts +++ b/protocol-designer/cypress/e2e/migrations.cy.ts @@ -6,6 +6,7 @@ import { TestFilePath } from '../support/testFiles' describe('Protocol fixtures migrate and match snapshots', () => { beforeEach(() => { cy.visit('/') + cy.closeAnalyticsModal() }) const testCases: MigrateTestCase[] = [ diff --git a/protocol-designer/cypress/e2e/settings.cy.ts b/protocol-designer/cypress/e2e/settings.cy.ts index 5ce896aa883..a9802484d89 100644 --- a/protocol-designer/cypress/e2e/settings.cy.ts +++ b/protocol-designer/cypress/e2e/settings.cy.ts @@ -1,6 +1,7 @@ describe('The Settings Page', () => { before(() => { cy.visit('/') + cy.closeAnalyticsModal() }) it('content and toggle state', () => { @@ -19,19 +20,19 @@ describe('The Settings Page', () => { cy.getByTestId('analyticsToggle') .should('exist') .should('be.visible') - .find('path[aria-roledescription="ot-toggle-input-off"]') + .find('path[aria-roledescription="ot-toggle-input-on"]') .should('exist') // Toggle the share sessions with Opentrons setting cy.getByTestId('analyticsToggle').click() cy.getByTestId('analyticsToggle') - .find('path[aria-roledescription="ot-toggle-input-on"]') + .find('path[aria-roledescription="ot-toggle-input-off"]') .should('exist') // Navigate away from the settings page // Then return to see privacy toggle remains toggled on cy.visit('/') cy.openSettingsPage() cy.getByTestId('analyticsToggle').find( - 'path[aria-roledescription="ot-toggle-input-on"]' + 'path[aria-roledescription="ot-toggle-input-off"]' ) // Toggle off editing timeline tips // Navigate away from the settings page diff --git a/protocol-designer/cypress/support/commands.ts b/protocol-designer/cypress/support/commands.ts index 3f9ffd8ddd8..5a40d7762cb 100644 --- a/protocol-designer/cypress/support/commands.ts +++ b/protocol-designer/cypress/support/commands.ts @@ -9,6 +9,7 @@ declare global { verifyFullHeader: () => Cypress.Chainable verifyCreateNewHeader: () => Cypress.Chainable clickCreateNew: () => Cypress.Chainable + closeAnalyticsModal: () => Cypress.Chainable closeAnnouncementModal: () => Cypress.Chainable verifyHomePage: () => Cypress.Chainable importProtocol: (protocolFile: string) => Cypress.Chainable @@ -61,6 +62,7 @@ export const locators = { eula: 'a[href="https://opentrons.com/eula"]', privacyToggle: 'Settings_hotKeys', analyticsToggleTestId: 'analyticsToggle', + confirm: 'Confirm', } // General Custom Commands @@ -111,6 +113,13 @@ Cypress.Commands.add('clickCreateNew', () => { cy.contains(locators.createProtocol).click() }) +Cypress.Commands.add('closeAnalyticsModal', () => { + cy.get('button') + .contains(locators.confirm) + .should('be.visible') + .click({ force: true }) +}) + // Header Import Cypress.Commands.add('importProtocol', (protocolFilePath: string) => { cy.contains(locators.import).click() diff --git a/protocol-designer/fixtures/protocol/8/doItAllV3MigratedToV8.json b/protocol-designer/fixtures/protocol/8/doItAllV3MigratedToV8.json index 4b8ebc3b4db..0a88ef75afc 100644 --- a/protocol-designer/fixtures/protocol/8/doItAllV3MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/doItAllV3MigratedToV8.json @@ -143,7 +143,7 @@ "pauseAction": "untilTime", "pauseMessage": "", "pauseTemperature": null, - "pauseTime": "0:1:2", + "pauseTime": "00:01:02", "id": "5db07ad0-75c7-11ea-b42f-4b64e50f43e5", "stepType": "pause", "stepName": "pause", diff --git a/protocol-designer/fixtures/protocol/8/doItAllV4MigratedToV8.json b/protocol-designer/fixtures/protocol/8/doItAllV4MigratedToV8.json index 441100a7352..bf503ff3219 100644 --- a/protocol-designer/fixtures/protocol/8/doItAllV4MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/doItAllV4MigratedToV8.json @@ -184,7 +184,7 @@ "pauseAction": "untilTime", "pauseMessage": "", "pauseTemperature": null, - "pauseTime": "0:1:2", + "pauseTime": "00:01:02", "id": "5db07ad0-75c7-11ea-b42f-4b64e50f43e5", "stepType": "pause", "stepName": "pause", diff --git a/protocol-designer/fixtures/protocol/8/doItAllV7MigratedToV8.json b/protocol-designer/fixtures/protocol/8/doItAllV7MigratedToV8.json index d89eb64b25d..4ba403c4590 100644 --- a/protocol-designer/fixtures/protocol/8/doItAllV7MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/doItAllV7MigratedToV8.json @@ -251,7 +251,7 @@ "pauseAction": "untilTime", "pauseMessage": "", "pauseTemperature": null, - "pauseTime": "0:1:0", + "pauseTime": "00:01:00", "id": "e3989707-210d-457f-a9bb-a85b3ef9b59c", "stepType": "pause", "stepName": "pause", diff --git a/protocol-designer/fixtures/protocol/8/doItAllV8.json b/protocol-designer/fixtures/protocol/8/doItAllV8.json index 5618fde0e9b..9206bdcf267 100644 --- a/protocol-designer/fixtures/protocol/8/doItAllV8.json +++ b/protocol-designer/fixtures/protocol/8/doItAllV8.json @@ -13,7 +13,7 @@ }, "designerApplication": { "name": "opentrons/protocol-designer", - "version": "8.2.0", + "version": "8.2.2", "data": { "_internalAppBuildDate": "Mon, 11 Nov 2024 20:18:16 GMT", "defaultValues": { diff --git a/protocol-designer/fixtures/protocol/8/example_1_1_0MigratedToV8.json b/protocol-designer/fixtures/protocol/8/example_1_1_0MigratedToV8.json index 985ddf3e2ee..ca6c9b8f9fc 100644 --- a/protocol-designer/fixtures/protocol/8/example_1_1_0MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/example_1_1_0MigratedToV8.json @@ -176,7 +176,7 @@ "pauseAction": "untilTime", "pauseMessage": "Delay plz", "pauseTemperature": null, - "pauseTime": "1:2:3", + "pauseTime": "01:02:03", "id": "2e622080-92a6-11e9-ac62-1b173f839d9e", "stepType": "pause", "stepName": "pause", diff --git a/protocol-designer/fixtures/protocol/8/newAdvancedSettingsAndMultiTemp.json b/protocol-designer/fixtures/protocol/8/newAdvancedSettingsAndMultiTemp.json index 6fe09bdbd02..9c6d24abaca 100644 --- a/protocol-designer/fixtures/protocol/8/newAdvancedSettingsAndMultiTemp.json +++ b/protocol-designer/fixtures/protocol/8/newAdvancedSettingsAndMultiTemp.json @@ -13,7 +13,7 @@ }, "designerApplication": { "name": "opentrons/protocol-designer", - "version": "8.2.0", + "version": "8.2.2", "data": { "_internalAppBuildDate": "Mon, 11 Nov 2024 20:10:44 GMT", "defaultValues": { diff --git a/protocol-designer/fixtures/protocol/8/ninetySixChannelFullAndColumn.json b/protocol-designer/fixtures/protocol/8/ninetySixChannelFullAndColumn.json index cc871cd6917..fe6d08df65c 100644 --- a/protocol-designer/fixtures/protocol/8/ninetySixChannelFullAndColumn.json +++ b/protocol-designer/fixtures/protocol/8/ninetySixChannelFullAndColumn.json @@ -13,7 +13,7 @@ }, "designerApplication": { "name": "opentrons/protocol-designer", - "version": "8.2.0", + "version": "8.2.2", "data": { "_internalAppBuildDate": "Wed, 01 May 2024 13:32:34 GMT", "defaultValues": { diff --git a/protocol-designer/package.json b/protocol-designer/package.json index 547b15de023..8be7d0a965b 100755 --- a/protocol-designer/package.json +++ b/protocol-designer/package.json @@ -21,7 +21,6 @@ "dependencies": { "@fontsource/public-sans": "5.0.3", "@hot-loader/react-dom": "17.0.1", - "@vitejs/plugin-react": "^4.2.1", "@vituum/vite-plugin-postcss": "1.1.0", "@hookform/resolvers": "3.1.1", "@opentrons/components": "link:../components", @@ -34,7 +33,6 @@ "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", "ajv": "6.12.3", - "classnames": "2.2.5", "cookie": "0.3.1", "core-js": "3.2.1", "date-fns": "2.10.0", @@ -62,7 +60,6 @@ "styled-components": "5.3.6", "ua-parser-js": "^0.7.23", "uuid": "3.3.2", - "vite": "5.0.5", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.4", "postcss-apply": "0.12.0", diff --git a/protocol-designer/src/NavigationBar.tsx b/protocol-designer/src/NavigationBar.tsx deleted file mode 100644 index 7ac392f2b4b..00000000000 --- a/protocol-designer/src/NavigationBar.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import type * as React from 'react' -import { useLocation, useNavigate } from 'react-router-dom' -import styled from 'styled-components' -import { useDispatch, useSelector } from 'react-redux' -import { useTranslation } from 'react-i18next' - -import { - ALIGN_CENTER, - Btn, - COLORS, - CURSOR_POINTER, - DIRECTION_COLUMN, - Flex, - JUSTIFY_SPACE_BETWEEN, - SPACING, - StyledText, -} from '@opentrons/components' -import { toggleNewProtocolModal } from './navigation/actions' -import { actions as loadFileActions } from './load-file' -import { BUTTON_LINK_STYLE } from './atoms' -import { getHasUnsavedChanges } from './load-file/selectors' -import { SettingsIcon } from './molecules' -import type { ThunkDispatch } from './types' - -export function NavigationBar(): JSX.Element | null { - const { t } = useTranslation(['shared', 'alert']) - const location = useLocation() - const navigate = useNavigate() - const dispatch: ThunkDispatch = useDispatch() - const loadFile = ( - fileChangeEvent: React.ChangeEvent - ): void => { - dispatch(loadFileActions.loadProtocolFile(fileChangeEvent)) - dispatch(toggleNewProtocolModal(false)) - } - const hasUnsavedChanges = useSelector(getHasUnsavedChanges) - - const handleCreateNew = (): void => { - if ( - !hasUnsavedChanges || - window.confirm(t('alert:confirm_create_new') as string) - ) { - dispatch(toggleNewProtocolModal(true)) - navigate('/createNew') - } - } - - return location.pathname === '/designer' || - location.pathname === '/liquids' ? null : ( - - - - - {t('opentrons')} - - - {t('protocol_designer')} - - - {t('version', { version: process.env.OT_PD_VERSION })} - - - - {location.pathname === '/createNew' ? null : ( - - - {t('create_new')} - - - )} - - - - {t('import')} - - - - - {location.pathname === '/createNew' ? null : } - - - - ) -} - -const StyledLabel = styled.label` - height: 20px; - cursor: ${CURSOR_POINTER}; - input[type='file'] { - display: none; - } -` diff --git a/protocol-designer/src/ProtocolRoutes.tsx b/protocol-designer/src/ProtocolRoutes.tsx index 7350aa0a8da..854eacfa993 100644 --- a/protocol-designer/src/ProtocolRoutes.tsx +++ b/protocol-designer/src/ProtocolRoutes.tsx @@ -1,18 +1,20 @@ import { Route, Navigate, Routes, useNavigate } from 'react-router-dom' import { ErrorBoundary } from 'react-error-boundary' import { Box } from '@opentrons/components' -import { Landing } from './pages/Landing' -import { ProtocolOverview } from './pages/ProtocolOverview' -import { Liquids } from './pages/Liquids' -import { Designer } from './pages/Designer' -import { CreateNewProtocolWizard } from './pages/CreateNewProtocolWizard' -import { NavigationBar } from './NavigationBar' -import { Settings } from './pages/Settings' import { - Kitchen, + CreateNewProtocolWizard, + Designer, + Landing, + Liquids, + ProtocolOverview, + Settings, +} from './pages' +import { FileUploadMessagesModal, - LabwareUploadModal, GateModal, + Kitchen, + LabwareUploadModal, + Navigation, } from './organisms' import { ProtocolDesignerAppFallback } from './resources/ProtocolDesignerAppFallback' @@ -59,9 +61,6 @@ export function ProtocolRoutes(): JSX.Element { path: '/', } const allRoutes: RouteProps[] = [...pdRoutes, landingPage] - const showGateModal = - process.env.NODE_ENV === 'production' || process.env.OT_PD_SHOW_GATE - const navigate = useNavigate() const handleReset = (): void => { navigate('/', { replace: true }) @@ -72,10 +71,10 @@ export function ProtocolRoutes(): JSX.Element { FallbackComponent={ProtocolDesignerAppFallback} onReset={handleReset} > - + - {showGateModal ? : null} + diff --git a/protocol-designer/src/__testing-utils__/renderWithProviders.tsx b/protocol-designer/src/__testing-utils__/renderWithProviders.tsx index 11e3ba16d9b..4c3115281f5 100644 --- a/protocol-designer/src/__testing-utils__/renderWithProviders.tsx +++ b/protocol-designer/src/__testing-utils__/renderWithProviders.tsx @@ -1,6 +1,5 @@ // render using targetted component using @testing-library/react // with wrapping providers for i18next and redux -import type * as React from 'react' import { QueryClient, QueryClientProvider } from 'react-query' import { I18nextProvider } from 'react-i18next' import { Provider } from 'react-redux' @@ -8,16 +7,22 @@ import { vi } from 'vitest' import { render } from '@testing-library/react' import { createStore } from 'redux' +import type { + ComponentProps, + ComponentType, + PropsWithChildren, + ReactElement, +} from 'react' import type { PreloadedState, Store } from 'redux' import type { RenderOptions, RenderResult } from '@testing-library/react' export interface RenderWithProvidersOptions extends RenderOptions { initialState?: State - i18nInstance: React.ComponentProps['i18n'] + i18nInstance: ComponentProps['i18n'] } export function renderWithProviders( - Component: React.ReactElement, + Component: ReactElement, options?: RenderWithProvidersOptions ): [RenderResult, Store] { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions @@ -32,7 +37,7 @@ export function renderWithProviders( const queryClient = new QueryClient() - const ProviderWrapper: React.ComponentType> = ({ + const ProviderWrapper: ComponentType> = ({ children, }) => { const BaseWrapper = ( diff --git a/protocol-designer/src/__tests__/persist.test.ts b/protocol-designer/src/__tests__/persist.test.ts index b6683979c6f..4b396924b8b 100644 --- a/protocol-designer/src/__tests__/persist.test.ts +++ b/protocol-designer/src/__tests__/persist.test.ts @@ -4,12 +4,14 @@ import type { MockInstance } from 'vitest' import * as persist from '../persist' describe('persist', () => { - let getItemSpy: MockInstance - let setItemSpy: MockInstance + let getItemSpy: MockInstance<(key: string) => string | null> + let setItemSpy: MockInstance<(key: string, value: string) => void> beforeEach(() => { const LocalStorageProto = Object.getPrototypeOf(global.localStorage) - getItemSpy = vi.spyOn(LocalStorageProto, 'getItem') + getItemSpy = vi.spyOn(LocalStorageProto, 'getItem') as MockInstance< + (key: string) => string | null + > setItemSpy = vi.spyOn(LocalStorageProto, 'setItem') }) diff --git a/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts b/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts index 5f5c9aa4012..cecf0755c0a 100644 --- a/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts +++ b/protocol-designer/src/analytics/__tests__/reduxActionToAnalyticsEvent.test.ts @@ -154,9 +154,10 @@ describe('reduxActionToAnalyticsEvent', () => { id: 'stepId', pipette: 'pipetteId', otherField: 123, - aspirateFlowRate: undefined, - dispenseFlowRate: undefined, - aspirateAirGap: undefined, + aspirateFlowRate: 'default', + dispenseFlowRate: 'default', + aspirateAirGap: 'default', + dispenseAirGap: 'default', nested: { inner: true }, // de-nested fields __nested__inner: true, diff --git a/protocol-designer/src/analytics/actions.ts b/protocol-designer/src/analytics/actions.ts index a6fa4fc18cf..d1357dcfc05 100644 --- a/protocol-designer/src/analytics/actions.ts +++ b/protocol-designer/src/analytics/actions.ts @@ -1,9 +1,10 @@ +import { OLDEST_MIGRATEABLE_VERSION } from '../load-file/migration' import { setMixpanelTracking } from './mixpanel' import type { AnalyticsEvent } from './mixpanel' export interface SetOptIn { type: 'SET_OPT_IN' - payload: boolean + payload: { hasOptedIn: boolean; appVersion: string } } const _setOptIn = (payload: SetOptIn['payload']): SetOptIn => { @@ -16,12 +17,20 @@ const _setOptIn = (payload: SetOptIn['payload']): SetOptIn => { return { type: 'SET_OPT_IN', - payload, + payload: { hasOptedIn: payload.hasOptedIn, appVersion: payload.appVersion }, } } -export const optIn = (): SetOptIn => _setOptIn(true) -export const optOut = (): SetOptIn => _setOptIn(false) +export const optIn = (): SetOptIn => + _setOptIn({ + hasOptedIn: true, + appVersion: process.env.OT_PD_VERSION || OLDEST_MIGRATEABLE_VERSION, + }) +export const optOut = (): SetOptIn => + _setOptIn({ + hasOptedIn: false, + appVersion: process.env.OT_PD_VERSION || OLDEST_MIGRATEABLE_VERSION, + }) export interface AnalyticsEventAction { type: 'ANALYTICS_EVENT' payload: AnalyticsEvent diff --git a/protocol-designer/src/analytics/middleware.ts b/protocol-designer/src/analytics/middleware.ts index 6c798e353ff..661bb2a65ff 100644 --- a/protocol-designer/src/analytics/middleware.ts +++ b/protocol-designer/src/analytics/middleware.ts @@ -158,9 +158,14 @@ export const reduxActionToAnalyticsEvent = ( name: `${modifiedStepName}Step`, properties: { ...stepArgModified, - aspirateAirGap: stepArgModified.aspirateAirGapVolume, - aspirateFlowRate: stepArgModified.aspirateFlowRateUlSec, - dispenseFlowRate: stepArgModified.dispenseFlowRateUlSec, + aspirateAirGap: + stepArgModified.aspirateAirGapVolume ?? DEFAULT_VALUE, + aspirateFlowRate: + stepArgModified.aspirateFlowRateUlSec ?? DEFAULT_VALUE, + dispenseFlowRate: + stepArgModified.dispenseFlowRateUlSec ?? DEFAULT_VALUE, + dispenseAirGap: + stepArgModified.dispenseAirGapVolume ?? DEFAULT_VALUE, blowoutFlowRate: stepArgModified.blowoutFlowRateUlSec, aspirateOffsetFromBottomMm: stepArgModified.aspirateOffsetFromBottomMm === @@ -202,8 +207,10 @@ export const reduxActionToAnalyticsEvent = ( name: `mixStep`, properties: { ...stepArgModified, - aspirateFlowRate: stepArgModified.aspirateFlowRateUlSec, - dispenseFlowRate: stepArgModified.dispenseFlowRateUlSec, + aspirateFlowRate: + stepArgModified.aspirateFlowRateUlSec ?? DEFAULT_VALUE, + dispenseFlowRate: + stepArgModified.dispenseFlowRateUlSec ?? DEFAULT_VALUE, blowoutFlowRate: stepArgModified.blowoutFlowRateUlSec, aspirateOffsetFromBottomMm: stepArgModified.aspirateOffsetFromBottomMm === @@ -499,7 +506,7 @@ export const trackEventMiddleware: Middleware = ({ // NOTE: this is the Redux state AFTER the action has been fully dispatched const state = getState() - const optedIn = getHasOptedIn(state as BaseState) ?? false + const optedIn = getHasOptedIn(state as BaseState)?.hasOptedIn ?? false const event = reduxActionToAnalyticsEvent(state as BaseState, action) if (event != null) { diff --git a/protocol-designer/src/analytics/mixpanel.ts b/protocol-designer/src/analytics/mixpanel.ts index 6304753d8c7..c15c95a8f51 100644 --- a/protocol-designer/src/analytics/mixpanel.ts +++ b/protocol-designer/src/analytics/mixpanel.ts @@ -1,10 +1,8 @@ -// TODO(IL, 2020-09-09): reconcile with app/src/analytics/mixpanel.js, which this is derived from import mixpanel from 'mixpanel-browser' import { getIsProduction } from '../networking/opentronsWebApi' import { getHasOptedIn } from './selectors' import type { BaseState } from '../types' -// TODO(IL, 2020-09-09): AnalyticsEvent type copied from app/src/analytics/types.js, consider merging export type AnalyticsEvent = | { name: string @@ -19,18 +17,20 @@ const MIXPANEL_ID = getIsProduction() : process.env.OT_PD_MIXPANEL_DEV_ID const MIXPANEL_OPTS = { - // opt out by default opt_out_tracking_by_default: true, } export function initializeMixpanel(state: BaseState): void { - const optedIn = getHasOptedIn(state) ?? false + const optedIn = getHasOptedIn(state)?.hasOptedIn ?? false if (MIXPANEL_ID != null) { - console.debug('Initializing Mixpanel', { optedIn }) - - mixpanel.init(MIXPANEL_ID, MIXPANEL_OPTS) - setMixpanelTracking(optedIn) - trackEvent({ name: 'appOpen', properties: {} }, optedIn) // TODO IMMEDIATELY: do we want this? + try { + console.debug('Initializing Mixpanel', { optedIn }) + mixpanel.init(MIXPANEL_ID, MIXPANEL_OPTS) + setMixpanelTracking(optedIn) + trackEvent({ name: 'appOpen', properties: {} }, optedIn) + } catch (error) { + console.error('Error initializing Mixpanel:', error) + } } else { console.warn('MIXPANEL_ID not found; this is a bug if build is production') } @@ -40,32 +40,38 @@ export function initializeMixpanel(state: BaseState): void { export function trackEvent(event: AnalyticsEvent, optedIn: boolean): void { console.debug('Trackable event', { event, optedIn }) if (MIXPANEL_ID != null && optedIn) { - if ('superProperties' in event && event.superProperties != null) { - mixpanel.register(event.superProperties) - } - if ('name' in event && event.name != null) { - mixpanel.track(event.name, event.properties) + try { + if ('superProperties' in event && event.superProperties != null) { + mixpanel.register(event.superProperties) + } + if ('name' in event && event.name != null) { + mixpanel.track(event.name, event.properties) + } + } catch (error) { + console.error('Error tracking event:', error) } } } export function setMixpanelTracking(optedIn: boolean): void { if (MIXPANEL_ID != null) { - if (optedIn) { - console.debug('User has opted into analytics; tracking with Mixpanel') - mixpanel.opt_in_tracking() - // Register "super properties" which are included with all events - mixpanel.register({ - appVersion: process.env.OT_PD_VERSION, - // NOTE(IL, 2020): Since PD may be in the same Mixpanel project as other OT web apps, this 'appName' property is intended to distinguish it - appName: 'protocolDesigner', - }) - } else { - console.debug( - 'User has opted out of analytics; stopping Mixpanel tracking' - ) - mixpanel.opt_out_tracking() - mixpanel.reset() + try { + if (optedIn) { + console.debug('User has opted into analytics; tracking with Mixpanel') + mixpanel.opt_in_tracking() + mixpanel.register({ + appVersion: process.env.OT_PD_VERSION, + appName: 'protocolDesigner', + }) + } else { + console.debug( + 'User has opted out of analytics; stopping Mixpanel tracking' + ) + mixpanel.opt_out_tracking() + mixpanel.reset() + } + } catch (error) { + console.error('Error setting Mixpanel tracking:', error) } } } diff --git a/protocol-designer/src/analytics/reducers.ts b/protocol-designer/src/analytics/reducers.ts index 6d4f71ebaa6..6aff3b00a84 100644 --- a/protocol-designer/src/analytics/reducers.ts +++ b/protocol-designer/src/analytics/reducers.ts @@ -4,8 +4,14 @@ import type { Reducer } from 'redux' import type { Action } from '../types' import type { SetOptIn } from './actions' import type { RehydratePersistedAction } from '../persist' -type OptInState = boolean | null -const optInInitialState = null +export interface OptInState { + hasOptedIn: boolean + appVersion?: string +} +const optInInitialState: OptInState = { + hasOptedIn: true, +} + // @ts-expect-error(sb, 2021-6-17): cannot use string literals as action type // TODO IMMEDIATELY: refactor this to the old fashioned way if we cannot have type safety: https://github.com/redux-utilities/redux-actions/issues/282#issuecomment-595163081 const hasOptedIn: Reducer = handleActions( @@ -17,7 +23,11 @@ const hasOptedIn: Reducer = handleActions( action: RehydratePersistedAction ) => { const persistedState = action.payload?.['analytics.hasOptedIn'] - return persistedState !== undefined ? persistedState : optInInitialState + if (persistedState == null || persistedState?.hasOptedIn == null) { + return optInInitialState + } else { + return persistedState + } }, }, optInInitialState diff --git a/protocol-designer/src/analytics/selectors.ts b/protocol-designer/src/analytics/selectors.ts index 77a37bbfcb1..e98c11e3ab7 100644 --- a/protocol-designer/src/analytics/selectors.ts +++ b/protocol-designer/src/analytics/selectors.ts @@ -1,3 +1,4 @@ import type { BaseState } from '../types' -export const getHasOptedIn = (state: BaseState): boolean | null => +import type { OptInState } from './reducers' +export const getHasOptedIn = (state: BaseState): OptInState => state.analytics.hasOptedIn diff --git a/protocol-designer/src/assets/localization/en/application.json b/protocol-designer/src/assets/localization/en/application.json index 0cbdb9cc6d3..c825037224b 100644 --- a/protocol-designer/src/assets/localization/en/application.json +++ b/protocol-designer/src/assets/localization/en/application.json @@ -5,12 +5,14 @@ "cancel": "cancel", "date_created": "Date Created", "description": "Description", + "dest": "Destination", "edit": "edit", "exit_batch_edit": "exit batch edit", "go_back": "Go back", "information": "Information", "labware": "labware", "last_exported": "Last Exported", + "location": "Location", "magnet_height_caption": "Must be between {{low}} to {{high}}.", "magnet_recommended": "The recommended height is {{default}}", "manually": "Manually", @@ -37,11 +39,15 @@ "moveLabware": "move", "moveLiquid": "transfer", "pause": "pause", + "plateReader": "absorbance reader", "profile_settings": "profile settings", "profile_steps": "profile steps", "temperature": "temperature", "thermocycler": "thermocycler" }, + "select": "Select", + "selected": "Selected", + "source": "Source", "temperature": "Temperature (˚C)", "time": "Time", "units": { diff --git a/protocol-designer/src/assets/localization/en/button.json b/protocol-designer/src/assets/localization/en/button.json index bedb95b28c1..af643b9b815 100644 --- a/protocol-designer/src/assets/localization/en/button.json +++ b/protocol-designer/src/assets/localization/en/button.json @@ -1,5 +1,5 @@ { - "add_step": "+ Add Step", + "add_step": "Add Step", "add_off_deck": "+ Off-deck labware", "cancel": "cancel", "close": "close", diff --git a/protocol-designer/src/assets/localization/en/feature_flags.json b/protocol-designer/src/assets/localization/en/feature_flags.json index 9a13b327be8..92a074088ba 100644 --- a/protocol-designer/src/assets/localization/en/feature_flags.json +++ b/protocol-designer/src/assets/localization/en/feature_flags.json @@ -31,5 +31,9 @@ "OT_PD_ENABLE_REACT_SCAN": { "title": "Enable React Scan", "description": "Enable React Scan support for components rendering check" + }, + "OT_PD_ENABLE_LIQUID_CLASSES": { + "title": "Enable liquid classes", + "description": "Enable liquid classes support" } } diff --git a/protocol-designer/src/assets/localization/en/liquids.json b/protocol-designer/src/assets/localization/en/liquids.json index ebd4800dacb..36a31cf8116 100644 --- a/protocol-designer/src/assets/localization/en/liquids.json +++ b/protocol-designer/src/assets/localization/en/liquids.json @@ -10,6 +10,10 @@ "display_color": "Color", "liquid_volume": "Liquid volume by well", "liquid": "Liquid", + "liquid_class": { + "title": "Liquid class", + "tooltip": "Applies predefined pipetting settings to transfer and mix steps using this liquid" + }, "liquids_added": "Liquids added", "liquids": "Liquids", "microliters": "µL", diff --git a/protocol-designer/src/assets/localization/en/modal.json b/protocol-designer/src/assets/localization/en/modal.json index 2c065e2d6b6..8530f61a28a 100644 --- a/protocol-designer/src/assets/localization/en/modal.json +++ b/protocol-designer/src/assets/localization/en/modal.json @@ -52,7 +52,7 @@ "body6": "All protocols require {{app}} version 7.3.0 or later to run." }, "redesign": { - "body1": "Welcome to Protocol Designer 8.2.0!", + "body1": "Welcome to Protocol Designer {{version}}!", "body2": "We’re excited to release the new Opentrons Protocol Designer, now with a fresh redesign! Enjoy the same functionality with the added ability to:", "body3": "Add multiple Heater-Shaker Modules and Magnetic Blocks to the deck (Flex only).", "body4": "All protocols now require Opentrons App version 8.2.0+ to run.", diff --git a/protocol-designer/src/assets/localization/en/shared.json b/protocol-designer/src/assets/localization/en/shared.json index 58579da36ff..212ae62bf05 100644 --- a/protocol-designer/src/assets/localization/en/shared.json +++ b/protocol-designer/src/assets/localization/en/shared.json @@ -19,6 +19,7 @@ "destination_well": "Destination Well", "developer_ff": "Developer Feature Flags", "done": "Done", + "download_protocol": "Download protocol", "edit_existing": "Edit existing protocol", "edit_instruments": "Edit Instruments", "edit_pipette": "Edit Pipette", @@ -101,7 +102,7 @@ "only_tiprack": "Incompatible file type", "opentrons_flex": "Opentrons Flex", "opentrons": "Opentrons", - "opentrons_collects_data": "In order to improve our products, Opentrons would like to collect data related to your use of Protocol Designer. With your consent, Opentrons will collect and store analytics and session data, including through the use of cookies and similar technologies, solely for the purpose enhancing our products.", + "opentrons_collects_data": "In order to improve our products, Opentrons would like to collect data related to your use of Protocol Designer. Opentrons will collect and store analytics and session data, including through the use of cookies and similar technologies, solely for the purpose enhancing our products.", "ot2": "Opentrons OT-2", "overwrite_labware": "Overwrite labware", "overwrite": "Click Overwrite to replace the existing labware with the new labware.", diff --git a/protocol-designer/src/assets/localization/en/starting_deck_state.json b/protocol-designer/src/assets/localization/en/starting_deck_state.json index 7e0ba531843..acf45939403 100644 --- a/protocol-designer/src/assets/localization/en/starting_deck_state.json +++ b/protocol-designer/src/assets/localization/en/starting_deck_state.json @@ -22,6 +22,7 @@ "custom": "Custom labware definitions", "customize_slot": "Customize slot", "deck_hardware": "Deck hardware", + "deck_slots_full": "Deck slots are full", "define_liquid": "Define a liquid", "done": "Done", "double_click_to_edit": "Double-click to edit", @@ -33,6 +34,7 @@ "edit_slot": "Edit slot", "edit": "Edit", "gen1_gen2_different_units": "Switching between GEN1 and GEN2 Magnetic Modules will clear all non-default engage heights from existing magnet steps in your protocol. GEN1 and GEN2 Magnetic Modules do not use the same units.", + "gripper_required_for_plate_reader": "Gripper required to add absorbance reader", "heater_shaker_adjacent_to": "A module is adjacent to this slot. The Heater-Shaker cannot be placed next to a module", "heater_shaker_adjacent": "A Heater-Shaker is adjacent to this slot. Modules cannot be placed next to a Heater-Shaker", "heater_shaker_trash": "The heater-shaker cannot be next to the trash bin", @@ -47,6 +49,7 @@ "onDeck": "On deck", "one_item": "No more than 1 {{hardware}} allowed on the deck at one time", "only_display_rec": "Only display recommended labware", + "plate_reader_no_labware": "Labware cannot be loaded onto a plate reader. You can move labware onto the plate reader when building your protocol", "protocol_starting_deck": "Protocol starting deck", "read_more_gen1_gen2": "Read more about the differences between GEN1 and GEN2 Magnetic Modules", "rename_lab": "Rename labware", diff --git a/protocol-designer/src/atoms/ToggleButton/index.tsx b/protocol-designer/src/atoms/ToggleButton/index.tsx index 0dfb605ec7b..732430b9d4e 100644 --- a/protocol-designer/src/atoms/ToggleButton/index.tsx +++ b/protocol-designer/src/atoms/ToggleButton/index.tsx @@ -1,8 +1,8 @@ -import type * as React from 'react' import { css } from 'styled-components' import { Btn, Icon, COLORS, Flex } from '@opentrons/components' +import type { MouseEvent } from 'react' import type { StyleProps } from '@opentrons/components' const TOGGLE_DISABLED_STYLES = css` @@ -42,7 +42,7 @@ interface ToggleButtonProps extends StyleProps { label?: string | null disabled?: boolean | null id?: string - onClick?: (e: React.MouseEvent) => void + onClick?: (e: MouseEvent) => void } export function ToggleButton(props: ToggleButtonProps): JSX.Element { diff --git a/protocol-designer/src/atoms/constants.ts b/protocol-designer/src/atoms/constants.ts index 620a3d10dbc..31fd22da638 100644 --- a/protocol-designer/src/atoms/constants.ts +++ b/protocol-designer/src/atoms/constants.ts @@ -6,9 +6,20 @@ import { } from '@opentrons/components' import type { FlattenSimpleInterpolation } from 'styled-components' -export const BUTTON_LINK_STYLE = css` - color: ${COLORS.grey60}; +export const LINK_BUTTON_STYLE = css` + color: ${COLORS.black90}; + &:hover { + color: ${COLORS.blue50}; + } + + &:focus-visible { + color: ${COLORS.blue50}; + outline: 2px solid ${COLORS.blue50}; + outline-offset: 0.25rem; + } + + &:disabled { color: ${COLORS.grey40}; } ` @@ -32,3 +43,5 @@ export const COLUMN_STYLE = css` min-width: calc((${MIN_OVERVIEW_WIDTH} - ${COLUMN_GRID_GAP}) * 0.5); flex: 1; ` + +export const NAV_BAR_HEIGHT_REM = 4 diff --git a/protocol-designer/src/configureStore.ts b/protocol-designer/src/configureStore.ts index f7c31f00810..9f049628c0a 100644 --- a/protocol-designer/src/configureStore.ts +++ b/protocol-designer/src/configureStore.ts @@ -89,9 +89,9 @@ export function configureStore(): StoreType { /* preloadedState, */ composeEnhancers( applyMiddleware( - trackEventMiddleware as Middleware, any>, + thunk, timelineMiddleware as Middleware, any>, - thunk + trackEventMiddleware as Middleware, any> ) ) as StoreEnhancer ) diff --git a/protocol-designer/src/feature-flags/reducers.ts b/protocol-designer/src/feature-flags/reducers.ts index 42782c88479..bcffd39c5e7 100644 --- a/protocol-designer/src/feature-flags/reducers.ts +++ b/protocol-designer/src/feature-flags/reducers.ts @@ -30,6 +30,8 @@ const initialFlags: Flags = { OT_PD_ENABLE_HOT_KEYS_DISPLAY: process.env.OT_PD_ENABLE_HOT_KEYS_DISPLAY === '1' || true, OT_PD_ENABLE_REACT_SCAN: process.env.OT_PD_ENABLE_REACT_SCAN === '1' || false, + OT_PD_ENABLE_LIQUID_CLASSES: + process.env.OT_PD_ENABLE_REACT_SCAN === '1' || false, } // @ts-expect-error(sa, 2021-6-10): cannot use string literals as action type // TODO IMMEDIATELY: refactor this to the old fashioned way if we cannot have type safety: https://github.com/redux-utilities/redux-actions/issues/282#issuecomment-595163081 diff --git a/protocol-designer/src/feature-flags/selectors.ts b/protocol-designer/src/feature-flags/selectors.ts index 72b25fca895..6b8a70f8b30 100644 --- a/protocol-designer/src/feature-flags/selectors.ts +++ b/protocol-designer/src/feature-flags/selectors.ts @@ -45,3 +45,7 @@ export const getEnableReactScan: Selector = createSelector( getFeatureFlagData, flags => flags.OT_PD_ENABLE_REACT_SCAN ?? false ) +export const getEnableLiquidClasses: Selector = createSelector( + getFeatureFlagData, + flags => flags.OT_PD_ENABLE_LIQUID_CLASSES ?? false +) diff --git a/protocol-designer/src/feature-flags/types.ts b/protocol-designer/src/feature-flags/types.ts index 84bab18e474..6840786d149 100644 --- a/protocol-designer/src/feature-flags/types.ts +++ b/protocol-designer/src/feature-flags/types.ts @@ -36,6 +36,7 @@ export type FlagTypes = | 'OT_PD_ENABLE_RETURN_TIP' | 'OT_PD_ENABLE_HOT_KEYS_DISPLAY' | 'OT_PD_ENABLE_REACT_SCAN' + | 'OT_PD_ENABLE_LIQUID_CLASSES' // flags that are not in this list only show in prerelease mode export const userFacingFlags: FlagTypes[] = [ 'OT_PD_DISABLE_MODULE_RESTRICTIONS', @@ -49,5 +50,6 @@ export const allFlags: FlagTypes[] = [ 'OT_PD_ENABLE_COMMENT', 'OT_PD_ENABLE_RETURN_TIP', 'OT_PD_ENABLE_REACT_SCAN', + 'OT_PD_ENABLE_LIQUID_CLASSES', ] export type Flags = Partial> diff --git a/protocol-designer/src/form-types.ts b/protocol-designer/src/form-types.ts index 1a9962ae582..3d6f1bcb38d 100644 --- a/protocol-designer/src/form-types.ts +++ b/protocol-designer/src/form-types.ts @@ -143,6 +143,7 @@ export type StepType = | 'pause' | 'temperature' | 'thermocycler' + | 'plateReader' export const stepIconsByType: Record = { comment: 'comment', @@ -155,6 +156,7 @@ export const stepIconsByType: Record = { temperature: 'ot-temperature-v2', thermocycler: 'ot-thermocycler', heaterShaker: 'ot-heater-shaker', + plateReader: 'ot-absorbance', } // ===== Unprocessed form types ===== export interface AnnotationFields { @@ -360,7 +362,7 @@ export interface HydratedTemperatureFormData { targetTemperature: string | null } export interface HydratedHeaterShakerFormData { - heaterShakerSetTimer: 'true' | 'false' | null + heaterShakerSetTimer: boolean | null heaterShakerTimer: string | null id: string latchOpen: boolean diff --git a/protocol-designer/src/labware-defs/actions.ts b/protocol-designer/src/labware-defs/actions.ts index 20959a37b5d..5a6526d7956 100644 --- a/protocol-designer/src/labware-defs/actions.ts +++ b/protocol-designer/src/labware-defs/actions.ts @@ -11,6 +11,8 @@ import { } from '@opentrons/shared-data' import { getAllWellSetsForLabware } from '../utils' import * as labwareDefSelectors from './selectors' + +import type { SyntheticEvent } from 'react' import type { ThunkAction } from '../types' import type { LabwareUploadMessage } from './types' import type { LabwareDefinition2 } from '@opentrons/shared-data' @@ -89,7 +91,7 @@ const getIsOverwriteMismatched = ( const _createCustomLabwareDef: ( onlyTiprack: boolean ) => ( - event: React.SyntheticEvent + event: SyntheticEvent ) => ThunkAction = onlyTiprack => event => (dispatch, getState) => { const customLabwareDefs = values( labwareDefSelectors.getCustomLabwareDefsByURI(getState()) @@ -242,11 +244,11 @@ const _createCustomLabwareDef: ( } export const createCustomLabwareDef: ( - event: React.SyntheticEvent + event: SyntheticEvent ) => ThunkAction = _createCustomLabwareDef(false) export const createCustomTiprackDef: ( - event: React.SyntheticEvent + event: SyntheticEvent ) => ThunkAction = _createCustomLabwareDef(true) interface DismissLabwareUploadMessage { diff --git a/protocol-designer/src/labware-ingred/__tests__/ingredients.test.ts b/protocol-designer/src/labware-ingred/__tests__/ingredients.test.ts index b771cd16bbf..c81883001ac 100644 --- a/protocol-designer/src/labware-ingred/__tests__/ingredients.test.ts +++ b/protocol-designer/src/labware-ingred/__tests__/ingredients.test.ts @@ -19,6 +19,7 @@ describe('DUPLICATE_LABWARE action', () => { wellDetailsByLocation: null, concentration: '50 mol/ng', description: '', + liquidClass: null, displayColor: '#b925ff', serialize: false, }, @@ -27,6 +28,7 @@ describe('DUPLICATE_LABWARE action', () => { wellDetailsByLocation: null, concentration: '100%', description: '', + liquidClass: null, displayColor: '#ffd600', serialize: false, }, diff --git a/protocol-designer/src/labware-ingred/selectors.ts b/protocol-designer/src/labware-ingred/selectors.ts index eb7767af225..80325e33b96 100644 --- a/protocol-designer/src/labware-ingred/selectors.ts +++ b/protocol-designer/src/labware-ingred/selectors.ts @@ -4,7 +4,7 @@ import mapValues from 'lodash/mapValues' import max from 'lodash/max' import reduce from 'lodash/reduce' import type { Selector } from 'reselect' -import type { Options } from '@opentrons/components' +import type { DropdownOption } from '@opentrons/components' import type { LabwareLiquidState } from '@opentrons/step-generation' import type { CutoutId } from '@opentrons/shared-data' import type { @@ -59,18 +59,18 @@ const getLiquidNamesById: Selector< string > ) -const getLiquidSelectionOptions: Selector = createSelector( - getLiquidGroupsById, - liquidGroupsById => { - return Object.keys(liquidGroupsById).map(id => ({ - // NOTE: if these fallbacks are used, it's a bug - name: liquidGroupsById[id] - ? liquidGroupsById[id].name || `(Unnamed Liquid: ${String(id)})` - : 'Missing Liquid', - value: id, - })) - } -) +const getLiquidSelectionOptions: Selector< + RootSlice, + DropdownOption[] +> = createSelector(getLiquidGroupsById, liquidGroupsById => { + return Object.keys(liquidGroupsById).map(id => ({ + // NOTE: if these fallbacks are used, it's a bug + name: liquidGroupsById[id] + ? liquidGroupsById[id].name || `(Unnamed Liquid: ${String(id)})` + : 'Missing Liquid', + value: id, + })) +}) // false or selected slot to add labware to, eg 'A2' const selectedAddLabwareSlot = (state: BaseState): DeckSlot | false => @@ -113,6 +113,7 @@ const allIngredientNamesIds: Selector< ingredientId: ingredId, name: ingreds[ingredId].name, displayColor: ingreds[ingredId].displayColor, + liquidClass: ingreds[ingredId].liquidClass, })) }) const getLabwareSelectionMode: Selector = createSelector( diff --git a/protocol-designer/src/labware-ingred/types.ts b/protocol-designer/src/labware-ingred/types.ts index 6e9567722f3..6b7628735d8 100644 --- a/protocol-designer/src/labware-ingred/types.ts +++ b/protocol-designer/src/labware-ingred/types.ts @@ -19,23 +19,22 @@ export interface WellContents { selected?: boolean maxVolume?: number } -export type ContentsByWell = { - [wellName: string]: WellContents -} | null -export interface WellContentsByLabware { - [labwareId: string]: ContentsByWell -} +export type ContentsByWell = Record | null +export type WellContentsByLabware = Record // ==== INGREDIENTS ==== +// TODO(ND: 12/17/2024): add migration for liquids in >8.3.0 export type OrderedLiquids = Array<{ ingredientId: string - name: string | null | undefined - displayColor: string | null | undefined + name?: string | null + displayColor?: string | null + liquidClass?: string | null }> // TODO: Ian 2018-10-15 audit & rename these confusing types export interface LiquidGroup { - name: string | null | undefined - description: string | null | undefined + name: string | null + description: string | null displayColor: string + liquidClass: string | null serialize: boolean } export type IngredInputs = LiquidGroup & { diff --git a/protocol-designer/src/liquid-defs/utils.ts b/protocol-designer/src/liquid-defs/utils.ts new file mode 100644 index 00000000000..cb9bc9398c9 --- /dev/null +++ b/protocol-designer/src/liquid-defs/utils.ts @@ -0,0 +1,15 @@ +import { getAllLiquidClassDefs } from '@opentrons/shared-data' + +const liquidClassDefs = getAllLiquidClassDefs() +export const getLiquidClassDisplayName = ( + liquidClass: string | null +): string | null => { + if (liquidClass == null) { + return null + } + if (!(liquidClass in liquidClassDefs)) { + console.warn(`Liquid class ${liquidClass} not found`) + return null + } + return liquidClassDefs[liquidClass].displayName +} diff --git a/protocol-designer/src/load-file/actions.ts b/protocol-designer/src/load-file/actions.ts index a698696c525..a42a998edc2 100644 --- a/protocol-designer/src/load-file/actions.ts +++ b/protocol-designer/src/load-file/actions.ts @@ -1,6 +1,8 @@ import { migration } from './migration' import { selectors as fileDataSelectors } from '../file-data' import { saveFile } from './utils' + +import type { SyntheticEvent } from 'react' import type { PDProtocolFile } from '../file-types' import type { GetState, ThunkAction, ThunkDispatch } from '../types' import type { @@ -32,7 +34,7 @@ export const loadFileAction = (payload: PDProtocolFile): LoadFileAction => ({ }) // load file thunk, handles file loading errors export const loadProtocolFile = ( - event: React.SyntheticEvent + event: SyntheticEvent ): ThunkAction => (dispatch: ThunkDispatch, getState: GetState) => { const fileError = ( errorType: FileUploadErrorType, diff --git a/protocol-designer/src/load-file/migration/8_2_0.ts b/protocol-designer/src/load-file/migration/8_2_0.ts index 6bb79ac10d9..24ab1428f09 100644 --- a/protocol-designer/src/load-file/migration/8_2_0.ts +++ b/protocol-designer/src/load-file/migration/8_2_0.ts @@ -10,8 +10,11 @@ const getTimeFromIndividualUnits = ( minutes: any, hours?: any ): string => { - const hoursString = hours !== undefined ? `${hours ?? 0}:` : '' - return `${hoursString}${minutes ?? 0}:${seconds ?? 0}` + const pad = (num: number): string => String(num).padStart(2, '0') + const hoursString = hours !== undefined ? `${pad(Number(hours) ?? 0)}:` : '' + return `${hoursString}${pad(Number(minutes) ?? 0)}:${pad( + Number(seconds) ?? 0 + )}` } export const migrateFile = ( diff --git a/protocol-designer/src/load-file/migration/8_2_2.ts b/protocol-designer/src/load-file/migration/8_2_2.ts new file mode 100644 index 00000000000..007aa288787 --- /dev/null +++ b/protocol-designer/src/load-file/migration/8_2_2.ts @@ -0,0 +1,51 @@ +import type { ProtocolFile } from '@opentrons/shared-data' +import type { DesignerApplicationData } from './utils/getLoadLiquidCommands' + +export const migrateFile = ( + appData: ProtocolFile +): ProtocolFile => { + const { designerApplication } = appData + + if (designerApplication == null || designerApplication?.data == null) { + throw Error('The designerApplication key in your file is corrupt.') + } + const savedStepForms = designerApplication.data + ?.savedStepForms as DesignerApplicationData['savedStepForms'] + + const savedStepsWithUpdatedHeaterShakerTimerField = Object.values( + savedStepForms + ).reduce((acc, form) => { + if (form.stepType === 'heaterShaker') { + const { id, heaterShakerSetTimer } = form + let newSetTimer = heaterShakerSetTimer + + if (heaterShakerSetTimer === 'false') { + newSetTimer = false + } else if (heaterShakerSetTimer === 'true') { + newSetTimer = true + } + return { + ...acc, + [id]: { + ...form, + heaterShakerSetTimer: newSetTimer, + }, + } + } + return acc + }, {}) + + return { + ...appData, + designerApplication: { + ...designerApplication, + data: { + ...designerApplication.data, + savedStepForms: { + ...designerApplication.data.savedStepForms, + ...savedStepsWithUpdatedHeaterShakerTimerField, + }, + }, + }, + } +} diff --git a/protocol-designer/src/load-file/migration/index.ts b/protocol-designer/src/load-file/migration/index.ts index 1ef3f346153..27afb8eb132 100644 --- a/protocol-designer/src/load-file/migration/index.ts +++ b/protocol-designer/src/load-file/migration/index.ts @@ -12,6 +12,8 @@ import { migrateFile as migrateFileSeven } from './7_0_0' import { migrateFile as migrateFileEight } from './8_0_0' import { migrateFile as migrateFileEightOne } from './8_1_0' import { migrateFile as migrateFileEightTwo } from './8_2_0' +import { migrateFile as migrateFileEightTwoPointTwo } from './8_2_2' + import type { PDProtocolFile } from '../../file-types' export const OLDEST_MIGRATEABLE_VERSION = '1.0.0' @@ -54,6 +56,8 @@ const allMigrationsByVersion: MigrationsByVersion = { '8.1.0': migrateFileEightOne, // @ts-expect-error '8.2.0': migrateFileEightTwo, + // @ts-expect-error + '8.2.2': migrateFileEightTwoPointTwo, } export const migration = ( file: any diff --git a/protocol-designer/src/molecules/CheckboxExpandStepFormField/index.tsx b/protocol-designer/src/molecules/CheckboxExpandStepFormField/index.tsx index 5c4d749d228..7b58de44ae0 100644 --- a/protocol-designer/src/molecules/CheckboxExpandStepFormField/index.tsx +++ b/protocol-designer/src/molecules/CheckboxExpandStepFormField/index.tsx @@ -13,12 +13,14 @@ import { useHoverTooltip, } from '@opentrons/components' +import type { ReactNode } from 'react' + interface CheckboxExpandStepFormFieldProps { title: string checkboxUpdateValue: (value: unknown) => void checkboxValue: unknown isChecked: boolean - children?: React.ReactNode + children?: ReactNode tooltipText?: string | null disabled?: boolean } diff --git a/protocol-designer/src/molecules/CheckboxStepFormField/index.tsx b/protocol-designer/src/molecules/CheckboxStepFormField/index.tsx index bb6d1748418..3ee5c057b7e 100644 --- a/protocol-designer/src/molecules/CheckboxStepFormField/index.tsx +++ b/protocol-designer/src/molecules/CheckboxStepFormField/index.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Checkbox, Flex, @@ -7,13 +6,15 @@ import { Tooltip, useHoverTooltip, } from '@opentrons/components' + +import type { ReactElement, ReactNode } from 'react' import type { Placement } from '@opentrons/components' import type { FieldProps } from '../../pages/Designer/ProtocolSteps/StepForm/types' type CheckboxStepFormFieldProps = FieldProps & { - children?: React.ReactElement + children?: ReactElement label?: string - tooltipContent?: React.ReactNode + tooltipContent?: ReactNode tooltipPlacement?: Placement } diff --git a/protocol-designer/src/molecules/DropdownStepFormField/index.tsx b/protocol-designer/src/molecules/DropdownStepFormField/index.tsx index 01f82972d60..d40fcc7063f 100644 --- a/protocol-designer/src/molecules/DropdownStepFormField/index.tsx +++ b/protocol-designer/src/molecules/DropdownStepFormField/index.tsx @@ -1,23 +1,31 @@ import { useTranslation } from 'react-i18next' -import { useEffect } from 'react' +import { useDispatch } from 'react-redux' import { + ALIGN_CENTER, COLORS, DIRECTION_COLUMN, + DeckInfoLabel, DropdownMenu, Flex, ListItem, SPACING, StyledText, } from '@opentrons/components' -import type { Options } from '@opentrons/components' +import { selectDropdownItem } from '../../ui/steps/actions/actions' +import type { DropdownOption } from '@opentrons/components' import type { FieldProps } from '../../pages/Designer/ProtocolSteps/StepForm/types' export interface DropdownStepFormFieldProps extends FieldProps { - options: Options + options: DropdownOption[] title: string width?: string + onEnter?: (id: string) => void + onExit?: () => void } +const FIRST_FIELDS = ['aspirate_labware', 'labware', 'moduleId'] +const SECOND_FIELDS = ['dispense_labware', 'newLocation'] + export function DropdownStepFormField( props: DropdownStepFormFieldProps ): JSX.Element { @@ -31,16 +39,44 @@ export function DropdownStepFormField( padding = `0 ${SPACING.spacing16}`, width = '17.5rem', onFieldFocus, + onEnter, + onExit, onFieldBlur, + name: fieldName, } = props - const { t } = useTranslation('tooltip') + const { t } = useTranslation(['tooltip', 'application']) + const dispatch = useDispatch() const availableOptionId = options.find(opt => opt.value === value) + const handleSelection = (value: string): void => { + let text = t('application:selected') + if (fieldName === 'newLocation') { + text = t('application:location') + } else if (fieldName === 'aspirate_labware') { + text = t('application:source') + } else if (fieldName === 'dispense_labware') { + text = t('application:dest') + } - useEffect(() => { - if (options.length === 1) { - updateValue(options[0].value) + const selection = { + id: value, + text, + } + if (FIRST_FIELDS.includes(fieldName)) { + dispatch( + selectDropdownItem({ + selection: { ...selection, field: '1' }, + mode: 'add', + }) + ) + } else if (SECOND_FIELDS.includes(fieldName)) { + dispatch( + selectDropdownItem({ + selection: { ...selection, field: '2' }, + mode: 'add', + }) + ) } - }, []) + } return ( @@ -59,7 +95,10 @@ export function DropdownStepFormField( } onClick={value => { updateValue(value) + handleSelection(value) }} + onEnter={onEnter} + onExit={onExit} /> ) : ( - - - {options[0].name} - + + {options[0].deckLabel != null ? ( + + ) : null} + + + {options[0].name} + + + {options[0].subtext} + + diff --git a/protocol-designer/src/organisms/ProtocolNavBar/__tests__/LiquidButton.test.tsx b/protocol-designer/src/molecules/LiquidButton/__tests__/LiquidButton.test.tsx similarity index 94% rename from protocol-designer/src/organisms/ProtocolNavBar/__tests__/LiquidButton.test.tsx rename to protocol-designer/src/molecules/LiquidButton/__tests__/LiquidButton.test.tsx index 179d512f262..08bd1962583 100644 --- a/protocol-designer/src/organisms/ProtocolNavBar/__tests__/LiquidButton.test.tsx +++ b/protocol-designer/src/molecules/LiquidButton/__tests__/LiquidButton.test.tsx @@ -3,7 +3,7 @@ import { fireEvent, screen } from '@testing-library/react' import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' -import { LiquidButton } from '../LiquidButton' +import { LiquidButton } from '../../../molecules/LiquidButton' import type { ComponentProps } from 'react' diff --git a/protocol-designer/src/organisms/ProtocolNavBar/LiquidButton.tsx b/protocol-designer/src/molecules/LiquidButton/index.tsx similarity index 100% rename from protocol-designer/src/organisms/ProtocolNavBar/LiquidButton.tsx rename to protocol-designer/src/molecules/LiquidButton/index.tsx diff --git a/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx b/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx index 5c34b465647..a99f6547f7a 100644 --- a/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx +++ b/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx @@ -42,6 +42,7 @@ export function ToggleExpandStepFormField( toggleValue, caption, toggleElement = 'toggle', + name, ...restProps } = props @@ -49,17 +50,24 @@ export function ToggleExpandStepFormField( restProps.updateValue(null) } + // TODO: refactor this, it is messy const onToggleUpdateValue = (): void => { - if (typeof toggleValue === 'boolean') { + if (toggleValue === 'engage' || toggleValue === 'disengage') { + const newValue = toggleValue === 'engage' ? 'disengage' : 'engage' + toggleUpdateValue(newValue) + } else if (toggleValue === 'true' || toggleValue === 'false') { + const newValue = toggleValue === 'true' ? 'false' : 'true' + toggleUpdateValue(newValue) + if (newValue === 'true') { + resetFieldValue() + } + } else if (toggleValue == null) { + toggleUpdateValue(name === 'targetTemperature' ? 'true' : true) + } else { toggleUpdateValue(!toggleValue) if (toggleValue) { resetFieldValue() } - } else if (toggleValue === 'engage' || toggleValue === 'disengage') { - const newToggleValue = toggleValue === 'engage' ? 'disengage' : 'engage' - toggleUpdateValue(newToggleValue) - } else if (toggleValue == null) { - toggleUpdateValue(true) } } @@ -99,6 +107,7 @@ export function ToggleExpandStepFormField( {isSelected ? ( { dispatch(selectDesignerTab({ tab: 'startingDeck' })) }} @@ -58,7 +58,7 @@ export const ErrorContents = ( { dispatch(selectDesignerTab({ tab: 'startingDeck' })) }} diff --git a/protocol-designer/src/organisms/Alerts/FormAlerts.tsx b/protocol-designer/src/organisms/Alerts/FormAlerts.tsx index b6080a05a73..8348b5bd798 100644 --- a/protocol-designer/src/organisms/Alerts/FormAlerts.tsx +++ b/protocol-designer/src/organisms/Alerts/FormAlerts.tsx @@ -184,6 +184,7 @@ function FormAlertsComponent(props: FormAlertsProps): JSX.Element | null { {showFormErrors ? formErrors.map((error, key) => makeAlert('error', error, key)) diff --git a/protocol-designer/src/organisms/Alerts/__tests__/FormAlerts.test.tsx b/protocol-designer/src/organisms/Alerts/__tests__/FormAlerts.test.tsx index 14110951a76..da88e015f64 100644 --- a/protocol-designer/src/organisms/Alerts/__tests__/FormAlerts.test.tsx +++ b/protocol-designer/src/organisms/Alerts/__tests__/FormAlerts.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -18,20 +17,22 @@ import { dismissTimelineWarning, } from '../../../dismiss/actions' +import type { ComponentProps } from 'react' + vi.mock('../../../dismiss/actions') vi.mock('../../../ui/steps') vi.mock('../../../top-selectors/timelineWarnings') vi.mock('../../../dismiss/selectors') vi.mock('../../../step-forms/selectors') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('FormAlerts', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/Alerts/types.ts b/protocol-designer/src/organisms/Alerts/types.ts index 0453688d667..c3396ad79d0 100644 --- a/protocol-designer/src/organisms/Alerts/types.ts +++ b/protocol-designer/src/organisms/Alerts/types.ts @@ -1,9 +1,11 @@ +import type { ReactNode } from 'react' + export type AlertLevel = 'timeline' | 'form' type AlertType = 'error' | 'warning' interface AlertData { title: string - description: React.ReactNode + description: ReactNode dismissId?: string } diff --git a/protocol-designer/src/organisms/AnnouncementModal/announcements.tsx b/protocol-designer/src/organisms/AnnouncementModal/announcements.tsx index 142f7c5b8f2..e84930a26c0 100644 --- a/protocol-designer/src/organisms/AnnouncementModal/announcements.tsx +++ b/protocol-designer/src/organisms/AnnouncementModal/announcements.tsx @@ -56,6 +56,7 @@ const OPENTRONS_PD = 'Opentrons Protocol Designer' export const useAnnouncements = (): Announcement[] => { const { t } = useTranslation('modal') + const pdVersion = process.env.OT_PD_VERSION return [ { @@ -331,7 +332,7 @@ export const useAnnouncements = (): Announcement[] => { { announcementKey: 'redesign8.2', image: , - heading: t('announcements.redesign.body1'), + heading: t('announcements.redesign.body1', { version: pdVersion }), message: ( diff --git a/protocol-designer/src/organisms/AssignLiquidsModal/LiquidCard.tsx b/protocol-designer/src/organisms/AssignLiquidsModal/LiquidCard.tsx index a422b4b210e..4f33b968db4 100644 --- a/protocol-designer/src/organisms/AssignLiquidsModal/LiquidCard.tsx +++ b/protocol-designer/src/organisms/AssignLiquidsModal/LiquidCard.tsx @@ -20,6 +20,7 @@ import { } from '@opentrons/components' import { LINE_CLAMP_TEXT_STYLE } from '../../atoms' +import { getEnableLiquidClasses } from '../../feature-flags/selectors' import { removeWellsContents } from '../../labware-ingred/actions' import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors' import { getLabwareEntities } from '../../step-forms/selectors' @@ -34,7 +35,7 @@ interface LiquidCardProps { export function LiquidCard(props: LiquidCardProps): JSX.Element { const { info } = props - const { name, color, liquidIndex } = info + const { name, color, liquidClassDisplayName, liquidIndex } = info const { t } = useTranslation('liquids') const dispatch = useDispatch() const [isExpanded, setIsExpanded] = useState(false) @@ -49,6 +50,7 @@ export function LiquidCard(props: LiquidCardProps): JSX.Element { const allWellContentsForActiveItem = useSelector( wellContentsSelectors.getAllWellContentsForActiveItem ) + const enableLiquidClasses = useSelector(getEnableLiquidClasses) const wellContents = allWellContentsForActiveItem != null && labwareId != null ? allWellContentsForActiveItem[labwareId] @@ -104,13 +106,24 @@ export function LiquidCard(props: LiquidCardProps): JSX.Element { > - + {name} + {liquidClassDisplayName != null && enableLiquidClasses ? ( + + ) : null} - ) => void = e => { + const handleChangeVolume: (e: ChangeEvent) => void = e => { const value: string | null | undefined = e.currentTarget.value const masked = fieldProcessors.composeMaskers( fieldProcessors.maskToFloat, @@ -214,6 +216,9 @@ export function LiquidToolbox(props: LiquidToolboxProps): JSX.Element { liquidIndex: liquid, name: foundLiquid?.name ?? '', color: foundLiquid?.displayColor ?? '', + liquidClassDisplayName: getLiquidClassDisplayName( + foundLiquid?.liquidClass ?? null + ), } }) .filter(Boolean) diff --git a/protocol-designer/src/organisms/AssignLiquidsModal/index.tsx b/protocol-designer/src/organisms/AssignLiquidsModal/index.tsx index a7c891e7c3c..8372c5886fc 100644 --- a/protocol-designer/src/organisms/AssignLiquidsModal/index.tsx +++ b/protocol-designer/src/organisms/AssignLiquidsModal/index.tsx @@ -21,7 +21,7 @@ import { getSelectedWells } from '../../well-selection/selectors' import { SelectableLabware } from '../Labware/SelectableLabware' import { wellFillFromWellContents } from '../LabwareOnDeck/utils' import { deselectWells, selectWells } from '../../well-selection/actions' -import { PROTOCOL_NAV_BAR_HEIGHT_REM } from '../ProtocolNavBar' +import { NAV_BAR_HEIGHT_REM } from '../../atoms' import { LiquidToolbox } from './LiquidToolbox' import type { WellGroup } from '@opentrons/components' @@ -52,7 +52,7 @@ export function AssignLiquidsModal(): JSX.Element | null { return ( void handleContinue: () => void } diff --git a/protocol-designer/src/organisms/ConfirmDeleteModal/index.tsx b/protocol-designer/src/organisms/ConfirmDeleteModal/index.tsx index 89de28330b6..70a767706a7 100644 --- a/protocol-designer/src/organisms/ConfirmDeleteModal/index.tsx +++ b/protocol-designer/src/organisms/ConfirmDeleteModal/index.tsx @@ -12,7 +12,7 @@ import { StyledText, } from '@opentrons/components' import { getMainPagePortalEl } from '../Portal' -import type * as React from 'react' +import type { MouseEvent } from 'react' export const DELETE_PROFILE_CYCLE: 'deleteProfileCycle' = 'deleteProfileCycle' export const CLOSE_STEP_FORM_WITH_CHANGES: 'closeStepFormWithChanges' = @@ -36,7 +36,7 @@ interface Props { modalType: DeleteModalType onCancelClick: () => unknown // TODO(sa, 2021-7-2): iron out this type, I think the weirdness comes from the return type of onConditionalConfirm - onContinueClick: ((event: React.MouseEvent) => unknown) | (() => unknown) + onContinueClick: ((event: MouseEvent) => unknown) | (() => unknown) } export function ConfirmDeleteModal(props: Props): JSX.Element { diff --git a/protocol-designer/src/organisms/DefineLiquidsModal/index.tsx b/protocol-designer/src/organisms/DefineLiquidsModal/index.tsx index da5d02a4a31..3e2e90d8d94 100644 --- a/protocol-designer/src/organisms/DefineLiquidsModal/index.tsx +++ b/protocol-designer/src/organisms/DefineLiquidsModal/index.tsx @@ -6,12 +6,16 @@ import { yupResolver } from '@hookform/resolvers/yup' import * as Yup from 'yup' import { Controller, useForm } from 'react-hook-form' import styled from 'styled-components' -import { DEFAULT_LIQUID_COLORS } from '@opentrons/shared-data' +import { + DEFAULT_LIQUID_COLORS, + getAllLiquidClassDefs, +} from '@opentrons/shared-data' import { BORDERS, Btn, COLORS, DIRECTION_COLUMN, + DropdownMenu, Flex, InputField, JUSTIFY_END, @@ -30,6 +34,7 @@ import * as labwareIngredActions from '../../labware-ingred/actions' import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors' import { HandleEnter } from '../../atoms/HandleEnter' import { LINE_CLAMP_TEXT_STYLE } from '../../atoms' +import { getEnableLiquidClasses } from '../../feature-flags/selectors' import { swatchColors } from './swatchColors' import type { ColorResult, RGBColor } from 'react-color' @@ -40,8 +45,9 @@ import type { LiquidGroup } from '../../labware-ingred/types' interface LiquidEditFormValues { name: string displayColor: string - description?: string | null - serialize?: boolean + description: string + liquidClass: string + serialize: boolean [key: string]: unknown } @@ -49,6 +55,7 @@ const liquidEditFormSchema: any = Yup.object().shape({ name: Yup.string().required('liquid name is required'), displayColor: Yup.string(), description: Yup.string(), + liquidClass: Yup.string(), serialize: Yup.boolean(), }) @@ -77,6 +84,9 @@ export function DefineLiquidsModal( const allIngredientGroupFields = useSelector( labwareIngredSelectors.allIngredientGroupFields ) + const enableLiquidClasses = useSelector(getEnableLiquidClasses) + const liquidClassDefs = getAllLiquidClassDefs() + const liquidGroupId = selectedLiquidGroupState.liquidGroupId const deleteLiquidGroup = (): void => { if (liquidGroupId != null) { @@ -107,13 +117,14 @@ export function DefineLiquidsModal( const initialValues: LiquidEditFormValues = { name: selectedIngredFields?.name ?? '', displayColor: selectedIngredFields?.displayColor ?? swatchColors(liquidId), + liquidClass: selectedIngredFields?.liquidClass ?? '', description: selectedIngredFields?.description ?? '', serialize: selectedIngredFields?.serialize ?? false, } const { handleSubmit, - formState: { errors, touchedFields }, + formState, control, watch, setValue, @@ -125,12 +136,15 @@ export function DefineLiquidsModal( }) const name = watch('name') const color = watch('displayColor') + const liquidClass = watch('liquidClass') + const { errors, touchedFields } = formState const handleLiquidEdits = (values: LiquidEditFormValues): void => { saveForm({ name: values.name, displayColor: values.displayColor, - description: values.description ?? null, + liquidClass: values.liquidClass ? values.liquidClass : null, + description: values.description ? values.description : null, serialize: values.serialize ?? false, }) } @@ -142,6 +156,15 @@ export function DefineLiquidsModal( return `#${toHex(r)}${toHex(g)}${toHex(b)}${toHex(alpha)}` } + const liquidClassOptions = [ + { name: 'Choose an option', value: '' }, + ...Object.entries(liquidClassDefs).map( + ([liquidClassDefName, { displayName }]) => { + return { name: displayName, value: liquidClassDefName } + } + ), + ] + return ( { @@ -182,6 +205,7 @@ export function DefineLiquidsModal( left="4.375rem" top="4.6875rem" ref={chooseColorWrapperRef} + zIndex={2} > - + + {enableLiquidClasses ? ( + + ( + value === liquidClass + ) ?? liquidClassOptions[0] + } + onClick={value => { + field.onChange(value) + setValue('liquidClass', value) + }} + /> + )} + /> + + ) : null} { @@ -21,17 +21,22 @@ vi.mock('react-router-dom', async importOriginal => { return { ...reactRouterDom, useNavigate: () => mockNavigate, + useLocation: () => ({ + location: { + pathname: '/designer', + }, + }), } }) -const render = (props: ComponentProps) => { - return renderWithProviders(, { +const render = (props: ComponentProps) => { + return renderWithProviders(, { i18nInstance: i18n, }) } -describe('ProtocolNavBar', () => { - let props: ComponentProps +describe('DesignerNavigation', () => { + let props: ComponentProps beforeEach(() => { props = { hasZoomInSlot: false, diff --git a/protocol-designer/src/organisms/ProtocolNavBar/index.tsx b/protocol-designer/src/organisms/DesignerNavigation/index.tsx similarity index 77% rename from protocol-designer/src/organisms/ProtocolNavBar/index.tsx rename to protocol-designer/src/organisms/DesignerNavigation/index.tsx index fc9d5bff942..09213844a77 100644 --- a/protocol-designer/src/organisms/ProtocolNavBar/index.tsx +++ b/protocol-designer/src/organisms/DesignerNavigation/index.tsx @@ -1,6 +1,6 @@ import { useDispatch, useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import { @@ -17,42 +17,43 @@ import { TYPOGRAPHY, } from '@opentrons/components' import { getFileMetadata } from '../../file-data/selectors' -import { selectTerminalItem } from '../../ui/steps/actions/actions' -import { LINE_CLAMP_TEXT_STYLE } from '../../atoms' +import { + selectDropdownItem, + selectTerminalItem, +} from '../../ui/steps/actions/actions' +import { LINE_CLAMP_TEXT_STYLE, NAV_BAR_HEIGHT_REM } from '../../atoms' import { useKitchen } from '../Kitchen/hooks' -import { LiquidButton } from './LiquidButton' +import { LiquidButton } from '../../molecules/LiquidButton' import type { StyleProps, TabProps } from '@opentrons/components' -export const PROTOCOL_NAV_BAR_HEIGHT_REM = 4 - -interface ProtocolNavBarProps { +interface DesignerNavigationProps { hasZoomInSlot?: boolean tabs?: TabProps[] hasTrashEntity?: boolean showLiquidOverflowMenu?: (liquidOverflowMenu: boolean) => void - liquidPage?: boolean } - -export function ProtocolNavBar({ +// Note: this navigation is used in design page and liquids page +export function DesignerNavigation({ hasZoomInSlot, tabs = [], hasTrashEntity, showLiquidOverflowMenu, - liquidPage = false, -}: ProtocolNavBarProps): JSX.Element { +}: DesignerNavigationProps): JSX.Element { const { t } = useTranslation('starting_deck_state') + const location = useLocation() const metadata = useSelector(getFileMetadata) const { makeSnackbar } = useKitchen() const navigate = useNavigate() const dispatch = useDispatch() + const isLiquidsPage = location.pathname === '/liquids' - const showProtocolEditButtons = !(hasZoomInSlot || liquidPage) + const showProtocolEditButtons = !(hasZoomInSlot === true || isLiquidsPage) let metadataText = t('edit_protocol') - if (liquidPage) { + if (isLiquidsPage) { metadataText = t('add_liquid') - } else if (hasZoomInSlot) { + } else if (hasZoomInSlot === true) { metadataText = t('add_hardware_labware') } return ( @@ -78,12 +79,18 @@ export function ProtocolNavBar({ ) : null} - {liquidPage ? null : ( + {isLiquidsPage ? null : ( { - if (hasTrashEntity) { + if (hasTrashEntity === true) { navigate('/overview') dispatch(selectTerminalItem('__initial_setup__')) + dispatch( + selectDropdownItem({ + selection: null, + mode: 'clear', + }) + ) } else { makeSnackbar(t('trash_required') as string) } @@ -100,12 +107,12 @@ export function ProtocolNavBar({ const NavContainer = styled(Flex)<{ showShadow: boolean }>` z-index: ${props => (props.showShadow === true ? 11 : 0)}; padding: ${SPACING.spacing12}; - height: ${PROTOCOL_NAV_BAR_HEIGHT_REM}rem; + height: ${NAV_BAR_HEIGHT_REM}rem; width: 100%; justify-content: ${JUSTIFY_SPACE_BETWEEN}; align-items: ${ALIGN_CENTER}; box-shadow: ${props => - props.showShadow + props.showShadow === true ? `0px 1px 3px 0px ${COLORS.black90}${COLORS.opacity20HexCode}` : 'none'}; ` @@ -119,7 +126,7 @@ const MetadataContainer = styled.div.withConfig({ display: flex; flex-direction: ${DIRECTION_COLUMN}; text-align: ${props => - props.showProtocolEditButtons === true + props.showProtocolEditButtons ? TYPOGRAPHY.textAlignCenter : TYPOGRAPHY.textAlignLeft}; diff --git a/protocol-designer/src/organisms/EditInstrumentsModal/index.tsx b/protocol-designer/src/organisms/EditInstrumentsModal/index.tsx index 0140abf8e70..5de4843883c 100644 --- a/protocol-designer/src/organisms/EditInstrumentsModal/index.tsx +++ b/protocol-designer/src/organisms/EditInstrumentsModal/index.tsx @@ -64,7 +64,7 @@ import { setFeatureFlags } from '../../feature-flags/actions' import { createCustomTiprackDef } from '../../labware-defs/actions' import { deleteContainer } from '../../labware-ingred/actions' import { selectors as stepFormSelectors } from '../../step-forms' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { getMainPagePortalEl } from '../Portal' import { getSectionsFromPipetteName, @@ -236,7 +236,7 @@ export function EditInstrumentsModal( {has96Channel || (leftPipette == null && rightPipette == null) ? null : ( dispatch( changeSavedStepForm({ @@ -354,7 +354,7 @@ export function EditInstrumentsModal( ) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('EditNickNameModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/EditProtocolMetadataModal/__tests__/EditProtocolMetadataModal.test.tsx b/protocol-designer/src/organisms/EditProtocolMetadataModal/__tests__/EditProtocolMetadataModal.test.tsx index 3d4b8aace51..6eee963d099 100644 --- a/protocol-designer/src/organisms/EditProtocolMetadataModal/__tests__/EditProtocolMetadataModal.test.tsx +++ b/protocol-designer/src/organisms/EditProtocolMetadataModal/__tests__/EditProtocolMetadataModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -7,18 +6,18 @@ import { renderWithProviders } from '../../../__testing-utils__' import { EditProtocolMetadataModal } from '..' import { selectors as fileSelectors } from '../../../file-data' +import type { ComponentProps } from 'react' + vi.mock('../../../file-data') -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('EditProtocolMetadataModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/FileUploadMessagesModal/utils.tsx b/protocol-designer/src/organisms/FileUploadMessagesModal/utils.tsx index 8b10662278f..72f0f9fa2c2 100644 --- a/protocol-designer/src/organisms/FileUploadMessagesModal/utils.tsx +++ b/protocol-designer/src/organisms/FileUploadMessagesModal/utils.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { Trans, useTranslation } from 'react-i18next' import { COLORS, @@ -8,11 +7,12 @@ import { StyledText, } from '@opentrons/components' +import type { ReactNode } from 'react' import type { FileUploadMessage } from '../../load-file' export interface ModalContents { title: string - body: React.ReactNode + body: ReactNode } interface ModalProps { diff --git a/protocol-designer/src/organisms/GateModal/index.tsx b/protocol-designer/src/organisms/GateModal/index.tsx index cfe35b1b24a..c97db1d5898 100644 --- a/protocol-designer/src/organisms/GateModal/index.tsx +++ b/protocol-designer/src/organisms/GateModal/index.tsx @@ -9,7 +9,6 @@ import { Modal, PrimaryButton, SPACING, - SecondaryButton, StyledText, } from '@opentrons/components' import { @@ -22,12 +21,15 @@ const EULA_URL = 'https://opentrons.com/eula' export function GateModal(): JSX.Element | null { const { t } = useTranslation('shared') - const hasOptedIn = useSelector(analyticsSelectors.getHasOptedIn) + const { appVersion, hasOptedIn } = useSelector( + analyticsSelectors.getHasOptedIn + ) const dispatch = useDispatch() - if (hasOptedIn == null) { + if (appVersion == null || hasOptedIn == null) { return ( - dispatch(analyticsActions.optOut())} - > - - {t('reject')} - - dispatch(analyticsActions.optIn())}> - {t('agree')} + {t('confirm')} @@ -85,9 +80,6 @@ export function GateModal(): JSX.Element | null { }} /> - - {t('analytics_tracking')} - ) diff --git a/protocol-designer/src/organisms/IncompatibleTipsModal/__tests__/IncompatibleTipsModal.test.tsx b/protocol-designer/src/organisms/IncompatibleTipsModal/__tests__/IncompatibleTipsModal.test.tsx index 2a1a8cf7e4e..7cb887d706e 100644 --- a/protocol-designer/src/organisms/IncompatibleTipsModal/__tests__/IncompatibleTipsModal.test.tsx +++ b/protocol-designer/src/organisms/IncompatibleTipsModal/__tests__/IncompatibleTipsModal.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { i18n } from '../../../assets/localization' @@ -6,16 +5,18 @@ import { renderWithProviders } from '../../../__testing-utils__' import { setFeatureFlags } from '../../../feature-flags/actions' import { IncompatibleTipsModal } from '..' +import type { ComponentProps } from 'react' + vi.mock('../../../feature-flags/actions') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('IncompatibleTipsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/Labware/SelectableLabware.tsx b/protocol-designer/src/organisms/Labware/SelectableLabware.tsx index 25c904d7407..27c77f7e7cb 100644 --- a/protocol-designer/src/organisms/Labware/SelectableLabware.tsx +++ b/protocol-designer/src/organisms/Labware/SelectableLabware.tsx @@ -12,7 +12,7 @@ import { SingleLabware } from './SingleLabware' import { WellTooltip } from './WellTooltip' import { SelectionRect } from './SelectionRect' -import type * as React from 'react' +import type { ComponentProps } from 'react' import type { WellMouseEvent, WellGroup, @@ -25,10 +25,7 @@ import type { GenericRect } from '../../collision-types' import type { NozzleType } from '../../types' export interface SelectableLabwareProps { - labwareProps: Omit< - React.ComponentProps, - 'selectedWells' - > + labwareProps: Omit, 'selectedWells'> /** array of primary wells. Overrides labwareProps.selectedWells */ selectedPrimaryWells: WellGroup selectWells: (wellGroup: WellGroup) => unknown diff --git a/protocol-designer/src/organisms/Labware/SelectionRect.tsx b/protocol-designer/src/organisms/Labware/SelectionRect.tsx index 73c7229c39e..97e834fdded 100644 --- a/protocol-designer/src/organisms/Labware/SelectionRect.tsx +++ b/protocol-designer/src/organisms/Labware/SelectionRect.tsx @@ -1,12 +1,14 @@ -import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { css } from 'styled-components' + +import type { ReactNode, MouseEventHandler } from 'react' import type { DragRect, GenericRect } from '../../collision-types' interface SelectionRectProps { onSelectionMove?: (e: MouseEvent, arg: GenericRect) => void onSelectionDone?: (e: MouseEvent, arg: GenericRect) => void svg?: boolean // set true if this is an embedded SVG - children?: React.ReactNode + children?: ReactNode originXOffset?: number originYOffset?: number } @@ -20,9 +22,9 @@ export function SelectionRect(props: SelectionRectProps): JSX.Element { originXOffset = 0, originYOffset = 0, } = props - const [positions, setPositions] = React.useState(null) - const parentRef = React.useRef(null) - const renderRect = (args: DragRect): React.ReactNode => { + const [positions, setPositions] = useState(null) + const parentRef = useRef(null) + const renderRect = (args: DragRect): ReactNode => { const { xStart, yStart, xDynamic, yDynamic } = args const left = Math.min(xStart, xDynamic) const top = Math.min(yStart, yDynamic) @@ -117,7 +119,7 @@ export function SelectionRect(props: SelectionRectProps): JSX.Element { onSelectionDone && finalRect && onSelectionDone(e, finalRect) } - const handleMouseDown: React.MouseEventHandler = e => { + const handleMouseDown: MouseEventHandler = e => { setPositions({ xStart: e.clientX, xDynamic: e.clientX, @@ -126,7 +128,7 @@ export function SelectionRect(props: SelectionRectProps): JSX.Element { }) } - React.useEffect(() => { + useEffect(() => { document.addEventListener('mousemove', handleDrag) document.addEventListener('mouseup', handleMouseUp) return () => { diff --git a/protocol-designer/src/organisms/Labware/SingleLabware.tsx b/protocol-designer/src/organisms/Labware/SingleLabware.tsx index 1952fea3fc8..a0f7e5c4774 100644 --- a/protocol-designer/src/organisms/Labware/SingleLabware.tsx +++ b/protocol-designer/src/organisms/Labware/SingleLabware.tsx @@ -1,7 +1,7 @@ import { LabwareRender, RobotWorkSpace } from '@opentrons/components' -import type * as React from 'react' +import type { ComponentProps } from 'react' -type Props = React.ComponentProps +type Props = ComponentProps /** Avoid boilerplate for viewbox-based-on-labware-dimensions */ export function SingleLabware(props: Props): JSX.Element { diff --git a/protocol-designer/src/organisms/Labware/WellTooltip.tsx b/protocol-designer/src/organisms/Labware/WellTooltip.tsx index 51e9236427e..0479aaf58fc 100644 --- a/protocol-designer/src/organisms/Labware/WellTooltip.tsx +++ b/protocol-designer/src/organisms/Labware/WellTooltip.tsx @@ -1,4 +1,4 @@ -import * as React from 'react' +import { Fragment, useState } from 'react' import { useSelector } from 'react-redux' import map from 'lodash/map' import reduce from 'lodash/reduce' @@ -9,6 +9,8 @@ import { getMainPagePortalEl } from '../../organisms' import { selectors } from '../../labware-ingred/selectors' import { formatVolume } from '../../pages/Designer/ProtocolSteps/Timeline/utils' import { swatchColors } from '../DefineLiquidsModal/swatchColors' + +import type { MouseEvent, ReactNode } from 'react' import type { LocationLiquidState } from '@opentrons/step-generation' import type { WellIngredientNames } from '../../steplist/types' @@ -19,13 +21,13 @@ interface WellTooltipParams { makeHandleMouseEnterWell: ( wellName: string, wellIngreds: LocationLiquidState - ) => (e: React.MouseEvent) => void + ) => (e: MouseEvent) => void handleMouseLeaveWell: (val: unknown) => void tooltipWellName?: string | null } interface WellTooltipProps { - children: (wellTooltipParams: WellTooltipParams) => React.ReactNode + children: (wellTooltipParams: WellTooltipParams) => ReactNode ingredNames: WellIngredientNames } @@ -46,14 +48,12 @@ const initialTooltipState: State = { export const WellTooltip = (props: WellTooltipProps): JSX.Element => { const { children } = props - const [tooltipState, setTooltipState] = React.useState( - initialTooltipState - ) + const [tooltipState, setTooltipState] = useState(initialTooltipState) const makeHandleMouseEnterWell: ( wellName: string, wellIngreds: LocationLiquidState - ) => (e: React.MouseEvent) => void = (wellName, wellIngreds) => e => { + ) => (e: MouseEvent) => void = (wellName, wellIngreds) => e => { const { target } = e if (target instanceof Element) { const wellBoundingRect = target.getBoundingClientRect() @@ -211,7 +211,7 @@ export const WellTooltip = (props: WellTooltipProps): JSX.Element => { {hasMultipleIngreds && ( - +
        { {`${tooltipWellName} Total Volume`} {formatVolume(totalLiquidVolume, 2)}µl
        -
        + )}
        ) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('MaterialsListModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -137,7 +137,7 @@ describe('MaterialsListModal', () => { lidTargetTemp: null, lidOpen: false, }, - slot: 'span7_8_10_11', + slot: '7', type: 'thermocyclerModuleType', }, ] as ModuleOnDeck[] @@ -162,6 +162,7 @@ describe('MaterialsListModal', () => { ingredientId: mockId, name: 'mockName', displayColor: 'mockDisplayColor', + liquidClass: null, }, ], } diff --git a/protocol-designer/src/__tests__/NavigationBar.test.tsx b/protocol-designer/src/organisms/Navigation/__tests__/Navigation.test.tsx similarity index 66% rename from protocol-designer/src/__tests__/NavigationBar.test.tsx rename to protocol-designer/src/organisms/Navigation/__tests__/Navigation.test.tsx index deac271d6d5..90d0fe4b917 100644 --- a/protocol-designer/src/__tests__/NavigationBar.test.tsx +++ b/protocol-designer/src/organisms/Navigation/__tests__/Navigation.test.tsx @@ -2,27 +2,27 @@ import { describe, it, vi, beforeEach, expect } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' -import { i18n } from '../assets/localization' -import { renderWithProviders } from '../__testing-utils__' -import { NavigationBar } from '../NavigationBar' -import { getHasUnsavedChanges } from '../load-file/selectors' -import { toggleNewProtocolModal } from '../navigation/actions' -import { SettingsIcon } from '../molecules' +import { i18n } from '../../../assets/localization' +import { renderWithProviders } from '../../../__testing-utils__' +import { getHasUnsavedChanges } from '../../../load-file/selectors' +import { toggleNewProtocolModal } from '../../../navigation/actions' +import { SettingsIcon } from '../../SettingsIcon' +import { Navigation } from '..' -vi.mock('../molecules') -vi.mock('../navigation/actions') -vi.mock('../file-data/selectors') -vi.mock('../load-file/selectors') +vi.mock('../../SettingsIcon') +vi.mock('../../../navigation/actions') +vi.mock('../../../file-data/selectors') +vi.mock('../../../load-file/selectors') const render = () => { return renderWithProviders( - + , { i18nInstance: i18n } ) } -describe('NavigationBar', () => { +describe('Navigation', () => { beforeEach(() => { vi.mocked(getHasUnsavedChanges).mockReturnValue(false) vi.mocked(SettingsIcon).mockReturnValue(
        mock SettingsIcon
        ) diff --git a/protocol-designer/src/organisms/Navigation/index.tsx b/protocol-designer/src/organisms/Navigation/index.tsx new file mode 100644 index 00000000000..f1487fc6af1 --- /dev/null +++ b/protocol-designer/src/organisms/Navigation/index.tsx @@ -0,0 +1,91 @@ +import { useLocation, useNavigate } from 'react-router-dom' +import styled from 'styled-components' +import { useDispatch, useSelector } from 'react-redux' +import { useTranslation } from 'react-i18next' + +import { + ALIGN_CENTER, + Btn, + COLORS, + CURSOR_POINTER, + Flex, + JUSTIFY_SPACE_BETWEEN, + SPACING, + StyledText, +} from '@opentrons/components' +import { toggleNewProtocolModal } from '../../navigation/actions' +import { actions as loadFileActions } from '../../load-file' +import { LINK_BUTTON_STYLE } from '../../atoms' +import { getHasUnsavedChanges } from '../../load-file/selectors' +import { SettingsIcon } from '../SettingsIcon' + +import type { ChangeEvent } from 'react' +import type { ThunkDispatch } from '../../types' + +export function Navigation(): JSX.Element | null { + const { t } = useTranslation(['shared', 'alert']) + const location = useLocation() + const navigate = useNavigate() + const dispatch: ThunkDispatch = useDispatch() + const loadFile = (fileChangeEvent: ChangeEvent): void => { + dispatch(loadFileActions.loadProtocolFile(fileChangeEvent)) + dispatch(toggleNewProtocolModal(false)) + } + const hasUnsavedChanges = useSelector(getHasUnsavedChanges) + + const handleCreateNew = (): void => { + if ( + !hasUnsavedChanges || + window.confirm(t('alert:confirm_create_new') as string) + ) { + dispatch(toggleNewProtocolModal(true)) + navigate('/createNew') + } + } + + return location.pathname === '/designer' || + location.pathname === '/liquids' ? null : ( + + + + {t('opentrons')} + + + {t('protocol_designer')} + + + {t('version', { version: process.env.OT_PD_VERSION })} + + + + {location.pathname === '/createNew' ? null : ( + + + {t('create_new')} + + + )} + + + + {t('import')} + + + + + {location.pathname === '/createNew' ? null : } + + + ) +} + +const StyledLabel = styled.label` + height: 1.25rem; + cursor: ${CURSOR_POINTER}; + input[type='file'] { + display: none; + } +` diff --git a/protocol-designer/src/organisms/PipetteInfoItem/__tests__/PipetteInfoItem.test.tsx b/protocol-designer/src/organisms/PipetteInfoItem/__tests__/PipetteInfoItem.test.tsx index ae60f21c4fb..50c05f24fb4 100644 --- a/protocol-designer/src/organisms/PipetteInfoItem/__tests__/PipetteInfoItem.test.tsx +++ b/protocol-designer/src/organisms/PipetteInfoItem/__tests__/PipetteInfoItem.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -7,16 +6,18 @@ import { renderWithProviders } from '../../../__testing-utils__' import { getLabwareDefsByURI } from '../../../labware-defs/selectors' import { PipetteInfoItem } from '..' +import type { ComponentProps } from 'react' + vi.mock('../../../labware-defs/selectors') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('PipetteInfoItem', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/PipetteInfoItem/index.tsx b/protocol-designer/src/organisms/PipetteInfoItem/index.tsx index 5b98a2c81a2..deff4e274fc 100644 --- a/protocol-designer/src/organisms/PipetteInfoItem/index.tsx +++ b/protocol-designer/src/organisms/PipetteInfoItem/index.tsx @@ -12,7 +12,7 @@ import { TYPOGRAPHY, } from '@opentrons/components' import { getPipetteSpecsV2 } from '@opentrons/shared-data' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { getLabwareDefsByURI } from '../../labware-defs/selectors' import type { PipetteMount, PipetteName } from '@opentrons/shared-data' @@ -68,7 +68,7 @@ export function PipetteInfoItem(props: PipetteInfoItemProps): JSX.Element { @@ -80,7 +80,7 @@ export function PipetteInfoItem(props: PipetteInfoItemProps): JSX.Element { cleanForm() }} textDecoration={TYPOGRAPHY.textDecorationUnderline} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} padding={SPACING.spacing4} > diff --git a/protocol-designer/src/organisms/RenameStepModal/__tests__/RenameStepModal.test.tsx b/protocol-designer/src/organisms/RenameStepModal/__tests__/RenameStepModal.test.tsx index 3bdc35028ae..e9ac1e790d4 100644 --- a/protocol-designer/src/organisms/RenameStepModal/__tests__/RenameStepModal.test.tsx +++ b/protocol-designer/src/organisms/RenameStepModal/__tests__/RenameStepModal.test.tsx @@ -6,16 +6,18 @@ import { PAUSE_UNTIL_RESUME } from '../../../constants' import { renameStep } from '../../../labware-ingred/actions' import { RenameStepModal } from '..' +import type { ComponentProps } from 'react' + vi.mock('../../../labware-ingred/actions') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('EditNickNameModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/SelectWellsModal/__tests__/SelectWellsModal.test.tsx b/protocol-designer/src/organisms/SelectWellsModal/__tests__/SelectWellsModal.test.tsx index fa1614e240d..104022625c4 100644 --- a/protocol-designer/src/organisms/SelectWellsModal/__tests__/SelectWellsModal.test.tsx +++ b/protocol-designer/src/organisms/SelectWellsModal/__tests__/SelectWellsModal.test.tsx @@ -14,6 +14,8 @@ import { } from '../../../step-forms/selectors' import { SelectableLabware } from '../../Labware/SelectableLabware' import { SelectWellsModal } from '..' + +import type { ComponentProps } from 'react' import type { LabwareDefinition2, PipetteName } from '@opentrons/shared-data' vi.mock('../../../step-forms/selectors') @@ -21,7 +23,7 @@ vi.mock('../../../labware-ingred/selectors') vi.mock('../../../top-selectors/well-contents') vi.mock('../../Labware/SelectableLabware') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -30,7 +32,7 @@ const render = (props: React.ComponentProps) => { const labwareId = 'mockId' const pipId = 'mockPipId' describe('SelectWellsModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/molecules/SettingsIcon/__tests__/SettingsIcon.test.tsx b/protocol-designer/src/organisms/SettingsIcon/__tests__/SettingsIcon.test.tsx similarity index 99% rename from protocol-designer/src/molecules/SettingsIcon/__tests__/SettingsIcon.test.tsx rename to protocol-designer/src/organisms/SettingsIcon/__tests__/SettingsIcon.test.tsx index ee2258b0a7c..8c4a9e1ec2b 100644 --- a/protocol-designer/src/molecules/SettingsIcon/__tests__/SettingsIcon.test.tsx +++ b/protocol-designer/src/organisms/SettingsIcon/__tests__/SettingsIcon.test.tsx @@ -3,6 +3,7 @@ import { fireEvent, screen } from '@testing-library/react' import { renderWithProviders } from '../../../__testing-utils__' import { getFileMetadata } from '../../../file-data/selectors' import { SettingsIcon } from '..' + import type { NavigateFunction } from 'react-router-dom' const mockNavigate = vi.fn() diff --git a/protocol-designer/src/molecules/SettingsIcon/index.tsx b/protocol-designer/src/organisms/SettingsIcon/index.tsx similarity index 54% rename from protocol-designer/src/molecules/SettingsIcon/index.tsx rename to protocol-designer/src/organisms/SettingsIcon/index.tsx index 0953ce992cb..934d6371b2c 100644 --- a/protocol-designer/src/molecules/SettingsIcon/index.tsx +++ b/protocol-designer/src/organisms/SettingsIcon/index.tsx @@ -1,17 +1,18 @@ import { useSelector } from 'react-redux' import { useLocation, useNavigate } from 'react-router-dom' +import { css } from 'styled-components' + import { BORDERS, Btn, COLORS, + CURSOR_POINTER, Flex, Icon, JUSTIFY_CENTER, } from '@opentrons/components' import { getFileMetadata } from '../../file-data/selectors' -import { BUTTON_LINK_STYLE } from '../../atoms/constants' -// TODO(ja): this icon needs to be updated to match css states and correct svg export const SettingsIcon = (): JSX.Element => { const location = useLocation() const navigate = useNavigate() @@ -32,22 +33,57 @@ export const SettingsIcon = (): JSX.Element => { data-testid="SettingsIcon" borderRadius={BORDERS.borderRadiusFull} backgroundColor={ - location.pathname === '/settings' ? COLORS.grey30 : COLORS.transparent + location.pathname === '/settings' ? COLORS.grey35 : COLORS.transparent } - cursor="pointer" - width="2rem" - height="2rem" + cursor={CURSOR_POINTER} justifyContent={JUSTIFY_CENTER} > - + ) } + +const GEAR_ICON_STYLE = css` + width: 2rem; + height: 2rem; + border-radius: 50%; + color: ${COLORS.grey60}; + + &:hover { + background-color: ${COLORS.grey30}; + } + + &:active { + color: ${COLORS.grey60}; + background-color: ${COLORS.grey35}; + } + + &:focus-visible { + position: relative; + outline: none; + + /* blue ring */ + &::after { + content: ''; + position: absolute; + top: -0.5rem; + left: -0.5rem; + right: -0.5rem; + bottom: -0.5rem; + + border: 3px solid ${COLORS.blue50}; + border-radius: 50%; + pointer-events: none; + box-sizing: content-box; + } + background-color: ${COLORS.grey35}; + } +` diff --git a/protocol-designer/src/organisms/SlotInformation/__tests__/SlotInformation.test.tsx b/protocol-designer/src/organisms/SlotInformation/__tests__/SlotInformation.test.tsx index 784590aeb20..c72b55e3b54 100644 --- a/protocol-designer/src/organisms/SlotInformation/__tests__/SlotInformation.test.tsx +++ b/protocol-designer/src/organisms/SlotInformation/__tests__/SlotInformation.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, beforeEach, expect, vi } from 'vitest' import { screen } from '@testing-library/react' import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' @@ -7,6 +6,7 @@ import { i18n } from '../../../assets/localization' import { SlotInformation } from '..' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' const mockLiquids = ['Mastermix', 'Ethanol', 'Water'] @@ -24,14 +24,14 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, }) } describe('SlotInformation', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/TipPositionModal/ZTipPositionModal.tsx b/protocol-designer/src/organisms/TipPositionModal/ZTipPositionModal.tsx index 7809a976313..6453824b4d4 100644 --- a/protocol-designer/src/organisms/TipPositionModal/ZTipPositionModal.tsx +++ b/protocol-designer/src/organisms/TipPositionModal/ZTipPositionModal.tsx @@ -17,12 +17,13 @@ import { } from '@opentrons/components' import { DEFAULT_MM_BLOWOUT_OFFSET_FROM_TOP } from '../../constants' import { getIsTouchTipField } from '../../form-types' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { getMainPagePortalEl } from '../Portal' import * as utils from './utils' import { TOO_MANY_DECIMALS } from './constants' import { TipPositionZOnlyView } from './TipPositionZOnlyView' +import type { ChangeEvent } from 'react' import type { StepFieldName } from '../../form-types' interface ZTipPositionModalProps { @@ -131,9 +132,7 @@ export function ZTipPositionModal(props: ZTipPositionModalProps): JSX.Element { } } - const handleInputFieldChange = ( - e: React.ChangeEvent - ): void => { + const handleInputFieldChange = (e: ChangeEvent): void => { handleChange(e.currentTarget.value) setPristine(false) } @@ -156,7 +155,7 @@ export function ZTipPositionModal(props: ZTipPositionModalProps): JSX.Element { onClick={() => { setValue(utils.roundValue(defaultMm, 'up').toString()) }} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} > {t('shared:reset_to_default')} diff --git a/protocol-designer/src/organisms/TipPositionModal/__tests__/TipPositionModal.test.tsx b/protocol-designer/src/organisms/TipPositionModal/__tests__/TipPositionModal.test.tsx index 7a3c871d709..7c1e1da85bb 100644 --- a/protocol-designer/src/organisms/TipPositionModal/__tests__/TipPositionModal.test.tsx +++ b/protocol-designer/src/organisms/TipPositionModal/__tests__/TipPositionModal.test.tsx @@ -5,10 +5,10 @@ import { i18n } from '../../../assets/localization' import { TipPositionSideView } from '../TipPositionSideView' import { TipPositionModal } from '..' -import type * as React from 'react' +import type { ComponentProps } from 'react' vi.mock('../TipPositionSideView') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -19,7 +19,7 @@ const mockUpdateXSpec = vi.fn() const mockUpdateYSpec = vi.fn() describe('TipPositionModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/TipPositionModal/__tests__/ZTipPositionModal.test.tsx b/protocol-designer/src/organisms/TipPositionModal/__tests__/ZTipPositionModal.test.tsx index 20e148af0de..31e4a4bf86e 100644 --- a/protocol-designer/src/organisms/TipPositionModal/__tests__/ZTipPositionModal.test.tsx +++ b/protocol-designer/src/organisms/TipPositionModal/__tests__/ZTipPositionModal.test.tsx @@ -4,17 +4,18 @@ import { renderWithProviders } from '../../../__testing-utils__' import { i18n } from '../../../assets/localization' import { ZTipPositionModal } from '../ZTipPositionModal' import { TipPositionZOnlyView } from '../TipPositionZOnlyView' -import type * as React from 'react' + +import type { ComponentProps } from 'react' vi.mock('../TipPositionZOnlyView') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('ZTipPositionModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/TipPositionModal/index.tsx b/protocol-designer/src/organisms/TipPositionModal/index.tsx index cdef7a6855b..e3e01447bf0 100644 --- a/protocol-designer/src/organisms/TipPositionModal/index.tsx +++ b/protocol-designer/src/organisms/TipPositionModal/index.tsx @@ -18,13 +18,14 @@ import { TYPOGRAPHY, } from '@opentrons/components' import { getIsTouchTipField } from '../../form-types' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { getMainPagePortalEl } from '../Portal' import { TOO_MANY_DECIMALS, PERCENT_RANGE_TO_SHOW_WARNING } from './constants' import * as utils from './utils' import { TipPositionTopView } from './TipPositionTopView' import { TipPositionSideView } from './TipPositionSideView' +import type { ChangeEvent } from 'react' import type { StepFieldName } from '../../form-types' type Offset = 'x' | 'y' | 'z' @@ -179,9 +180,7 @@ export function TipPositionModal( } } - const handleZInputFieldChange = ( - e: React.ChangeEvent - ): void => { + const handleZInputFieldChange = (e: ChangeEvent): void => { handleZChange(e.currentTarget.value) setPristine(false) } @@ -200,9 +199,7 @@ export function TipPositionModal( } } - const handleXInputFieldChange = ( - e: React.ChangeEvent - ): void => { + const handleXInputFieldChange = (e: ChangeEvent): void => { handleXChange(e.currentTarget.value) setPristine(false) } @@ -221,9 +218,7 @@ export function TipPositionModal( } } - const handleYInputFieldChange = ( - e: React.ChangeEvent - ): void => { + const handleYInputFieldChange = (e: ChangeEvent): void => { handleYChange(e.currentTarget.value) setPristine(false) } @@ -257,7 +252,7 @@ export function TipPositionModal( setYValue('0') setZValue('1') }} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} > {t('shared:reset_to_default')} @@ -331,7 +326,7 @@ export function TipPositionModal( { setView(view === 'side' ? 'top' : 'side') }} diff --git a/protocol-designer/src/organisms/WellOrderModal/__tests__/WellOrderModal.test.tsx b/protocol-designer/src/organisms/WellOrderModal/__tests__/WellOrderModal.test.tsx index 798bd8abc89..7afd6d1c139 100644 --- a/protocol-designer/src/organisms/WellOrderModal/__tests__/WellOrderModal.test.tsx +++ b/protocol-designer/src/organisms/WellOrderModal/__tests__/WellOrderModal.test.tsx @@ -4,14 +4,16 @@ import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { WellOrderModal } from '..' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('WellOrderModal', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/organisms/WellOrderModal/index.tsx b/protocol-designer/src/organisms/WellOrderModal/index.tsx index b7b181f6a72..54b4ad437ef 100644 --- a/protocol-designer/src/organisms/WellOrderModal/index.tsx +++ b/protocol-designer/src/organisms/WellOrderModal/index.tsx @@ -14,7 +14,7 @@ import { DropdownMenu, ALIGN_CENTER, } from '@opentrons/components' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { getMainPagePortalEl } from '../Portal' import { WellOrderVisualization } from './WellOrderVisualization' import type { WellOrderOption } from '../../form-types' @@ -164,7 +164,7 @@ export function WellOrderModal(props: WellOrderModalProps): JSX.Element | null { padding={`0 ${SPACING.spacing24} ${SPACING.spacing24}`} alignItems={ALIGN_CENTER} > - + {t('shared:reset_to_default')} diff --git a/protocol-designer/src/organisms/index.ts b/protocol-designer/src/organisms/index.ts index 0bf328fed4e..cbab9d62f8e 100644 --- a/protocol-designer/src/organisms/index.ts +++ b/protocol-designer/src/organisms/index.ts @@ -6,6 +6,7 @@ export * from './BlockingHintModal' export * from './ConfirmDeleteModal' export * from './ConfirmDeleteStagingAreaModal' export * from './DefineLiquidsModal' +export * from './DesignerNavigation' export * from './DisabledScreen' export * from './EditInstrumentsModal' export * from './EditNickNameModal' @@ -17,10 +18,11 @@ export * from './Kitchen' export * from './KnowledgeLink' export * from './LabwareOnDeck' export * from './LabwareUploadModal' +export * from './Navigation' export * from './PipetteInfoItem' export * from './Portal' -export * from './ProtocolNavBar' export * from './SelectWellsModal' +export * from './SettingsIcon' export * from './SlotDetailsContainer' export * from './SlotInformation' export * from './TipPositionModal' diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/AddMetadata.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/AddMetadata.tsx index d622b0ab626..50617a936a3 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/AddMetadata.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/AddMetadata.tsx @@ -67,8 +67,8 @@ export function AddMetadata(props: AddMetadataProps): JSX.Element | null { {...register('fields.name')} type="text" value={watch('fields.name')} - min={''} - max={''} + min="" + max="" autoFocus /> @@ -86,8 +86,8 @@ export function AddMetadata(props: AddMetadataProps): JSX.Element | null { {...register('fields.organizationOrAuthor')} type="text" value={watch('fields.organizationOrAuthor')} - min={''} - max={''} + min="" + max="" /> diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/SelectModules.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/SelectModules.tsx index dfd05979524..568859d205d 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/SelectModules.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/SelectModules.tsx @@ -14,6 +14,7 @@ import { WRAP, } from '@opentrons/components' import { + ABSORBANCE_READER_TYPE, ABSORBANCE_READER_V1, FLEX_ROBOT_TYPE, getModuleDisplayName, @@ -70,6 +71,7 @@ export function SelectModules(props: WizardTileProps): JSX.Element | null { TEMPERATURE_MODULE_TYPE, HEATERSHAKER_MODULE_TYPE, MAGNETIC_BLOCK_TYPE, + ABSORBANCE_READER_TYPE, ] const handleAddModule = ( diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx index bae902d4153..ff96f699267 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/SelectPipettes.tsx @@ -38,7 +38,7 @@ import { setFeatureFlags } from '../../feature-flags/actions' import { createCustomTiprackDef } from '../../labware-defs/actions' import { useKitchen } from '../../organisms/Kitchen/hooks' import { IncompatibleTipsModal, PipetteInfoItem } from '../../organisms' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { WizardBody } from './WizardBody' import { PIPETTE_GENS, PIPETTE_TYPES, PIPETTE_VOLUMES } from './constants' import { getTiprackOptions } from './utils' @@ -450,7 +450,7 @@ export function SelectPipettes(props: WizardTileProps): JSX.Element | null { (pipettesByMount.left.tiprackDefURI == null && pipettesByMount.right.tiprackDefURI == null) ? null : ( { const leftPipetteName = pipettesByMount.left.pipetteName const rightPipetteName = pipettesByMount.right.pipetteName diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx index d0c9c57cb37..ba3f0648672 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/WizardBody.tsx @@ -25,14 +25,16 @@ import three from '../../assets/images/onboarding_animation_3.webm' import four from '../../assets/images/onboarding_animation_4.webm' import five from '../../assets/images/onboarding_animation_5.webm' import six from '../../assets/images/onboarding_animation_6.webm' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' + +import type { ReactNode } from 'react' import type { RobotType } from '@opentrons/shared-data' interface WizardBodyProps { robotType: RobotType stepNumber: number header: string - children: React.ReactNode + children: ReactNode proceed: () => void disabled?: boolean goBack?: () => void @@ -144,7 +146,7 @@ export function WizardBody(props: WizardBodyProps): JSX.Element { alignItems={ALIGN_CENTER} > {goBack != null ? ( - + {t('go_back')} diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/AddMetadata.test.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/AddMetadata.test.tsx index 63bced829a2..2c2731ca9c5 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/AddMetadata.test.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/AddMetadata.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' @@ -7,9 +6,10 @@ import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { AddMetadata } from '../AddMetadata' +import type { ComponentProps } from 'react' import type { WizardFormState, WizardTileProps } from '../types' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -35,7 +35,7 @@ const mockWizardTileProps: Partial = { } describe('AddMetadata', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectFixtures.test.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectFixtures.test.tsx index 42702c2a507..b4d205aeb71 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectFixtures.test.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectFixtures.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' @@ -6,9 +5,11 @@ import { fireEvent, screen } from '@testing-library/react' import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { SelectFixtures } from '../SelectFixtures' + +import type { ComponentProps } from 'react' import type { WizardFormState, WizardTileProps } from '../types' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -34,7 +35,7 @@ const mockWizardTileProps: Partial = { } describe('SelectFixtures', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectGripper.test.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectGripper.test.tsx index 87ab1bb07e3..fbd0b4c6388 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectGripper.test.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectGripper.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' @@ -7,6 +6,7 @@ import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { SelectGripper } from '../SelectGripper' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' import type { WizardFormState, WizardTileProps } from '../types' @@ -20,7 +20,7 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -45,7 +45,7 @@ const mockWizardTileProps: Partial = { } describe('SelectGripper', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectModules.test.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectModules.test.tsx index a18f06bf508..30dcf7fae43 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectModules.test.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectModules.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' @@ -7,11 +6,13 @@ import { i18n } from '../../../assets/localization' import { getEnableAbsorbanceReader } from '../../../feature-flags/selectors' import { renderWithProviders } from '../../../__testing-utils__' import { SelectModules } from '../SelectModules' + +import type { ComponentProps } from 'react' import type { WizardFormState, WizardTileProps } from '../types' vi.mock('../../../feature-flags/selectors') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -37,7 +38,7 @@ const mockWizardTileProps: Partial = { } describe('SelectModules', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectPipettes.test.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectPipettes.test.tsx index 016a143a4bc..7af0b1dc597 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectPipettes.test.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectPipettes.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' @@ -12,6 +11,7 @@ import { createCustomTiprackDef } from '../../../labware-defs/actions' import { SelectPipettes } from '../SelectPipettes' import { getTiprackOptions } from '../utils' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' import type { WizardFormState, WizardTileProps } from '../types' @@ -31,7 +31,7 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -56,7 +56,7 @@ const mockWizardTileProps: Partial = { } describe('SelectPipettes', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectRobot.test.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectRobot.test.tsx index 241fdb159ba..29b11194e66 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectRobot.test.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/SelectRobot.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data' @@ -6,9 +5,11 @@ import { fireEvent, screen } from '@testing-library/react' import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { SelectRobot } from '../SelectRobot' + +import type { ComponentProps } from 'react' import type { WizardFormState, WizardTileProps } from '../types' -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -32,7 +33,7 @@ const mockWizardTileProps: Partial = { } describe('SelectRobot', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/WizardBody.test.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/WizardBody.test.tsx index fe33c8266c9..669dc7bac92 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/WizardBody.test.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/__tests__/WizardBody.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -7,14 +6,16 @@ import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { WizardBody } from '../WizardBody' -const render = (props: React.ComponentProps) => { +import type { ComponentProps } from 'react' + +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('WizardBody', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/CreateNewProtocolWizard/index.tsx b/protocol-designer/src/pages/CreateNewProtocolWizard/index.tsx index df079c72318..d3451605756 100644 --- a/protocol-designer/src/pages/CreateNewProtocolWizard/index.tsx +++ b/protocol-designer/src/pages/CreateNewProtocolWizard/index.tsx @@ -46,6 +46,8 @@ import { SelectModules } from './SelectModules' import { SelectFixtures } from './SelectFixtures' import { AddMetadata } from './AddMetadata' import { getTrashSlot } from './utils' + +import type { Dispatch, SetStateAction } from 'react' import type { ThunkDispatch } from 'redux-thunk' import type { NormalizedPipette } from '@opentrons/step-generation' import type { BaseState } from '../../types' @@ -389,7 +391,7 @@ interface CreateFileFormProps { createProtocolFile: (values: WizardFormState) => void goBack: () => void proceed: () => void - setWizardSteps: React.Dispatch> + setWizardSteps: Dispatch> analyticsStartTime: Date } diff --git a/protocol-designer/src/pages/Designer/DeckSetup/DeckItemHighlight.tsx b/protocol-designer/src/pages/Designer/DeckSetup/DeckItemHighlight.tsx new file mode 100644 index 00000000000..98a08fed0a7 --- /dev/null +++ b/protocol-designer/src/pages/Designer/DeckSetup/DeckItemHighlight.tsx @@ -0,0 +1,94 @@ +import { useTranslation } from 'react-i18next' +import { useSelector } from 'react-redux' +import { + ALIGN_CENTER, + BORDERS, + COLORS, + CURSOR_POINTER, + DISPLAY_FLEX, + DeckLabelSet, + POSITION_ABSOLUTE, + PRODUCT, + RobotCoordsForeignDiv, +} from '@opentrons/components' +import { + getHoveredDropdownItem, + getSelectedDropdownItem, +} from '../../../ui/steps/selectors' +import type { CoordinateTuple, Dimensions } from '@opentrons/shared-data' +import type { DeckSetupTabType } from '../types' + +interface DeckItemHighlightProps extends DeckSetupTabType { + slotBoundingBox: Dimensions + // can be slotId or labwareId (for off-deck labware) + itemId: string + slotPosition: CoordinateTuple | null +} + +export function DeckItemHighlight( + props: DeckItemHighlightProps +): JSX.Element | null { + const { tab, slotBoundingBox, itemId, slotPosition } = props + const { t } = useTranslation('application') + const hoveredDropdownSelection = useSelector(getHoveredDropdownItem) + const selectedDropdownLocation = useSelector(getSelectedDropdownItem) + + const isHovered = + hoveredDropdownSelection?.id != null + ? hoveredDropdownSelection.id === itemId + : false + const isSelected = selectedDropdownLocation.some( + selected => selected.id === itemId && selected.field === '2' + ) + + if ( + tab === 'startingDeck' || + slotPosition === null || + (!isHovered && !isSelected) + ) { + return null + } + + return ( + <> + + + + ) +} diff --git a/protocol-designer/src/pages/Designer/DeckSetup/DeckItemHover.tsx b/protocol-designer/src/pages/Designer/DeckSetup/DeckItemHover.tsx index 35961ab04cd..0b480340279 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/DeckItemHover.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/DeckItemHover.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { css } from 'styled-components' @@ -18,6 +17,7 @@ import { } from '@opentrons/components' import { getDeckSetupForActiveItem } from '../../../top-selectors/labware-locations' +import type { Dispatch, SetStateAction } from 'react' import type { CoordinateTuple, DeckSlotId, @@ -27,12 +27,12 @@ import type { DeckSetupTabType } from '../types' interface DeckItemHoverProps extends DeckSetupTabType { hover: string | null - setHover: React.Dispatch> + setHover: Dispatch> slotBoundingBox: Dimensions // can be slotId or labwareId (for off-deck labware) itemId: string slotPosition: CoordinateTuple | null - setShowMenuListForId: React.Dispatch> + setShowMenuListForId: Dispatch> menuListId: DeckSlotId | null isSelected?: boolean } diff --git a/protocol-designer/src/pages/Designer/DeckSetup/DeckSetupDetails.tsx b/protocol-designer/src/pages/Designer/DeckSetup/DeckSetupDetails.tsx index 53a61df3faf..110bf9535a2 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/DeckSetupDetails.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/DeckSetupDetails.tsx @@ -29,6 +29,7 @@ import { HoveredItems } from './HoveredItems' import { SelectedHoveredItems } from './SelectedHoveredItems' import { getAdjacentLabware } from './utils' import { SlotWarning } from './SlotWarning' +import { HighlightItems } from './HighlightItems' import type { ComponentProps, Dispatch, SetStateAction } from 'react' import type { ThermocyclerVizProps } from '@opentrons/components' @@ -258,17 +259,19 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element { ) : null} {labwareLoadedOnModule == null ? ( - + <> + + ) : null} @@ -431,6 +434,9 @@ export function DeckSetupDetails(props: DeckSetupDetailsProps): JSX.Element { ) })} + {/* highlight items from Protocol steps */} + + {/* selected hardware + labware */} ( false ) + const additionalEquipment = useSelector(getAdditionalEquipment) + const isGripperAttached = Object.values(additionalEquipment).some( + equipment => equipment?.name === 'gripper' + ) const [selectedHardware, setSelectedHardware] = useState< ModuleModel | Fixture | null >(null) @@ -231,7 +235,9 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null { disabled: selectedFixture === 'wasteChute' || selectedFixture === 'wasteChuteAndStagingArea' || - selectedFixture === 'trashBin', + selectedFixture === 'trashBin' || + selectedModuleModel === ABSORBANCE_READER_V1, + disabledReasonForTooltip: t('plate_reader_no_labware'), isActive: tab === 'labware', onClick: () => { setTab('labware') @@ -320,21 +326,28 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null { } if (selectedModuleModel != null) { // create module + const moduleType = getModuleType(selectedModuleModel) + // enforce gripper present in order to add plate reader + if (moduleType === ABSORBANCE_READER_TYPE && !isGripperAttached) { + makeSnackbar(t('gripper_required_for_plate_reader') as string) + return + } dispatch( createModule({ slot, - type: getModuleType(selectedModuleModel), + type: moduleType, model: selectedModuleModel, }) ) } if ( - selectedModuleModel == null && - selectedLabwareDefUri != null && - (createdLabwareForSlot?.labwareDefURI !== selectedLabwareDefUri || - (selectedNestedLabwareDefUri != null && - selectedNestedLabwareDefUri !== - createdNestedLabwareForSlot?.labwareDefURI)) + (slot === 'offDeck' && selectedLabwareDefUri != null) || + (selectedModuleModel == null && + selectedLabwareDefUri != null && + (createdLabwareForSlot?.labwareDefURI !== selectedLabwareDefUri || + (selectedNestedLabwareDefUri != null && + selectedNestedLabwareDefUri !== + createdNestedLabwareForSlot?.labwareDefURI))) ) { // create adapter + labware on deck dispatch( @@ -370,7 +383,7 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null { position === POSITION_FIXED ? { right: SPACING.spacing12, - top: `calc(${PROTOCOL_NAV_BAR_HEIGHT_REM}rem + ${SPACING.spacing12})`, + top: `calc(${NAV_BAR_HEIGHT_REM}rem + ${SPACING.spacing12})`, } : {} return ( @@ -397,7 +410,7 @@ export function DeckSetupTools(props: DeckSetupToolsProps): JSX.Element | null { ) : null} {changeModuleWarning} diff --git a/protocol-designer/src/pages/Designer/DeckSetup/FixtureRender.tsx b/protocol-designer/src/pages/Designer/DeckSetup/FixtureRender.tsx index 9a2ecad9c7b..3696cd7deaa 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/FixtureRender.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/FixtureRender.tsx @@ -2,18 +2,23 @@ import { Fragment } from 'react' import { useSelector } from 'react-redux' import { COLORS, + FixedTrash, FlexTrash, SingleSlotFixture, StagingAreaFixture, WasteChuteFixture, WasteChuteStagingAreaFixture, } from '@opentrons/components' -import { getPositionFromSlotId } from '@opentrons/shared-data' +import { OT2_ROBOT_TYPE, getPositionFromSlotId } from '@opentrons/shared-data' import { getInitialDeckSetup } from '../../../step-forms/selectors' import { LabwareOnDeck as LabwareOnDeckComponent } from '../../../organisms' import { lightFill, darkFill } from './DeckSetupContainer' import { getAdjacentLabware } from './utils' -import type { TrashCutoutId, StagingAreaLocation } from '@opentrons/components' +import type { + TrashCutoutId, + StagingAreaLocation, + DeckLabelProps, +} from '@opentrons/components' import type { CutoutId, DeckDefinition, @@ -27,9 +32,11 @@ interface FixtureRenderProps { cutout: CutoutId robotType: RobotType deckDef: DeckDefinition + showHighlight?: boolean + tagInfo?: DeckLabelProps[] } export const FixtureRender = (props: FixtureRenderProps): JSX.Element => { - const { fixture, cutout, deckDef, robotType } = props + const { fixture, cutout, deckDef, robotType, showHighlight, tagInfo } = props const deckSetup = useSelector(getInitialDeckSetup) const { labware } = deckSetup const adjacentLabware = getAdjacentLabware(fixture, cutout, labware) @@ -61,22 +68,28 @@ export const FixtureRender = (props: FixtureRenderProps): JSX.Element => { ) } case 'trashBin': { - return ( - - - - - ) + if (robotType === OT2_ROBOT_TYPE && showHighlight) { + return + } else { + return ( + + + + + ) + } } case 'wasteChute': { return ( @@ -85,6 +98,8 @@ export const FixtureRender = (props: FixtureRenderProps): JSX.Element => { cutoutId={cutout as typeof WASTE_CHUTE_CUTOUT} deckDefinition={deckDef} fixtureBaseColor={lightFill} + showHighlight={showHighlight} + tagInfo={tagInfo} /> ) } @@ -95,6 +110,8 @@ export const FixtureRender = (props: FixtureRenderProps): JSX.Element => { cutoutId={cutout as typeof WASTE_CHUTE_CUTOUT} deckDefinition={deckDef} fixtureBaseColor={lightFill} + showHighlight={showHighlight} + tagInfo={tagInfo} /> {renderLabwareOnDeck()} diff --git a/protocol-designer/src/pages/Designer/DeckSetup/HighlightItems.tsx b/protocol-designer/src/pages/Designer/DeckSetup/HighlightItems.tsx new file mode 100644 index 00000000000..c9e4726eae3 --- /dev/null +++ b/protocol-designer/src/pages/Designer/DeckSetup/HighlightItems.tsx @@ -0,0 +1,327 @@ +import { useSelector } from 'react-redux' +import { useTranslation } from 'react-i18next' + +import { + STANDARD_FLEX_SLOTS, + STANDARD_OT2_SLOTS, + THERMOCYCLER_MODULE_TYPE, + WASTE_CHUTE_CUTOUT, + getAddressableAreaFromSlotId, + getPositionFromSlotId, + inferModuleOrientationFromXCoordinate, +} from '@opentrons/shared-data' +import { getDeckSetupForActiveItem } from '../../../top-selectors/labware-locations' +import { + getHoveredDropdownItem, + getSelectedDropdownItem, +} from '../../../ui/steps/selectors' +import { getDesignerTab } from '../../../file-data/selectors' +import { LabwareLabel } from '../LabwareLabel' +import { ModuleLabel } from './ModuleLabel' +import { FixtureRender } from './FixtureRender' +import { DeckItemHighlight } from './DeckItemHighlight' +import type { AdditionalEquipmentName } from '@opentrons/step-generation' +import type { + RobotType, + DeckDefinition, + CutoutId, + AddressableAreaName, +} from '@opentrons/shared-data' +import type { LabwareOnDeck, ModuleOnDeck } from '../../../step-forms' +import type { Fixture } from './constants' + +interface HighlightItemsProps { + deckDef: DeckDefinition + robotType: RobotType +} + +const SLOTS = [ + ...STANDARD_FLEX_SLOTS, + ...STANDARD_OT2_SLOTS, + 'A4', + 'B4', + 'C4', + 'D4', + 'cutoutD3', +] + +export function HighlightItems(props: HighlightItemsProps): JSX.Element | null { + const { robotType, deckDef } = props + const { t } = useTranslation('application') + const tab = useSelector(getDesignerTab) + const { labware, modules, additionalEquipmentOnDeck } = useSelector( + getDeckSetupForActiveItem + ) + const hoveredItem = useSelector(getHoveredDropdownItem) + const selectedDropdownItems = useSelector(getSelectedDropdownItem) + + if ( + hoveredItem == null && + (selectedDropdownItems == null || selectedDropdownItems.length === 0) + ) { + return null + } + + const hoveredItemLabware: LabwareOnDeck | null = + hoveredItem?.id != null && labware[hoveredItem.id] != null + ? labware[hoveredItem.id] + : null + const selectedItemLabwares = selectedDropdownItems.filter( + selected => selected.id != null && labware[selected.id] + ) + const hoveredItemModule: ModuleOnDeck | null = + hoveredItem?.id != null && modules[hoveredItem.id] != null + ? modules[hoveredItem.id] + : null + const selectedItemModule = selectedDropdownItems.find( + selected => selected.id != null && modules[selected.id] + ) + const hoveredItemTrash: { + name: AdditionalEquipmentName + id: string + location?: string | undefined + } | null = + hoveredItem?.id != null && additionalEquipmentOnDeck[hoveredItem.id] != null + ? additionalEquipmentOnDeck[hoveredItem.id] + : null + const selectedItemTrash = selectedDropdownItems.find( + selected => selected.id != null && additionalEquipmentOnDeck[selected.id] + ) + + const hoveredDeckItem: string | null = + hoveredItem?.id != null && + SLOTS.includes(hoveredItem.id as AddressableAreaName) + ? hoveredItem.id + : null + const selectedItemSlot = selectedDropdownItems.find( + selected => + selected.id != null && SLOTS.includes(selected.id as AddressableAreaName) + ) + + const getLabwareItems = (): JSX.Element[] => { + const items: JSX.Element[] = [] + + if (hoveredItemLabware != null || selectedItemLabwares.length > 0) { + const selectedLabwaresOnDeck = selectedItemLabwares + .map(item => (item?.id != null ? labware[item.id] : null)) + .filter(Boolean) + + const labwaresToRender = + hoveredItemLabware != null + ? [hoveredItemLabware] + : selectedLabwaresOnDeck + + labwaresToRender.forEach((labwareOnDeck, index) => { + if (!labwareOnDeck) { + console.warn( + `labwareOnDeck was null as ${labwareOnDeck}, expected to find a matching entity` + ) + return + } + + let labwareSlot = labwareOnDeck.slot + const hasTC = Object.values(modules).some( + module => module.type === THERMOCYCLER_MODULE_TYPE + ) + + if (modules[labwareSlot]) { + labwareSlot = modules[labwareSlot].slot + } else if (labware[labwareSlot]) { + const adapter = labware[labwareSlot] + labwareSlot = modules[adapter.slot]?.slot ?? adapter.slot + } + + const position = getPositionFromSlotId(labwareSlot, deckDef) + if (position != null) { + items.push( + selected.id === labwareOnDeck.id + )} + isLast={true} + position={ + hasTC && labwareSlot === 'B1' ? [-20, 282, 0] : position + } + labwareDef={labwareOnDeck.def} + labelText={ + hoveredItemLabware == null + ? selectedItemLabwares.find( + selected => selected.id === labwareOnDeck.id + )?.text ?? '' + : hoveredItem.text ?? '' + } + /> + ) + } + }) + } + + return items + } + + const getModuleItems = (): JSX.Element[] => { + const items: JSX.Element[] = [] + + if (hoveredItemModule != null || selectedItemModule != null) { + const selectedModuleOnDeck = + selectedItemModule?.id != null ? modules[selectedItemModule.id] : null + const moduleOnDeck = hoveredItemModule ?? selectedModuleOnDeck + + if (!moduleOnDeck) { + console.warn( + `moduleOnDeck was null as ${moduleOnDeck}, expected to find a matching entity` + ) + return items + } + + const position = getPositionFromSlotId(moduleOnDeck.slot, deckDef) + if (position != null) { + items.push( + + ) + } + } + + return items + } + + const getTrashItems = (): JSX.Element[] => { + const items: JSX.Element[] = [] + + if (hoveredItemTrash != null || selectedItemTrash != null) { + const selectedTrashOnDeck = + selectedItemTrash?.id != null + ? additionalEquipmentOnDeck[selectedItemTrash.id] + : null + const trashOnDeck = hoveredItemTrash ?? selectedTrashOnDeck + + if (!trashOnDeck) { + console.warn( + `trashOnDeck was null as ${trashOnDeck}, expected to find a matching entity` + ) + return [] + } + + if (hoveredItemTrash != null) { + items.push( + + ) + } + + if (selectedTrashOnDeck != null && selectedItemTrash != null) { + items.push( + + ) + } + } + + return items + } + + const getDeckItems = (): JSX.Element[] => { + const items: JSX.Element[] = [] + + if (hoveredDeckItem != null || selectedItemSlot != null) { + const slot = hoveredDeckItem ?? selectedItemSlot?.id + + if (slot === WASTE_CHUTE_CUTOUT) { + items.push( + + ) + } else { + const addressableArea = + slot != null && slot !== WASTE_CHUTE_CUTOUT + ? getAddressableAreaFromSlotId(slot, deckDef) + : null + + if (!addressableArea) { + console.warn( + `addressableArea was null as ${addressableArea}, expected to find a matching entity` + ) + return [] + } + items.push( + + ) + } + } + + return items + } + + const renderItems = (): JSX.Element[] => { + return [ + ...getLabwareItems(), + ...getModuleItems(), + ...getTrashItems(), + ...getDeckItems(), + ] + } + + return <>{renderItems()} +} diff --git a/protocol-designer/src/pages/Designer/DeckSetup/HoveredItems.tsx b/protocol-designer/src/pages/Designer/DeckSetup/HoveredItems.tsx index 79bba166f88..7a478a37f98 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/HoveredItems.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/HoveredItems.tsx @@ -76,6 +76,7 @@ export const HoveredItems = ( text: selectedLabwareDef.metadata.displayName, isLast: false, isSelected: true, + isZoomed: true, }, ] : [] @@ -108,6 +109,7 @@ export const HoveredItems = ( orientation={orientation} isSelected={false} isLast={true} + slot={selectedSlot.slot} /> ) : null} diff --git a/protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx b/protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx index be4f457429e..995b06219d6 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/LabwareTools.tsx @@ -32,12 +32,13 @@ import { getModuleType, } from '@opentrons/shared-data' -import { BUTTON_LINK_STYLE } from '../../../atoms' +import { LINK_BUTTON_STYLE } from '../../../atoms' import { selectors as stepFormSelectors } from '../../../step-forms' import { getOnlyLatestDefs } from '../../../labware-defs' import { ADAPTER_96_CHANNEL, getLabwareIsCompatible as _getLabwareIsCompatible, + getLabwareCompatibleWithAbsorbanceReader, } from '../../../utils/labwareModuleCompatibility' import { getHas96Channel } from '../../../utils' import { createCustomLabwareDef } from '../../../labware-defs/actions' @@ -49,6 +50,7 @@ import { selectLabware, selectNestedLabware, } from '../../../labware-ingred/actions' +import { getEnableAbsorbanceReader } from '../../../feature-flags/selectors' import { ALL_ORDERED_CATEGORIES, CUSTOM_CATEGORY, @@ -59,6 +61,7 @@ import { getLabwareCompatibleWithAdapter, } from './utils' +import type { ChangeEvent, Dispatch, SetStateAction } from 'react' import type { DeckSlotId, LabwareDefinition2 } from '@opentrons/shared-data' import type { ModuleOnDeck } from '../../../step-forms' import type { ThunkDispatch } from '../../../types' @@ -73,9 +76,9 @@ interface LabwareToolsProps { slot: DeckSlotId setHoveredLabware: (defUri: string | null) => void searchTerm: string - setSearchTerm: React.Dispatch> + setSearchTerm: Dispatch> areCategoriesExpanded: CategoryExpand - setAreCategoriesExpanded: React.Dispatch> + setAreCategoriesExpanded: Dispatch> handleReset: () => void } @@ -132,13 +135,17 @@ export function LabwareTools(props: LabwareToolsProps): JSX.Element { robotType === OT2_ROBOT_TYPE ? isNextToHeaterShaker : false ) + const enablePlateReader = useSelector(getEnableAbsorbanceReader) + const getLabwareCompatible = useCallback( (def: LabwareDefinition2) => { // assume that custom (non-standard) labware is (potentially) compatible if (moduleType == null || !getLabwareDefIsStandard(def)) { return true } - return _getLabwareIsCompatible(def, moduleType) + return moduleType === ABSORBANCE_READER_TYPE + ? getLabwareCompatibleWithAbsorbanceReader(def) + : _getLabwareIsCompatible(def, moduleType) }, [moduleType] ) @@ -167,7 +174,8 @@ export function LabwareTools(props: LabwareToolsProps): JSX.Element { moduleType !== HEATERSHAKER_MODULE_TYPE) || (isAdapter96Channel && !has96Channel) || (slot === 'offDeck' && isAdapter) || - (PLATE_READER_LOADNAME === parameters.loadName && + (!enablePlateReader && + PLATE_READER_LOADNAME === parameters.loadName && moduleType !== ABSORBANCE_READER_TYPE) ) }, @@ -267,7 +275,7 @@ export function LabwareTools(props: LabwareToolsProps): JSX.Element { (isNextToHeaterShaker && robotType === OT2_ROBOT_TYPE) ? ( ) => { + onChange={(e: ChangeEvent) => { isNextToHeaterShaker ? setFilterHeight(e.currentTarget.checked) : setFilterRecommended(e.currentTarget.checked) @@ -484,7 +492,7 @@ export function LabwareTools(props: LabwareToolsProps): JSX.Element { alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_CENTER} > - + {t('upload_custom_labware')} diff --git a/protocol-designer/src/pages/Designer/DeckSetup/ModuleLabel.tsx b/protocol-designer/src/pages/Designer/DeckSetup/ModuleLabel.tsx index 1b8eaa4e73b..568eeb5f0d9 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/ModuleLabel.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/ModuleLabel.tsx @@ -1,13 +1,21 @@ import { useRef, useState, useEffect } from 'react' +import { useSelector } from 'react-redux' import { DeckLabelSet } from '@opentrons/components' import { + FLEX_ROBOT_TYPE, + FLEX_STANDARD_DECKID, HEATERSHAKER_MODULE_TYPE, - MAGNETIC_MODULE_TYPE, + OT2_STANDARD_DECKID, TEMPERATURE_MODULE_TYPE, getModuleDef2, } from '@opentrons/shared-data' +import { getRobotType } from '../../../file-data/selectors' import type { DeckLabelProps } from '@opentrons/components' -import type { CoordinateTuple, ModuleModel } from '@opentrons/shared-data' +import type { + CoordinateTuple, + DeckSlotId, + ModuleModel, +} from '@opentrons/shared-data' interface ModuleLabelProps { moduleModel: ModuleModel @@ -15,7 +23,10 @@ interface ModuleLabelProps { orientation: 'left' | 'right' isSelected: boolean isLast: boolean + slot: DeckSlotId | null + isZoomed?: boolean labwareInfos?: DeckLabelProps[] + labelName?: string } export const ModuleLabel = (props: ModuleLabelProps): JSX.Element => { const { @@ -25,7 +36,11 @@ export const ModuleLabel = (props: ModuleLabelProps): JSX.Element => { isSelected, isLast, labwareInfos = [], + isZoomed = true, + labelName, + slot, } = props + const robotType = useSelector(getRobotType) const labelContainerRef = useRef(null) const [labelContainerHeight, setLabelContainerHeight] = useState(12) @@ -36,38 +51,50 @@ export const ModuleLabel = (props: ModuleLabelProps): JSX.Element => { }, [labwareInfos]) const def = getModuleDef2(moduleModel) - const overhang = - def?.dimensions.labwareInterfaceXDimension != null - ? def.dimensions.xDimension - def?.dimensions.labwareInterfaceXDimension + const slotTransformKey = + robotType === FLEX_ROBOT_TYPE ? FLEX_STANDARD_DECKID : OT2_STANDARD_DECKID + const cornerOffsetsFromSlotFromTransform = + slot != null && !isZoomed + ? def?.slotTransforms?.[slotTransformKey]?.[slot]?.cornerOffsetFromSlot + : null + const tempAdjustmentX = + def?.moduleType === TEMPERATURE_MODULE_TYPE && orientation === 'right' + ? def?.dimensions.xDimension - (def?.dimensions.footprintXDimension ?? 0) // shift depending on side of deck + : 0 + const tempAdjustmentY = def?.moduleType === TEMPERATURE_MODULE_TYPE ? -1 : 0 + const heaterShakerAdjustmentX = + def?.moduleType === HEATERSHAKER_MODULE_TYPE && orientation === 'right' // shift depending on side of deck + ? 7 // TODO(ND: 12/18/2024): investigate further why the module definition does not contain sufficient info to find this offset : 0 - // TODO(ja 9/6/24): definitely need to refine these overhang values - let leftOverhang = overhang - if (def?.moduleType === TEMPERATURE_MODULE_TYPE) { - leftOverhang = overhang * 2 - } else if (def?.moduleType === HEATERSHAKER_MODULE_TYPE) { - leftOverhang = overhang + 14 - } else if (def?.moduleType === MAGNETIC_MODULE_TYPE) { - leftOverhang = overhang + 8 - } return ( diff --git a/protocol-designer/src/pages/Designer/DeckSetup/SelectedHoveredItems.tsx b/protocol-designer/src/pages/Designer/DeckSetup/SelectedHoveredItems.tsx index 33bb727fa38..b150cdb9274 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/SelectedHoveredItems.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/SelectedHoveredItems.tsx @@ -115,6 +115,7 @@ export const SelectedHoveredItems = ( text: def.metadata.displayName, isSelected: true, isLast: hoveredLabware == null && selectedNestedLabwareDefUri == null, + isZoomed: true, } labwareInfos.push(selectedLabwareLabel) } @@ -123,6 +124,7 @@ export const SelectedHoveredItems = ( text: selectedNestedLabwareDef.metadata.displayName, isSelected: true, isLast: hoveredLabware == null, + isZoomed: true, } labwareInfos.push(selectedNestedLabwareLabel) } @@ -136,6 +138,7 @@ export const SelectedHoveredItems = ( text: hoveredLabwareDef.metadata.displayName, isSelected: false, isLast: true, + isZoomed: true, } labwareInfos.push(hoverLabelLabel) } @@ -185,6 +188,7 @@ export const SelectedHoveredItems = ( orientation={orientation} isSelected={true} labwareInfos={labwareInfos} + slot={selectedSlot.slot} /> ) : null} @@ -208,6 +212,7 @@ export const SelectedHoveredItems = ( selectedNestedLabwareDef?.metadata.displayName ?? 'unknown name', isSelected: true, isLast: true, + isZoomed: true, }, ]} /> diff --git a/protocol-designer/src/pages/Designer/DeckSetup/SlotOverflowMenu.tsx b/protocol-designer/src/pages/Designer/DeckSetup/SlotOverflowMenu.tsx index c6c37c5be31..6cf595a0d97 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/SlotOverflowMenu.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/SlotOverflowMenu.tsx @@ -21,21 +21,26 @@ import { getCutoutIdFromAddressableArea, getDeckDefFromRobotType, } from '@opentrons/shared-data' -import { getDeckSetupForActiveItem } from '../../../top-selectors/labware-locations' +import { getRobotType } from '../../../file-data/selectors' +import { + deleteContainer, + duplicateLabware, + openIngredientSelector, +} from '../../../labware-ingred/actions' +import { getNextAvailableDeckSlot } from '../../../labware-ingred/utils' import { deleteModule } from '../../../step-forms/actions' import { ConfirmDeleteStagingAreaModal, EditNickNameModal, } from '../../../organisms' +import { useKitchen } from '../../../organisms/Kitchen/hooks' import { deleteDeckFixture } from '../../../step-forms/actions/additionalItems' -import { - deleteContainer, - duplicateLabware, - openIngredientSelector, -} from '../../../labware-ingred/actions' +import { getDeckSetupForActiveItem } from '../../../top-selectors/labware-locations' + import { getStagingAreaAddressableAreas } from '../../../utils' import { selectors as labwareIngredSelectors } from '../../../labware-ingred/selectors' + import type { MouseEvent, SetStateAction } from 'react' import type { AddressableAreaName, @@ -68,6 +73,7 @@ const TOP_SLOT_Y_POSITION = 50 const TOP_SLOT_Y_POSITION_ALL_BUTTONS = 110 const TOP_SLOT_Y_POSITION_2_BUTTONS = 35 const STAGING_AREA_SLOTS = ['A4', 'B4', 'C4', 'D4'] + interface SlotOverflowMenuProps { // can be off-deck id or deck slot location: DeckSlotId | string @@ -105,11 +111,16 @@ export function SlotOverflowMenu( labwareIngredSelectors.getLiquidsByLabwareId ) + const robotType = useSelector(getRobotType) + + const { makeSnackbar } = useKitchen() + const { labware: deckSetupLabware, modules: deckSetupModules, additionalEquipmentOnDeck, } = deckSetup + const isOffDeckLocation = deckSetupLabware[location] != null const moduleOnSlot = Object.values(deckSetupModules).find( @@ -120,6 +131,9 @@ export function SlotOverflowMenu( ? lw.id === location : lw.slot === location || lw.slot === moduleOnSlot?.id ) + const isSpace = + getNextAvailableDeckSlot(deckSetup, robotType, labwareOnSlot?.def) != null + const isLabwareTiprack = labwareOnSlot?.def.parameters.isTiprack ?? false const isLabwareAnAdapter = labwareOnSlot?.def.allowedRoles?.includes('adapter') ?? false @@ -158,6 +172,24 @@ export function SlotOverflowMenu( location as AddressableAreaName ) + const handleDuplicate = (): void => { + if (!isSpace) { + makeSnackbar(t('deck_slots_full') as string) + return + } + + if ( + labwareOnSlot != null && + !isLabwareAnAdapter && + nestedLabwareOnSlot == null + ) { + dispatch(duplicateLabware(labwareOnSlot.id)) + } else if (nestedLabwareOnSlot != null) { + dispatch(duplicateLabware(nestedLabwareOnSlot.id)) + } + setShowMenuList(false) + } + const handleClear = (): void => { // clear module from slot if (moduleOnSlot != null) { @@ -309,20 +341,7 @@ export function SlotOverflowMenu( {showDuplicateBtn ? ( - { - if ( - labwareOnSlot != null && - !isLabwareAnAdapter && - nestedLabwareOnSlot == null - ) { - dispatch(duplicateLabware(labwareOnSlot.id)) - } else if (nestedLabwareOnSlot != null) { - dispatch(duplicateLabware(nestedLabwareOnSlot.id)) - } - setShowMenuList(false) - }} - > + {t('duplicate')} diff --git a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/DeckSetupContainer.test.tsx b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/DeckSetupContainer.test.tsx index eb77c79190a..a5d0226472d 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/DeckSetupContainer.test.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/DeckSetupContainer.test.tsx @@ -8,14 +8,19 @@ import { renderWithProviders } from '../../../../__testing-utils__' import { selectors } from '../../../../labware-ingred/selectors' import { getDeckSetupForActiveItem } from '../../../../top-selectors/labware-locations' -import { DeckSetupTools } from '../DeckSetupTools' -import { DeckSetupContainer } from '../DeckSetupContainer' +import { + getHoveredDropdownItem, + getSelectedDropdownItem, +} from '../../../../ui/steps/selectors' import { getSelectedTerminalItemId } from '../../../../ui/steps' import { getDisableModuleRestrictions } from '../../../../feature-flags/selectors' import { getRobotType } from '../../../../file-data/selectors' import { DeckSetupDetails } from '../DeckSetupDetails' +import { DeckSetupTools } from '../DeckSetupTools' +import { DeckSetupContainer } from '../DeckSetupContainer' import type * as OpentronsComponents from '@opentrons/components' +vi.mock('../../../../ui/steps/selectors') vi.mock('../../../../top-selectors/labware-locations') vi.mock('../../../../feature-flags/selectors') vi.mock('../DeckSetupTools') @@ -41,6 +46,10 @@ describe('DeckSetupContainer', () => { slot: 'D3', cutout: 'cutoutD3', }) + vi.mocked(getSelectedDropdownItem).mockReturnValue([ + { id: null, text: null }, + ]) + vi.mocked(getHoveredDropdownItem).mockReturnValue({ id: null, text: null }) vi.mocked(DeckSetupTools).mockReturnValue(
        mock DeckSetupTools
        ) vi.mocked(DeckSetupDetails).mockReturnValue(
        mock DeckSetupDetails
        diff --git a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/DeckSetupTools.test.tsx b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/DeckSetupTools.test.tsx index 5eab480710e..6d7cc88479e 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/DeckSetupTools.test.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/DeckSetupTools.test.tsx @@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' import { + ABSORBANCE_READER_V1, FLEX_ROBOT_TYPE, HEATERSHAKER_MODULE_V1, fixture96Plate, @@ -9,7 +10,9 @@ import { import { i18n } from '../../../../assets/localization' import { renderWithProviders } from '../../../../__testing-utils__' import { deleteContainer } from '../../../../labware-ingred/actions' +import { useKitchen } from '../../../../organisms/Kitchen/hooks' import { deleteModule } from '../../../../step-forms/actions' +import { getAdditionalEquipment } from '../../../../step-forms/selectors' import { getRobotType } from '../../../../file-data/selectors' import { getEnableAbsorbanceReader } from '../../../../feature-flags/selectors' import { deleteDeckFixture } from '../../../../step-forms/actions/additionalItems' @@ -19,7 +22,7 @@ import { getDeckSetupForActiveItem } from '../../../../top-selectors/labware-loc import { DeckSetupTools } from '../DeckSetupTools' import { LabwareTools } from '../LabwareTools' -import type * as React from 'react' +import type { ComponentProps } from 'react' import type { LabwareDefinition2 } from '@opentrons/shared-data' vi.mock('../LabwareTools') @@ -31,14 +34,18 @@ vi.mock('../../../../step-forms/actions') vi.mock('../../../../step-forms/actions/additionalItems') vi.mock('../../../../labware-ingred/selectors') vi.mock('../../../../tutorial/selectors') -const render = (props: React.ComponentProps) => { +vi.mock('../../../../step-forms/selectors') +vi.mock('../../../../organisms/Kitchen/hooks') +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } +const mockMakeSnackbar = vi.fn() + describe('DeckSetupTools', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -66,6 +73,12 @@ describe('DeckSetupTools', () => { pipettes: {}, }) vi.mocked(getDismissedHints).mockReturnValue([]) + vi.mocked(getAdditionalEquipment).mockReturnValue({}) + vi.mocked(useKitchen).mockReturnValue({ + makeSnackbar: mockMakeSnackbar, + bakeToast: vi.fn(), + eatToast: vi.fn(), + }) }) afterEach(() => { vi.resetAllMocks() @@ -164,4 +177,32 @@ describe('DeckSetupTools', () => { fireEvent.click(screen.getByText('Done')) expect(props.onCloseClick).toHaveBeenCalled() }) + it('should save plate reader if gripper configured', () => { + vi.mocked(getAdditionalEquipment).mockReturnValue({ + gripperUri: { name: 'gripper', id: 'gripperId' }, + }) + vi.mocked(selectors.getZoomedInSlotInfo).mockReturnValue({ + selectedLabwareDefUri: null, + selectedNestedLabwareDefUri: null, + selectedFixture: null, + selectedModuleModel: ABSORBANCE_READER_V1, + selectedSlot: { slot: 'D3', cutout: 'cutoutD3' }, + }) + render(props) + fireEvent.click(screen.getByText('Done')) + expect(props.onCloseClick).toHaveBeenCalled() + }) + it('should prevent saving plate reader and make toast if gripper not configured', () => { + vi.mocked(selectors.getZoomedInSlotInfo).mockReturnValue({ + selectedLabwareDefUri: null, + selectedNestedLabwareDefUri: null, + selectedFixture: null, + selectedModuleModel: ABSORBANCE_READER_V1, + selectedSlot: { slot: 'D3', cutout: 'cutoutD3' }, + }) + render(props) + fireEvent.click(screen.getByText('Done')) + expect(props.onCloseClick).not.toHaveBeenCalled() + expect(mockMakeSnackbar).toHaveBeenCalled() + }) }) diff --git a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/HoveredItems.test.tsx b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/HoveredItems.test.tsx index 605af62250e..4f911f558dc 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/HoveredItems.test.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/HoveredItems.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -14,6 +13,8 @@ import { getCustomLabwareDefsByURI } from '../../../../labware-defs/selectors' import { getDesignerTab } from '../../../../file-data/selectors' import { FixtureRender } from '../FixtureRender' import { HoveredItems } from '../HoveredItems' + +import type { ComponentProps } from 'react' import type * as OpentronsComponents from '@opentrons/components' vi.mock('../FixtureRender') @@ -29,12 +30,12 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('HoveredItems', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/LabwareTools.test.tsx b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/LabwareTools.test.tsx index 479724f3527..ce41f0e9210 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/LabwareTools.test.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/LabwareTools.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -21,24 +20,27 @@ import { createCustomLabwareDef } from '../../../../labware-defs/actions' import { getCustomLabwareDefsByURI } from '../../../../labware-defs/selectors' import { getRobotType } from '../../../../file-data/selectors' import { LabwareTools } from '../LabwareTools' + +import type { ComponentProps } from 'react' import type { LabwareDefinition2, PipetteV2Specs } from '@opentrons/shared-data' vi.mock('../../../../utils') vi.mock('../../../../step-forms/selectors') +vi.mock('../../../../feature-flags/selectors') vi.mock('../../../../file-data/selectors') vi.mock('../../../../labware-defs/selectors') vi.mock('../../../../labware-defs/actions') vi.mock('../../../../labware-ingred/selectors') vi.mock('../../../../labware-ingred/actions') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('LabwareTools', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/SelectedHoveredItems.test.tsx b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/SelectedHoveredItems.test.tsx index 4c705749bc6..59496c9049b 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/SelectedHoveredItems.test.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/SelectedHoveredItems.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -17,6 +16,8 @@ import { getDesignerTab } from '../../../../file-data/selectors' import { LabwareOnDeck } from '../../../../organisms' import { FixtureRender } from '../FixtureRender' import { SelectedHoveredItems } from '../SelectedHoveredItems' + +import type { ComponentProps } from 'react' import type * as OpentronsComponents from '@opentrons/components' import type { LabwareDefinition2 } from '@opentrons/shared-data' @@ -34,12 +35,12 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('SelectedHoveredItems', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/SlotOverflowMenu.test.tsx b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/SlotOverflowMenu.test.tsx index 56d5af2f806..1924ac7c961 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/SlotOverflowMenu.test.tsx +++ b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/SlotOverflowMenu.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -11,12 +10,15 @@ import { openIngredientSelector, } from '../../../../labware-ingred/actions' import { EditNickNameModal } from '../../../../organisms' +import { useKitchen } from '../../../../organisms/Kitchen/hooks' import { deleteModule } from '../../../../step-forms/actions' import { deleteDeckFixture } from '../../../../step-forms/actions/additionalItems' import { getDeckSetupForActiveItem } from '../../../../top-selectors/labware-locations' import { selectors as labwareIngredSelectors } from '../../../../labware-ingred/selectors' +import { getNextAvailableDeckSlot } from '../../../../labware-ingred/utils' import { SlotOverflowMenu } from '../SlotOverflowMenu' +import type { ComponentProps } from 'react' import type { NavigateFunction } from 'react-router-dom' import type { LabwareDefinition2 } from '@opentrons/shared-data' @@ -28,6 +30,9 @@ vi.mock('../../../../labware-ingred/actions') vi.mock('../../../../labware-ingred/selectors') vi.mock('../../../../step-forms/actions/additionalItems') vi.mock('../../../../organisms') +vi.mock('../../../../file-data/selectors') +vi.mock('../../../../labware-ingred/utils') +vi.mock('../../../../organisms/Kitchen/hooks') vi.mock('react-router-dom', async importOriginal => { const actual = await importOriginal() return { @@ -36,16 +41,17 @@ vi.mock('react-router-dom', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } const MOCK_STAGING_AREA_ID = 'MOCK_STAGING_AREA_ID' +const MOCK_MAKE_SNACKBAR = vi.fn() describe('SlotOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -91,6 +97,12 @@ describe('SlotOverflowMenu', () => {
        mockEditNickNameModal
        ) vi.mocked(labwareIngredSelectors.getLiquidsByLabwareId).mockReturnValue({}) + vi.mocked(getNextAvailableDeckSlot).mockReturnValue('A1') + vi.mocked(useKitchen).mockReturnValue({ + makeSnackbar: MOCK_MAKE_SNACKBAR, + eatToast: vi.fn(), + bakeToast: vi.fn(), + }) }) afterEach(() => { @@ -165,4 +177,11 @@ describe('SlotOverflowMenu', () => { expect(vi.mocked(deleteModule)).toHaveBeenCalledOnce() expect(vi.mocked(deleteModule)).toHaveBeenCalledWith('modId') }) + + it('renders snackbar if duplicate is clicked and the deck is full', () => { + vi.mocked(getNextAvailableDeckSlot).mockReturnValue(null) + render(props) + fireEvent.click(screen.getByRole('button', { name: 'Duplicate labware' })) + expect(MOCK_MAKE_SNACKBAR).toHaveBeenCalled() + }) }) diff --git a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/utils.test.ts b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/utils.test.ts index 50325ad7197..7d48c7ea71d 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/__tests__/utils.test.ts +++ b/protocol-designer/src/pages/Designer/DeckSetup/__tests__/utils.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect } from 'vitest' import { + ABSORBANCE_READER_V1, FLEX_ROBOT_TYPE, HEATERSHAKER_MODULE_TYPE, HEATERSHAKER_MODULE_V1, @@ -45,12 +46,13 @@ describe('getModuleModelsBySlot', () => { }) it('renders all flex modules for B1', () => { expect(getModuleModelsBySlot(true, FLEX_ROBOT_TYPE, 'B1')).toEqual( - FLEX_MODULE_MODELS + FLEX_MODULE_MODELS.filter(model => model !== ABSORBANCE_READER_V1) ) }) it('renders all flex modules for C1', () => { const noTC = FLEX_MODULE_MODELS.filter( - model => model !== THERMOCYCLER_MODULE_V2 + model => + model !== THERMOCYCLER_MODULE_V2 && model !== ABSORBANCE_READER_V1 ) expect(getModuleModelsBySlot(true, FLEX_ROBOT_TYPE, 'C1')).toEqual(noTC) }) diff --git a/protocol-designer/src/pages/Designer/DeckSetup/constants.ts b/protocol-designer/src/pages/Designer/DeckSetup/constants.ts index e1acb64424d..479b185a765 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/constants.ts +++ b/protocol-designer/src/pages/Designer/DeckSetup/constants.ts @@ -13,6 +13,7 @@ import { HEATERSHAKER_MODULE_TYPE, MAGNETIC_BLOCK_TYPE, ABSORBANCE_READER_TYPE, + ABSORBANCE_READER_V1, } from '@opentrons/shared-data' import type { ModuleModel, ModuleType } from '@opentrons/shared-data' @@ -92,9 +93,7 @@ export const RECOMMENDED_LABWARE_BY_MODULE: { [K in ModuleType]: string[] } = { 'nest_96_wellplate_2ml_deep', 'opentrons_96_wellplate_200ul_pcr_full_skirt', ], - [ABSORBANCE_READER_TYPE]: [ - 'opentrons_flex_lid_absorbance_plate_reader_module', - ], + [ABSORBANCE_READER_TYPE]: ['nest_96_wellplate_200ul_flat'], } export const MOAM_MODELS_WITH_FF: ModuleModel[] = [TEMPERATURE_MODULE_V2] @@ -102,6 +101,7 @@ export const MOAM_MODELS: ModuleModel[] = [ TEMPERATURE_MODULE_V2, HEATERSHAKER_MODULE_V1, MAGNETIC_BLOCK_V1, + ABSORBANCE_READER_V1, ] export const MAX_MOAM_MODULES = 7 diff --git a/protocol-designer/src/pages/Designer/DeckSetup/utils.ts b/protocol-designer/src/pages/Designer/DeckSetup/utils.ts index a288947365a..a51a850bb84 100644 --- a/protocol-designer/src/pages/Designer/DeckSetup/utils.ts +++ b/protocol-designer/src/pages/Designer/DeckSetup/utils.ts @@ -5,8 +5,9 @@ import { FLEX_ROBOT_TYPE, FLEX_STAGING_AREA_SLOT_ADDRESSABLE_AREAS, HEATERSHAKER_MODULE_TYPE, - MAGNETIC_BLOCK_V1, + HEATERSHAKER_MODULE_V1, OT2_ROBOT_TYPE, + TEMPERATURE_MODULE_V2, THERMOCYCLER_MODULE_TYPE, THERMOCYCLER_MODULE_V2, getAreSlotsAdjacent, @@ -21,6 +22,7 @@ import { RECOMMENDED_LABWARE_BY_MODULE, } from './constants' +import type { Dispatch, SetStateAction } from 'react' import type { AddressableAreaName, CutoutFixture, @@ -61,33 +63,32 @@ export function getModuleModelsBySlot( robotType: RobotType, slot: DeckSlotId ): ModuleModel[] { - const FLEX_MIDDLE_SLOTS = ['B2', 'C2', 'A2', 'D2'] + const FLEX_MIDDLE_SLOTS = new Set(['B2', 'C2', 'A2', 'D2']) const OT2_MIDDLE_SLOTS = ['2', '5', '8', '11'] - const FILTERED_MODULES = enableAbsorbanceReader - ? FLEX_MODULE_MODELS - : FLEX_MODULE_MODELS.filter(model => model !== ABSORBANCE_READER_V1) - let moduleModels: ModuleModel[] = FILTERED_MODULES + const FLEX_RIGHT_SLOTS = new Set(['A3', 'B3', 'C3', 'D3']) + + let moduleModels: ModuleModel[] = FLEX_MODULE_MODELS switch (robotType) { case FLEX_ROBOT_TYPE: { - if (slot !== 'B1' && !FLEX_MIDDLE_SLOTS.includes(slot)) { - moduleModels = FILTERED_MODULES.filter( - model => model !== THERMOCYCLER_MODULE_V2 - ) - } - if (FLEX_MIDDLE_SLOTS.includes(slot)) { - moduleModels = FILTERED_MODULES.filter( - model => model === MAGNETIC_BLOCK_V1 - ) - } - if ( - FLEX_STAGING_AREA_SLOT_ADDRESSABLE_AREAS.includes( - slot as AddressableAreaName - ) - ) { - moduleModels = [] - } + moduleModels = FLEX_STAGING_AREA_SLOT_ADDRESSABLE_AREAS.includes( + slot as AddressableAreaName + ) + ? [] + : FLEX_MODULE_MODELS.filter(model => { + if (model === THERMOCYCLER_MODULE_V2) { + return slot === 'B1' + } else if (model === ABSORBANCE_READER_V1) { + return FLEX_RIGHT_SLOTS.has(slot) && enableAbsorbanceReader + } else if ( + model === TEMPERATURE_MODULE_V2 || + model === HEATERSHAKER_MODULE_V1 + ) { + return !FLEX_MIDDLE_SLOTS.has(slot) + } + return true + }) break } case OT2_ROBOT_TYPE: { @@ -229,7 +230,7 @@ export function zoomInOnCoordinate(props: ZoomInOnCoordinateProps): string { export interface AnimateZoomProps { targetViewBox: string viewBox: string - setViewBox: React.Dispatch> + setViewBox: Dispatch> } type ViewBox = [number, number, number, number] diff --git a/protocol-designer/src/pages/Designer/HighlightLabware.tsx b/protocol-designer/src/pages/Designer/HighlightLabware.tsx index c2bddc9fbd4..9e2359e66c1 100644 --- a/protocol-designer/src/pages/Designer/HighlightLabware.tsx +++ b/protocol-designer/src/pages/Designer/HighlightLabware.tsx @@ -1,6 +1,7 @@ import { useSelector } from 'react-redux' import { getLabwareEntities } from '../../step-forms/selectors' import { getHoveredStepLabware } from '../../ui/steps' +import { getDesignerTab } from '../../file-data/selectors' import { LabwareLabel } from './LabwareLabel' import type { CoordinateTuple } from '@opentrons/shared-data' import type { LabwareOnDeck } from '../../step-forms' @@ -16,6 +17,7 @@ export function HighlightLabware( const { labwareOnDeck, position } = props const labwareEntities = useSelector(getLabwareEntities) const hoveredLabware = useSelector(getHoveredStepLabware) + const tab = useSelector(getDesignerTab) const adapterId = labwareEntities[labwareOnDeck.slot] != null ? labwareEntities[labwareOnDeck.slot].id @@ -23,6 +25,9 @@ export function HighlightLabware( const highlighted = hoveredLabware.includes(adapterId ?? labwareOnDeck.id) + if (tab === 'protocolSteps') { + return null + } if (highlighted) { return ( { +export const LabwareLabel = (props: LabwareLabelProps): JSX.Element => { const { labwareDef, position, isSelected, isLast, nestedLabwareInfo = [], + labelText = labwareDef.metadata.displayName, } = props const labelContainerRef = useRef(null) const designerTab = useSelector(getDesignerTab) const [labelContainerHeight, setLabelContainerHeight] = useState(0) - const deckLabels = - designerTab === 'startingDeck' - ? [ - ...nestedLabwareInfo, - { - text: labwareDef.metadata.displayName, - isSelected: isSelected, - isLast: isLast, - }, - ] - : [] + const deckLabels = [ + ...nestedLabwareInfo, + { + text: labelText, + isSelected: isSelected, + isLast: isLast, + isZoomed: designerTab === 'startingDeck', + }, + ] useEffect(() => { if (labelContainerRef.current) { diff --git a/protocol-designer/src/pages/Designer/LiquidsOverflowMenu.tsx b/protocol-designer/src/pages/Designer/LiquidsOverflowMenu.tsx index c141ecee427..1a4b2e12c29 100644 --- a/protocol-designer/src/pages/Designer/LiquidsOverflowMenu.tsx +++ b/protocol-designer/src/pages/Designer/LiquidsOverflowMenu.tsx @@ -23,7 +23,7 @@ import { LINE_CLAMP_TEXT_STYLE } from '../../atoms' import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors' import * as labwareIngredActions from '../../labware-ingred/actions' -import type { MouseEvent } from 'react' +import type { MouseEvent, RefObject } from 'react' import type { ThunkDispatch } from '../../types' const NAV_HEIGHT = '64px' @@ -31,7 +31,7 @@ const NAV_HEIGHT = '64px' interface LiquidsOverflowMenuProps { onClose: () => void showLiquidsModal: () => void - overflowWrapperRef: React.RefObject + overflowWrapperRef: RefObject } export function LiquidsOverflowMenu( diff --git a/protocol-designer/src/pages/Designer/Offdeck/HighlightOffdeckSlot.tsx b/protocol-designer/src/pages/Designer/Offdeck/HighlightOffdeckSlot.tsx new file mode 100644 index 00000000000..4f8ddfdbbea --- /dev/null +++ b/protocol-designer/src/pages/Designer/Offdeck/HighlightOffdeckSlot.tsx @@ -0,0 +1,76 @@ +import { useSelector } from 'react-redux' +import { useTranslation } from 'react-i18next' +import { DeckLabelSet, Flex, POSITION_RELATIVE } from '@opentrons/components' +import { + getHoveredDropdownItem, + getSelectedDropdownItem, +} from '../../../ui/steps/selectors' +import type { CoordinateTuple } from '@opentrons/shared-data' +import type { LabwareOnDeck } from '../../../step-forms' + +interface HighlightOffdeckSlotProps { + labwareOnDeck?: LabwareOnDeck + position: CoordinateTuple +} + +export function HighlightOffdeckSlot( + props: HighlightOffdeckSlotProps +): JSX.Element | null { + const { labwareOnDeck, position } = props + const { t } = useTranslation('application') + const hoveredDropdownItem = useSelector(getHoveredDropdownItem) + const selectedDropdownSelection = useSelector(getSelectedDropdownItem) + + if (labwareOnDeck != null) { + const isLabwareSelectionSelected = selectedDropdownSelection.some( + selected => selected.id === labwareOnDeck?.id + ) + const highlighted = hoveredDropdownItem.id === labwareOnDeck?.id + if (highlighted ?? isLabwareSelectionSelected) { + return ( + + + + ) + } + } else { + const highlightedNewLocation = hoveredDropdownItem.id === 'offDeck' + const selected = selectedDropdownSelection.some( + selected => selected.id === 'offDeck' + ) + if (highlightedNewLocation ?? selected) { + return ( + + ) + } + } + return null +} diff --git a/protocol-designer/src/pages/Designer/Offdeck/OffDeckDetails.tsx b/protocol-designer/src/pages/Designer/Offdeck/OffDeckDetails.tsx index 3759aabf4d5..05720f81555 100644 --- a/protocol-designer/src/pages/Designer/Offdeck/OffDeckDetails.tsx +++ b/protocol-designer/src/pages/Designer/Offdeck/OffDeckDetails.tsx @@ -23,12 +23,17 @@ import { DeckItemHover } from '../DeckSetup/DeckItemHover' import { SlotDetailsContainer } from '../../../organisms' import { wellFillFromWellContents } from '../../../organisms/LabwareOnDeck/utils' import { getRobotType } from '../../../file-data/selectors' +import { + getHoveredDropdownItem, + getSelectedDropdownItem, +} from '../../../ui/steps/selectors' import { SlotOverflowMenu } from '../DeckSetup/SlotOverflowMenu' -import type { DeckSlotId } from '@opentrons/shared-data' +import { HighlightOffdeckSlot } from './HighlightOffdeckSlot' +import type { CoordinateTuple, DeckSlotId } from '@opentrons/shared-data' import type { DeckSetupTabType } from '../types' const OFFDECK_MAP_WIDTH = '41.625rem' - +const ZERO_SLOT_POSITION: CoordinateTuple = [0, 0, 0] interface OffDeckDetailsProps extends DeckSetupTabType { addLabware: () => void } @@ -39,6 +44,8 @@ export function OffDeckDetails(props: OffDeckDetailsProps): JSX.Element { const [menuListId, setShowMenuListForId] = useState(null) const robotType = useSelector(getRobotType) const deckSetup = useSelector(getDeckSetupForActiveItem) + const hoveredDropdownItem = useSelector(getHoveredDropdownItem) + const selectedDropdownSelection = useSelector(getSelectedDropdownItem) const offDeckLabware = Object.values(deckSetup.labware).filter( lw => lw.slot === 'offDeck' ) @@ -98,7 +105,7 @@ export function OffDeckDetails(props: OffDeckDetailsProps): JSX.Element { - + {offDeckLabware.map(lw => { const wellContents = allWellContentsForActiveItem ? allWellContentsForActiveItem[lw.id] @@ -110,8 +117,21 @@ export function OffDeckDetails(props: OffDeckDetailsProps): JSX.Element { yDimension: dimensions.yDimension ?? 0, zDimension: dimensions.zDimension ?? 0, } + const isLabwareSelectionSelected = selectedDropdownSelection.some( + selected => selected.id === lw.id + ) + const highlighted = hoveredDropdownItem.id === lw.id return ( - + + )} + {menuListId === lw.id ? ( - // TODO fix this rendering position { setShowMenuListForId(null) }} - menuListSlotPosition={[0, 0, 0]} + menuListSlotPosition={ZERO_SLOT_POSITION} invertY /> @@ -161,6 +185,9 @@ export function OffDeckDetails(props: OffDeckDetailsProps): JSX.Element { ) })} + + + {tab === 'startingDeck' ? ( { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('OffDeckDetails', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -55,6 +63,11 @@ describe('OffDeckDetails', () => { }) vi.mocked(selectors.getLiquidDisplayColors).mockReturnValue([]) vi.mocked(getAllWellContentsForActiveItem).mockReturnValue({}) + vi.mocked(HighlightOffdeckSlot).mockReturnValue( +
        Highlight Offdeck Slot
        + ) + vi.mocked(getSelectedDropdownItem).mockReturnValue([]) + vi.mocked(getHoveredDropdownItem).mockReturnValue({ id: null, text: null }) }) it('renders off-deck overview with 1 labware', () => { @@ -62,5 +75,6 @@ describe('OffDeckDetails', () => { screen.getByText('OFF-DECK LABWARE') screen.getByText('mock LabwareRender') screen.getByText('Add labware') + expect(screen.getAllByText('Highlight Offdeck Slot')).toHaveLength(2) }) }) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/BatchEditToolbox/BatchEditMixTools.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/BatchEditToolbox/BatchEditMixTools.tsx index 6f39f7ff632..1aa64e02057 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/BatchEditToolbox/BatchEditMixTools.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/BatchEditToolbox/BatchEditMixTools.tsx @@ -92,11 +92,11 @@ export function BatchEditMixTools(props: BatchEditMixToolsProps): JSX.Element { propsForFields.mix_wellOrder_second.updateValue } firstValue={ - (propsForFields.mix_wellOrder_first.name ?? + (propsForFields.mix_wellOrder_first?.value ?? 't2b') as WellOrderOption } secondValue={ - (propsForFields.mix_wellOrder_second.name ?? + (propsForFields.mix_wellOrder_second?.value ?? 'l2r') as WellOrderOption } firstName="mix_wellOrder_first" diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/BatchEditToolbox/BatchEditMoveLiquidTools.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/BatchEditToolbox/BatchEditMoveLiquidTools.tsx index 561a926cc8f..12d7f1442e8 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/BatchEditToolbox/BatchEditMoveLiquidTools.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/BatchEditToolbox/BatchEditMoveLiquidTools.tsx @@ -91,10 +91,10 @@ export function BatchEditMoveLiquidTools( propsForFields[addFieldNamePrefix('wellOrder_second')].updateValue } firstValue={ - (propsForFields.wellOrder_first.name ?? 't2b') as WellOrderOption + (propsForFields.wellOrder_first?.value ?? 't2b') as WellOrderOption } secondValue={ - (propsForFields.wellOrder_second.name ?? 'l2r') as WellOrderOption + (propsForFields.wellOrder_second?.value ?? 'l2r') as WellOrderOption } firstName={addFieldNamePrefix('wellOrder_first')} secondName={addFieldNamePrefix('wellOrder_second')} diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx new file mode 100644 index 00000000000..210abdec6ba --- /dev/null +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/DraggableSidebar.tsx @@ -0,0 +1,119 @@ +import { useState, useRef, useCallback, useEffect } from 'react' +import styled from 'styled-components' +import { + Box, + COLORS, + DIRECTION_COLUMN, + DISPLAY_FLEX, + Flex, + JUSTIFY_SPACE_BETWEEN, +} from '@opentrons/components' +import { TimelineToolbox } from './Timeline/TimelineToolbox' + +const INITIAL_SIDEBAR_WIDTH = 276 +const MIN_SIDEBAR_WIDTH = 80 +const MAX_SIDEBAR_WIDTH = 350 + +interface DraggableSidebarProps { + setTargetWidth: (width: number) => void +} + +// Note (kk:2024/12/20 the designer will revisit responsive sidebar design in 2025 +// we will need to update the details to align with the updated design +export function DraggableSidebar({ + setTargetWidth, +}: DraggableSidebarProps): JSX.Element { + const sidebarRef = useRef(null) + const [isResizing, setIsResizing] = useState(false) + const [sidebarWidth, setSidebarWidth] = useState(INITIAL_SIDEBAR_WIDTH) + + const startResizing = useCallback(() => { + setIsResizing(true) + }, []) + + const stopResizing = useCallback(() => { + setIsResizing(false) + }, []) + + const resize = useCallback( + (mouseMoveEvent: MouseEvent) => { + if (isResizing && sidebarRef.current != null) { + const newWidth = + mouseMoveEvent.clientX - + sidebarRef.current.getBoundingClientRect().left + + if (newWidth >= MIN_SIDEBAR_WIDTH && newWidth <= MAX_SIDEBAR_WIDTH) { + setSidebarWidth(newWidth) + setTargetWidth(newWidth) + } + } + }, + [isResizing, setTargetWidth] + ) + + useEffect(() => { + window.addEventListener('mousemove', resize) + window.addEventListener('mouseup', stopResizing) + + return () => { + window.removeEventListener('mousemove', resize) + window.removeEventListener('mouseup', stopResizing) + } + }, [resize, stopResizing]) + + return ( + + + + + + + + + ) +} + +const SidebarContainer = styled(Box)` + display: ${DISPLAY_FLEX}; + flex-direction: ${DIRECTION_COLUMN}; + border-right: 1px solid #ccc; + position: relative; + /* overflow: hidden; */ + height: 100%; +` + +const SidebarContent = styled(Flex)` + flex: 1; +` + +interface SidebarResizerProps { + dragging: boolean +} + +const SidebarResizer = styled(Flex)` + user-select: none; + width: 2px; + cursor: ew-resize; + background-color: #ddd; + position: absolute; + top: 0; + right: 0; + bottom: 0; + margin: 0; + padding: 0; + transition: background-color 0.2s ease; + + &:hover { + background-color: ${COLORS.blue50}; /* Hover state */ + } + + ${props => + props.dragging === true && + ` + background-color: ${COLORS.blue55}; /* Dragging state */ + `} +` diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/BlowoutLocationField.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/BlowoutLocationField.tsx index 44c837796bf..28e8228f220 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/BlowoutLocationField.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/BlowoutLocationField.tsx @@ -2,11 +2,11 @@ import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { selectors as uiLabwareSelectors } from '../../../../../ui/labware' import { DropdownStepFormField } from '../../../../../molecules' -import type { Options } from '@opentrons/components' +import type { DropdownOption } from '@opentrons/components' import type { FieldProps } from '../types' type BlowoutLocationDropdownProps = FieldProps & { - options: Options + options: DropdownOption[] } export function BlowoutLocationField( diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/LabwareField.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/LabwareField.tsx index 5fb840e980b..07e08b8a299 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/LabwareField.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/LabwareField.tsx @@ -1,17 +1,19 @@ -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { getDisposalOptions, getLabwareOptions, } from '../../../../../ui/labware/selectors' +import { hoverSelection } from '../../../../../ui/steps/actions/actions' import { DropdownStepFormField } from '../../../../../molecules' import type { FieldProps } from '../types' export function LabwareField(props: FieldProps): JSX.Element { const { name } = props - const { i18n, t } = useTranslation('protocol_steps') + const { i18n, t } = useTranslation(['protocol_steps', 'application']) const disposalOptions = useSelector(getDisposalOptions) const options = useSelector(getLabwareOptions) + const dispatch = useDispatch() const allOptions = name === 'dispense_labware' ? [...options, ...disposalOptions] @@ -23,6 +25,12 @@ export function LabwareField(props: FieldProps): JSX.Element { name={name} options={allOptions} title={i18n.format(t(`${name}`), 'capitalize')} + onEnter={(id: string) => { + dispatch(hoverSelection({ id, text: t('application:select') })) + }} + onExit={() => { + dispatch(hoverSelection({ id: null, text: null })) + }} /> ) } diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PathField.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PathField.tsx index e9676cbb951..691706a2884 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PathField.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/PipetteFields/PathField.tsx @@ -17,6 +17,8 @@ import SINGLE_IMAGE from '../../../../../assets/images/path_single_transfers.svg import MULTI_DISPENSE_IMAGE from '../../../../../assets/images/path_multi_dispense.svg' import MULTI_ASPIRATE_IMAGE from '../../../../../assets/images/path_multi_aspirate.svg' import { getDisabledPathMap } from './utils' + +import type { ChangeEvent, ReactNode } from 'react' import type { PathOption } from '../../../../../form-types' import type { FieldProps } from '../types' import type { DisabledPathMap, ValuesForPath } from './utils' @@ -60,10 +62,10 @@ interface PathButtonProps { disabled: boolean selected: boolean subtitle: string - onClick: (e: React.ChangeEvent) => void + onClick: (e: ChangeEvent) => void path: PathOption id?: string - children?: React.ReactNode + children?: ReactNode } function PathButton(props: PathButtonProps): JSX.Element { diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx index b37b1eaebce..68637ec0aa5 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepFormToolbox.tsx @@ -17,13 +17,17 @@ import { TYPOGRAPHY, } from '@opentrons/components' import { stepIconsByType } from '../../../../form-types' -import { FormAlerts, PROTOCOL_NAV_BAR_HEIGHT_REM } from '../../../../organisms' +import { + LINK_BUTTON_STYLE, + LINE_CLAMP_TEXT_STYLE, + NAV_BAR_HEIGHT_REM, +} from '../../../../atoms' +import { FormAlerts } from '../../../../organisms' import { useKitchen } from '../../../../organisms/Kitchen/hooks' import { RenameStepModal } from '../../../../organisms/RenameStepModal' import { getFormWarningsForSelectedStep } from '../../../../dismiss/selectors' import { getTimelineWarningsForSelectedStep } from '../../../../top-selectors/timelineWarnings' import { getRobotStateTimeline } from '../../../../file-data/selectors' -import { BUTTON_LINK_STYLE, LINE_CLAMP_TEXT_STYLE } from '../../../../atoms' import { analyticsEvent } from '../../../../analytics/actions' import { getFormLevelErrorsForUnsavedForm, @@ -41,6 +45,7 @@ import { MoveLabwareTools, MoveLiquidTools, PauseTools, + PlateReaderTools, TemperatureTools, ThermocyclerTools, } from './StepTools' @@ -51,6 +56,8 @@ import { capitalizeFirstLetter, getIsErrorOnCurrentPage, } from './utils' + +import type { ComponentType } from 'react' import type { StepFieldName } from '../../../../steplist/fieldLevel' import type { FormData, StepType } from '../../../../form-types' import type { AnalyticsEvent } from '../../../../analytics/mixpanel' @@ -61,9 +68,13 @@ import type { LiquidHandlingTab, StepFormProps, } from './types' +import { + hoverSelection, + selectDropdownItem, +} from '../../../../ui/steps/actions/actions' type StepFormMap = { - [K in StepType]?: React.ComponentType | null + [K in StepType]?: ComponentType | null } const STEP_FORM_MAP: StepFormMap = { @@ -76,6 +87,7 @@ const STEP_FORM_MAP: StepFormMap = { thermocycler: ThermocyclerTools, heaterShaker: HeaterShakerTools, comment: CommentTools, + plateReader: PlateReaderTools, } interface StepFormToolboxProps { @@ -235,6 +247,8 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element { }) ) dispatch(analyticsEvent(stepDuration)) + dispatch(selectDropdownItem({ selection: null, mode: 'clear' })) + dispatch(hoverSelection({ id: null, text: null })) } else { setShowFormErrors(true) if (tab === 'aspirate' && isDispenseError && !isAspirateError) { @@ -273,7 +287,7 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element { ) : null} { setIsRename(true) }} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} textDecoration={TYPOGRAPHY.textDecorationUnderline} > @@ -296,7 +310,16 @@ export function StepFormToolbox(props: StepFormToolboxProps): JSX.Element {
        } childrenPadding="0" - onCloseClick={handleClose} + onCloseClick={() => { + handleClose() + dispatch( + selectDropdownItem({ + selection: null, + mode: 'clear', + }) + ) + dispatch(hoverSelection({ id: null, text: null })) + }} closeButton={} confirmButton={ diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx index 3447de54069..423805cfc78 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx @@ -1,4 +1,4 @@ -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { Box, @@ -8,6 +8,7 @@ import { SPACING, StyledText, } from '@opentrons/components' +import { hoverSelection } from '../../../../../../ui/steps/actions/actions' import { getHeaterShakerLabwareOptions } from '../../../../../../ui/modules/selectors' import { DropdownStepFormField, @@ -21,7 +22,7 @@ export function HeaterShakerTools(props: StepFormProps): JSX.Element { const { propsForFields, formData, visibleFormErrors } = props const { t } = useTranslation(['application', 'form', 'protocol_steps']) const moduleLabwareOptions = useSelector(getHeaterShakerLabwareOptions) - + const dispatch = useDispatch() const mappedErrorsToField = getFormErrorsMappedToField(visibleFormErrors) return ( @@ -34,6 +35,12 @@ export function HeaterShakerTools(props: StepFormProps): JSX.Element { {...propsForFields.moduleId} options={moduleLabwareOptions} title={t('protocol_steps:module')} + onEnter={(id: string) => { + dispatch(hoverSelection({ id, text: t('select') })) + }} + onExit={() => { + dispatch(hoverSelection({ id: null, text: null })) + }} /> - - - {t('protocol_steps:module')} - - - - - {slotInfo[0]} - - - {slotInfo[1]} - - - } - description={ - - - - } - /> - - + { + dispatch( + hoverSelection({ + id, + text: t('application:location'), + }) + ) + }} + onExit={() => { + dispatch(hoverSelection({ id: null, text: null })) + }} /> ) } diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLabwareTools/MoveLabwareField.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLabwareTools/MoveLabwareField.tsx index 539905ec4c2..c27e95e1eb9 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLabwareTools/MoveLabwareField.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLabwareTools/MoveLabwareField.tsx @@ -1,17 +1,25 @@ -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { getMoveLabwareOptions } from '../../../../../../ui/labware/selectors' import { DropdownStepFormField } from '../../../../../../molecules' +import { hoverSelection } from '../../../../../../ui/steps/actions/actions' import type { FieldProps } from '../../types' export function MoveLabwareField(props: FieldProps): JSX.Element { const options = useSelector(getMoveLabwareOptions) - const { t } = useTranslation('protocol_steps') + const dispatch = useDispatch() + const { t } = useTranslation(['protocol_steps', 'application']) return ( { + dispatch(hoverSelection({ id, text: t('application:select') })) + }} + onExit={() => { + dispatch(hoverSelection({ id: null, text: null })) + }} /> ) } diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLiquidTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLiquidTools/index.tsx index fcb64ff8834..6ea949c7550 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLiquidTools/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MoveLiquidTools/index.tsx @@ -1,4 +1,3 @@ -import { useEffect } from 'react' import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { @@ -98,11 +97,14 @@ export function MoveLiquidTools(props: StepFormProps): JSX.Element { additionalEquipmentEntities[String(propsForFields.dispense_labware.value)] ?.name === 'trashBin' - const destinationLabwareType = getTrashOrLabware( - labwares, - additionalEquipmentEntities, - formData.dispense_labware as string - ) + const destinationLabwareType = + formData.dispense_labware != null + ? getTrashOrLabware( + labwares, + additionalEquipmentEntities, + formData.dispense_labware as string + ) + : null const isDestinationTrash = destinationLabwareType != null ? ['trashBin', 'wasteChute'].includes(destinationLabwareType) @@ -134,13 +136,6 @@ export function MoveLiquidTools(props: StepFormProps): JSX.Element { const mappedErrorsToField = getFormErrorsMappedToField(visibleFormErrors) - // auto-collapse blowout field if disposal volume is checked - useEffect(() => { - if (formData.disposalVolume_checkbox) { - propsForFields.blowout_checkbox.updateValue(false) - } - }, [formData.disposalVolume_checkbox]) - return toolboxStep === 0 ? ( TODO: ADD PLATE READER TOOLS
        +} diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/TemperatureTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/TemperatureTools/index.tsx index 2067ab00485..39bd3a77b8c 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/TemperatureTools/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/TemperatureTools/index.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { Box, COLORS, @@ -7,6 +7,7 @@ import { Flex, SPACING, } from '@opentrons/components' +import { hoverSelection } from '../../../../../../ui/steps/actions/actions' import { getTemperatureLabwareOptions } from '../../../../../../ui/modules/selectors' import { DropdownStepFormField, @@ -20,7 +21,7 @@ export function TemperatureTools(props: StepFormProps): JSX.Element { const { propsForFields, formData, visibleFormErrors } = props const { t } = useTranslation(['application', 'form', 'protocol_steps']) const moduleLabwareOptions = useSelector(getTemperatureLabwareOptions) - + const dispatch = useDispatch() const mappedErrorsToField = getFormErrorsMappedToField(visibleFormErrors) return ( @@ -34,6 +35,12 @@ export function TemperatureTools(props: StepFormProps): JSX.Element { tooltipContent={null} options={moduleLabwareOptions} title={t('protocol_steps:module')} + onEnter={(id: string) => { + dispatch(hoverSelection({ id, text: t('select') })) + }} + onExit={() => { + dispatch(hoverSelection({ id: null, text: null })) + }} /> @@ -44,7 +51,7 @@ export function TemperatureTools(props: StepFormProps): JSX.Element { title={t('form:step_edit_form.moduleState')} fieldTitle={t('form:step_edit_form.field.temperature.setTemperature')} units={t('units.degrees')} - isSelected={formData.setTemperature === true} + isSelected={formData.setTemperature === 'true'} onLabel={t('form:step_edit_form.field.temperature.toggleOn')} offLabel={t('form:step_edit_form.field.temperature.toggleOff')} formLevelError={getFormLevelError( diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerCycle.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerCycle.tsx index 9c0e356c158..1a26a708ed5 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerCycle.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerCycle.tsx @@ -20,7 +20,7 @@ import { StyledText, TYPOGRAPHY, } from '@opentrons/components' -import { BUTTON_LINK_STYLE } from '../../../../../../atoms' +import { LINK_BUTTON_STYLE } from '../../../../../../atoms' import { isTimeFormatMinutesSeconds, temperatureRangeFieldValue, @@ -33,6 +33,7 @@ import { import { uuid } from '../../../../../../utils' import { getTimeFromString, getStepIndex } from './utils' +import type { ChangeEvent, Dispatch, SetStateAction } from 'react' import type { ThermocyclerStepTypeGeneral } from './ThermocyclerProfileModal' import type { ThermocyclerStepType } from './ThermocyclerStep' @@ -57,12 +58,12 @@ interface CycleStepType { interface ThermocyclerCycleProps { steps: ThermocyclerStepTypeGeneral[] - setSteps: React.Dispatch> - setShowCreateNewCycle: React.Dispatch> + setSteps: Dispatch> + setShowCreateNewCycle: Dispatch> step?: ThermocyclerCycleType backgroundColor?: string readOnly?: boolean - setIsInEdit: React.Dispatch> + setIsInEdit: Dispatch> } export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element { @@ -262,7 +263,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element { whiteSpace={NO_WRAP} textDecoration={TYPOGRAPHY.textDecorationUnderline} padding={SPACING.spacing4} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} > {i18n.format( @@ -329,7 +330,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element { }} padding={SPACING.spacing4} css={[ - BUTTON_LINK_STYLE, + LINK_BUTTON_STYLE, css` visibility: ${hover ? 'visible' : 'hidden'}; opacity: ${hover ? 1 : 0}; @@ -394,7 +395,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element { 'capitalize' )} value={stepState.name.value} - onChange={(e: React.ChangeEvent) => { + onChange={(e: ChangeEvent) => { handleValueUpdate( cycleStepId, 'name', @@ -417,7 +418,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element { )} units={t('units.degrees')} value={stepState.temp.value} - onChange={(e: React.ChangeEvent) => { + onChange={(e: ChangeEvent) => { handleValueUpdate( cycleStepId, 'temp', @@ -454,7 +455,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element { )} units={t('units.time')} value={stepState.time.value} - onChange={(e: React.ChangeEvent) => { + onChange={(e: ChangeEvent) => { handleValueUpdate( cycleStepId, 'time', @@ -527,7 +528,7 @@ export function ThermocyclerCycle(props: ThermocyclerCycleProps): JSX.Element { whiteSpace={NO_WRAP} textDecoration={TYPOGRAPHY.textDecorationUnderline} padding={SPACING.spacing4} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} > {i18n.format( diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerProfileModal.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerProfileModal.tsx index 6c952296ad3..b1f87f6f997 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerProfileModal.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerProfileModal.tsx @@ -17,6 +17,7 @@ import { import { ThermocyclerCycle } from './ThermocyclerCycle' import { ThermocyclerStep } from './ThermocyclerStep' +import type { Dispatch, SetStateAction } from 'react' import type { FormData } from '../../../../../../form-types' import type { FieldPropsByName } from '../../types' import type { ThermocyclerCycleType } from './ThermocyclerCycle' @@ -29,7 +30,7 @@ export type ThermocyclerStepTypeGeneral = interface ThermocyclerModalProps { formData: FormData propsForFields: FieldPropsByName - setShowProfileModal: React.Dispatch> + setShowProfileModal: Dispatch> } export function ThermocyclerProfileModal( diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerStep.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerStep.tsx index 22dda2fc368..327e3676690 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerStep.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/ThermocyclerStep.tsx @@ -18,7 +18,7 @@ import { StyledText, TYPOGRAPHY, } from '@opentrons/components' -import { BUTTON_LINK_STYLE } from '../../../../../../atoms' +import { LINK_BUTTON_STYLE } from '../../../../../../atoms' import { temperatureRangeFieldValue, isTimeFormatMinutesSeconds, @@ -30,6 +30,7 @@ import { import { uuid } from '../../../../../../utils' import { getTimeFromString, getStepIndex } from './utils' +import type { ChangeEvent, Dispatch, SetStateAction } from 'react' import type { ThermocyclerStepTypeGeneral } from './ThermocyclerProfileModal' export interface ThermocyclerStepType { @@ -43,9 +44,9 @@ export interface ThermocyclerStepType { interface ThermocyclerStepProps { steps: ThermocyclerStepTypeGeneral[] - setSteps: React.Dispatch> - setShowCreateNewStep: React.Dispatch> - setIsInEdit: React.Dispatch> + setSteps: Dispatch> + setShowCreateNewStep: Dispatch> + setIsInEdit: Dispatch> step?: ThermocyclerStepType backgroundColor?: string readOnly?: boolean @@ -160,7 +161,7 @@ export function ThermocyclerStep(props: ThermocyclerStepProps): JSX.Element { whiteSpace={NO_WRAP} textDecoration={TYPOGRAPHY.textDecorationUnderline} padding={SPACING.spacing4} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} > {i18n.format( @@ -221,7 +222,7 @@ export function ThermocyclerStep(props: ThermocyclerStepProps): JSX.Element { }} padding={SPACING.spacing4} css={[ - BUTTON_LINK_STYLE, + LINK_BUTTON_STYLE, css` visibility: ${hover ? 'visible' : 'hidden'}; opacity: ${hover ? 1 : 0}; @@ -263,7 +264,7 @@ export function ThermocyclerStep(props: ThermocyclerStepProps): JSX.Element { 'capitalize' )} value={stepState.name.value} - onChange={(e: React.ChangeEvent) => { + onChange={(e: ChangeEvent) => { handleValueUpdate('name', e.target.value as string) }} /> @@ -280,7 +281,7 @@ export function ThermocyclerStep(props: ThermocyclerStepProps): JSX.Element { )} units={t('units.degrees')} value={stepState.temp.value} - onChange={(e: React.ChangeEvent) => { + onChange={(e: ChangeEvent) => { handleValueUpdate( 'temp', maskToFloat(e.target.value), @@ -308,7 +309,7 @@ export function ThermocyclerStep(props: ThermocyclerStepProps): JSX.Element { )} units={t('units.time')} value={stepState.time.value} - onChange={(e: React.ChangeEvent) => { + onChange={(e: ChangeEvent) => { handleValueUpdate( 'time', maskToTime(e.target.value), diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/index.tsx index 3e85004549e..335facdfd6c 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/ThermocyclerTools/index.tsx @@ -8,7 +8,6 @@ import { RadioButton, SPACING, } from '@opentrons/components' - import { ProfileSettings } from './ProfileSettings' import { ProfileStepsSummary } from './ProfileStepsSummary' import { ThermocyclerState } from './ThermocyclerState' @@ -27,8 +26,7 @@ export function ThermocyclerTools(props: StepFormProps): JSX.Element { focusedField, setShowFormErrors, } = props - const { t } = useTranslation('form') - + const { t } = useTranslation(['form', 'application']) const [contentType, setContentType] = useState( formData.thermocyclerFormType as ThermocyclerContentType ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/__tests__/MagnetTools.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/__tests__/MagnetTools.test.tsx index 15b4adcd78b..7d5c250462e 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/__tests__/MagnetTools.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/__tests__/MagnetTools.test.tsx @@ -31,7 +31,7 @@ const render = (props: ComponentProps) => { } describe('MagnetTools', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -103,9 +103,6 @@ describe('MagnetTools', () => { it('renders the text and a switch button for v2', () => { render(props) screen.getByText('Module') - screen.getByText('10') - screen.getByText('mock labware') - screen.getByText('mock module') screen.getByText('Magnet state') screen.getByLabelText('Engage') const toggleButton = screen.getByRole('switch') diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/__tests__/TemperatureTools.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/__tests__/TemperatureTools.test.tsx index 904377f66f4..1df389f2d18 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/__tests__/TemperatureTools.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/__tests__/TemperatureTools.test.tsx @@ -7,6 +7,8 @@ import { getTemperatureModuleIds, } from '../../../../../../ui/modules/selectors' import { TemperatureTools } from '../TemperatureTools' + +import type { ComponentProps } from 'react' import type * as ModulesSelectors from '../../../../../../ui/modules/selectors' vi.mock('../../../../../../ui/modules/selectors', async importOriginal => { @@ -17,14 +19,14 @@ vi.mock('../../../../../../ui/modules/selectors', async importOriginal => { getTemperatureModuleIds: vi.fn(), } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TemperatureTools', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/index.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/index.ts index 7f0eff60340..f62b0127c36 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/index.ts +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/index.ts @@ -4,6 +4,7 @@ export { MagnetTools } from './MagnetTools' export { MixTools } from './MixTools' export { MoveLabwareTools } from './MoveLabwareTools' export { MoveLiquidTools } from './MoveLiquidTools' +export { PlateReaderTools } from './PlateReaderTools' export { PauseTools } from './PauseTools' export { TemperatureTools } from './TemperatureTools' export { ThermocyclerTools } from './ThermocyclerTools' diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/types.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/types.ts index ffbfd8b32c3..8e05bda853a 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/types.ts +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/types.ts @@ -1,3 +1,4 @@ +import type { Dispatch, SetStateAction } from 'react' import type { FormData, StepFieldName } from '../../../../form-types' import type { StepFormErrors } from '../../../../steplist' export interface FocusHandlers { @@ -30,7 +31,7 @@ export interface StepFormProps { visibleFormErrors: StepFormErrors showFormErrors: boolean focusedField?: string | null - setShowFormErrors?: React.Dispatch> + setShowFormErrors?: Dispatch> tab: LiquidHandlingTab - setTab: React.Dispatch> + setTab: Dispatch> } diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/utils.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/utils.ts index db336a0aba1..753746ecd87 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/utils.ts +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/utils.ts @@ -14,7 +14,7 @@ import { import { i18n } from '../../../../assets/localization' import { PROFILE_CYCLE } from '../../../../form-types' import type { PipetteEntity } from '@opentrons/step-generation' -import type { Options } from '@opentrons/components' +import type { DropdownOption } from '@opentrons/components' import type { ProfileFormError } from '../../../../steplist/formLevel/profileErrors' import type { FormWarning } from '../../../../steplist/formLevel/warnings' import type { StepFormErrors } from '../../../../steplist/types' @@ -32,7 +32,7 @@ import type { FieldProps, FieldPropsByName, FocusHandlers } from './types' export function getBlowoutLocationOptionsForForm(args: { stepType: StepType path?: PathOption | null | undefined -}): Options { +}): DropdownOption[] { const { stepType, path } = args // TODO: Ian 2019-02-21 use i18n for names const destOption = { diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx index 7ce06184378..e9494eb6e44 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/AddStepButton.tsx @@ -2,26 +2,36 @@ import { useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' +import { css } from 'styled-components' + import { - useHoverTooltip, - TOOLTIP_TOP, - TOOLTIP_FIXED, - Tooltip, + ALIGN_CENTER, + BORDERS, COLORS, DIRECTION_COLUMN, + DISPLAY_FLEX, Flex, - POSITION_ABSOLUTE, - BORDERS, + Icon, + JUSTIFY_CENTER, NO_WRAP, - useOnClickOutside, + POSITION_ABSOLUTE, SecondaryButton, + SPACING, + StyledText, + TOOLTIP_FIXED, + TOOLTIP_TOP, + Tooltip, + useHoverTooltip, + useOnClickOutside, } from '@opentrons/components' import { + ABSORBANCE_READER_TYPE, HEATERSHAKER_MODULE_TYPE, MAGNETIC_MODULE_TYPE, TEMPERATURE_MODULE_TYPE, THERMOCYCLER_MODULE_TYPE, } from '@opentrons/shared-data' + import { actions as stepsActions, getIsMultiSelectMode, @@ -35,8 +45,10 @@ import { ConfirmDeleteModal, getMainPagePortalEl, } from '../../../../organisms' -import { getEnableComment } from '../../../../feature-flags/selectors' - +import { + getEnableAbsorbanceReader, + getEnableComment, +} from '../../../../feature-flags/selectors' import { AddStepOverflowButton } from './AddStepOverflowButton' import type { MouseEvent } from 'react' @@ -44,7 +56,11 @@ import type { ThunkDispatch } from 'redux-thunk' import type { BaseState } from '../../../../types' import type { StepType } from '../../../../form-types' -export function AddStepButton(): JSX.Element { +interface AddStepButtonProps { + hasText: boolean +} + +export function AddStepButton({ hasText }: AddStepButtonProps): JSX.Element { const { t } = useTranslation(['tooltip', 'button']) const enableComment = useSelector(getEnableComment) const dispatch = useDispatch>() @@ -71,32 +87,22 @@ export function AddStepButton(): JSX.Element { const [enqueuedStepType, setEnqueuedStepType] = useState( null ) + const enableAbsorbanceReader = useSelector(getEnableAbsorbanceReader) const getSupportedSteps = (): Array< Exclude - > => - enableComment - ? [ - 'comment', - 'moveLabware', - 'moveLiquid', - 'mix', - 'pause', - 'heaterShaker', - 'magnet', - 'temperature', - 'thermocycler', - ] - : [ - 'moveLabware', - 'moveLiquid', - 'mix', - 'pause', - 'heaterShaker', - 'magnet', - 'temperature', - 'thermocycler', - ] + > => [ + 'comment', + 'moveLabware', + 'moveLiquid', + 'mix', + 'pause', + 'heaterShaker', + 'magnet', + 'temperature', + 'thermocycler', + 'plateReader', + ] const isStepTypeEnabled: Record< Exclude, boolean @@ -110,6 +116,9 @@ export function AddStepButton(): JSX.Element { temperature: getIsModuleOnDeck(modules, TEMPERATURE_MODULE_TYPE), thermocycler: getIsModuleOnDeck(modules, THERMOCYCLER_MODULE_TYPE), heaterShaker: getIsModuleOnDeck(modules, HEATERSHAKER_MODULE_TYPE), + plateReader: + getIsModuleOnDeck(modules, ABSORBANCE_READER_TYPE) && + enableAbsorbanceReader, } const addStep = (stepType: StepType): ReturnType => @@ -154,16 +163,8 @@ export function AddStepButton(): JSX.Element { {showStepOverflowMenu ? ( { e.preventDefault() e.stopPropagation() @@ -179,6 +180,10 @@ export function AddStepButton(): JSX.Element { )} - {t('button:add_step')} + + {hasText ? {t('button:add_step')} : null} ) } + +const STEP_OVERFLOW_MENU_STYLE = css` + position: ${POSITION_ABSOLUTE}; + z-index: 5; + right: -7.75rem; + white-space: ${NO_WRAP}; + bottom: 4.2rem; + border-radius: ${BORDERS.borderRadius8}; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2); + background-color: ${COLORS.white}; + flex-direction: ${DIRECTION_COLUMN}; +` diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx index 70cdf70a984..0051499bf15 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/ConnectedStepInfo.tsx @@ -1,5 +1,5 @@ import { useDispatch, useSelector } from 'react-redux' -import type { Dispatch, SetStateAction } from 'react' + import { useTranslation } from 'react-i18next' import { useConditionalConfirm } from '@opentrons/components' import * as timelineWarningSelectors from '../../../../top-selectors/timelineWarnings' @@ -34,6 +34,7 @@ import { nonePressed, } from './utils' +import type { Dispatch, MouseEvent, SetStateAction } from 'react' import type { ThunkDispatch } from 'redux-thunk' import type { HoverOnStepAction, @@ -49,6 +50,7 @@ export interface ConnectedStepInfoProps { dragHovered?: boolean openedOverflowMenuId?: string | null setOpenedOverflowMenuId?: Dispatch> + sidebarWidth: number } export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element { @@ -58,6 +60,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element { dragHovered = false, openedOverflowMenuId, setOpenedOverflowMenuId, + sidebarWidth, } = props const { t } = useTranslation('application') const dispatch = useDispatch>() @@ -115,7 +118,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element { dispatch(stepsActions.hoverOnStep(stepId)) const unhighlightStep = (): HoverOnStepAction => dispatch(stepsActions.hoverOnStep(null)) - const handleSelectStep = (event: React.MouseEvent): void => { + const handleSelectStep = (event: MouseEvent): void => { if (selectedStep !== stepId) { dispatch(toggleViewSubstep(null)) dispatch(hoverOnStep(null)) @@ -227,6 +230,7 @@ export function ConnectedStepInfo(props: ConnectedStepInfoProps): JSX.Element { step.stepName || t(`stepType.${step.stepType}`) }`} dragHovered={dragHovered} + sidebarWidth={sidebarWidth} /> ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx index 02e1ad772fa..592cdec02f3 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/DraggableSteps.tsx @@ -31,6 +31,7 @@ interface DragDropStepProps extends ConnectedStepItemProps { orderedStepIds: string[] openedOverflowMenuId?: string | null setOpenedOverflowMenuId?: Dispatch> + sidebarWidth: number } interface DropType { @@ -46,6 +47,7 @@ function DragDropStep(props: DragDropStepProps): JSX.Element { stepNumber, openedOverflowMenuId, setOpenedOverflowMenuId, + sidebarWidth, } = props const stepRef = useRef(null) @@ -94,6 +96,7 @@ function DragDropStep(props: DragDropStepProps): JSX.Element { stepNumber={stepNumber} stepId={stepId} dragHovered={hovered} + sidebarWidth={sidebarWidth} /> ) @@ -102,9 +105,10 @@ function DragDropStep(props: DragDropStepProps): JSX.Element { interface DraggableStepsProps { orderedStepIds: StepIdType[] reorderSteps: (steps: StepIdType[]) => void + sidebarWidth: number } export function DraggableSteps(props: DraggableStepsProps): JSX.Element | null { - const { orderedStepIds, reorderSteps } = props + const { orderedStepIds, reorderSteps, sidebarWidth } = props const { t } = useTranslation('shared') const [openedOverflowMenuId, setOpenedOverflowMenuId] = useState< string | null @@ -146,14 +150,21 @@ export function DraggableSteps(props: DraggableStepsProps): JSX.Element | null { orderedStepIds={orderedStepIds} openedOverflowMenuId={openedOverflowMenuId} setOpenedOverflowMenuId={setOpenedOverflowMenuId} + sidebarWidth={sidebarWidth} /> ))} - +
        ) } -function StepDragPreview(): JSX.Element | null { +interface StepDragPreviewProps { + sidebarWidth: number +} + +function StepDragPreview({ + sidebarWidth, +}: StepDragPreviewProps): JSX.Element | null { const [{ isDragging, itemType, item, currentOffset }] = useDrag(() => ({ type: DND_TYPES.STEP_ITEM, collect: (monitor: DragLayerMonitor) => ({ @@ -182,6 +193,7 @@ function StepDragPreview(): JSX.Element | null {
        ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx index 9d0289ddb9c..346c296855f 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/PresavedStep.tsx @@ -10,7 +10,13 @@ import { } from '../../../../ui/steps' import { StepContainer } from './StepContainer' -export function PresavedStep(): JSX.Element | null { +interface PresavedStepProps { + sidebarWidth: number +} + +export function PresavedStep({ + sidebarWidth, +}: PresavedStepProps): JSX.Element | null { const { t } = useTranslation('application') const presavedStepForm = useSelector(stepFormSelectors.getPresavedStepForm) const stepNumber = useSelector(stepFormSelectors.getOrderedStepIds).length + 1 @@ -39,6 +45,7 @@ export function PresavedStep(): JSX.Element | null { hovered={hovered} iconName={stepIconsByType[stepType]} title={`${stepNumber}. ${t(`stepType.${stepType}`)}`} + sidebarWidth={sidebarWidth} /> ) } diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx index 3db0802fbd5..d0534b234cf 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/StepContainer.tsx @@ -12,6 +12,7 @@ import { Divider, Flex, Icon, + JUSTIFY_CENTER, JUSTIFY_SPACE_BETWEEN, JUSTIFY_START, OverflowBtn, @@ -48,9 +49,12 @@ import type { BaseState } from '../../../../types' const STARTING_DECK_STATE = 'Starting deck' const FINAL_DECK_STATE = 'Ending deck' const PX_HEIGHT_TO_TOP_OF_CONTAINER = 32 +const PX_SIDEBAR_MIN_WIDTH_FOR_ICON = 179 + export interface StepContainerProps { title: string iconName: IconName + sidebarWidth: number openedOverflowMenuId?: string | null setOpenedOverflowMenuId?: Dispatch> stepId?: string @@ -83,6 +87,7 @@ export function StepContainer(props: StepContainerProps): JSX.Element { dragHovered = false, setOpenedOverflowMenuId, openedOverflowMenuId, + sidebarWidth, } = props const [top, setTop] = useState(0) const menuRootRef = useRef(null) @@ -91,6 +96,7 @@ export function StepContainer(props: StepContainerProps): JSX.Element { const dispatch = useDispatch>() const multiSelectItemIds = useSelector(getMultiSelectItemIds) + const hasText = sidebarWidth > PX_SIDEBAR_MIN_WIDTH_FOR_ICON let backgroundColor = isStartingOrEndingState ? COLORS.blue20 : COLORS.grey20 let color = COLORS.black90 if (selected) { @@ -183,14 +189,14 @@ export function StepContainer(props: StepContainerProps): JSX.Element { return ( <> - {showDeleteConfirmation && ( + {showDeleteConfirmation === true && ( )} - {showMultiDeleteConfirmation && ( + {showMultiDeleteConfirmation === true && ( - {iconName && ( + {iconName != null && ( )} - - {capitalizeFirstLetterAfterNumber(title)} - + {hasText ? ( + + {capitalizeFirstLetterAfterNumber(title)} + + ) : null} {selected && !isStartingOrEndingState ? ( + menuRootRef: MutableRefObject top: number - setOpenedOverflowMenuId: React.Dispatch> + setOpenedOverflowMenuId: Dispatch> handleEdit: () => void confirmDelete: () => void confirmMultiDelete: () => void @@ -106,7 +108,7 @@ export function StepOverflowMenu(props: StepOverflowMenuProps): JSX.Element { boxShadow="0px 1px 3px rgba(0, 0, 0, 0.2)" backgroundColor={COLORS.white} flexDirection={DIRECTION_COLUMN} - onClick={(e: React.MouseEvent) => { + onClick={(e: MouseEvent) => { e.preventDefault() e.stopPropagation() }} diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubstepsToolbox.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubStepsToolbox.tsx similarity index 96% rename from protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubstepsToolbox.tsx rename to protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubStepsToolbox.tsx index eb0a0ba835b..a7e272c3623 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubstepsToolbox.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/SubStepsToolbox.tsx @@ -22,12 +22,12 @@ import { ThermocyclerProfileSubsteps } from './ThermocyclerProfileSubsteps' import type { SubstepIdentifier } from '../../../../steplist' import type { HoverOnSubstepAction } from '../../../../ui/steps' -interface SubstepsToolboxProps { +interface SubStepsToolboxProps { stepId: string } -export function SubstepsToolbox( - props: SubstepsToolboxProps +export function SubStepsToolbox( + props: SubStepsToolboxProps ): JSX.Element | null { const { stepId } = props const { t, i18n } = useTranslation([ diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TerminalItemStep.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TerminalItemStep.tsx index fc808579af4..7c92c22376d 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TerminalItemStep.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TerminalItemStep.tsx @@ -1,5 +1,7 @@ +import { useTranslation } from 'react-i18next' import { useSelector, useDispatch } from 'react-redux' import { useConditionalConfirm } from '@opentrons/components' + import { getHoveredTerminalItemId, getSelectedTerminalItemId, @@ -10,6 +12,7 @@ import { getCurrentFormIsPresaved, getCurrentFormHasUnsavedChanges, } from '../../../../step-forms/selectors' +import { START_TERMINAL_ITEM_ID } from '../../../../steplist' import { CLOSE_STEP_FORM_WITH_CHANGES, CLOSE_UNSAVED_STEP_FORM, @@ -31,11 +34,12 @@ import type { ThunkDispatch } from '../../../../types' export interface TerminalItemStepProps { id: TerminalItemId - title: string + sidebarWidth: number } export function TerminalItemStep(props: TerminalItemStepProps): JSX.Element { - const { id, title } = props + const { id, sidebarWidth } = props + const { t } = useTranslation('protocol_steps') const hovered = useSelector(getHoveredTerminalItemId) === id const selected = useSelector(getSelectedTerminalItemId) === id const currentFormIsPresaved = useSelector(getCurrentFormIsPresaved) @@ -83,14 +87,16 @@ export function TerminalItemStep(props: TerminalItemStepProps): JSX.Element { ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx index 796b8726f1b..7e7b0d83b6c 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/TimelineToolbox.tsx @@ -4,12 +4,14 @@ import { useTranslation } from 'react-i18next' import { DIRECTION_COLUMN, Flex, + OVERFLOW_WRAP_ANYWHERE, POSITION_RELATIVE, SPACING, StyledText, Toolbox, } from '@opentrons/components' -import { PROTOCOL_NAV_BAR_HEIGHT_REM } from '../../../../organisms' + +import { NAV_BAR_HEIGHT_REM } from '../../../../atoms' import { END_TERMINAL_ITEM_ID, START_TERMINAL_ITEM_ID, @@ -26,7 +28,14 @@ import { DraggableSteps } from './DraggableSteps' import type { StepIdType } from '../../../../form-types' import type { ThunkDispatch } from '../../../../types' -export const TimelineToolbox = (): JSX.Element => { +const SIDEBAR_MIN_WIDTH_FOR_ICON = 179 +interface TimelineToolboxProps { + sidebarWidth: number +} + +export const TimelineToolbox = ({ + sidebarWidth, +}: TimelineToolboxProps): JSX.Element => { const { t } = useTranslation('protocol_steps') const orderedStepIds = useSelector(stepFormSelectors.getOrderedStepIds) const formData = useSelector(getUnsavedForm) @@ -62,16 +71,23 @@ export const TimelineToolbox = (): JSX.Element => { + {t('timeline')} } titlePadding={SPACING.spacing12} childrenPadding={SPACING.spacing12} - confirmButton={formData != null ? undefined : } + confirmButton={ + formData != null ? undefined : ( + SIDEBAR_MIN_WIDTH_FOR_ICON} /> + ) + } > { > { dispatch(steplistActions.reorderSteps(stepIds)) }} + sidebarWidth={sidebarWidth} + /> + + - - ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx index 4b7b716b0c7..a2fcea0a7e2 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/AddStepButton.test.tsx @@ -1,4 +1,4 @@ -import { describe, it, vi, beforeEach } from 'vitest' +import { describe, it, vi, beforeEach, expect } from 'vitest' import { fireEvent, screen } from '@testing-library/react' import { HEATERSHAKER_MODULE_TYPE, @@ -20,18 +20,25 @@ import { } from '../../../../../step-forms/selectors' import { getIsMultiSelectMode } from '../../../../../ui/steps' +import type { ComponentProps } from 'react' + vi.mock('../../../../../feature-flags/selectors') vi.mock('../../../../../ui/steps') vi.mock('../../../../../step-forms/selectors') -const render = () => { - return renderWithProviders(, { +const render = (props: ComponentProps) => { + return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('AddStepButton', () => { + let props: ComponentProps + beforeEach(() => { + props = { + hasText: true, + } vi.mocked(getEnableComment).mockReturnValue(true) vi.mocked(getCurrentFormIsPresaved).mockReturnValue(false) vi.mocked(getIsMultiSelectMode).mockReturnValue(false) @@ -73,8 +80,8 @@ describe('AddStepButton', () => { }) it('renders add step button and clicking on it renders the overflow menu with all modules', () => { - render() - fireEvent.click(screen.getByText('+ Add Step')) + render(props) + fireEvent.click(screen.getByText('Add Step')) screen.getByText('Comment') screen.getByText('Transfer') screen.getByText('Mix') @@ -84,4 +91,11 @@ describe('AddStepButton', () => { screen.getByText('Temperature') screen.getByText('Magnet') }) + + it('should not render texts if hasText is false', () => { + props.hasText = false + render(props) + const text = screen.queryByText('Add Step') + expect(text).toBeNull() + }) }) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx index 32df562e61a..be874bb60d2 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepContainer.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' import { fireEvent, screen } from '@testing-library/react' @@ -8,19 +7,21 @@ import { getUnsavedForm } from '../../../../../step-forms/selectors' import { StepContainer } from '../StepContainer' import { StepOverflowMenu } from '../StepOverflowMenu' +import type { ComponentProps } from 'react' + vi.mock('../../../../../step-forms/selectors') vi.mock('../../../../../ui/steps/actions/actions') vi.mock('../../../../../ui/steps/selectors') vi.mock('../StepOverflowMenu') -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('StepContainer', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { @@ -32,6 +33,7 @@ describe('StepContainer', () => { stepId: 'mockStepId', hasError: false, isStepAfterError: false, + sidebarWidth: 350, } vi.mocked(StepOverflowMenu).mockReturnValue(
        mock StepOverflowMenu
        diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepOverflowMenu.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepOverflowMenu.test.tsx index 9c1de4044d0..55197e85ed4 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepOverflowMenu.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/StepOverflowMenu.test.tsx @@ -20,7 +20,8 @@ import { toggleViewSubstep, } from '../../../../../ui/steps/actions/actions' import { StepOverflowMenu } from '../StepOverflowMenu' -import type * as React from 'react' + +import type { ComponentProps } from 'react' import type * as OpentronsComponents from '@opentrons/components' const mockConfirm = vi.fn() @@ -46,7 +47,7 @@ vi.mock('@opentrons/components', async importOriginal => { })), } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -54,7 +55,7 @@ const render = (props: React.ComponentProps) => { const moveLiquidStepId = 'mockId' describe('StepOverflowMenu', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/ThermocyclerProfileSubsteps.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/ThermocyclerProfileSubsteps.test.tsx index b4fb8af436f..a665e82b3a4 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/ThermocyclerProfileSubsteps.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/ThermocyclerProfileSubsteps.test.tsx @@ -4,11 +4,11 @@ import { renderWithProviders } from '../../../../../__testing-utils__' import { i18n } from '../../../../../assets/localization' import { getSavedStepForms } from '../../../../../step-forms/selectors' import { ThermocyclerProfileSubsteps } from '../ThermocyclerProfileSubsteps' + +import type { ComponentProps } from 'react' import type { FormData } from '../../../../../form-types' -const render = ( - props: React.ComponentProps -) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] @@ -55,7 +55,7 @@ const MOCK_THERMOCYCLER_SUBSTEP_ITEMS = { } describe('TimelineToolbox', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { stepId: THERMOCYCLER_STEP_ID } vi.mocked(getSavedStepForms).mockReturnValue({ diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx index c4ae078c572..f6b11ce36a5 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/__tests__/TimelineToolbox.test.tsx @@ -6,25 +6,32 @@ import { getOrderedStepIds, getUnsavedForm, } from '../../../../../step-forms/selectors' -import { TimelineToolbox } from '../TimelineToolbox' import { TerminalItemStep } from '../TerminalItemStep' import { DraggableSteps } from '../DraggableSteps' import { PresavedStep } from '../PresavedStep' import { AddStepButton } from '../AddStepButton' +import { TimelineToolbox } from '../TimelineToolbox' + +import type { ComponentProps } from 'react' vi.mock('../AddStepButton') vi.mock('../DraggableSteps') vi.mock('../PresavedStep') vi.mock('../TerminalItemStep') vi.mock('../../../../../step-forms/selectors') -const render = () => { - return renderWithProviders(, { +const render = (props: ComponentProps) => { + return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('TimelineToolbox', () => { + let props: ComponentProps + beforeEach(() => { + props = { + sidebarWidth: 350, + } vi.mocked(getOrderedStepIds).mockReturnValue(['mock1Step']) vi.mocked(getUnsavedForm).mockReturnValue(null) vi.mocked(TerminalItemStep).mockReturnValue( @@ -34,8 +41,9 @@ describe('TimelineToolbox', () => { vi.mocked(PresavedStep).mockReturnValue(
        mock PresavedStep
        ) vi.mocked(AddStepButton).mockReturnValue(
        mock AddStepButton
        ) }) + it('renders 2 terminal item steps, a draggable step and presaved step with toolbox title', () => { - render() + render(props) screen.getByText('Timeline') screen.getByText('mock AddStepButton') screen.getByText('mock PresavedStep') diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts index 2b4945b756b..14a0f0058af 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/index.ts @@ -1,2 +1,2 @@ -export * from './SubstepsToolbox' +export * from './SubStepsToolbox' export * from './TimelineToolbox' diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts index 2d918b7790f..0e6620523f7 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/Timeline/utils.ts @@ -1,6 +1,8 @@ import round from 'lodash/round' import uniq from 'lodash/uniq' import { UAParser } from 'ua-parser-js' + +import type { MouseEvent } from 'react' import type { StepIdType } from '../../../../form-types' export const capitalizeFirstLetterAfterNumber = (title: string): string => @@ -118,7 +120,7 @@ export const nonePressed = (keysPressed: boolean[]): boolean => keysPressed.every(keyPress => keyPress === false) export const getMouseClickKeyInfo = ( - event: React.MouseEvent + event: MouseEvent ): { isShiftKeyPressed: boolean; isMetaKeyPressed: boolean } => { const isMac: boolean = getUserOS() === 'Mac OS' const isShiftKeyPressed: boolean = event.shiftKey diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx new file mode 100644 index 00000000000..4ea6b03d2ab --- /dev/null +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/DraggableSidebar.test.tsx @@ -0,0 +1,41 @@ +import { describe, it, vi, beforeEach } from 'vitest' +import { screen } from '@testing-library/react' + +import { i18n } from '../../../../assets/localization' +import { renderWithProviders } from '../../../../__testing-utils__' +import { DraggableSidebar } from '../DraggableSidebar' + +import type { ComponentProps } from 'react' + +vi.mock('../../../../step-forms/selectors') +vi.mock('../../../../ui/steps/selectors') +vi.mock('../../../../feature-flags/selectors') +vi.mock('../Timeline/DraggableSteps') +vi.mock('../Timeline/PresavedStep') +vi.mock('../Timeline/AddStepButton') + +const mockSetTargetWidth = vi.fn() + +const render = (props: ComponentProps) => { + return renderWithProviders(, { + i18nInstance: i18n, + }) +} + +describe('DraggableSidebar', () => { + let props: ComponentProps + beforeEach(() => { + props = { + setTargetWidth: mockSetTargetWidth, + } + }) + + it('renders initial timeline toolbox', () => { + render(props) + screen.getByText('Timeline') + screen.getByText('Starting deck') + screen.getByText('Ending deck') + }) + + // ToDo (kk: 2024/12/12): Add more tests +}) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx index 9ff98460fc6..31c1c93eafc 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/__tests__/ProtocolSteps.test.tsx @@ -19,8 +19,10 @@ import { import { getEnableHotKeysDisplay } from '../../../../feature-flags/selectors' import { DeckSetupContainer } from '../../DeckSetup' import { OffDeck } from '../../Offdeck' +import { SubStepsToolbox } from '../Timeline' +import { DraggableSidebar } from '../DraggableSidebar' import { ProtocolSteps } from '..' -import { SubstepsToolbox, TimelineToolbox } from '../Timeline' + import type { SavedStepFormState } from '../../../../step-forms' vi.mock('../../Offdeck') @@ -31,6 +33,7 @@ vi.mock('../StepForm') vi.mock('../../DeckSetup') vi.mock('../StepSummary.tsx') vi.mock('../Timeline') +vi.mock('../DraggableSidebar') vi.mock('../../../../feature-flags/selectors') vi.mock('../../../../file-data/selectors') vi.mock('../../../../organisms/Alerts') @@ -64,7 +67,9 @@ describe('ProtocolSteps', () => { timeline: [], errors: [], }) - vi.mocked(TimelineToolbox).mockReturnValue(
        mock TimelineToolbox
        ) + vi.mocked(DraggableSidebar).mockReturnValue( +
        mock DraggableSidebar
        + ) vi.mocked(DeckSetupContainer).mockReturnValue(
        mock DeckSetupContainer
        ) @@ -72,7 +77,7 @@ describe('ProtocolSteps', () => { vi.mocked(OffDeck).mockReturnValue(
        mock OffDeck
        ) vi.mocked(getUnsavedForm).mockReturnValue(null) vi.mocked(getSelectedSubstep).mockReturnValue(null) - vi.mocked(SubstepsToolbox).mockReturnValue(
        mock SubstepsToolbox
        ) + vi.mocked(SubStepsToolbox).mockReturnValue(
        mock SubStepsToolbox
        ) vi.mocked(getEnableHotKeysDisplay).mockReturnValue(true) vi.mocked(getSavedStepForms).mockReturnValue( MOCK_STEP_FORMS as SavedStepFormState @@ -84,7 +89,7 @@ describe('ProtocolSteps', () => { it('renders each component in ProtocolSteps', () => { render() - screen.getByText('mock TimelineToolbox') + screen.getByText('mock DraggableSidebar') screen.getByText('mock DeckSetupContainer') }) @@ -98,7 +103,7 @@ describe('ProtocolSteps', () => { it('renders the substepToolbox when selectedSubstep is not null', () => { vi.mocked(getSelectedSubstep).mockReturnValue('mockId') render() - screen.getByText('mock SubstepsToolbox') + screen.getByText('mock SubStepsToolbox') }) it('renders the hot keys display', () => { diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx index 97f337c2dcd..38f4979b0cf 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/index.tsx @@ -1,6 +1,7 @@ import { useState } from 'react' import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' + import { ALIGN_CENTER, COLORS, @@ -10,6 +11,7 @@ import { JUSTIFY_CENTER, JUSTIFY_SPACE_BETWEEN, POSITION_FIXED, + POSITION_RELATIVE, SPACING, StyledText, Tag, @@ -30,7 +32,7 @@ import { } from '../../../ui/steps/selectors' import { DeckSetupContainer } from '../DeckSetup' import { OffDeck } from '../Offdeck' -import { TimelineToolbox, SubstepsToolbox } from './Timeline' +import { SubStepsToolbox } from './Timeline' import { StepForm } from './StepForm' import { StepSummary } from './StepSummary' import { BatchEditToolbox } from './BatchEditToolbox' @@ -39,6 +41,7 @@ import { getRobotStateTimeline, } from '../../../file-data/selectors' import { TimelineAlerts } from '../../../organisms' +import { DraggableSidebar } from './DraggableSidebar' const CONTENT_MAX_WIDTH = '44.6704375rem' @@ -56,6 +59,7 @@ export function ProtocolSteps(): JSX.Element { const [deckView, setDeckView] = useState< typeof leftString | typeof rightString >(leftString) + const [targetWidth, setTargetWidth] = useState(350) const currentHoveredStepId = useSelector(getHoveredStepId) const currentSelectedStepId = useSelector(getSelectedStepId) @@ -82,15 +86,17 @@ export function ProtocolSteps(): JSX.Element { width="100%" padding={SPACING.spacing12} gridGap={SPACING.spacing16} - justifyContent={JUSTIFY_SPACE_BETWEEN} > - + + + {formData == null && selectedSubstep ? ( - + ) : null} {isMultiSelectMode ? : null} diff --git a/protocol-designer/src/pages/Designer/__tests__/LiquidsOverflowMenu.test.tsx b/protocol-designer/src/pages/Designer/__tests__/LiquidsOverflowMenu.test.tsx index 45fbebe5494..6e646fb6db7 100644 --- a/protocol-designer/src/pages/Designer/__tests__/LiquidsOverflowMenu.test.tsx +++ b/protocol-designer/src/pages/Designer/__tests__/LiquidsOverflowMenu.test.tsx @@ -42,6 +42,7 @@ describe('SlotOverflowMenu', () => { displayColor: 'mockColor', name: 'mockname', ingredientId: '0', + liquidClass: null, }, ]) }) diff --git a/protocol-designer/src/pages/Designer/index.tsx b/protocol-designer/src/pages/Designer/index.tsx index f9f343735d4..5e560fcf6e4 100644 --- a/protocol-designer/src/pages/Designer/index.tsx +++ b/protocol-designer/src/pages/Designer/index.tsx @@ -13,11 +13,14 @@ import { ToggleGroup, useOnClickOutside, } from '@opentrons/components' -import { selectTerminalItem } from '../../ui/steps/actions/actions' +import { + selectDropdownItem, + selectTerminalItem, +} from '../../ui/steps/actions/actions' import { useKitchen } from '../../organisms/Kitchen/hooks' import { getDeckSetupForActiveItem } from '../../top-selectors/labware-locations' import { generateNewProtocol } from '../../labware-ingred/actions' -import { DefineLiquidsModal, ProtocolNavBar } from '../../organisms' +import { DefineLiquidsModal, DesignerNavigation } from '../../organisms' import { selectDesignerTab } from '../../file-data/actions' import { getDesignerTab, getFileMetadata } from '../../file-data/selectors' import { DeckSetupContainer } from './DeckSetup' @@ -68,6 +71,12 @@ export function Designer(): JSX.Element { isActive: tab === 'startingDeck', onClick: () => { dispatch(selectDesignerTab({ tab: 'startingDeck' })) + dispatch( + selectDropdownItem({ + selection: null, + mode: 'clear', + }) + ) }, } const protocolStepTab = { @@ -151,7 +160,7 @@ export function Designer(): JSX.Element { /> ) : null} - { describe('Landing', () => { beforeEach(() => { - vi.mocked(getHasOptedIn).mockReturnValue(false) + vi.mocked(getHasOptedIn).mockReturnValue({ + hasOptedIn: false, + appVersion: '8.2.1', + }) vi.mocked(getFileMetadata).mockReturnValue({}) vi.mocked(loadProtocolFile).mockReturnValue(vi.fn()) vi.mocked(useAnnouncements).mockReturnValue({} as any) diff --git a/protocol-designer/src/pages/Landing/index.tsx b/protocol-designer/src/pages/Landing/index.tsx index 3a9ea55bfd3..1e0424c5630 100644 --- a/protocol-designer/src/pages/Landing/index.tsx +++ b/protocol-designer/src/pages/Landing/index.tsx @@ -17,7 +17,7 @@ import { StyledText, TYPOGRAPHY, } from '@opentrons/components' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { AnnouncementModal } from '../../organisms' import { actions as loadFileActions } from '../../load-file' import { getFileMetadata } from '../../file-data/selectors' @@ -28,6 +28,7 @@ import { useAnnouncements } from '../../organisms/AnnouncementModal/announcement import { getLocalStorageItem, localStorageAnnouncementKey } from '../../persist' import welcomeImage from '../../assets/images/welcome_page.png' +import type { ChangeEvent, ComponentProps } from 'react' import type { ThunkDispatch } from '../../types' export function Landing(): JSX.Element { @@ -38,7 +39,7 @@ export function Landing(): JSX.Element { const [showAnnouncementModal, setShowAnnouncementModal] = useState( false ) - const hasOptedIn = useSelector(getHasOptedIn) + const { hasOptedIn } = useSelector(getHasOptedIn) const { bakeToast, eatToast } = useKitchen() const announcements = useAnnouncements() const lastAnnouncement = announcements[announcements.length - 1] @@ -77,9 +78,7 @@ export function Landing(): JSX.Element { } }, [metadata, navigate]) - const loadFile = ( - fileChangeEvent: React.ChangeEvent - ): void => { + const loadFile = (fileChangeEvent: ChangeEvent): void => { dispatch(loadFileActions.loadProtocolFile(fileChangeEvent)) } @@ -128,7 +127,7 @@ export function Landing(): JSX.Element {
        - + { dispatch(toggleNewProtocolModal(true)) @@ -137,7 +136,7 @@ export function Landing(): JSX.Element { /> - + {t('edit_existing')} @@ -165,7 +164,7 @@ const ButtonText = styled.span` font-weight: ${TYPOGRAPHY.fontWeightSemiBold}; ` -const StyledNavLink = styled(NavLink)>` +const StyledNavLink = styled(NavLink)>` color: ${COLORS.white}; text-decoration: none; ` diff --git a/protocol-designer/src/pages/Liquids/__tests__/Liquids.test.tsx b/protocol-designer/src/pages/Liquids/__tests__/Liquids.test.tsx index e8c752bdf3c..f510ec96014 100644 --- a/protocol-designer/src/pages/Liquids/__tests__/Liquids.test.tsx +++ b/protocol-designer/src/pages/Liquids/__tests__/Liquids.test.tsx @@ -4,7 +4,7 @@ import { screen } from '@testing-library/react' import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { selectors as labwareIngredSelectors } from '../../../labware-ingred/selectors' -import { AssignLiquidsModal, ProtocolNavBar } from '../../../organisms' +import { AssignLiquidsModal, DesignerNavigation } from '../../../organisms' import { LiquidsOverflowMenu } from '../../Designer/LiquidsOverflowMenu' import { Liquids } from '..' @@ -42,7 +42,9 @@ describe('Liquids', () => { vi.mocked(AssignLiquidsModal).mockReturnValue(
        mock AssignLiquidsModal
        ) - vi.mocked(ProtocolNavBar).mockReturnValue(
        mock ProtocolNavBar
        ) + vi.mocked(DesignerNavigation).mockReturnValue( +
        mock DesignerNavigation
        + ) vi.mocked(LiquidsOverflowMenu).mockReturnValue(
        mock LiquidsOverflowMenu
        ) @@ -55,7 +57,7 @@ describe('Liquids', () => { it('renders nav and assign liquids modal', () => { render() - screen.getByText('mock ProtocolNavBar') + screen.getByText('mock DesignerNavigation') screen.getByText('mock AssignLiquidsModal') }) }) diff --git a/protocol-designer/src/pages/Liquids/index.tsx b/protocol-designer/src/pages/Liquids/index.tsx index c8382498863..3604f9e3d24 100644 --- a/protocol-designer/src/pages/Liquids/index.tsx +++ b/protocol-designer/src/pages/Liquids/index.tsx @@ -9,7 +9,7 @@ import { import { AssignLiquidsModal, DefineLiquidsModal, - ProtocolNavBar, + DesignerNavigation, } from '../../organisms' import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors' import { LiquidsOverflowMenu } from '../Designer/LiquidsOverflowMenu' @@ -59,10 +59,7 @@ export function Liquids(): JSX.Element { ) : null} - + diff --git a/protocol-designer/src/pages/ProtocolOverview/InstrumentsInfo.tsx b/protocol-designer/src/pages/ProtocolOverview/InstrumentsInfo.tsx index 63ce567a805..ee40f77aef4 100644 --- a/protocol-designer/src/pages/ProtocolOverview/InstrumentsInfo.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/InstrumentsInfo.tsx @@ -14,7 +14,7 @@ import { } from '@opentrons/components' import { getPipetteSpecsV2, FLEX_ROBOT_TYPE } from '@opentrons/shared-data' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import type { PipetteName, RobotType } from '@opentrons/shared-data' import type { AdditionalEquipmentEntities } from '@opentrons/step-generation' @@ -87,7 +87,7 @@ export function InstrumentsInfo({ onClick={() => { setShowEditInstrumentsModal(true) }} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} > {t('edit')} diff --git a/protocol-designer/src/pages/ProtocolOverview/LiquidDefinitions.tsx b/protocol-designer/src/pages/ProtocolOverview/LiquidDefinitions.tsx index 378f14e13ad..b55b615eb11 100644 --- a/protocol-designer/src/pages/ProtocolOverview/LiquidDefinitions.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/LiquidDefinitions.tsx @@ -1,4 +1,5 @@ import { useTranslation } from 'react-i18next' +import { useSelector } from 'react-redux' import { ALIGN_CENTER, DIRECTION_COLUMN, @@ -9,10 +10,42 @@ import { ListItemDescriptor, SPACING, StyledText, + Tag, } from '@opentrons/components' import { LINE_CLAMP_TEXT_STYLE } from '../../atoms' +import { getEnableLiquidClasses } from '../../feature-flags/selectors' +import { getLiquidClassDisplayName } from '../../liquid-defs/utils' -import type { AllIngredGroupFields } from '../../labware-ingred/types' +import type { + AllIngredGroupFields, + IngredInputs, +} from '../../labware-ingred/types' + +const getLiquidDescription = ( + liquid: IngredInputs, + enableLiquidClasses: boolean +): JSX.Element | null => { + const { description, liquidClass } = liquid + const liquidClassDisplayName = getLiquidClassDisplayName(liquidClass) + const liquidClassInfo = + !enableLiquidClasses || liquidClassDisplayName == null ? null : ( + + ) + + return liquidClassInfo == null && !description ? null : ( + + {description ? ( + + {description} + + ) : null} + {liquidClassInfo} + + ) +} interface LiquidDefinitionsProps { allIngredientGroupFields: AllIngredGroupFields @@ -22,6 +55,7 @@ export function LiquidDefinitions({ allIngredientGroupFields, }: LiquidDefinitionsProps): JSX.Element { const { t } = useTranslation('protocol_overview') + const enableLiquidClasses = useSelector(getEnableLiquidClasses) return ( @@ -30,43 +64,45 @@ export function LiquidDefinitions({ {Object.keys(allIngredientGroupFields).length > 0 ? ( - Object.values(allIngredientGroupFields).map((liquid, index) => ( - - - - { + return ( + + - {liquid.name} - - - } - content={ - - {liquid.description != null && liquid.description !== '' - ? liquid.description - : t('na')} - - } - /> - - )) + + + {liquid.name} + +
        + } + content={ + getLiquidDescription(liquid, enableLiquidClasses) ?? ( + + {t('na')} + + ) + } + /> + + ) + }) ) : ( )} diff --git a/protocol-designer/src/pages/ProtocolOverview/ProtocolMetadata.tsx b/protocol-designer/src/pages/ProtocolOverview/ProtocolMetadata.tsx index d750edaaad5..564c99d9f89 100644 --- a/protocol-designer/src/pages/ProtocolOverview/ProtocolMetadata.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/ProtocolMetadata.tsx @@ -12,7 +12,7 @@ import { TYPOGRAPHY, } from '@opentrons/components' -import { BUTTON_LINK_STYLE, LINE_CLAMP_TEXT_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE, LINE_CLAMP_TEXT_STYLE } from '../../atoms' const REQUIRED_APP_VERSION = '8.2.0' @@ -46,7 +46,7 @@ export function ProtocolMetadata({ onClick={() => { setShowEditMetadataModal(true) }} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} data-testid="ProtocolOverview_MetadataEditButton" > diff --git a/protocol-designer/src/pages/ProtocolOverview/StartingDeck.tsx b/protocol-designer/src/pages/ProtocolOverview/StartingDeck.tsx index 8dac5003b6f..48e3cb178b4 100644 --- a/protocol-designer/src/pages/ProtocolOverview/StartingDeck.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/StartingDeck.tsx @@ -15,7 +15,7 @@ import { TYPOGRAPHY, } from '@opentrons/components' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { SlotDetailsContainer } from '../../organisms' import { getInitialDeckSetup } from '../../step-forms/selectors' import { DeckThumbnail } from './DeckThumbnail' @@ -72,7 +72,7 @@ function StartingDeckHeader(props: StartingDeckHeaderProps): JSX.Element { onClick={() => { setShowMaterialsListModal(true) }} - css={BUTTON_LINK_STYLE} + css={LINK_BUTTON_STYLE} > {t('protocol_overview:materials_list')} diff --git a/protocol-designer/src/pages/ProtocolOverview/UnusedModalContent.tsx b/protocol-designer/src/pages/ProtocolOverview/UnusedModalContent.tsx index d9c1a575891..75568b4e9c8 100644 --- a/protocol-designer/src/pages/ProtocolOverview/UnusedModalContent.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/UnusedModalContent.tsx @@ -6,6 +6,7 @@ import { } from '@opentrons/components' import { getModuleDisplayName } from '@opentrons/shared-data' +import type { ReactNode } from 'react' import type { ModuleOnDeck, PipetteOnDeck } from '../../step-forms' import type { HintKey } from '../../tutorial' import type { Fixture } from './index' @@ -20,7 +21,7 @@ interface MissingContent { } export interface WarningContent { - content: React.ReactNode + content: ReactNode heading?: string titleElement?: JSX.Element hintKey?: HintKey diff --git a/protocol-designer/src/pages/ProtocolOverview/__tests__/DeckThumbnail.test.tsx b/protocol-designer/src/pages/ProtocolOverview/__tests__/DeckThumbnail.test.tsx index f8b4aafd9af..bdae73a3e8f 100644 --- a/protocol-designer/src/pages/ProtocolOverview/__tests__/DeckThumbnail.test.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/__tests__/DeckThumbnail.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach, expect } from 'vitest' import '@testing-library/jest-dom/vitest' import { screen } from '@testing-library/react' @@ -8,6 +7,8 @@ import { getInitialDeckSetup } from '../../../step-forms/selectors' import { LabwareOnDeck } from '../../../organisms' import { getRobotType } from '../../../file-data/selectors' import { DeckThumbnail } from '../DeckThumbnail' + +import type { ComponentProps } from 'react' import type { LabwareDefinition2 } from '@opentrons/shared-data' import type * as Components from '@opentrons/components' @@ -23,12 +24,12 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders()[0] } describe('DeckThumbnail', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/ProtocolOverview/__tests__/LiquidDefinitions.test.tsx b/protocol-designer/src/pages/ProtocolOverview/__tests__/LiquidDefinitions.test.tsx index 832cea4d800..ff4af37fa4a 100644 --- a/protocol-designer/src/pages/ProtocolOverview/__tests__/LiquidDefinitions.test.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/__tests__/LiquidDefinitions.test.tsx @@ -8,6 +8,8 @@ import { LiquidDefinitions } from '../LiquidDefinitions' import type { ComponentProps } from 'react' import type { InfoScreen } from '@opentrons/components' +vi.mock('../../../feature-flags/selectors') + vi.mock('@opentrons/components', async importOriginal => { const actual = await importOriginal() return { @@ -21,6 +23,7 @@ const mockAllIngredientGroupFields = { name: 'EtOH', displayColor: '#b925ff', description: 'Immer fisch Hergestllter EtOH', + liquidClass: null, serialize: false, liquidGroupId: '0', }, @@ -28,6 +31,7 @@ const mockAllIngredientGroupFields = { name: '10mM Tris pH8,5', displayColor: '#ffd600', description: null, + liquidClass: null, serialize: false, liquidGroupId: '1', }, @@ -35,6 +39,7 @@ const mockAllIngredientGroupFields = { name: 'Amplicon PCR sample + AMPure XP beads', displayColor: '#9dffd8', description: '25µl Amplicon PCR + 20 µl AMPure XP beads', + liquidClass: 'Water', serialize: false, liquidGroupId: '2', }, diff --git a/protocol-designer/src/pages/ProtocolOverview/__tests__/OffdeckThumbnail.test.tsx b/protocol-designer/src/pages/ProtocolOverview/__tests__/OffdeckThumbnail.test.tsx index ce96b79923a..027eaaab2a8 100644 --- a/protocol-designer/src/pages/ProtocolOverview/__tests__/OffdeckThumbnail.test.tsx +++ b/protocol-designer/src/pages/ProtocolOverview/__tests__/OffdeckThumbnail.test.tsx @@ -1,4 +1,3 @@ -import type * as React from 'react' import { describe, it, vi, beforeEach } from 'vitest' import '@testing-library/jest-dom/vitest' import { FLEX_ROBOT_TYPE, fixture12Trough } from '@opentrons/shared-data' @@ -11,6 +10,7 @@ import { getInitialDeckSetup } from '../../../step-forms/selectors' import { getAllWellContentsForActiveItem } from '../../../top-selectors/well-contents' import { OffDeckThumbnail } from '../OffdeckThumbnail' +import type { ComponentProps } from 'react' import type { LabwareDefinition2 } from '@opentrons/shared-data' import type * as Components from '@opentrons/components' @@ -26,14 +26,14 @@ vi.mock('@opentrons/components', async importOriginal => { } }) -const render = (props: React.ComponentProps) => { +const render = (props: ComponentProps) => { return renderWithProviders(, { i18nInstance: i18n, })[0] } describe('OffDeckThumbnail', () => { - let props: React.ComponentProps + let props: ComponentProps beforeEach(() => { props = { diff --git a/protocol-designer/src/pages/Settings/__tests__/Settings.test.tsx b/protocol-designer/src/pages/Settings/__tests__/Settings.test.tsx index 8a1b948e953..671a31ccfc4 100644 --- a/protocol-designer/src/pages/Settings/__tests__/Settings.test.tsx +++ b/protocol-designer/src/pages/Settings/__tests__/Settings.test.tsx @@ -32,7 +32,10 @@ const render = () => { describe('Settings', () => { beforeEach(() => { - vi.mocked(getHasOptedIn).mockReturnValue(false) + vi.mocked(getHasOptedIn).mockReturnValue({ + hasOptedIn: false, + appVersion: '8.2.1', + }) vi.mocked(getFeatureFlagData).mockReturnValue({}) vi.mocked(getCanClearHintDismissals).mockReturnValue(true) }) diff --git a/protocol-designer/src/pages/Settings/index.tsx b/protocol-designer/src/pages/Settings/index.tsx index b678327adb8..fa364525f99 100644 --- a/protocol-designer/src/pages/Settings/index.tsx +++ b/protocol-designer/src/pages/Settings/index.tsx @@ -27,7 +27,7 @@ import { selectors as tutorialSelectors, } from '../../tutorial' import { ToggleButton } from '../../atoms/ToggleButton' -import { BUTTON_LINK_STYLE } from '../../atoms' +import { LINK_BUTTON_STYLE } from '../../atoms' import { actions as featureFlagActions } from '../../feature-flags' import { getFeatureFlagData } from '../../feature-flags/selectors' import type { FlagTypes } from '../../feature-flags' @@ -42,18 +42,16 @@ export function Settings(): JSX.Element { const [showAnnouncementModal, setShowAnnouncementModal] = useState( false ) - const hasOptedIn = useSelector(analyticsSelectors.getHasOptedIn) + const { hasOptedIn } = useSelector(analyticsSelectors.getHasOptedIn) const flags = useSelector(getFeatureFlagData) const canClearHintDismissals = useSelector( tutorialSelectors.getCanClearHintDismissals ) - const _toggleOptedIn = hasOptedIn - ? analyticsActions.optOut - : analyticsActions.optIn - const prereleaseModeEnabled = flags.PRERELEASE_MODE === true const pdVersion = process.env.OT_PD_VERSION + const prereleaseModeEnabled = flags.PRERELEASE_MODE === true + const allFlags = Object.keys(flags) as FlagTypes[] const getDescription = (flag: FlagTypes): string => { @@ -144,7 +142,7 @@ export function Settings(): JSX.Element {
        { setShowAnnouncementModal(true) @@ -280,7 +278,13 @@ export function Settings(): JSX.Element { ? TOGGLE_ENABLED_STYLES : TOGGLE_DISABLED_STYLES } - onClick={() => dispatch(_toggleOptedIn())} + onClick={() => + dispatch( + hasOptedIn + ? analyticsActions.optOut() + : analyticsActions.optIn() + ) + } > 'featureFlags.flags'?: Record - 'analytics.hasOptedIn'?: boolean | null + 'analytics.hasOptedIn'?: { + hasOptedIn: boolean + appVersion?: string + } } } export const getLocalStorageItem = (path: string): unknown => { diff --git a/protocol-designer/src/resources/ProtocolDesignerAppFallback.tsx b/protocol-designer/src/resources/ProtocolDesignerAppFallback.tsx index 32eabb78a30..3791d4b97b3 100644 --- a/protocol-designer/src/resources/ProtocolDesignerAppFallback.tsx +++ b/protocol-designer/src/resources/ProtocolDesignerAppFallback.tsx @@ -4,15 +4,19 @@ import { useTranslation } from 'react-i18next' import type { FallbackProps } from 'react-error-boundary' +import { actions } from '../load-file' import { AlertPrimaryButton, ALIGN_FLEX_END, DIRECTION_COLUMN, Flex, Modal, + SecondaryButton, SPACING, StyledText, } from '@opentrons/components' +import { useDispatch } from 'react-redux' +import type { ThunkDispatch } from '../types' export function ProtocolDesignerAppFallback({ error, @@ -20,9 +24,13 @@ export function ProtocolDesignerAppFallback({ }: FallbackProps): JSX.Element { const { t } = useTranslation('shared') + const dispatch: ThunkDispatch = useDispatch() const handleReloadClick = (): void => { resetErrorBoundary() } + const handleDownloadProtocol = (): void => { + dispatch(actions.saveProtocolFile()) + } return ( @@ -35,12 +43,14 @@ export function ProtocolDesignerAppFallback({ {error.message} - - {t('reload_app')} - + + + {t('download_protocol')} + + + {t('reload_app')} + +
        ) diff --git a/protocol-designer/src/step-forms/reducers/index.ts b/protocol-designer/src/step-forms/reducers/index.ts index 4e35d9af6e3..defa067ed52 100644 --- a/protocol-designer/src/step-forms/reducers/index.ts +++ b/protocol-designer/src/step-forms/reducers/index.ts @@ -15,6 +15,7 @@ import { THERMOCYCLER_MODULE_TYPE, WASTE_CHUTE_ADDRESSABLE_AREAS, MOVABLE_TRASH_ADDRESSABLE_AREAS, + WASTE_CHUTE_CUTOUT, } from '@opentrons/shared-data' import { rootReducer as labwareDefsRootReducer } from '../../labware-defs' import { getCutoutIdByAddressableArea, uuid } from '../../utils' @@ -39,7 +40,7 @@ import { createPresavedStepForm, getDeckItemIdInSlot, getIdsInRange, - getUnoccupiedSlotForMoveableTrash, + getUnoccupiedSlotForTrash, } from '../utils' import type { Reducer } from 'redux' @@ -1154,9 +1155,9 @@ export const additionalEquipmentInvariantProperties = handleActions +type PipettesForInstrumentGroup = ComponentProps export const getPipettesForInstrumentGroup: Selector< BaseState, PipettesForInstrumentGroup @@ -731,7 +733,7 @@ export const getUnsavedFormIsPristineSetTempForm: Selector< (unsavedForm, isPresaved) => { const isSetTempForm = unsavedForm?.stepType === 'temperature' && - unsavedForm?.setTemperature === 'true' + unsavedForm?.targetTemperature != null return isPresaved && isSetTempForm } ) @@ -745,7 +747,7 @@ export const getUnsavedFormIsPristineHeaterShakerForm: Selector< (unsavedForm, isPresaved) => { const isSetHsTempForm = unsavedForm?.stepType === 'heaterShaker' && - unsavedForm?.targetHeaterShakerTemperature !== null + unsavedForm?.targetHeaterShakerTemperature != null return isPresaved && isSetHsTempForm } diff --git a/protocol-designer/src/step-forms/test/createPresavedStepForm.test.ts b/protocol-designer/src/step-forms/test/createPresavedStepForm.test.ts index 9a78b49d0ed..77e1f25a229 100644 --- a/protocol-designer/src/step-forms/test/createPresavedStepForm.test.ts +++ b/protocol-designer/src/step-forms/test/createPresavedStepForm.test.ts @@ -37,6 +37,7 @@ beforeEach(() => { def: { parameters: { magneticModuleEngageHeight: EXAMPLE_ENGAGE_HEIGHT, + isTiprack: false, }, }, } diff --git a/protocol-designer/src/step-forms/test/selectors.test.ts b/protocol-designer/src/step-forms/test/selectors.test.ts index 64525e1fd53..55c2539fa1c 100644 --- a/protocol-designer/src/step-forms/test/selectors.test.ts +++ b/protocol-designer/src/step-forms/test/selectors.test.ts @@ -160,7 +160,7 @@ describe('getUnsavedFormIsPristineSetTempForm', () => { // @ts-expect-error(jr, 4/8/22): missing module id const formData: FormData = { stepType: 'temperature', - setTemperature: 'true', + targetTemperature: 33, } const expected = true // @ts-expect-error(jr, 4/8/22): resultFunc (from reselect) is not part of their Selector interface diff --git a/protocol-designer/src/step-forms/test/utils.test.ts b/protocol-designer/src/step-forms/test/utils.test.ts index d7dbe1960b5..f6cad4e12a3 100644 --- a/protocol-designer/src/step-forms/test/utils.test.ts +++ b/protocol-designer/src/step-forms/test/utils.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect } from 'vitest' -import { getIdsInRange, getUnoccupiedSlotForMoveableTrash } from '../utils' +import { WASTE_CHUTE_CUTOUT } from '@opentrons/shared-data' +import { getIdsInRange, getUnoccupiedSlotForTrash } from '../utils' import type { AddressableAreaName, CreateCommand } from '@opentrons/shared-data' describe('getIdsInRange', () => { @@ -31,7 +32,7 @@ describe('getIdsInRange', () => { expect(getIdsInRange(orderedIds, 'T', 'T')).toEqual(['T']) }) }) -describe('getUnoccupiedSlotForMoveableTrash', () => { +describe('getUnoccupiedSlotForTrash', () => { it('returns slot C1 when all other slots are occupied by modules, labware, moveLabware, and staging areas', () => { const mockPDFile: any = { commands: [ @@ -146,11 +147,237 @@ describe('getUnoccupiedSlotForMoveableTrash', () => { const mockHasWasteChuteCommands = false expect( - getUnoccupiedSlotForMoveableTrash( + getUnoccupiedSlotForTrash( mockPDFile, mockHasWasteChuteCommands, mockStagingAreaSlotNames ) ).toStrictEqual('C3') }) + it('returns cutoutD3 for waste chute when every slot is occupied except for D3 on a staging area', () => { + const mockPDFile: any = { + commands: [ + { + key: '159e778d-0fc5-4d24-a662-b1e59a7babda', + commandType: 'loadModule', + params: { + model: 'thermocyclerModuleV2', + location: { slotName: 'B1' }, + moduleId: + '8932e104-7d57-42cf-88e4-ade334c84a76:thermocyclerModuleType', + }, + }, + { + key: '7d1fdcce-fa27-4520-8f97-a901751a4396', + commandType: 'loadModule', + params: { + model: 'temperatureModuleV2', + location: { slotName: 'C1' }, + moduleId: + '2944a6a5-45f7-4d96-a4a2-d2853206a9f0:temperatureModuleType', + }, + }, + { + key: '1c223945-bfa3-4174-9923-5ed84afd1820', + commandType: 'loadModule', + params: { + model: 'heaterShakerModuleV1', + location: { slotName: 'D1' }, + moduleId: + '528620a6-6eb9-4000-bce3-a58809e16d4c:heaterShakerModuleType', + }, + }, + { + key: 'e06d0fd5-2ca8-4d0a-bcfd-4121849604da', + commandType: 'loadModule', + params: { + model: 'magneticBlockV1', + location: { slotName: 'D2' }, + moduleId: 'c8f8c89f-06df-468c-895d-33006db69beb:magneticBlockType', + }, + }, + { + key: 'f49ebdff-9780-4ca0-994c-2d2dd7c04b1d', + commandType: 'loadLabware', + params: { + displayName: 'Opentrons 96 Well Aluminum Block', + labwareId: + 'a69bcf2e-9461-4d43-be63-f3b8db66e5e7:opentrons/opentrons_96_well_aluminum_block/1', + loadName: 'opentrons_96_well_aluminum_block', + namespace: 'opentrons', + version: 1, + location: { + moduleId: + '2944a6a5-45f7-4d96-a4a2-d2853206a9f0:temperatureModuleType', + }, + }, + }, + { + key: 'dda244f9-ff80-4ede-a585-1a546a88ee77', + commandType: 'loadLabware', + params: { + displayName: 'Opentrons 96 PCR Heater-Shaker Adapter', + labwareId: + '723a9551-ebba-4b4a-a92e-8d1fa0e813df:opentrons/opentrons_96_pcr_adapter/1', + loadName: 'opentrons_96_pcr_adapter', + namespace: 'opentrons', + version: 1, + location: { + moduleId: + '528620a6-6eb9-4000-bce3-a58809e16d4c:heaterShakerModuleType', + }, + }, + }, + { + key: '8c28ac95-c8d0-4481-8204-26b1babb54bf', + commandType: 'loadLabware', + params: { + displayName: 'Opentrons Flex 96 Tip Rack 50 µL', + labwareId: + 'c80cffe7-d89d-430e-ba96-3c12f879e993:opentrons/opentrons_flex_96_tiprack_50ul/1', + loadName: 'opentrons_flex_96_tiprack_50ul', + namespace: 'opentrons', + version: 1, + location: { slotName: 'C3' }, + }, + }, + { + key: 'f0357fde-125a-464c-98ed-b1b9492daab8', + commandType: 'loadLabware', + params: { + displayName: 'Opentrons Flex 96 Filter Tip Rack 200 µL (1)', + labwareId: + '0a2d4b6f-a43d-428a-98f2-284809596776:opentrons/opentrons_flex_96_filtertiprack_200ul/1', + loadName: 'opentrons_flex_96_filtertiprack_200ul', + namespace: 'opentrons', + version: 1, + location: { slotName: 'A3' }, + }, + }, + { + key: 'e27ba758-8d28-486f-a443-6e2276842ad0', + commandType: 'loadLabware', + params: { + displayName: 'Opentrons Flex 96 Filter Tip Rack 200 µL (2)', + labwareId: + '417a6bb2-8831-4b4d-840b-7d9329606865:opentrons/opentrons_flex_96_filtertiprack_200ul/1', + loadName: 'opentrons_flex_96_filtertiprack_200ul', + namespace: 'opentrons', + version: 1, + location: { slotName: 'B3' }, + }, + }, + { + key: '37848c2a-4a1b-44f0-851a-d264368c47f8', + commandType: 'loadLabware', + params: { + displayName: 'Opentrons Flex 96 Filter Tip Rack 200 µL (3)', + labwareId: + 'ebb13651-0a60-4f42-ab85-f7084aeb0c08:opentrons/opentrons_flex_96_filtertiprack_200ul/1', + loadName: 'opentrons_flex_96_filtertiprack_200ul', + namespace: 'opentrons', + version: 1, + location: { slotName: 'A2' }, + }, + }, + { + key: '768626df-b249-4d68-8f95-193b03113457', + commandType: 'loadLabware', + params: { + displayName: 'Opentrons Flex 96 Filter Tip Rack 200 µL (4)', + labwareId: + 'b17e8c1b-a308-4eaa-a852-10ad300ddea8:opentrons/opentrons_flex_96_filtertiprack_200ul/1', + loadName: 'opentrons_flex_96_filtertiprack_200ul', + namespace: 'opentrons', + version: 1, + location: { slotName: 'B2' }, + }, + }, + { + key: 'b12a4e6e-7ffc-421f-a2b6-44ae49d6f7bf', + commandType: 'loadLabware', + params: { + displayName: 'Reagent Plate', + labwareId: + 'aab3280f-6e7b-4e60-8326-c1d38999e08f:opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2', + loadName: 'opentrons_96_wellplate_200ul_pcr_full_skirt', + namespace: 'opentrons', + version: 2, + location: { + labwareId: + 'a69bcf2e-9461-4d43-be63-f3b8db66e5e7:opentrons/opentrons_96_well_aluminum_block/1', + }, + }, + }, + { + key: 'e6863a1e-8aa0-4484-9aff-74ea9195a815', + commandType: 'loadLabware', + params: { + displayName: 'Sample Plate 1', + labwareId: + '8e755287-33cb-483f-b525-fff876893754:opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2', + loadName: 'opentrons_96_wellplate_200ul_pcr_full_skirt', + namespace: 'opentrons', + version: 2, + location: { + labwareId: + '723a9551-ebba-4b4a-a92e-8d1fa0e813df:opentrons/opentrons_96_pcr_adapter/1', + }, + }, + }, + { + key: 'b29f48ef-3b20-457e-8499-df709818c47f', + commandType: 'loadLabware', + params: { + displayName: 'NEST 96 Deep Well Plate 2mL', + labwareId: + 'f0d30267-b0f6-493a-b0ea-70303428fa83:opentrons/nest_96_wellplate_2ml_deep/2', + loadName: 'nest_96_wellplate_2ml_deep', + namespace: 'opentrons', + version: 2, + location: { + moduleId: + 'c8f8c89f-06df-468c-895d-33006db69beb:magneticBlockType', + }, + }, + }, + { + key: '50be2f72-c7bc-4fd4-b10c-2054b90f922d', + commandType: 'loadLabware', + params: { + displayName: 'NEST 12 Well Reservoir 15 mL', + labwareId: + 'b60bbc39-cd82-4ede-b744-e88777a32b62:opentrons/nest_12_reservoir_15ml/1', + loadName: 'nest_12_reservoir_15ml', + namespace: 'opentrons', + version: 1, + location: { slotName: 'C2' }, + }, + }, + { + key: 'a2f0c011-9983-46d9-a3ae-763a04856651', + commandType: 'loadLabware', + params: { + displayName: 'Opentrons Flex 96 Tip Rack 50 µL (1)', + labwareId: + '0d3d02a6-6501-4f28-81b9-12b2fe66998b:opentrons/opentrons_flex_96_tiprack_50ul/1', + loadName: 'opentrons_flex_96_tiprack_50ul', + namespace: 'opentrons', + version: 1, + location: { addressableAreaName: 'D4' }, + }, + }, + ], + } + const mockStagingAreaSlotNames: AddressableAreaName[] = ['D4'] + const mockHasWasteChuteCommands = false + + expect( + getUnoccupiedSlotForTrash( + mockPDFile, + mockHasWasteChuteCommands, + mockStagingAreaSlotNames + ) + ).toStrictEqual(WASTE_CHUTE_CUTOUT) + }) }) diff --git a/protocol-designer/src/step-forms/types.ts b/protocol-designer/src/step-forms/types.ts index b170a128843..cec0a6ee22c 100644 --- a/protocol-designer/src/step-forms/types.ts +++ b/protocol-designer/src/step-forms/types.ts @@ -64,6 +64,7 @@ export interface MagneticBlockState { } export interface AbsorbanceReaderState { type: typeof ABSORBANCE_READER_TYPE + lidOpen: boolean | null } export interface ModuleTemporalProperties { slot: DeckSlot diff --git a/protocol-designer/src/step-forms/utils/createPresavedStepForm.ts b/protocol-designer/src/step-forms/utils/createPresavedStepForm.ts index 387a3068a18..5951fbf0a04 100644 --- a/protocol-designer/src/step-forms/utils/createPresavedStepForm.ts +++ b/protocol-designer/src/step-forms/utils/createPresavedStepForm.ts @@ -2,6 +2,7 @@ import last from 'lodash/last' import { HEATERSHAKER_MODULE_TYPE, MAGNETIC_MODULE_TYPE, + TEMPERATURE_MODULE_TYPE, THERMOCYCLER_MODULE_TYPE, } from '@opentrons/shared-data' import { @@ -29,6 +30,7 @@ import type { FormData, StepType, StepIdType } from '../../form-types' import type { InitialDeckSetup } from '../types' import type { FormPatch } from '../../steplist/actions/types' import type { SavedStepFormState, OrderedStepIdsState } from '../reducers' + export interface CreatePresavedStepFormArgs { stepId: StepIdType stepType: StepType @@ -118,6 +120,64 @@ const _patchDefaultDropTipLocation = (args: { return null } +const _patchDefaultLabwareLocations = (args: { + labwareEntities: LabwareEntities + pipetteEntities: PipetteEntities + stepType: StepType +}): FormUpdater => formData => { + const { labwareEntities, pipetteEntities, stepType } = args + + const formHasMoveLabware = + formData && 'labware' in formData && stepType === 'moveLabware' + + const filteredLabware = Object.values(labwareEntities).filter( + lw => + // Filter out the tiprack, adapter, and lid entities + !lw.def?.parameters.isTiprack && + !lw.def?.allowedRoles?.includes('adapter') && + !lw.def?.allowedRoles?.includes('lid') + ) + + const filteredMoveLabware = Object.values(labwareEntities).filter( + lw => + // Filter out adapter entities + !lw.def?.allowedRoles?.includes('adapter') + ) + + const formHasAspirateLabware = formData && 'aspirate_labware' in formData + const formHasMixLabware = + formData && 'labware' in formData && stepType === 'mix' + + if (filteredLabware.length === 1 && formHasAspirateLabware) { + return handleFormChange( + { aspirate_labware: filteredLabware[0].id ?? null }, + formData, + pipetteEntities, + labwareEntities + ) + } + + if (filteredLabware.length === 1 && formHasMixLabware) { + return handleFormChange( + { labware: filteredLabware[0].id ?? null }, + formData, + pipetteEntities, + labwareEntities + ) + } + + if (filteredMoveLabware.length === 1 && formHasMoveLabware) { + return handleFormChange( + { labware: filteredMoveLabware[0].id }, + formData, + pipetteEntities, + labwareEntities + ) + } + + return null +} + const _patchDefaultMagnetFields = (args: { initialDeckSetup: InitialDeckSetup orderedStepIds: OrderedStepIdsState @@ -168,13 +228,17 @@ const _patchTemperatureModuleId = (args: { stepType: StepType }): FormUpdater => () => { const { initialDeckSetup, orderedStepIds, savedStepForms, stepType } = args + const numOfModules = + Object.values(initialDeckSetup.modules).filter( + module => module.type === TEMPERATURE_MODULE_TYPE + )?.length ?? 1 const hasTemperatureModuleId = stepType === 'pause' || stepType === 'temperature' // Auto-populate moduleId field of 'pause' and 'temperature' steps. // // Bypass dependent field changes, do not use handleFormChange - if (hasTemperatureModuleId) { + if (hasTemperatureModuleId && numOfModules === 1) { const moduleId = getNextDefaultTemperatureModuleId( savedStepForms, orderedStepIds, @@ -195,6 +259,10 @@ const _patchHeaterShakerModuleId = (args: { stepType: StepType }): FormUpdater => () => { const { initialDeckSetup, stepType } = args + const numOfModules = + Object.values(initialDeckSetup.modules).filter( + module => module.type === HEATERSHAKER_MODULE_TYPE + )?.length ?? 1 const hasHeaterShakerModuleId = stepType === 'pause' || stepType === 'heaterShaker' @@ -202,7 +270,7 @@ const _patchHeaterShakerModuleId = (args: { // Note, if both a temperature module and a heater shaker module are present, the pause form // will default to use the heater shaker // Bypass dependent field changes, do not use handleFormChange - if (hasHeaterShakerModuleId) { + if (hasHeaterShakerModuleId && numOfModules === 1) { const moduleId = getModuleOnDeckByType(initialDeckSetup, HEATERSHAKER_MODULE_TYPE)?.id ?? null @@ -273,6 +341,12 @@ export const createPresavedStepForm = ({ additionalEquipmentEntities, }) + const updateDefaultLabwareLocations = _patchDefaultLabwareLocations({ + labwareEntities, + pipetteEntities, + stepType, + }) + const updateDefaultPipette = _patchDefaultPipette({ initialDeckSetup, labwareEntities, @@ -317,6 +391,7 @@ export const createPresavedStepForm = ({ updateThermocyclerFields, updateHeaterShakerModuleId, updateMagneticModuleId, + updateDefaultLabwareLocations, ].reduce( (acc, updater: FormUpdater) => { const updates = updater(acc) diff --git a/protocol-designer/src/step-forms/utils/index.ts b/protocol-designer/src/step-forms/utils/index.ts index aa880038bbe..042a6edfbd4 100644 --- a/protocol-designer/src/step-forms/utils/index.ts +++ b/protocol-designer/src/step-forms/utils/index.ts @@ -316,7 +316,7 @@ export function getHydratedForm( return hydratedForm } -export const getUnoccupiedSlotForMoveableTrash = ( +export const getUnoccupiedSlotForTrash = ( file: PDProtocolFile, hasWasteChuteCommands: boolean, stagingAreaSlotNames: AddressableAreaName[] @@ -385,6 +385,15 @@ export const getUnoccupiedSlotForMoveableTrash = ( !wasteChuteSlot.includes(cutout.value as typeof WASTE_CHUTE_CUTOUT) && !stagingAreaCutoutIds.includes(cutout.value as CutoutId) ) + // if all slots are occupied except for D3 on a staging area, then auto-generate the waste chute + if ( + unoccupiedSlot == null && + !allLoadLabwareSlotNames.includes('D3') && + stagingAreaCutoutIds.includes(WASTE_CHUTE_CUTOUT) + ) { + return WASTE_CHUTE_CUTOUT + } + if (unoccupiedSlot == null) { console.error( 'Expected to find an unoccupied slot for auto-generating a trash bin but could not' diff --git a/protocol-designer/src/steplist/formLevel/errors.ts b/protocol-designer/src/steplist/formLevel/errors.ts index add7662903a..484690e7e37 100644 --- a/protocol-designer/src/steplist/formLevel/errors.ts +++ b/protocol-designer/src/steplist/formLevel/errors.ts @@ -1,5 +1,3 @@ -import type * as React from 'react' - import { MAGNETIC_MODULE_V1, MAGNETIC_MODULE_V2 } from '@opentrons/shared-data' import { @@ -17,6 +15,7 @@ import { canPipetteUseLabware } from '../../utils' import { getWellRatio } from '../utils' import { getTimeFromForm } from '../utils/getTimeFromForm' +import type { ReactNode } from 'react' import type { LabwareDefinition2, PipetteV2Specs } from '@opentrons/shared-data' import type { LabwareEntities, PipetteEntity } from '@opentrons/step-generation' import type { StepFieldName } from '../../form-types' @@ -51,7 +50,7 @@ export type FormErrorKey = export interface FormError { title: string - body?: React.ReactNode + body?: ReactNode dependentFields: StepFieldName[] showAtField?: boolean showAtForm?: boolean diff --git a/protocol-designer/src/steplist/formLevel/handleFormChange/dependentFieldsUpdateMoveLiquid.ts b/protocol-designer/src/steplist/formLevel/handleFormChange/dependentFieldsUpdateMoveLiquid.ts index 847d45ed4bc..940d715a06d 100644 --- a/protocol-designer/src/steplist/formLevel/handleFormChange/dependentFieldsUpdateMoveLiquid.ts +++ b/protocol-designer/src/steplist/formLevel/handleFormChange/dependentFieldsUpdateMoveLiquid.ts @@ -428,6 +428,7 @@ const updatePatchDisposalVolumeFields = ( ...patch, disposalVolume_checkbox: true, disposalVolume_volume: recommendedMinimumDisposalVol, + blowout_checkbox: false, } } diff --git a/protocol-designer/src/steplist/formLevel/handleFormChange/test/moveLiquid.test.ts b/protocol-designer/src/steplist/formLevel/handleFormChange/test/moveLiquid.test.ts index 7ac47b9695f..68244e1cb80 100644 --- a/protocol-designer/src/steplist/formLevel/handleFormChange/test/moveLiquid.test.ts +++ b/protocol-designer/src/steplist/formLevel/handleFormChange/test/moveLiquid.test.ts @@ -313,6 +313,7 @@ describe('disposal volume should update...', () => { dispense_mix_checkbox: false, dispense_mix_times: null, dispense_mix_volume: null, + blowout_checkbox: false, }) }) diff --git a/protocol-designer/src/steplist/formLevel/profileErrors.ts b/protocol-designer/src/steplist/formLevel/profileErrors.ts index d29a6b67708..dee8090dc8d 100644 --- a/protocol-designer/src/steplist/formLevel/profileErrors.ts +++ b/protocol-designer/src/steplist/formLevel/profileErrors.ts @@ -1,13 +1,15 @@ import uniqBy from 'lodash/uniqBy' import { THERMOCYCLER_PROFILE } from '../../constants' import { PROFILE_STEP } from '../../form-types' + +import type { ReactNode } from 'react' import type { ProfileStepItem } from '../../form-types' // TODO: real HydratedFormData type type HydratedFormData = any export interface ProfileFormError { title: string - body?: React.ReactNode + body?: ReactNode dependentProfileFields: string[] } type ProfileFormErrorKey = 'INVALID_PROFILE_DURATION' diff --git a/protocol-designer/src/steplist/formLevel/stepFormToArgs/moveLiquidFormToArgs.ts b/protocol-designer/src/steplist/formLevel/stepFormToArgs/moveLiquidFormToArgs.ts index b77cd8a8f2e..4ee644679dc 100644 --- a/protocol-designer/src/steplist/formLevel/stepFormToArgs/moveLiquidFormToArgs.ts +++ b/protocol-designer/src/steplist/formLevel/stepFormToArgs/moveLiquidFormToArgs.ts @@ -166,7 +166,13 @@ export const moveLiquidFormToArgs = ( 'dispense_delay_mmFromBottom' ) const blowoutLocation = - (fields.blowout_checkbox && fields.blowout_location) || null + (fields.blowout_checkbox && fields.blowout_location) || + (fields.disposalVolume_checkbox && + path === 'multiDispense' && + fields.disposalVolume_volume && + fields.blowout_location) || + null + const blowoutOffsetFromTopMm = blowoutLocation != null ? blowout_z_offset ?? DEFAULT_MM_BLOWOUT_OFFSET_FROM_TOP diff --git a/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/heaterShakerFormToArgs.test.ts b/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/heaterShakerFormToArgs.test.ts index 4329e6ca4f6..2a664085113 100644 --- a/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/heaterShakerFormToArgs.test.ts +++ b/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/heaterShakerFormToArgs.test.ts @@ -9,7 +9,7 @@ describe('heaterShakerFormToArgs', () => { id: 'id', stepDetails: 'step details', moduleId: 'moduleId', - heaterShakerSetTimer: 'true', + heaterShakerSetTimer: true, setHeaterShakerTemperature: true, setShake: true, latchOpen: false, @@ -35,7 +35,7 @@ describe('heaterShakerFormToArgs', () => { id: 'id', stepDetails: 'step details', moduleId: 'moduleId', - heaterShakerSetTimer: 'false', + heaterShakerSetTimer: false, setHeaterShakerTemperature: true, setShake: false, latchOpen: false, diff --git a/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/pauseFormToArgs.test.ts b/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/pauseFormToArgs.test.ts index ab466604a82..4cbb01e6272 100644 --- a/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/pauseFormToArgs.test.ts +++ b/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/pauseFormToArgs.test.ts @@ -56,7 +56,7 @@ describe('pauseFormToArgs', () => { pauseAction: PAUSE_UNTIL_TIME, description: 'some description', pauseMessage: 'some message', - pauseTime: '1:20:5', + pauseTime: '01:20:05', } const expected = { commandCreatorFnName: 'delay', diff --git a/protocol-designer/src/top-selectors/labware-locations/index.ts b/protocol-designer/src/top-selectors/labware-locations/index.ts index 34cd5fad561..6a2c5143b00 100644 --- a/protocol-designer/src/top-selectors/labware-locations/index.ts +++ b/protocol-designer/src/top-selectors/labware-locations/index.ts @@ -166,8 +166,8 @@ export const getUnoccupiedLabwareLocationOptions: Selector< { name: modIdWithAdapter != null - ? `${adapterDisplayName} on top of ${moduleUnderAdapter} in slot ${moduleSlotInfo}` - : `${adapterDisplayName} on slot ${adapterSlotInfo}`, + ? `${moduleSlotInfo} on ${moduleUnderAdapter} with ${adapterDisplayName}` + : `${adapterSlotInfo} with ${adapterDisplayName}`, value: labwareId, }, ] @@ -186,13 +186,9 @@ export const getUnoccupiedLabwareLocationOptions: Selector< : [ ...acc, { - name: `${getModuleDisplayName( + name: `${modOnDeck.slot} on ${getModuleDisplayName( moduleEntities[modId].model - )} in slot ${ - modOnDeck.slot === 'span7_8_10_11' - ? '7, 8, 10, 11' - : modOnDeck.slot - }`, + )}`, value: modId, }, ] @@ -234,7 +230,7 @@ export const getUnoccupiedLabwareLocationOptions: Selector< ) }) .map(slotId => ({ name: slotId, value: slotId })) - const offDeck = { name: 'Off-Deck', value: 'offDeck' } + const offDeck = { name: 'Off-deck', value: 'offDeck' } const wasteChuteSlot = { name: 'Waste Chute in D3', value: WASTE_CHUTE_CUTOUT, diff --git a/protocol-designer/src/top-selectors/substep-highlight.ts b/protocol-designer/src/top-selectors/substep-highlight.ts index 049bd59e7df..8724f86a909 100644 --- a/protocol-designer/src/top-selectors/substep-highlight.ts +++ b/protocol-designer/src/top-selectors/substep-highlight.ts @@ -4,7 +4,11 @@ import { ALL, COLUMN, getWellNamePerMultiTip } from '@opentrons/shared-data' import * as StepGeneration from '@opentrons/step-generation' import { selectors as stepFormSelectors } from '../step-forms' import { selectors as fileDataSelectors } from '../file-data' -import { getHoveredStepId, getHoveredSubstep } from '../ui/steps' +import { + getHoveredStepId, + getHoveredSubstep, + getSelectedStepId, +} from '../ui/steps' import { getWellSetForMultichannel } from '../utils' import type { WellGroup } from '@opentrons/components' import type { @@ -275,6 +279,7 @@ export const wellHighlightsByLabwareId: Selector< getHoveredSubstep, fileDataSelectors.getSubsteps, stepFormSelectors.getOrderedStepIds, + getSelectedStepId, ( robotStateTimeline, invariantContext, @@ -282,10 +287,11 @@ export const wellHighlightsByLabwareId: Selector< hoveredStepId, hoveredSubstep, substepsById, - orderedStepIds + orderedStepIds, + selectedStepId ) => { const timeline = robotStateTimeline.timeline - const stepId = hoveredStepId + const stepId = hoveredStepId || selectedStepId const timelineIndex = orderedStepIds.findIndex(i => i === stepId) const frame = timeline[timelineIndex] const robotState = frame && frame.robotState diff --git a/protocol-designer/src/types.ts b/protocol-designer/src/types.ts index 274f38948ac..3d19d320b4c 100644 --- a/protocol-designer/src/types.ts +++ b/protocol-designer/src/types.ts @@ -1,3 +1,4 @@ +import type { FC } from 'react' import type { OutputSelector } from 'reselect' import type { NozzleConfigurationStyle } from '@opentrons/shared-data' import type { RootState as Analytics } from './analytics' @@ -51,7 +52,7 @@ export interface RouteProps { /** the component rendered by a route match * drop developed components into slots held by placeholder div components * */ - Component: React.FC + Component: FC /** a route/page name to render in the nav bar */ name: string diff --git a/protocol-designer/src/ui/labware/__tests__/selectors.test.ts b/protocol-designer/src/ui/labware/__tests__/selectors.test.ts index 00359673dbb..e2c74b75508 100644 --- a/protocol-designer/src/ui/labware/__tests__/selectors.test.ts +++ b/protocol-designer/src/ui/labware/__tests__/selectors.test.ts @@ -9,7 +9,6 @@ import { THERMOCYCLER_MODULE_TYPE, THERMOCYCLER_MODULE_V1, } from '@opentrons/shared-data' -import { SPAN7_8_10_11_SLOT } from '../../../constants' import { getDisposalOptions, getLabwareOptions, @@ -102,7 +101,7 @@ describe('labware selectors', () => { expect( // @ts-expect-error(sa, 2021-6-15): resultFunc getDisposalOptions.resultFunc(additionalEquipmentEntities) - ).toEqual([{ name: 'Trash Bin', value: mockTrashId }]) + ).toEqual([{ name: 'Trash bin', value: mockTrashId }]) }) it('filters out additional equipment that is NOT trash when multiple trash bins present', () => { const mockTrashId = 'mockTrashId' @@ -129,8 +128,8 @@ describe('labware selectors', () => { // @ts-expect-error(sa, 2021-6-15): resultFunc getDisposalOptions.resultFunc(additionalEquipmentEntities) ).toEqual([ - { name: 'Trash Bin', value: mockTrashId }, - { name: 'Trash Bin', value: mockTrashId2 }, + { name: 'Trash bin', value: mockTrashId }, + { name: 'Trash bin', value: mockTrashId2 }, ]) }) }) @@ -142,7 +141,12 @@ describe('labware selectors', () => { getLabwareOptions.resultFunc( {}, {}, - { labware: {}, modules: {}, pipettes: {} }, + { + labware: {}, + modules: {}, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }, {}, {}, {} @@ -153,13 +157,13 @@ describe('labware selectors', () => { it('should return labware options when no modules are present, with no tipracks', () => { const labwareEntities = { ...tipracks, - ...trash, ...otherLabware, } const initialDeckSetup = { labware: labwareEntities, modules: {}, pipettes: {}, + additionalEquipmentOnDeck: {}, } expect( // @ts-expect-error(sa, 2021-6-15): resultFunc @@ -171,13 +175,10 @@ describe('labware selectors', () => { {}, {} ) - ).toEqual([ - { name: 'Source Plate', value: 'wellPlateId' }, - { name: 'Trash', value: mockTrash }, - ]) + ).toEqual([{ name: 'Source Plate', value: 'wellPlateId' }]) }) - it('should return labware options with module prefixes when a labware is on module', () => { + it('should return labware options with no module prefixes even when a labware is on module', () => { const labware = { wellPlateId: { ...otherLabware.wellPlateId, @@ -206,6 +207,9 @@ describe('labware selectors', () => { ...trash, ...labware, }, + additionalEquipmentOnDeck: { + trash: { id: 'trash', location: 'cutout12', name: 'trashBin' }, + }, modules: { magModuleId: { id: 'magModuleId', @@ -223,7 +227,7 @@ describe('labware selectors', () => { id: 'thermocyclerId', type: THERMOCYCLER_MODULE_TYPE, model: THERMOCYCLER_MODULE_V1, - slot: SPAN7_8_10_11_SLOT, + slot: '8', }, heaterShakerId: { id: 'heaterShakerId', @@ -253,11 +257,11 @@ describe('labware selectors', () => { {} ) ).toEqual([ - { name: 'HS Plate in Heater-Shaker', value: 'hsPlateId' }, - { name: 'TC Plate in Thermocycler', value: 'tcPlateId' }, - { name: 'Temp Plate in Temperature Module', value: 'tempPlateId' }, + { name: 'HS Plate in 6', value: 'hsPlateId' }, + { name: 'TC Plate in A1+B1', value: 'tcPlateId' }, + { name: 'Temp Plate in 3', value: 'tempPlateId' }, { name: 'Trash', value: mockTrash }, - { name: 'Well Plate in Magnetic Module', value: 'wellPlateId' }, + { name: 'Well Plate in 1', value: 'wellPlateId' }, ]) }) @@ -272,7 +276,6 @@ describe('labware selectors', () => { const initialDeckSetup = { pipettes: {}, labware: { - ...trash, ...labware, }, modules: { @@ -283,6 +286,9 @@ describe('labware selectors', () => { slot: '1', }, }, + additionalEquipmentOnDeck: { + trash: { id: 'trash', name: 'trashBin', location: 'cutout12' }, + }, } const nicknames: Record = { @@ -312,14 +318,14 @@ describe('labware selectors', () => { ) ).toEqual([ { name: 'Trash', value: mockTrash }, - { name: 'Well Plate in Magnetic Module', value: 'wellPlateId' }, + { name: 'Well Plate in 1', value: 'wellPlateId' }, ]) }) }) describe('_sortLabwareDropdownOptions', () => { const trashOption = { - name: 'Trash Bin', + name: 'Trash bin', value: mockTrash, } const zzzPlateOption = { name: 'Zzz Plate', value: 'zzz' } diff --git a/protocol-designer/src/ui/labware/selectors.ts b/protocol-designer/src/ui/labware/selectors.ts index 77369002aeb..34d5818611f 100644 --- a/protocol-designer/src/ui/labware/selectors.ts +++ b/protocol-designer/src/ui/labware/selectors.ts @@ -1,25 +1,32 @@ import { createSelector } from 'reselect' import mapValues from 'lodash/mapValues' import reduce from 'lodash/reduce' -import { getIsTiprack, getLabwareDisplayName } from '@opentrons/shared-data' +import { + TRASH_BIN_DISPLAY_NAME, + WASTE_CHUTE_DISPLAY_NAME, +} from '@opentrons/components' +import { + FLEX_ROBOT_TYPE, + OT2_ROBOT_TYPE, + getIsTiprack, + getLabwareDisplayName, +} from '@opentrons/shared-data' import * as stepFormSelectors from '../../step-forms/selectors' import { selectors as labwareIngredSelectors } from '../../labware-ingred/selectors' -import { getModuleShortNames, getModuleUnderLabware } from '../modules/utils' -import { getLabwareOffDeck, getLabwareInColumn4 } from './utils' +import { getLabwareLatestSlot } from './utils' import type { LabwareEntity, AdditionalEquipmentEntity, } from '@opentrons/step-generation' -import type { DropdownOption, Options } from '@opentrons/components' +import type { DropdownOption } from '@opentrons/components' +import type { RobotType } from '@opentrons/shared-data' import type { Selector } from '../../types' import type { AllTemporalPropertiesForTimelineFrame, SavedStepFormState, } from '../../step-forms' -const TRASH = 'Trash Bin' - export const getLabwareNicknamesById: Selector< Record > = createSelector( @@ -32,11 +39,13 @@ export const getLabwareNicknamesById: Selector< displayLabware[id]?.nickname || getLabwareDisplayName(labwareEntity.def) ) ) -export const _sortLabwareDropdownOptions = (options: Options): Options => +export const _sortLabwareDropdownOptions = ( + options: DropdownOption[] +): DropdownOption[] => options.sort((a, b) => { // special case for trash (always at the bottom of the list) - if (a.name === TRASH) return 1 - if (b.name === TRASH) return -1 + if (a.name === TRASH_BIN_DISPLAY_NAME) return 1 + if (b.name === TRASH_BIN_DISPLAY_NAME) return -1 // sort by name everything else by name return a.name.localeCompare(b.name) }) @@ -45,35 +54,21 @@ const getNickname = ( nicknamesById: Record, initialDeckSetup: AllTemporalPropertiesForTimelineFrame, labwareId: string, - savedStepForms: SavedStepFormState + savedStepForms: SavedStepFormState, + robotType: RobotType ): string => { - const isOffDeck = getLabwareOffDeck( - initialDeckSetup, - savedStepForms ?? {}, - labwareId - ) - - const moduleOnDeck = getModuleUnderLabware( + const latestSlot = getLabwareLatestSlot( initialDeckSetup, savedStepForms ?? {}, - labwareId - ) - const module = - moduleOnDeck != null ? getModuleShortNames(moduleOnDeck.type) : null - - const isLabwareInColumn4 = getLabwareInColumn4( - initialDeckSetup, - savedStepForms ?? {}, - labwareId + labwareId, + robotType ) let nickName: string = nicknamesById[labwareId] - if (module != null) { - nickName = `${nicknamesById[labwareId]} in ${module}` - } else if (isOffDeck) { + if (latestSlot != null && latestSlot !== 'offDeck') { + nickName = `${nicknamesById[labwareId]} in ${latestSlot}` + } else if (latestSlot != null && latestSlot === 'offDeck') { nickName = `${nicknamesById[labwareId]} off-deck` - } else if (isLabwareInColumn4) { - nickName = `${nicknamesById[labwareId]} in staging area slot` } return nickName } @@ -81,7 +76,7 @@ const getNickname = ( /** Returns options for labware dropdowns for moveLabware. * Ordered by display name / nickname, but with trash at the bottom. */ -export const getMoveLabwareOptions: Selector = createSelector( +export const getMoveLabwareOptions: Selector = createSelector( stepFormSelectors.getLabwareEntities, getLabwareNicknamesById, stepFormSelectors.getInitialDeckSetup, @@ -108,13 +103,19 @@ export const getMoveLabwareOptions: Selector = createSelector( const wasteChuteLocation = Object.values(additionalEquipmentEntities).find( aE => aE.name === 'wasteChute' )?.location + const trashBinLocation = Object.values(additionalEquipmentEntities).find( + aE => aE.name === 'trashBin' + )?.location + const robotType = + trashBinLocation === 'cutout12' ? OT2_ROBOT_TYPE : FLEX_ROBOT_TYPE + const moveLabwareOptions = reduce( labwareEntities, ( - acc: Options, + acc: DropdownOption[], labwareEntity: LabwareEntity, labwareId: string - ): Options => { + ): DropdownOption[] => { const isLabwareInWasteChute = filteredSavedStepFormIds.find( id => @@ -129,7 +130,8 @@ export const getMoveLabwareOptions: Selector = createSelector( nicknamesById, initialDeckSetup, labwareId, - savedStepForms + savedStepForms, + robotType ) // filter out moving trash, adapters, and labware in @@ -153,7 +155,7 @@ export const getMoveLabwareOptions: Selector = createSelector( /** Returns options for labware dropdowns for moveLiquids. * Ordered by display name / nickname, but with trash at the bottom. */ -export const getLabwareOptions: Selector = createSelector( +export const getLabwareOptions: Selector = createSelector( stepFormSelectors.getLabwareEntities, getLabwareNicknamesById, stepFormSelectors.getInitialDeckSetup, @@ -169,13 +171,19 @@ export const getLabwareOptions: Selector = createSelector( const wasteChuteLocation = Object.values(additionalEquipmentEntities).find( aE => aE.name === 'wasteChute' )?.location + const trashBinLocation = Object.values(additionalEquipmentEntities).find( + aE => aE.name === 'trashBin' + )?.location + const robotType = + trashBinLocation === 'cutout12' ? OT2_ROBOT_TYPE : FLEX_ROBOT_TYPE + const labwareOptions = reduce( labwareEntities, ( - acc: Options, + acc: DropdownOption[], labwareEntity: LabwareEntity, labwareId: string - ): Options => { + ): DropdownOption[] => { const isLabwareInWasteChute = Object.values(savedStepForms).find( form => form.stepType === 'moveLabware' && @@ -189,7 +197,8 @@ export const getLabwareOptions: Selector = createSelector( nicknamesById, initialDeckSetup, labwareId, - savedStepForms + savedStepForms, + robotType ) return getIsTiprack(labwareEntity.def) || @@ -220,7 +229,7 @@ export const getWasteChuteOption: Selector = createSelect const wasteChuteOption: DropdownOption | null = wasteChuteEntity != null ? { - name: 'Waste Chute', + name: WASTE_CHUTE_DISPLAY_NAME, value: wasteChuteEntity.id, } : null @@ -236,12 +245,15 @@ export const getDisposalOptions = createSelector( (additionalEquipment, wasteChuteOption) => { const trashBins = reduce( additionalEquipment, - (acc: Options, additionalEquipment: AdditionalEquipmentEntity): Options => + ( + acc: DropdownOption[], + additionalEquipment: AdditionalEquipmentEntity + ): DropdownOption[] => additionalEquipment.name === 'trashBin' ? [ ...acc, { - name: TRASH, + name: TRASH_BIN_DISPLAY_NAME, value: additionalEquipment.id ?? '', }, ] diff --git a/protocol-designer/src/ui/labware/utils.ts b/protocol-designer/src/ui/labware/utils.ts index 2377f7976db..d55c35b578f 100644 --- a/protocol-designer/src/ui/labware/utils.ts +++ b/protocol-designer/src/ui/labware/utils.ts @@ -1,39 +1,54 @@ -import { COLUMN_4_SLOTS } from '@opentrons/step-generation' +import { getHasWasteChute } from '@opentrons/step-generation' +import { WASTE_CHUTE_DISPLAY_NAME } from '@opentrons/components' +import { + FLEX_ROBOT_TYPE, + TC_MODULE_LOCATION_OT2, + TC_MODULE_LOCATION_OT3, + THERMOCYCLER_MODULE_TYPE, +} from '@opentrons/shared-data' +import type { RobotType } from '@opentrons/shared-data' import type { InitialDeckSetup, SavedStepFormState } from '../../step-forms' -export function getLabwareOffDeck( - initialDeckSetup: InitialDeckSetup, - savedStepFormState: SavedStepFormState, - labwareId: string -): boolean { - // latest moveLabware step related to labwareId - const moveLabwareStep = Object.values(savedStepFormState) - .filter( - state => - state.stepType === 'moveLabware' && - labwareId != null && - state.labware === labwareId - ) - .reverse()[0] - - if (moveLabwareStep?.newLocation === 'offDeck') { - return true - } else if ( - moveLabwareStep == null && - initialDeckSetup.labware[labwareId]?.slot === 'offDeck' - ) { - return true - } else return false +function resolveSlotLocation( + modules: InitialDeckSetup['modules'], + labware: InitialDeckSetup['labware'], + location: string, + robotType: RobotType +): string { + const TCSlot = + robotType === FLEX_ROBOT_TYPE + ? TC_MODULE_LOCATION_OT3 + : TC_MODULE_LOCATION_OT2 + if (location === 'offDeck') { + return 'offDeck' + } else if (modules[location] != null) { + return modules[location].type === THERMOCYCLER_MODULE_TYPE + ? TCSlot + : modules[location].slot + } else if (labware[location] != null) { + const adapter = labware[location] + if (modules[adapter.slot] != null) { + return modules[adapter.slot].type === THERMOCYCLER_MODULE_TYPE + ? TCSlot + : modules[adapter.slot].slot + } else { + return adapter.slot + } + } else { + return location + } } -export function getLabwareInColumn4( +export function getLabwareLatestSlot( initialDeckSetup: InitialDeckSetup, savedStepForms: SavedStepFormState, - labwareId: string -): boolean { - const isStartingInColumn4 = COLUMN_4_SLOTS.includes( - initialDeckSetup.labware[labwareId]?.slot - ) + labwareId: string, + robotType: RobotType +): string | null { + const { modules, labware, additionalEquipmentOnDeck } = initialDeckSetup + const initialSlot = labware[labwareId]?.slot + const hasWasteChute = getHasWasteChute(additionalEquipmentOnDeck) + // latest moveLabware step related to labwareId const moveLabwareStep = Object.values(savedStepForms) .filter( @@ -45,13 +60,25 @@ export function getLabwareInColumn4( .reverse()[0] if ( - moveLabwareStep?.newLocation != null && - COLUMN_4_SLOTS.includes(moveLabwareStep.newLocation as string) + hasWasteChute && + (initialSlot === 'D3' || moveLabwareStep?.newLocation === 'D3') ) { - return true - } else if (moveLabwareStep == null && isStartingInColumn4) { - return true + return WASTE_CHUTE_DISPLAY_NAME + } + + if (moveLabwareStep?.newLocation != null) { + return resolveSlotLocation( + modules, + labware, + moveLabwareStep.newLocation as string, + robotType + ) + } else if (moveLabwareStep == null) { + return resolveSlotLocation(modules, labware, initialSlot, robotType) } else { - return false + console.warn( + `Expected to find labware's location but could not with initial slot ${initialSlot}` + ) + return null } } diff --git a/protocol-designer/src/ui/modules/selectors.ts b/protocol-designer/src/ui/modules/selectors.ts index 53cf16ca49e..8fdd6e10c0d 100644 --- a/protocol-designer/src/ui/modules/selectors.ts +++ b/protocol-designer/src/ui/modules/selectors.ts @@ -15,7 +15,7 @@ import { getMagnetLabwareEngageHeight as getMagnetLabwareEngageHeightUtil, getModulesOnDeckByType, } from './utils' -import type { Options } from '@opentrons/components' +import type { DropdownOption } from '@opentrons/components' import type { Selector } from '../../types' import type { LabwareNamesByModuleId } from '../../steplist/types' @@ -35,7 +35,9 @@ export const getLabwareNamesByModuleId: Selector = creat ) /** Returns dropdown option for labware placed on magnetic module */ -export const getMagneticLabwareOptions: Selector = createSelector( +export const getMagneticLabwareOptions: Selector< + DropdownOption[] +> = createSelector( getInitialDeckSetup, getLabwareNicknamesById, (initialDeckSetup, nicknamesById) => { @@ -48,7 +50,9 @@ export const getMagneticLabwareOptions: Selector = createSelector( ) /** Returns dropdown option for labware placed on temperature module */ -export const getTemperatureLabwareOptions: Selector = createSelector( +export const getTemperatureLabwareOptions: Selector< + DropdownOption[] +> = createSelector( getInitialDeckSetup, getLabwareNicknamesById, (initialDeckSetup, nicknamesById) => { @@ -62,7 +66,9 @@ export const getTemperatureLabwareOptions: Selector = createSelector( ) /** Returns dropdown option for labware placed on heater shaker module */ -export const getHeaterShakerLabwareOptions: Selector = createSelector( +export const getHeaterShakerLabwareOptions: Selector< + DropdownOption[] +> = createSelector( getInitialDeckSetup, getLabwareNicknamesById, (initialDeckSetup, nicknamesById) => { diff --git a/protocol-designer/src/ui/modules/utils.ts b/protocol-designer/src/ui/modules/utils.ts index ec2a20a7474..d347c1b5388 100644 --- a/protocol-designer/src/ui/modules/utils.ts +++ b/protocol-designer/src/ui/modules/utils.ts @@ -1,9 +1,15 @@ import values from 'lodash/values' import { - MAGNETIC_MODULE_V1, + ABSORBANCE_READER_TYPE, getLabwareDefaultEngageHeight, + HEATERSHAKER_MODULE_TYPE, + MAGNETIC_BLOCK_TYPE, + MAGNETIC_MODULE_TYPE, + MAGNETIC_MODULE_V1, + TEMPERATURE_MODULE_TYPE, + THERMOCYCLER_MODULE_TYPE, } from '@opentrons/shared-data' -import type { Options } from '@opentrons/components' +import type { DropdownOption } from '@opentrons/components' import type { ModuleType } from '@opentrons/shared-data' import type { ModuleOnDeck, @@ -76,17 +82,17 @@ export function getModuleUnderLabware( export const getModuleShortNames = (type: ModuleType): string => { switch (type) { - case 'heaterShakerModuleType': - return 'Heater-Shaker' - case 'magneticBlockType': + case HEATERSHAKER_MODULE_TYPE: + return 'Heater-Shaker Module' + case MAGNETIC_BLOCK_TYPE: return 'Magnetic Block' - case 'magneticModuleType': + case MAGNETIC_MODULE_TYPE: return 'Magnetic Module' - case 'temperatureModuleType': + case TEMPERATURE_MODULE_TYPE: return 'Temperature Module' - case 'thermocyclerModuleType': + case THERMOCYCLER_MODULE_TYPE: return 'Thermocycler' - case 'absorbanceReaderType': + case ABSORBANCE_READER_TYPE: return 'Absorbance Reader' } } @@ -95,11 +101,11 @@ export function getModuleLabwareOptions( initialDeckSetup: InitialDeckSetup, nicknamesById: Record, type: ModuleType -): Options { +): DropdownOption[] { const labwares = initialDeckSetup.labware const modulesOnDeck = getModulesOnDeckByType(initialDeckSetup, type) const module = getModuleShortNames(type) - let options: Options = [] + let options: DropdownOption[] = [] if (modulesOnDeck != null) { options = modulesOnDeck.map(moduleOnDeck => { @@ -110,22 +116,25 @@ export function getModuleLabwareOptions( )?.id if (labwareOnAdapterId != null) { return { - name: `${nicknamesById[labwareOnAdapterId]} in ${ - nicknamesById[labware.id] - } in ${module} in slot ${moduleOnDeck.slot}`, + name: `${nicknamesById[labware.id]} with ${ + nicknamesById[labwareOnAdapterId] + }`, + deckLabel: moduleOnDeck.slot, + subtext: module, value: moduleOnDeck.id, } } else { return { - name: `${nicknamesById[labware.id]} in ${module} in slot ${ - moduleOnDeck.slot - }`, + name: nicknamesById[labware.id], + deckLabel: moduleOnDeck.slot, + subtext: module, value: moduleOnDeck.id, } } } else { return { - name: `No labware in ${module} in slot ${moduleOnDeck.slot}`, + name: module, + deckLabel: moduleOnDeck.slot, value: moduleOnDeck.id, } } diff --git a/protocol-designer/src/ui/steps/actions/__tests__/actions.test.ts b/protocol-designer/src/ui/steps/actions/__tests__/actions.test.ts index 7dbe2b12324..a4d62738ffe 100644 --- a/protocol-designer/src/ui/steps/actions/__tests__/actions.test.ts +++ b/protocol-designer/src/ui/steps/actions/__tests__/actions.test.ts @@ -7,7 +7,7 @@ import * as utils from '../../../../utils' import * as stepFormSelectors from '../../../../step-forms/selectors' import { getRobotStateTimeline } from '../../../../file-data/selectors' import { getMultiSelectLastSelected } from '../../selectors' -import { selectStep, selectAllSteps, deselectAllSteps } from '../actions' +import { selectAllSteps, deselectAllSteps } from '../actions' import { duplicateStep, duplicateMultipleSteps, @@ -52,38 +52,6 @@ const initialRobotState: RobotState = { } describe('steps actions', () => { - describe('selectStep', () => { - const stepId = 'stepId' - beforeEach(() => { - when(vi.mocked(stepFormSelectors.getSavedStepForms)) - .calledWith(expect.anything()) - .thenReturn({ - stepId: { - foo: 'getSavedStepFormsResult', - } as any, - }) - }) - afterEach(() => { - vi.resetAllMocks() - }) - // TODO(IL, 2020-04-17): also test scroll to top behavior - it('should select the step and populate the form', () => { - const store: any = mockStore() - store.dispatch(selectStep(stepId)) - expect(store.getActions()).toEqual([ - { - type: 'SELECT_STEP', - payload: stepId, - }, - { - type: 'POPULATE_FORM', - payload: { - foo: 'getSavedStepFormsResult', - }, - }, - ]) - }) - }) describe('selectAllSteps', () => { let ids: string[] beforeEach(() => { diff --git a/protocol-designer/src/ui/steps/actions/__tests__/addAndSelectStep.test.ts b/protocol-designer/src/ui/steps/actions/__tests__/addAndSelectStep.test.ts index 054133c6057..c19e9f56483 100644 --- a/protocol-designer/src/ui/steps/actions/__tests__/addAndSelectStep.test.ts +++ b/protocol-designer/src/ui/steps/actions/__tests__/addAndSelectStep.test.ts @@ -1,15 +1,19 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' +import { fixture12Trough, fixtureTiprack1000ul } from '@opentrons/shared-data' import { addAndSelectStep } from '../thunks' import { PRESAVED_STEP_ID } from '../../../../steplist/types' import { addHint } from '../../../../tutorial/actions' import { selectors as labwareIngredSelectors } from '../../../../labware-ingred/selectors' import * as fileDataSelectors from '../../../../file-data/selectors' +import { getInitialDeckSetup } from '../../../../step-forms/selectors' +import type { LabwareDefinition2 } from '@opentrons/shared-data' import type { StepType } from '../../../../form-types' vi.mock('../../../../tutorial/actions') vi.mock('../../../../ui/modules/selectors') vi.mock('../../../../labware-ingred/selectors') vi.mock('../../../../file-data/selectors') +vi.mock('../../../../step-forms/selectors') const dispatch = vi.fn() const getState = vi.fn() @@ -20,6 +24,12 @@ beforeEach(() => { vi.mocked(fileDataSelectors.getRobotStateTimeline).mockReturnValue( 'mockGetRobotStateTimelineValue' as any ) + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: {}, + labware: {}, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }) }) describe('addAndSelectStep', () => { it('should dispatch addStep thunk, and no hints when no hints are applicable (eg pause step)', () => { @@ -43,4 +53,310 @@ describe('addAndSelectStep', () => { ], ]) }) + it('should dispatch a thermocycler selected action if the step type is thermocycler', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: { + modId: { + type: 'thermocyclerModuleType', + id: 'modId', + slot: 'B2', + model: 'thermocyclerModuleV1', + moduleState: {} as any, + }, + }, + labware: {}, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }) + const stepType: StepType = 'thermocycler' + const payload = { + stepType, + } + addAndSelectStep(payload)(dispatch, getState) + expect(dispatch.mock.calls).toEqual([ + [ + { + type: 'ADD_STEP', + payload: { + id: PRESAVED_STEP_ID, + stepType: 'thermocycler', + }, + meta: { + robotStateTimeline: 'mockGetRobotStateTimelineValue', + }, + }, + ], + [ + { + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { id: 'modId', text: 'Selected', field: '1' }, + mode: 'add', + }, + }, + ], + ]) + }) + it('should dispatch a magnet module selected action if the step type is magnet', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: { + modId: { + type: 'magneticModuleType', + id: 'modId', + slot: '1', + model: 'magneticModuleV1', + moduleState: {} as any, + }, + }, + labware: {}, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }) + const stepType: StepType = 'magnet' + const payload = { + stepType, + } + addAndSelectStep(payload)(dispatch, getState) + expect(dispatch.mock.calls).toEqual([ + [ + { + type: 'ADD_STEP', + payload: { + id: PRESAVED_STEP_ID, + stepType: 'magnet', + }, + meta: { + robotStateTimeline: 'mockGetRobotStateTimelineValue', + }, + }, + ], + [ + { + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { id: 'modId', text: 'Selected', field: '1' }, + mode: 'add', + }, + }, + ], + ]) + }) + it('should dispatch a temperature module selected action if the step type is temperature and only 1 temp mod', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: { + modId: { + type: 'temperatureModuleType', + id: 'modId', + slot: 'B2', + model: 'temperatureModuleV1', + moduleState: {} as any, + }, + }, + labware: {}, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }) + const stepType: StepType = 'temperature' + const payload = { + stepType, + } + addAndSelectStep(payload)(dispatch, getState) + expect(dispatch.mock.calls).toEqual([ + [ + { + type: 'ADD_STEP', + payload: { + id: PRESAVED_STEP_ID, + stepType: 'temperature', + }, + meta: { + robotStateTimeline: 'mockGetRobotStateTimelineValue', + }, + }, + ], + [ + { + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { id: 'modId', text: 'Selected', field: '1' }, + mode: 'add', + }, + }, + ], + ]) + }) + it('should not dispatch hs module selected action if the step type is hs and 2 mods', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: { + modId: { + type: 'heaterShakerModuleType', + id: 'modId', + slot: 'B2', + model: 'heaterShakerModuleV1', + moduleState: {} as any, + }, + modId2: { + type: 'heaterShakerModuleType', + id: 'modId2', + slot: 'A1', + model: 'heaterShakerModuleV1', + moduleState: {} as any, + }, + }, + labware: {}, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }) + const stepType: StepType = 'heaterShaker' + const payload = { + stepType, + } + addAndSelectStep(payload)(dispatch, getState) + expect(dispatch.mock.calls).toEqual([ + [ + { + type: 'ADD_STEP', + payload: { + id: PRESAVED_STEP_ID, + stepType: 'heaterShaker', + }, + meta: { + robotStateTimeline: 'mockGetRobotStateTimelineValue', + }, + }, + ], + ]) + }) + it('should dispatch labware selected action if the step type is mix and only 1 labware', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: {}, + labware: { + labware: { + id: 'labware', + def: fixture12Trough as LabwareDefinition2, + labwareDefURI: 'mockDefUri', + slot: 'A1', + }, + labware2: { + id: 'labware2', + def: fixtureTiprack1000ul as LabwareDefinition2, + labwareDefURI: 'mockDefUri', + slot: 'B1', + }, + }, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }) + const stepType: StepType = 'mix' + const payload = { + stepType, + } + addAndSelectStep(payload)(dispatch, getState) + expect(dispatch.mock.calls).toEqual([ + [ + { + type: 'ADD_STEP', + payload: { + id: PRESAVED_STEP_ID, + stepType: 'mix', + }, + meta: { + robotStateTimeline: 'mockGetRobotStateTimelineValue', + }, + }, + ], + [ + { + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { id: 'labware', text: 'Selected', field: '1' }, + mode: 'add', + }, + }, + ], + ]) + }) + it('should not dispatch labware selected action if the step type is moveLiquid and 2 labware', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: {}, + labware: { + labware: { + id: 'labware', + def: fixture12Trough as LabwareDefinition2, + labwareDefURI: 'mockDefUri', + slot: 'A1', + }, + labware2: { + id: 'labware2', + def: fixture12Trough as LabwareDefinition2, + labwareDefURI: 'mockDefUri', + slot: 'B1', + }, + }, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }) + const stepType: StepType = 'moveLiquid' + const payload = { + stepType, + } + addAndSelectStep(payload)(dispatch, getState) + expect(dispatch.mock.calls).toEqual([ + [ + { + type: 'ADD_STEP', + payload: { + id: PRESAVED_STEP_ID, + stepType: 'moveLiquid', + }, + meta: { + robotStateTimeline: 'mockGetRobotStateTimelineValue', + }, + }, + ], + ]) + }) + it('should dispatch move labware selected action if the step type is moveLabware and only 1 labware', () => { + vi.mocked(getInitialDeckSetup).mockReturnValue({ + modules: {}, + labware: { + labware2: { + id: 'labware2', + def: fixtureTiprack1000ul as LabwareDefinition2, + labwareDefURI: 'mockDefUri', + slot: 'B1', + }, + }, + pipettes: {}, + additionalEquipmentOnDeck: {}, + }) + const stepType: StepType = 'moveLabware' + const payload = { + stepType, + } + addAndSelectStep(payload)(dispatch, getState) + expect(dispatch.mock.calls).toEqual([ + [ + { + type: 'ADD_STEP', + payload: { + id: PRESAVED_STEP_ID, + stepType: 'moveLabware', + }, + meta: { + robotStateTimeline: 'mockGetRobotStateTimelineValue', + }, + }, + ], + [ + { + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { id: 'labware2', text: 'Selected', field: '1' }, + mode: 'add', + }, + }, + ], + ]) + }) }) diff --git a/protocol-designer/src/ui/steps/actions/actions.ts b/protocol-designer/src/ui/steps/actions/actions.ts index c85db48137d..4343760c455 100644 --- a/protocol-designer/src/ui/steps/actions/actions.ts +++ b/protocol-designer/src/ui/steps/actions/actions.ts @@ -17,15 +17,19 @@ import type { AnalyticsEventAction } from '../../../analytics/actions' import type { TerminalItemId, SubstepIdentifier } from '../../../steplist/types' import type { AddStepAction, + ClearWellSelectionLabwareKeyAction, HoverOnStepAction, HoverOnSubstepAction, - SelectTerminalItemAction, HoverOnTerminalItemAction, - SetWellSelectionLabwareKeyAction, - ClearWellSelectionLabwareKeyAction, - SelectStepAction, + hoverSelectionAction, + Mode, + selectDropdownItemAction, + Selection, SelectMultipleStepsAction, SelectMultipleStepsForGroupAction, + SelectStepAction, + SelectTerminalItemAction, + SetWellSelectionLabwareKeyAction, ToggleViewSubstepAction, ViewSubstep, } from './types' @@ -48,6 +52,28 @@ export const addStep = (args: { }, } } +export const hoverSelection = (args: Selection): hoverSelectionAction => ({ + type: 'HOVER_DROPDOWN_ITEM', + payload: { id: args.id, text: args.text }, +}) +export const selectDropdownItem = (args: { + selection: Selection | null + mode: Mode +}): selectDropdownItemAction => ({ + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: + args.selection != null + ? { + id: args.selection.id, + text: args.selection.text, + field: args.selection.field, + } + : null, + mode: args.mode, + }, +}) + export const hoverOnSubstep = ( payload: SubstepIdentifier ): HoverOnSubstepAction => ({ @@ -95,9 +121,97 @@ export const resetSelectStep = (stepId: StepIdType): ThunkAction => ( type: 'POPULATE_FORM', payload: null, }) + dispatch({ + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { + id: null, + text: null, + }, + mode: 'clear', + }, + }) resetScrollElements() } +const setSelection = ( + formData: { + [x: string]: any + stepType: StepType + id: string + }, + dispatch: ThunkDispatch +): void => { + if (formData.stepType === 'moveLabware') { + dispatch({ + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { id: formData.labware, text: 'Selected', field: '1' }, + mode: 'add', + }, + }) + dispatch({ + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { id: formData.newLocation, text: 'Location', field: '2' }, + mode: 'add', + }, + }) + } else if (formData.stepType === 'moveLiquid') { + dispatch({ + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { + id: formData.aspirate_labware, + text: 'Source', + field: '1', + }, + mode: 'add', + }, + }) + dispatch({ + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { + id: formData.dispense_labware, + text: 'Destination', + field: '2', + }, + mode: 'add', + }, + }) + } else if (formData.stepType === 'mix') { + dispatch({ + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { + id: formData.labware, + text: 'Selected', + field: '1', + }, + mode: 'add', + }, + }) + } else if ( + formData.stepType === 'heaterShaker' || + formData.stepType === 'temperature' || + formData.stepType === 'thermocycler' || + formData.stepType === 'magnet' + ) { + dispatch({ + type: 'SELECT_DROPDOWN_ITEM', + payload: { + selection: { + id: formData.moduleId, + text: 'Selected', + field: '1', + }, + mode: 'add', + }, + }) + } +} + export const populateForm = (stepId: StepIdType): ThunkAction => ( dispatch: ThunkDispatch, getState: GetState @@ -108,9 +222,9 @@ export const populateForm = (stepId: StepIdType): ThunkAction => ( type: 'POPULATE_FORM', payload: formData, }) + setSelection(formData, dispatch) resetScrollElements() } - export const selectStep = (stepId: StepIdType): ThunkAction => ( dispatch: ThunkDispatch, getState: GetState @@ -126,9 +240,8 @@ export const selectStep = (stepId: StepIdType): ThunkAction => ( type: 'POPULATE_FORM', payload: formData, }) - resetScrollElements() + setSelection(formData, dispatch) } - // NOTE(sa, 2020-12-11): this is a thunk so that we can populate the batch edit form with things later export const selectMultipleSteps = ( stepIds: StepIdType[], diff --git a/protocol-designer/src/ui/steps/actions/thunks/index.ts b/protocol-designer/src/ui/steps/actions/thunks/index.ts index 47e0d846180..edb4bf08b7f 100644 --- a/protocol-designer/src/ui/steps/actions/thunks/index.ts +++ b/protocol-designer/src/ui/steps/actions/thunks/index.ts @@ -1,16 +1,23 @@ import last from 'lodash/last' +import { + HEATERSHAKER_MODULE_TYPE, + MAGNETIC_MODULE_TYPE, + TEMPERATURE_MODULE_TYPE, + THERMOCYCLER_MODULE_TYPE, +} from '@opentrons/shared-data' import { getUnsavedForm, getUnsavedFormIsPristineSetTempForm, getUnsavedFormIsPristineHeaterShakerForm, getOrderedStepIds, + getInitialDeckSetup, } from '../../../../step-forms/selectors' import { changeFormInput } from '../../../../steplist/actions/actions' import { PRESAVED_STEP_ID } from '../../../../steplist/types' import { PAUSE_UNTIL_TEMP } from '../../../../constants' import { uuid } from '../../../../utils' import { getMultiSelectLastSelected, getSelectedStepId } from '../../selectors' -import { addStep } from '../actions' +import { addStep, selectDropdownItem } from '../actions' import { actions as tutorialActions, selectors as tutorialSelectors, @@ -23,16 +30,128 @@ import type { DuplicateMultipleStepsAction, SelectMultipleStepsAction, } from '../types' + export const addAndSelectStep: (arg: { stepType: StepType }) => ThunkAction = payload => (dispatch, getState) => { const robotStateTimeline = fileDataSelectors.getRobotStateTimeline(getState()) + const initialDeckSetup = getInitialDeckSetup(getState()) + const { modules, labware } = initialDeckSetup dispatch( addStep({ stepType: payload.stepType, robotStateTimeline, }) ) + if (payload.stepType === 'thermocycler') { + const tcId = Object.entries(modules).find( + ([key, module]) => module.type === THERMOCYCLER_MODULE_TYPE + )?.[0] + if (tcId != null) { + dispatch( + selectDropdownItem({ + selection: { + id: tcId, + text: 'Selected', + field: '1', + }, + mode: 'add', + }) + ) + } + } else if (payload.stepType === 'magnet') { + const magId = Object.entries(modules).find( + ([key, module]) => module.type === MAGNETIC_MODULE_TYPE + )?.[0] + if (magId != null) { + dispatch( + selectDropdownItem({ + selection: { + id: magId, + text: 'Selected', + field: '1', + }, + mode: 'add', + }) + ) + } + } else if (payload.stepType === 'temperature') { + const temperatureModules = Object.entries(modules).filter( + ([key, module]) => module.type === TEMPERATURE_MODULE_TYPE + ) + // only set selected temperature module if only 1 type is on deck + const tempId = + temperatureModules.length === 1 ? temperatureModules[0][0] : null + if (tempId != null) { + dispatch( + selectDropdownItem({ + selection: { + id: tempId, + text: 'Selected', + field: '1', + }, + mode: 'add', + }) + ) + } + } else if (payload.stepType === 'heaterShaker') { + const hsModules = Object.entries(modules).filter( + ([key, module]) => module.type === HEATERSHAKER_MODULE_TYPE + ) + // only set selected h-s module if only 1 type is on deck + const hsId = hsModules.length === 1 ? hsModules[0][0] : null + if (hsId != null) { + dispatch( + selectDropdownItem({ + selection: { + id: hsId, + text: 'Selected', + field: '1', + }, + mode: 'add', + }) + ) + } + } else if (payload.stepType === 'mix' || payload.stepType === 'moveLiquid') { + const labwares = Object.entries(labware).filter( + ([key, lw]) => + !lw.def.parameters.isTiprack && + !lw.def.allowedRoles?.includes('adapter') && + !lw.def.allowedRoles?.includes('lid') + ) + // only set selected labware if only 1 available labware is on deck + const labwareId = labwares.length === 1 ? labwares[0][0] : null + if (labwareId != null) { + dispatch( + selectDropdownItem({ + selection: { + id: labwareId, + text: payload.stepType === 'moveLiquid' ? 'Source' : 'Selected', + field: '1', + }, + mode: 'add', + }) + ) + } + } else if (payload.stepType === 'moveLabware') { + const labwares = Object.entries(labware).filter( + ([key, lw]) => !lw.def.allowedRoles?.includes('adapter') + ) + // only set selected labware if only 1 available labware/tiprack/lid is on deck + const labwareId = labwares.length === 1 ? labwares[0][0] : null + if (labwareId != null) { + dispatch( + selectDropdownItem({ + selection: { + id: labwareId, + text: 'Selected', + field: '1', + }, + mode: 'add', + }) + ) + } + } } export interface ReorderSelectedStepAction { type: 'REORDER_SELECTED_STEP' diff --git a/protocol-designer/src/ui/steps/actions/types.ts b/protocol-designer/src/ui/steps/actions/types.ts index ebbf1e4dff2..52556930d1d 100644 --- a/protocol-designer/src/ui/steps/actions/types.ts +++ b/protocol-designer/src/ui/steps/actions/types.ts @@ -31,6 +31,24 @@ export interface DuplicateMultipleStepsAction { indexToInsert: number } } + +export type Mode = 'clear' | 'add' +export interface Selection { + id: string | null + text: string | null + field?: '1' | '2' +} +export interface selectDropdownItemAction { + type: 'SELECT_DROPDOWN_ITEM' + payload: { + selection: Selection | null + mode: 'add' | 'clear' + } +} +export interface hoverSelectionAction { + type: 'HOVER_DROPDOWN_ITEM' + payload: Selection +} export interface HoverOnSubstepAction { type: 'HOVER_ON_SUBSTEP' payload: SubstepIdentifier diff --git a/protocol-designer/src/ui/steps/reducers.ts b/protocol-designer/src/ui/steps/reducers.ts index 0a40cb6dbe2..e281cb01bc4 100644 --- a/protocol-designer/src/ui/steps/reducers.ts +++ b/protocol-designer/src/ui/steps/reducers.ts @@ -21,6 +21,7 @@ import type { SelectStepAction, SelectMultipleStepsAction, SelectTerminalItemAction, + Selection, } from './actions/types' export type CollapsedStepsState = Record @@ -188,6 +189,50 @@ const selectedSubstep: Reducer = handleActions( }, null ) +const hoveredDropdownItem: Reducer = handleActions( + { + HOVER_DROPDOWN_ITEM: ( + state, + action: { + payload: Selection + } + ) => action.payload, + }, + { id: null, text: null } +) +const selectedDropdownItem: Reducer = handleActions( + { + SELECT_DROPDOWN_ITEM: ( + state: Selection[], + action: { + payload: { + selection: Selection | null + mode: 'add' | 'clear' + } + } + ) => { + const { selection, mode } = action.payload + + switch (mode) { + case 'clear': + return [] + case 'add': { + if (!selection) { + return state + } + const updatedState = state.filter( + sel => sel.field !== selection.field + ) + + return [...updatedState, selection] + } + default: + return state + } + }, + }, + [] +) export interface StepsState { collapsedSteps: CollapsedStepsState selectedItem: SelectedItemState @@ -195,6 +240,8 @@ export interface StepsState { hoveredSubstep: SubstepIdentifier wellSelectionLabwareKey: string | null selectedSubstep: StepIdType | null + hoveredDropdownItem: Selection + selectedDropdownItem: Selection[] } export const _allReducers = { collapsedSteps, @@ -203,6 +250,8 @@ export const _allReducers = { hoveredSubstep, wellSelectionLabwareKey, selectedSubstep, + hoveredDropdownItem, + selectedDropdownItem, } export const rootReducer: Reducer = combineReducers( _allReducers diff --git a/protocol-designer/src/ui/steps/selectors.ts b/protocol-designer/src/ui/steps/selectors.ts index 53848c4a28a..c6f48ff2f2f 100644 --- a/protocol-designer/src/ui/steps/selectors.ts +++ b/protocol-designer/src/ui/steps/selectors.ts @@ -38,6 +38,7 @@ import type { CollapsedStepsState, HoverableItem, } from './reducers' +import type { Selection } from './actions/types' export const rootSelector = (state: BaseState): StepsState => state.ui.steps // ======= Selectors =============================================== @@ -102,6 +103,14 @@ export const getHoveredStepId: Selector = createSelector( item => item && item.selectionType === SINGLE_STEP_SELECTION_TYPE ? item.id : null ) +export const getHoveredDropdownItem: Selector = createSelector( + rootSelector, + (state: StepsState) => state.hoveredDropdownItem +) +export const getSelectedDropdownItem: Selector = createSelector( + rootSelector, + (state: StepsState) => state.selectedDropdownItem +) /** Array of labware (labwareId's) involved in hovered Step, or [] */ export const getHoveredStepLabware = createSelector( diff --git a/protocol-designer/src/utils/labwareModuleCompatibility.ts b/protocol-designer/src/utils/labwareModuleCompatibility.ts index 52fd34ec9ed..1664e060f0a 100644 --- a/protocol-designer/src/utils/labwareModuleCompatibility.ts +++ b/protocol-designer/src/utils/labwareModuleCompatibility.ts @@ -11,6 +11,9 @@ import type { LabwareDefByDefURI } from '../labware-defs' import type { LabwareOnDeck } from '../step-forms' import type { LabwareDefinition2, ModuleType } from '@opentrons/shared-data' // NOTE: this does not distinguish btw versions. Standard labware only (assumes namespace is 'opentrons') + +const PLATE_READER_MAX_LABWARE_Z_MM = 16 + export const COMPATIBLE_LABWARE_ALLOWLIST_BY_MODULE_TYPE: Record< ModuleType, Readonly @@ -155,6 +158,18 @@ export const getLabwareIsCustom = ( return labwareOnDeck.labwareDefURI in customLabwares } +// This breaks pattern with other module compatibility checks, but it more exactly mirrors Protocol Engine's logic +// See api/src/opentrons/protocol_engine/state/labware.py for details +export const getLabwareCompatibleWithAbsorbanceReader = ( + def: LabwareDefinition2 +): boolean => { + return ( + Object.entries(def.wells).length === 96 && + !def.parameters.isTiprack && + def.dimensions.zDimension <= PLATE_READER_MAX_LABWARE_Z_MM + ) +} + export const getAdapterLabwareIsAMatch = ( labwareId: string, allLabware: LabwareOnDeck[], diff --git a/robot-server/Config.in b/robot-server/Config.in index 359b031d79c..3d6c5ee8c82 100644 --- a/robot-server/Config.in +++ b/robot-server/Config.in @@ -14,6 +14,7 @@ config BR2_PACKAGE_PYTHON_OPENTRONS_ROBOT_SERVER select BR2_PACKAGE_PYTHON_UVICORN # runtime select BR2_PACKAGE_PYTHON_WSPROTO # runtime select BR2_PACKAGE_PYTHON_PAHO_MQTT # runtime + select BR2_PACKAGE_PYTHON_PYDANTIC_SETTINGS # runtime help Opentrons HTTP server. Controls an OT-2 robot. diff --git a/robot-server/Pipfile b/robot-server/Pipfile index c66f132ecd4..541cd8bca90 100755 --- a/robot-server/Pipfile +++ b/robot-server/Pipfile @@ -22,7 +22,7 @@ pytest-xdist = "~=2.5.0" requests = "==2.27.1" graphviz = "==0.19" mock = "~=5.0.1" -mypy = "==1.8.0" +mypy = "==1.11.0" flake8 = "==7.0.0" flake8-annotations = "~=3.0.1" flake8-docstrings = "~=1.7.0" @@ -37,18 +37,14 @@ sqlalchemy2-stubs = "==0.0.2a21" python-box = "==6.1.0" types-paho-mqtt = "==1.6.0.20240106" pyusb = "==1.2.1" -performance-metrics = {file = "../performance-metrics", editable = true} [packages] anyio = "==3.7.1" aiohttp = "==3.8.1" -# fastapi >=0.100.0 is intended for use with pydantic 2.x, and while it theoretically is -# backwards compatible, best to be sure -fastapi = "==0.99.1" +fastapi = "==0.100.0" python-dotenv = "==1.0.1" python-multipart = "==0.0.6" -# pydantic 2.x has many breaking api changes -pydantic = "==1.10.12" +pydantic = "==2.9.0" typing-extensions = ">=4.0.0,<5" uvicorn = "==0.27.0.post1" wsproto = "==1.2.0" @@ -63,4 +59,10 @@ opentrons-hardware = {editable = true, path='../hardware', extras=['FLEX']} opentrons = { editable = true, path = "../api"} opentrons-shared-data = { editable = true, path = "../shared-data/python" } server-utils = {editable = true, path = "./../server-utils"} +performance-metrics = {editable = true, path = "../performance-metrics" } robot-server = { editable = true, path = "."} +pydantic-settings = "==2.4.0" +# this is a dependency of jsonschema (which requires just >0.7.0). +# versions above 0.18.1 require a version of triomphe that requires a version of +# rust too new for openembedded +rpds-py = "==0.18.1" diff --git a/robot-server/Pipfile.lock b/robot-server/Pipfile.lock index 0f33283b0ee..df7ec2c7a92 100644 --- a/robot-server/Pipfile.lock +++ b/robot-server/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f43123500e774f5b9a92f2be8ae39a04af8df7ba2ae4b2a505f1bb4b751cc30a" + "sha256": "d1bb17b5871fccc0c2b2aebc24507d29016a8078c3a3894271fb4c2a920fb37a" }, "pipfile-spec": 6, "requires": { @@ -100,15 +100,24 @@ "sha256:25816a9eef030c774beaee22189a24e29bc43f81cebe574ef723851eaf89ddee", "sha256:9651e1373873c75786101330e302e114f85b6e8b5ad70b491497c8b3609a8449" ], + "markers": "python_version >= '3.8'", "version": "==0.3.1" }, "aiosignal": { "hashes": [ - "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", - "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17" + "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", + "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54" ], - "markers": "python_version >= '3.7'", - "version": "==1.3.1" + "markers": "python_version >= '3.9'", + "version": "==1.3.2" + }, + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" }, "anyio": { "hashes": [ @@ -129,11 +138,11 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", + "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308" ], - "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "markers": "python_version >= '3.8'", + "version": "==24.3.0" }, "charset-normalizer": { "hashes": [ @@ -154,167 +163,197 @@ }, "exceptiongroup": { "hashes": [ - "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", - "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.1" + "version": "==1.2.2" }, "fastapi": { "hashes": [ - "sha256:976df7bab51ac7beda9f68c4513b8c4490b5c1135c72aafd0a5ee4023ec5282e", - "sha256:ac78f717cd80d657bd183f94d33b9bda84aa376a46a9dab513586b8eef1dc6fc" + "sha256:271662daf986da8fa98dc2b7c7f61c4abdfdccfb4786d79ed8b2878f172c6d5f", + "sha256:acb5f941ea8215663283c10018323ba7ea737c571b67fc7e88e9469c7eb1d12e" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.99.1" + "version": "==0.100.0" }, "frozenlist": { "hashes": [ - "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", - "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", - "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", - "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5", - "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", - "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", - "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", - "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701", - "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d", - "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6", - "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", - "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", - "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", - "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", - "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a", - "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0", - "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", - "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826", - "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", - "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6", - "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", - "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", - "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0", - "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", - "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", - "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09", - "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", - "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", - "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", - "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b", - "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b", - "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d", - "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", - "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea", - "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", - "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", - "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897", - "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7", - "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", - "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9", - "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", - "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", - "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742", - "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09", - "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", - "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932", - "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", - "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", - "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", - "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d", - "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7", - "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", - "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", - "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e", - "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", - "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", - "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb", - "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", - "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8", - "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", - "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", - "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", - "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11", - "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", - "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", - "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0", - "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497", - "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", - "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0", - "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", - "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", - "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", - "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", - "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", - "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887", - "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", - "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74" + "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", + "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf", + "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6", + "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a", + "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d", + "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f", + "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", + "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", + "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", + "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", + "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec", + "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2", + "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c", + "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336", + "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4", + "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d", + "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b", + "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c", + "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10", + "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08", + "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942", + "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", + "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f", + "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10", + "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5", + "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", + "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", + "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", + "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d", + "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923", + "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", + "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", + "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17", + "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0", + "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", + "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", + "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c", + "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a", + "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0", + "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", + "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab", + "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", + "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3", + "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", + "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", + "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604", + "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", + "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5", + "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", + "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", + "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", + "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", + "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d", + "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", + "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3", + "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", + "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", + "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9", + "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf", + "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76", + "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba", + "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171", + "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb", + "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", + "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", + "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972", + "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d", + "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869", + "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9", + "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411", + "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723", + "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2", + "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b", + "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99", + "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e", + "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", + "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3", + "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb", + "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", + "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", + "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca", + "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45", + "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", + "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f", + "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5", + "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307", + "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e", + "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2", + "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778", + "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a", + "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30", + "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a" ], "markers": "python_version >= '3.8'", - "version": "==1.4.1" + "version": "==1.5.0" }, "greenlet": { "hashes": [ - "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", - "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", - "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", - "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", - "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", - "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", - "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", - "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", - "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", - "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", - "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", - "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", - "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", - "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", - "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", - "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", - "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", - "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", - "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", - "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", - "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", - "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", - "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", - "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", - "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", - "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", - "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", - "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", - "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", - "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", - "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", - "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", - "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", - "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", - "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", - "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", - "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", - "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", - "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", - "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", - "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", - "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", - "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", - "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", - "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", - "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", - "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", - "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", - "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", - "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", - "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", - "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", - "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", - "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", - "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", - "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", - "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", - "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" + "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", + "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7", + "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", + "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", + "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", + "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", + "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", + "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", + "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", + "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa", + "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", + "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", + "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", + "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22", + "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9", + "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", + "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba", + "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3", + "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", + "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", + "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291", + "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", + "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", + "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", + "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", + "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef", + "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c", + "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", + "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c", + "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", + "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", + "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8", + "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d", + "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", + "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145", + "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", + "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", + "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e", + "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", + "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1", + "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef", + "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", + "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", + "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", + "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437", + "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd", + "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981", + "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", + "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", + "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798", + "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", + "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", + "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", + "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", + "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af", + "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", + "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", + "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42", + "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e", + "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81", + "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", + "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", + "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc", + "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de", + "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111", + "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", + "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", + "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", + "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", + "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", + "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803", + "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", + "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f" ], "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", - "version": "==3.0.3" + "version": "==3.1.1" }, "h11": { "hashes": [ @@ -405,99 +444,101 @@ }, "multidict": { "hashes": [ - "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556", - "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c", - "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29", - "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b", - "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8", - "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7", - "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd", - "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40", - "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6", - "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3", - "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c", - "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9", - "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5", - "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae", - "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442", - "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9", - "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc", - "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c", - "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea", - "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5", - "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50", - "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182", - "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453", - "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e", - "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600", - "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733", - "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda", - "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241", - "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461", - "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e", - "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e", - "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b", - "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e", - "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7", - "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386", - "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd", - "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9", - "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf", - "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee", - "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5", - "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a", - "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271", - "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54", - "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4", - "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496", - "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb", - "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319", - "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3", - "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f", - "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527", - "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed", - "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604", - "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef", - "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8", - "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5", - "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5", - "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626", - "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c", - "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d", - "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c", - "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc", - "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc", - "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b", - "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38", - "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450", - "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1", - "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f", - "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3", - "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755", - "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226", - "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a", - "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046", - "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf", - "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479", - "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e", - "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1", - "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a", - "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83", - "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929", - "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93", - "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a", - "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c", - "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44", - "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89", - "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba", - "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e", - "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da", - "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24", - "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423", - "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef" + "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", + "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056", + "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", + "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", + "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b", + "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", + "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748", + "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", + "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f", + "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", + "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6", + "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada", + "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", + "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2", + "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d", + "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", + "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef", + "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", + "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", + "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60", + "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6", + "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", + "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478", + "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", + "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7", + "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56", + "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", + "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", + "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30", + "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb", + "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", + "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0", + "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", + "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c", + "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", + "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", + "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", + "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", + "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", + "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2", + "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", + "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3", + "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", + "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", + "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657", + "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581", + "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492", + "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43", + "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", + "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", + "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926", + "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057", + "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc", + "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80", + "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255", + "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1", + "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972", + "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53", + "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1", + "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423", + "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a", + "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160", + "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c", + "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd", + "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", + "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5", + "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", + "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", + "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", + "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", + "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4", + "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", + "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753", + "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28", + "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d", + "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a", + "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304", + "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", + "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429", + "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72", + "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399", + "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3", + "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392", + "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167", + "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c", + "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", + "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351", + "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76", + "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875", + "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd", + "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", + "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db" ], - "markers": "python_version >= '3.7'", - "version": "==6.0.5" + "markers": "python_version >= '3.8'", + "version": "==6.1.0" }, "numpy": { "hashes": [ @@ -545,11 +586,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "paho-mqtt": { "hashes": [ @@ -557,48 +598,210 @@ ], "version": "==1.6.1" }, + "performance-metrics": { + "editable": true, + "path": "../performance-metrics" + }, + "propcache": { + "hashes": [ + "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4", + "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4", + "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a", + "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f", + "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9", + "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d", + "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e", + "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6", + "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf", + "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034", + "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d", + "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16", + "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30", + "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba", + "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95", + "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d", + "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae", + "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348", + "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2", + "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", + "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce", + "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54", + "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629", + "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", + "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1", + "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b", + "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf", + "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b", + "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587", + "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097", + "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea", + "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24", + "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7", + "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541", + "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6", + "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634", + "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3", + "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d", + "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034", + "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465", + "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2", + "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf", + "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1", + "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04", + "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5", + "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583", + "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb", + "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b", + "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c", + "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958", + "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc", + "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4", + "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82", + "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e", + "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce", + "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9", + "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518", + "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536", + "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505", + "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052", + "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff", + "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1", + "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f", + "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681", + "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347", + "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af", + "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246", + "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787", + "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0", + "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f", + "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439", + "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3", + "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6", + "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca", + "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec", + "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d", + "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3", + "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16", + "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717", + "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6", + "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd", + "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212" + ], + "markers": "python_version >= '3.9'", + "version": "==0.2.1" + }, "pydantic": { "hashes": [ - "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303", - "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe", - "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47", - "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494", - "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33", - "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86", - "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d", - "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c", - "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a", - "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565", - "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb", - "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62", - "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62", - "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0", - "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523", - "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d", - "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405", - "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f", - "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b", - "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718", - "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed", - "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb", - "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5", - "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc", - "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942", - "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe", - "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246", - "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350", - "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303", - "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09", - "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33", - "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8", - "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a", - "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1", - "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6", - "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d" + "sha256:c7a8a9fdf7d100afa49647eae340e2d23efa382466a8d177efcd1381e9be5598", + "sha256:f66a7073abd93214a20c5f7b32d56843137a7a2e70d02111f3be287035c45370" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.10.12" + "markers": "python_version >= '3.8'", + "version": "==2.9.0" + }, + "pydantic-core": { + "hashes": [ + "sha256:0102e49ac7d2df3379ef8d658d3bc59d3d769b0bdb17da189b75efa861fc07b4", + "sha256:0123655fedacf035ab10c23450163c2f65a4174f2bb034b188240a6cf06bb123", + "sha256:043ef8469f72609c4c3a5e06a07a1f713d53df4d53112c6d49207c0bd3c3bd9b", + "sha256:0448b81c3dfcde439551bb04a9f41d7627f676b12701865c8a2574bcea034437", + "sha256:05b366fb8fe3d8683b11ac35fa08947d7b92be78ec64e3277d03bd7f9b7cda79", + "sha256:07049ec9306ec64e955b2e7c40c8d77dd78ea89adb97a2013d0b6e055c5ee4c5", + "sha256:084414ffe9a85a52940b49631321d636dadf3576c30259607b75516d131fecd0", + "sha256:086c5db95157dc84c63ff9d96ebb8856f47ce113c86b61065a066f8efbe80acf", + "sha256:12625e69b1199e94b0ae1c9a95d000484ce9f0182f9965a26572f054b1537e44", + "sha256:16b25a4a120a2bb7dab51b81e3d9f3cde4f9a4456566c403ed29ac81bf49744f", + "sha256:19f1352fe4b248cae22a89268720fc74e83f008057a652894f08fa931e77dced", + "sha256:1a2ab4f410f4b886de53b6bddf5dd6f337915a29dd9f22f20f3099659536b2f6", + "sha256:1c7b81beaf7c7ebde978377dc53679c6cba0e946426fc7ade54251dfe24a7604", + "sha256:1cf842265a3a820ebc6388b963ead065f5ce8f2068ac4e1c713ef77a67b71f7c", + "sha256:1eb37f7d6a8001c0f86dc8ff2ee8d08291a536d76e49e78cda8587bb54d8b329", + "sha256:23af245b8f2f4ee9e2c99cb3f93d0e22fb5c16df3f2f643f5a8da5caff12a653", + "sha256:257d6a410a0d8aeb50b4283dea39bb79b14303e0fab0f2b9d617701331ed1515", + "sha256:276ae78153a94b664e700ac362587c73b84399bd1145e135287513442e7dfbc7", + "sha256:2b1a195efd347ede8bcf723e932300292eb13a9d2a3c1f84eb8f37cbbc905b7f", + "sha256:329a721253c7e4cbd7aad4a377745fbcc0607f9d72a3cc2102dd40519be75ed2", + "sha256:358331e21a897151e54d58e08d0219acf98ebb14c567267a87e971f3d2a3be59", + "sha256:3649bd3ae6a8ebea7dc381afb7f3c6db237fc7cebd05c8ac36ca8a4187b03b30", + "sha256:3713dc093d5048bfaedbba7a8dbc53e74c44a140d45ede020dc347dda18daf3f", + "sha256:3ef71ec876fcc4d3bbf2ae81961959e8d62f8d74a83d116668409c224012e3af", + "sha256:41ae8537ad371ec018e3c5da0eb3f3e40ee1011eb9be1da7f965357c4623c501", + "sha256:4a801c5e1e13272e0909c520708122496647d1279d252c9e6e07dac216accc41", + "sha256:4c83c64d05ffbbe12d4e8498ab72bdb05bcc1026340a4a597dc647a13c1605ec", + "sha256:4cebb9794f67266d65e7e4cbe5dcf063e29fc7b81c79dc9475bd476d9534150e", + "sha256:5668b3173bb0b2e65020b60d83f5910a7224027232c9f5dc05a71a1deac9f960", + "sha256:56e6a12ec8d7679f41b3750ffa426d22b44ef97be226a9bab00a03365f217b2b", + "sha256:582871902e1902b3c8e9b2c347f32a792a07094110c1bca6c2ea89b90150caac", + "sha256:5c8aa40f6ca803f95b1c1c5aeaee6237b9e879e4dfb46ad713229a63651a95fb", + "sha256:5d813fd871b3d5c3005157622ee102e8908ad6011ec915a18bd8fde673c4360e", + "sha256:5dd0ec5f514ed40e49bf961d49cf1bc2c72e9b50f29a163b2cc9030c6742aa73", + "sha256:5f3cf3721eaf8741cffaf092487f1ca80831202ce91672776b02b875580e174a", + "sha256:6294907eaaccf71c076abdd1c7954e272efa39bb043161b4b8aa1cd76a16ce43", + "sha256:64d094ea1aa97c6ded4748d40886076a931a8bf6f61b6e43e4a1041769c39dd2", + "sha256:6650a7bbe17a2717167e3e23c186849bae5cef35d38949549f1c116031b2b3aa", + "sha256:67b6655311b00581914aba481729971b88bb8bc7996206590700a3ac85e457b8", + "sha256:6b06c5d4e8701ac2ba99a2ef835e4e1b187d41095a9c619c5b185c9068ed2a49", + "sha256:6ce883906810b4c3bd90e0ada1f9e808d9ecf1c5f0b60c6b8831d6100bcc7dd6", + "sha256:6db09153d8438425e98cdc9a289c5fade04a5d2128faff8f227c459da21b9703", + "sha256:6f80fba4af0cb1d2344869d56430e304a51396b70d46b91a55ed4959993c0589", + "sha256:743e5811b0c377eb830150d675b0847a74a44d4ad5ab8845923d5b3a756d8100", + "sha256:753294d42fb072aa1775bfe1a2ba1012427376718fa4c72de52005a3d2a22178", + "sha256:7568f682c06f10f30ef643a1e8eec4afeecdafde5c4af1b574c6df079e96f96c", + "sha256:7706e15cdbf42f8fab1e6425247dfa98f4a6f8c63746c995d6a2017f78e619ae", + "sha256:785e7f517ebb9890813d31cb5d328fa5eda825bb205065cde760b3150e4de1f7", + "sha256:7a05c0240f6c711eb381ac392de987ee974fa9336071fb697768dfdb151345ce", + "sha256:7ce7eaf9a98680b4312b7cebcdd9352531c43db00fca586115845df388f3c465", + "sha256:7ce8e26b86a91e305858e018afc7a6e932f17428b1eaa60154bd1f7ee888b5f8", + "sha256:7d0324a35ab436c9d768753cbc3c47a865a2cbc0757066cb864747baa61f6ece", + "sha256:7e9b24cca4037a561422bf5dc52b38d390fb61f7bfff64053ce1b72f6938e6b2", + "sha256:810ca06cca91de9107718dc83d9ac4d2e86efd6c02cba49a190abcaf33fb0472", + "sha256:820f6ee5c06bc868335e3b6e42d7ef41f50dfb3ea32fbd523ab679d10d8741c0", + "sha256:82764c0bd697159fe9947ad59b6db6d7329e88505c8f98990eb07e84cc0a5d81", + "sha256:8ae65fdfb8a841556b52935dfd4c3f79132dc5253b12c0061b96415208f4d622", + "sha256:8d5b0ff3218858859910295df6953d7bafac3a48d5cd18f4e3ed9999efd2245f", + "sha256:95d6bf449a1ac81de562d65d180af5d8c19672793c81877a2eda8fde5d08f2fd", + "sha256:964c7aa318da542cdcc60d4a648377ffe1a2ef0eb1e996026c7f74507b720a78", + "sha256:96ef39add33ff58cd4c112cbac076726b96b98bb8f1e7f7595288dcfb2f10b57", + "sha256:a6612c2a844043e4d10a8324c54cdff0042c558eef30bd705770793d70b224aa", + "sha256:a8031074a397a5925d06b590121f8339d34a5a74cfe6970f8a1124eb8b83f4ac", + "sha256:aab9e522efff3993a9e98ab14263d4e20211e62da088298089a03056980a3e69", + "sha256:ae579143826c6f05a361d9546446c432a165ecf1c0b720bbfd81152645cb897d", + "sha256:ae90b9e50fe1bd115b24785e962b51130340408156d34d67b5f8f3fa6540938e", + "sha256:b18cf68255a476b927910c6873d9ed00da692bb293c5b10b282bd48a0afe3ae2", + "sha256:b7efb12e5071ad8d5b547487bdad489fbd4a5a35a0fc36a1941517a6ad7f23e0", + "sha256:c4d9f15ffe68bcd3898b0ad7233af01b15c57d91cd1667f8d868e0eacbfe3f87", + "sha256:c53100c8ee5a1e102766abde2158077d8c374bee0639201f11d3032e3555dfbc", + "sha256:c57e493a0faea1e4c38f860d6862ba6832723396c884fbf938ff5e9b224200e2", + "sha256:c8319e0bd6a7b45ad76166cc3d5d6a36c97d0c82a196f478c3ee5346566eebfd", + "sha256:caffda619099cfd4f63d48462f6aadbecee3ad9603b4b88b60cb821c1b258576", + "sha256:cc0c316fba3ce72ac3ab7902a888b9dc4979162d320823679da270c2d9ad0cad", + "sha256:cdd02a08205dc90238669f082747612cb3c82bd2c717adc60f9b9ecadb540f80", + "sha256:d50ac34835c6a4a0d456b5db559b82047403c4317b3bc73b3455fefdbdc54b0a", + "sha256:d6b9dd6aa03c812017411734e496c44fef29b43dba1e3dd1fa7361bbacfc1354", + "sha256:da3131ef2b940b99106f29dfbc30d9505643f766704e14c5d5e504e6a480c35e", + "sha256:da43cbe593e3c87d07108d0ebd73771dc414488f1f91ed2e204b0370b94b37ac", + "sha256:dd59638025160056687d598b054b64a79183f8065eae0d3f5ca523cde9943940", + "sha256:e1895e949f8849bc2757c0dbac28422a04be031204df46a56ab34bcf98507342", + "sha256:e1a79ad49f346aa1a2921f31e8dbbab4d64484823e813a002679eaa46cba39e1", + "sha256:e460475719721d59cd54a350c1f71c797c763212c836bf48585478c5514d2854", + "sha256:e64ffaf8f6e17ca15eb48344d86a7a741454526f3a3fa56bc493ad9d7ec63936", + "sha256:e6e3ccebdbd6e53474b0bb7ab8b88e83c0cfe91484b25e058e581348ee5a01a5", + "sha256:e758d271ed0286d146cf7c04c539a5169a888dd0b57026be621547e756af55bc", + "sha256:f087879f1ffde024dd2788a30d55acd67959dcf6c431e9d3682d1c491a0eb474", + "sha256:f477d26183e94eaafc60b983ab25af2a809a1b48ce4debb57b343f671b7a90b6", + "sha256:fc535cb898ef88333cf317777ecdfe0faac1c2a3187ef7eb061b6f7ecf7e6bae" + ], + "markers": "python_version >= '3.8'", + "version": "==2.23.2" + }, + "pydantic-settings": { + "hashes": [ + "sha256:bb6849dc067f1687574c12a639e231f3a6feeed0a12d710c1382045c5db1c315", + "sha256:ed81c3a0f46392b4d7c0a565c05884e6e54b3456e6f0fe4d8814981172dc9a88" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.4.0" }, "pyrsistent": { "hashes": [ @@ -671,21 +874,135 @@ "markers": "python_version >= '3.7'", "version": "==0.0.6" }, + "pyusb": { + "hashes": [ + "sha256:2b4c7cb86dbadf044dfb9d3a4ff69fd217013dbe78a792177a3feb172449ea36", + "sha256:a4cc7404a203144754164b8b40994e2849fde1cfff06b08492f12fff9d9de7b9" + ], + "markers": "python_full_version >= '3.6.0'", + "version": "==1.2.1" + }, "robot-server": { "editable": true, "path": "." }, + "rpds-py": { + "hashes": [ + "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee", + "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc", + "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc", + "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944", + "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20", + "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7", + "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4", + "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6", + "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6", + "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93", + "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633", + "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0", + "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360", + "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8", + "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139", + "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7", + "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a", + "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9", + "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26", + "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724", + "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72", + "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b", + "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09", + "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100", + "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3", + "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261", + "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3", + "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9", + "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b", + "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3", + "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de", + "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d", + "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e", + "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8", + "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff", + "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5", + "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c", + "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e", + "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e", + "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4", + "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8", + "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922", + "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338", + "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d", + "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8", + "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2", + "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72", + "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80", + "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644", + "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae", + "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163", + "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104", + "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d", + "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60", + "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a", + "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d", + "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07", + "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49", + "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10", + "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f", + "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2", + "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8", + "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7", + "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88", + "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65", + "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0", + "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909", + "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8", + "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c", + "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184", + "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397", + "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a", + "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346", + "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590", + "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333", + "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb", + "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74", + "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e", + "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d", + "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa", + "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f", + "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53", + "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1", + "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac", + "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0", + "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd", + "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611", + "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f", + "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c", + "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5", + "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab", + "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc", + "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43", + "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da", + "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac", + "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843", + "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e", + "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89", + "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==0.18.1" + }, "server-utils": { "editable": true, "path": "./../server-utils" }, "setuptools": { "hashes": [ - "sha256:937a48c7cdb7a21eb53cd7f9b59e525503aa8abaf3584c730dc5f7a5bec3a650", - "sha256:a58a8fde0541dab0419750bcc521fbdf8585f6e5cb41909df3a472ef7b81ca95" + "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", + "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" ], - "markers": "python_version >= '3.8'", - "version": "==70.1.1" + "markers": "python_version >= '3.9'", + "version": "==75.6.0" }, "sniffio": { "hashes": [ @@ -772,6 +1089,14 @@ "markers": "python_version >= '3.8'", "version": "==4.12.2" }, + "tzdata": { + "hashes": [ + "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", + "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.2" + }, "uvicorn": { "hashes": [ "sha256:4b85ba02b8a20429b9b205d015cbeb788a12da527f731811b643fd739ef90d5f", @@ -783,79 +1108,74 @@ }, "wrapt": { "hashes": [ - "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", - "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", - "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", - "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", - "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", - "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", - "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", - "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", - "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", - "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", - "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", - "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", - "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", - "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", - "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", - "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", - "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", - "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", - "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", - "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", - "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", - "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", - "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", - "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", - "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", - "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", - "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", - "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", - "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", - "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", - "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", - "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", - "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", - "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", - "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", - "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", - "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", - "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", - "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", - "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", - "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", - "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", - "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", - "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", - "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", - "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", - "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", - "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", - "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", - "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", - "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", - "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", - "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", - "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", - "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", - "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", - "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", - "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", - "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", - "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", - "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", - "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", - "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", - "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", - "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", - "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", - "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", - "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", - "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", - "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" + "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d", + "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301", + "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635", + "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a", + "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed", + "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721", + "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801", + "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b", + "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1", + "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88", + "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8", + "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0", + "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f", + "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578", + "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7", + "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045", + "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada", + "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d", + "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b", + "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a", + "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977", + "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea", + "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346", + "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13", + "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22", + "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339", + "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9", + "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181", + "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c", + "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90", + "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a", + "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489", + "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f", + "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504", + "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea", + "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569", + "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4", + "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce", + "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab", + "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a", + "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f", + "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c", + "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9", + "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf", + "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d", + "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627", + "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d", + "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4", + "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c", + "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d", + "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad", + "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b", + "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33", + "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371", + "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1", + "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393", + "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106", + "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df", + "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379", + "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451", + "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b", + "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575", + "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed", + "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb", + "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838" ], - "markers": "python_version >= '3.6'", - "version": "==1.16.0" + "markers": "python_version >= '3.8'", + "version": "==1.17.0" }, "wsproto": { "hashes": [ @@ -868,99 +1188,91 @@ }, "yarl": { "hashes": [ - "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", - "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", - "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", - "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", - "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", - "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", - "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", - "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", - "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", - "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", - "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", - "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", - "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", - "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", - "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", - "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", - "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", - "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", - "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", - "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", - "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", - "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", - "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", - "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", - "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", - "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", - "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", - "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", - "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", - "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", - "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", - "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", - "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", - "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", - "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", - "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", - "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", - "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", - "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", - "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", - "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", - "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", - "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", - "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", - "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", - "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", - "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", - "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", - "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", - "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", - "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", - "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", - "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", - "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", - "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", - "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", - "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", - "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", - "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", - "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", - "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", - "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", - "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", - "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", - "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", - "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", - "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", - "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", - "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", - "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", - "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", - "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", - "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", - "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", - "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", - "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", - "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", - "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", - "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", - "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", - "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", - "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", - "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", - "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", - "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", - "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", - "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", - "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", - "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", - "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.4" + "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba", + "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193", + "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318", + "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee", + "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e", + "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1", + "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a", + "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186", + "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1", + "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50", + "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640", + "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb", + "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8", + "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc", + "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5", + "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58", + "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2", + "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393", + "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24", + "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b", + "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910", + "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c", + "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272", + "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed", + "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1", + "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04", + "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d", + "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5", + "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d", + "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889", + "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae", + "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b", + "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c", + "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576", + "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34", + "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477", + "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990", + "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2", + "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512", + "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069", + "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a", + "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6", + "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0", + "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8", + "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb", + "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa", + "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8", + "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e", + "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e", + "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985", + "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8", + "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", + "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5", + "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690", + "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10", + "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789", + "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", + "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca", + "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e", + "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5", + "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59", + "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9", + "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8", + "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db", + "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde", + "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7", + "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb", + "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3", + "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6", + "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285", + "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb", + "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8", + "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482", + "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd", + "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75", + "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760", + "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782", + "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53", + "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2", + "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1", + "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719", + "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62" + ], + "markers": "python_version >= '3.9'", + "version": "==1.18.3" }, "zipp": { "hashes": [ @@ -992,11 +1304,11 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", + "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308" ], - "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "markers": "python_version >= '3.8'", + "version": "==24.3.0" }, "black": { "hashes": [ @@ -1030,11 +1342,11 @@ }, "certifi": { "hashes": [ - "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", - "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" + "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", + "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" ], "markers": "python_version >= '3.6'", - "version": "==2024.6.2" + "version": "==2024.12.14" }, "charset-normalizer": { "hashes": [ @@ -1066,61 +1378,71 @@ "toml" ], "hashes": [ - "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f", - "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d", - "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747", - "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f", - "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d", - "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f", - "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47", - "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e", - "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba", - "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c", - "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b", - "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4", - "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7", - "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555", - "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233", - "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace", - "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805", - "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136", - "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4", - "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d", - "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806", - "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99", - "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8", - "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b", - "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5", - "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da", - "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0", - "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078", - "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f", - "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029", - "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353", - "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638", - "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9", - "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f", - "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7", - "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3", - "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e", - "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016", - "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088", - "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4", - "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882", - "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7", - "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53", - "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d", - "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080", - "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5", - "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d", - "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c", - "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8", - "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633", - "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9", - "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c" - ], - "markers": "python_version >= '3.8'", - "version": "==7.5.4" + "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", + "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", + "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f", + "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", + "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6", + "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae", + "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692", + "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4", + "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4", + "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", + "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d", + "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", + "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", + "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", + "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb", + "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", + "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08", + "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf", + "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b", + "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", + "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c", + "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae", + "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077", + "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00", + "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", + "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664", + "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014", + "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9", + "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6", + "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e", + "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", + "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", + "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", + "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", + "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", + "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", + "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030", + "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678", + "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", + "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902", + "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97", + "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845", + "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419", + "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464", + "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be", + "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", + "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", + "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be", + "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", + "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba", + "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", + "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", + "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4", + "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a", + "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a", + "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", + "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599", + "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", + "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", + "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", + "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1", + "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3" + ], + "markers": "python_version >= '3.9'", + "version": "==7.6.9" }, "decoy": { "hashes": [ @@ -1139,11 +1461,11 @@ }, "exceptiongroup": { "hashes": [ - "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", - "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.1" + "version": "==1.2.2" }, "execnet": { "hashes": [ @@ -1208,11 +1530,11 @@ }, "httpcore": { "hashes": [ - "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61", - "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5" + "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", + "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd" ], "markers": "python_version >= '3.8'", - "version": "==1.0.5" + "version": "==1.0.7" }, "httpx": { "hashes": [ @@ -1258,11 +1580,11 @@ }, "jsonschema-specifications": { "hashes": [ - "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", - "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", + "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf" ], - "markers": "python_version >= '3.8'", - "version": "==2023.12.1" + "markers": "python_version >= '3.9'", + "version": "==2024.10.1" }, "mccabe": { "hashes": [ @@ -1283,37 +1605,37 @@ }, "mypy": { "hashes": [ - "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", - "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", - "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", - "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", - "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", - "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", - "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", - "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", - "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", - "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", - "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", - "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", - "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", - "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", - "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", - "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", - "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", - "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", - "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", - "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", - "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", - "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", - "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", - "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", - "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", - "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", - "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.8.0" + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -1325,11 +1647,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "paho-mqtt": { "hashes": [ @@ -1347,23 +1669,19 @@ }, "pbr": { "hashes": [ - "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda", - "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9" + "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24", + "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a" ], "markers": "python_version >= '2.6'", - "version": "==6.0.0" - }, - "performance-metrics": { - "editable": true, - "file": "../performance-metrics" + "version": "==6.1.0" }, "platformdirs": { "hashes": [ - "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", - "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" ], "markers": "python_version >= '3.8'", - "version": "==4.2.2" + "version": "==4.3.6" }, "pluggy": { "hashes": [ @@ -1407,11 +1725,11 @@ }, "pyjwt": { "hashes": [ - "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de", - "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320" + "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", + "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb" ], - "markers": "python_version >= '3.7'", - "version": "==2.8.0" + "markers": "python_version >= '3.9'", + "version": "==2.10.1" }, "pykwalify": { "hashes": [ @@ -1431,12 +1749,12 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b", - "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268" + "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", + "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.23.7" + "version": "==0.23.8" }, "pytest-cov": { "hashes": [ @@ -1509,66 +1827,67 @@ "sha256:2b4c7cb86dbadf044dfb9d3a4ff69fd217013dbe78a792177a3feb172449ea36", "sha256:a4cc7404a203144754164b8b40994e2849fde1cfff06b08492f12fff9d9de7b9" ], - "index": "pypi", "markers": "python_full_version >= '3.6.0'", "version": "==1.2.1" }, "pyyaml": { "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" ], - "markers": "python_version >= '3.6'", - "version": "==6.0.1" + "markers": "python_version >= '3.8'", + "version": "==6.0.2" }, "referencing": { "hashes": [ @@ -1689,6 +2008,7 @@ "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89", "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64" ], + "index": "pypi", "markers": "python_version >= '3.8'", "version": "==0.18.1" }, @@ -1702,67 +2022,63 @@ }, "ruamel.yaml.clib": { "hashes": [ - "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d", - "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001", - "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462", - "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9", - "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe", - "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b", - "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b", - "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615", - "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62", - "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15", - "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b", - "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1", - "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9", - "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675", - "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899", - "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7", - "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7", - "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312", - "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa", - "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91", - "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b", - "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6", - "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3", - "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334", - "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5", - "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3", - "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe", - "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c", - "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed", - "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337", - "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880", - "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f", - "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d", - "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248", - "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d", - "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf", - "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512", - "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069", - "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb", - "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942", - "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d", - "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31", - "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92", - "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5", - "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28", - "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d", - "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1", - "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2", - "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875", - "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412" + "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b", + "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", + "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", + "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", + "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", + "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", + "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", + "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7", + "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", + "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", + "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", + "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", + "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", + "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", + "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519", + "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", + "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", + "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", + "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", + "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", + "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", + "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", + "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", + "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", + "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", + "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", + "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", + "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", + "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", + "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", + "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45", + "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", + "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12", + "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", + "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", + "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", + "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285", + "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed", + "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", + "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7", + "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", + "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", + "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", + "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", + "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987", + "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df" ], "markers": "python_version < '3.13' and platform_python_implementation == 'CPython'", - "version": "==0.2.8" + "version": "==0.2.12" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "version": "==1.17.0" }, "sniffio": { "hashes": [ @@ -1807,11 +2123,41 @@ }, "tomli": { "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", + "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", + "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", + "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", + "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", + "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", + "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", + "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", + "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", + "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", + "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", + "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", + "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", + "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", + "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", + "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", + "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", + "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", + "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", + "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", + "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", + "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", + "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", + "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", + "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", + "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", + "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", + "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", + "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", + "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", + "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", + "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7" ], "markers": "python_version < '3.11'", - "version": "==2.0.1" + "version": "==2.2.1" }, "types-mock": { "hashes": [ @@ -1857,11 +2203,11 @@ }, "urllib3": { "hashes": [ - "sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3", - "sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429" + "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", + "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.19" + "version": "==1.26.20" } } } diff --git a/robot-server/opentrons-robot-server.service b/robot-server/opentrons-robot-server.service index dbdbdc68046..e452da339e5 100644 --- a/robot-server/opentrons-robot-server.service +++ b/robot-server/opentrons-robot-server.service @@ -27,7 +27,7 @@ Environment=RUNNING_ON_PI=true Environment=OT_ROBOT_SERVER_persistence_directory=/data/opentrons_robot_server Restart=on-failure -TimeoutStartSec=3min +TimeoutStartSec=10min [Install] diff --git a/robot-server/pytest.ini b/robot-server/pytest.ini index 51ed89fd0d2..210861cb6ca 100644 --- a/robot-server/pytest.ini +++ b/robot-server/pytest.ini @@ -5,7 +5,11 @@ markers = addopts = --color=yes --strict-markers asyncio_mode = auto -# Don't allow any new code that uses features removed in SQLAlchemy 2.0. -# We should remove this when we upgrade to SQLAlchemy 2.0. filterwarnings = + # Don't allow any new code that uses features removed in SQLAlchemy 2.0. + # We should remove this when we upgrade to SQLAlchemy 2.0. error::sqlalchemy.exc.RemovedIn20Warning + # Pydantic's shims for its legacy v1 methods (e.g. `BaseModel.construct()`) + # are not type-checked properly. Forbid them, so we're forced to use their newer + # v2 replacements which are type-checked (e.g. ``BaseModel.model_construct()`) + error::pydantic.PydanticDeprecatedSince20 diff --git a/robot-server/robot_server/app_setup.py b/robot-server/robot_server/app_setup.py index 2a4d8b1f3da..265a6270033 100644 --- a/robot-server/robot_server/app_setup.py +++ b/robot-server/robot_server/app_setup.py @@ -114,7 +114,7 @@ async def _lifespan(app: FastAPI) -> AsyncGenerator[None, None]: ) # main router -app.include_router(router=router) +router.install_on_app(app) def _get_persistence_directory(settings: RobotServerSettings) -> Optional[Path]: diff --git a/robot-server/robot_server/client_data/router.py b/robot-server/robot_server/client_data/router.py index 3a619c2111c..847c5da59a9 100644 --- a/robot-server/robot_server/client_data/router.py +++ b/robot-server/robot_server/client_data/router.py @@ -4,6 +4,7 @@ from typing import Annotated, Literal import fastapi +from server_utils.fastapi_utils.light_router import LightRouter from robot_server.client_data.store import ( ClientData, @@ -18,7 +19,7 @@ get_client_data_publisher, ) -router = fastapi.APIRouter() +router = LightRouter() Key = Annotated[ @@ -73,7 +74,7 @@ async def put_client_data( # noqa: D103 ) -> SimpleBody[ClientData]: store.put(key, request_body.data) client_data_publisher.publish_client_data(key) - return SimpleBody.construct(data=store.get(key)) + return SimpleBody.model_construct(data=store.get(key)) @router.get( @@ -92,7 +93,7 @@ async def get_client_data( # noqa: D103 store: Annotated[ClientDataStore, fastapi.Depends(get_client_data_store)], ) -> SimpleBody[ClientData]: try: - return SimpleBody.construct(data=store.get(key)) + return SimpleBody.model_construct(data=store.get(key)) except KeyError as e: raise ClientDataKeyDoesNotExist.from_exc(e).as_error( fastapi.status.HTTP_404_NOT_FOUND @@ -125,7 +126,7 @@ async def delete_client_data( # noqa: D103 ) from e else: client_data_publisher.publish_client_data(key) - return SimpleEmptyBody.construct() + return SimpleEmptyBody.model_construct() @router.delete( @@ -143,4 +144,4 @@ async def delete_all_client_data( # noqa: D103 store.delete_all() for deleted_key in keys_that_will_be_deleted: client_data_publisher.publish_client_data(deleted_key) - return SimpleEmptyBody.construct() + return SimpleEmptyBody.model_construct() diff --git a/robot-server/robot_server/commands/router.py b/robot-server/robot_server/commands/router.py index e4d2d4a9f13..4d23bb73fbe 100644 --- a/robot-server/robot_server/commands/router.py +++ b/robot-server/robot_server/commands/router.py @@ -1,7 +1,8 @@ """Router for top-level /commands endpoints.""" from typing import Annotated, Final, List, Literal, Optional, cast -from fastapi import APIRouter, Depends, Query, status +from fastapi import Depends, Query, status +from server_utils.fastapi_utils.light_router import LightRouter from opentrons.protocol_engine import CommandIntent from opentrons.protocol_engine.errors import CommandDoesNotExistError @@ -25,18 +26,7 @@ _DEFAULT_COMMAND_LIST_LENGTH: Final = 20 -commands_router = APIRouter() - - -class RequestModelWithStatelessCommandCreate(RequestModel[StatelessCommandCreate]): - """Equivalent to RequestModel[StatelessCommandCreate]. - - This works around a Pydantic v<2 bug where RequestModel[StatelessCommandCreate] - doesn't parse using the StatelessCommandCreate union discriminator. - https://github.com/pydantic/pydantic/issues/3782 - """ - - data: StatelessCommandCreate +commands_router = LightRouter() class CommandNotFound(ErrorDetails): @@ -63,7 +53,7 @@ class CommandNotFound(ErrorDetails): }, ) async def create_command( - request_body: RequestModelWithStatelessCommandCreate, + request_body: RequestModel[StatelessCommandCreate], orchestrator: Annotated[RunOrchestrator, Depends(get_default_orchestrator)], waitUntilComplete: Annotated[ bool, @@ -109,7 +99,9 @@ async def create_command( Comes from a query parameter in the URL. orchestrator: The `RunOrchestrator` handling engine for command to be enqueued. """ - command_create = request_body.data.copy(update={"intent": CommandIntent.SETUP}) + command_create = request_body.data.model_copy( + update={"intent": CommandIntent.SETUP} + ) command = await orchestrator.add_command_and_wait_for_interval( command=command_create, wait_until_complete=waitUntilComplete, timeout=timeout ) @@ -117,7 +109,7 @@ async def create_command( response_data = cast(StatelessCommand, orchestrator.get_command(command.id)) return await PydanticResponse.create( - content=SimpleBody.construct(data=response_data), + content=SimpleBody.model_construct(data=response_data), status_code=status.HTTP_201_CREATED, ) @@ -168,7 +160,7 @@ async def get_commands_list( meta = MultiBodyMeta(cursor=cmd_slice.cursor, totalLength=cmd_slice.total_length) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=commands, meta=meta), + content=SimpleMultiBody.model_construct(data=commands, meta=meta), status_code=status.HTTP_200_OK, ) @@ -204,6 +196,6 @@ async def get_command( raise CommandNotFound.from_exc(e).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=cast(StatelessCommand, command)), + content=SimpleBody.model_construct(data=cast(StatelessCommand, command)), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/data_files/router.py b/robot-server/robot_server/data_files/router.py index cf4ba9fa649..ba5cac2f7fd 100644 --- a/robot-server/robot_server/data_files/router.py +++ b/robot-server/robot_server/data_files/router.py @@ -4,8 +4,9 @@ from textwrap import dedent from typing import Annotated, Optional, Literal, Union -from fastapi import APIRouter, UploadFile, File, Form, Depends, Response, status +from fastapi import UploadFile, File, Form, Depends, Response, status from opentrons.protocol_reader import FileHasher, FileReaderWriter +from server_utils.fastapi_utils.light_router import LightRouter from robot_server.service.json_api import ( SimpleBody, @@ -32,7 +33,7 @@ from ..protocols.dependencies import get_file_hasher, get_file_reader_writer from ..service.dependencies import get_current_time, get_unique_id -datafiles_router = APIRouter() +datafiles_router = LightRouter() class MultipleDataFileSources(ErrorDetails): @@ -138,8 +139,8 @@ async def upload_data_file( existing_file_info = data_files_store.get_file_info_by_hash(file_hash) if existing_file_info: return await PydanticResponse.create( - content=SimpleBody.construct( - data=DataFile.construct( + content=SimpleBody.model_construct( + data=DataFile.model_construct( id=existing_file_info.id, name=existing_file_info.name, createdAt=existing_file_info.created_at, @@ -162,8 +163,8 @@ async def upload_data_file( ) await data_files_store.insert(file_info) return await PydanticResponse.create( - content=SimpleBody.construct( - data=DataFile.construct( + content=SimpleBody.model_construct( + data=DataFile.model_construct( id=file_info.id, name=file_info.name, createdAt=created_at, @@ -199,8 +200,8 @@ async def get_data_file_info_by_id( raise FileIdNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) return await PydanticResponse.create( - content=SimpleBody.construct( - data=DataFile.construct( + content=SimpleBody.model_construct( + data=DataFile.model_construct( id=resource.id, name=resource.name, createdAt=resource.created_at, @@ -264,9 +265,9 @@ async def get_all_data_files( meta = MultiBodyMeta(cursor=0, totalLength=len(data_files)) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=[ - DataFile.construct( + DataFile.model_construct( id=data_file_info.id, name=data_file_info.name, createdAt=data_file_info.created_at, @@ -307,6 +308,6 @@ async def delete_file_by_id( raise DataFileInUse(detail=str(e)).as_error(status.HTTP_409_CONFLICT) from e return await PydanticResponse.create( - content=SimpleEmptyBody.construct(), + content=SimpleEmptyBody.model_construct(), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/deck_configuration/defaults.py b/robot-server/robot_server/deck_configuration/defaults.py index 3ed9a5ed395..fce59673771 100644 --- a/robot-server/robot_server/deck_configuration/defaults.py +++ b/robot-server/robot_server/deck_configuration/defaults.py @@ -4,64 +4,64 @@ from . import models -_for_flex = models.DeckConfigurationRequest.construct( +_for_flex = models.DeckConfigurationRequest.model_construct( cutoutFixtures=[ - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutA1", cutoutFixtureId="singleLeftSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutB1", cutoutFixtureId="singleLeftSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutC1", cutoutFixtureId="singleLeftSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutD1", cutoutFixtureId="singleLeftSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutA2", cutoutFixtureId="singleCenterSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutB2", cutoutFixtureId="singleCenterSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutC2", cutoutFixtureId="singleCenterSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutD2", cutoutFixtureId="singleCenterSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutA3", cutoutFixtureId="trashBinAdapter", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutB3", cutoutFixtureId="singleRightSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutC3", cutoutFixtureId="singleRightSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutD3", cutoutFixtureId="singleRightSlot", opentronsModuleSerialNumber=None, @@ -70,64 +70,64 @@ ) -_for_ot2 = models.DeckConfigurationRequest.construct( +_for_ot2 = models.DeckConfigurationRequest.model_construct( cutoutFixtures=[ - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout1", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout2", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout3", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout4", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout5", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout6", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout7", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout8", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout9", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout10", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout11", cutoutFixtureId="singleStandardSlot", opentronsModuleSerialNumber=None, ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout12", cutoutFixtureId="fixedTrashSlot", opentronsModuleSerialNumber=None, diff --git a/robot-server/robot_server/deck_configuration/router.py b/robot-server/robot_server/deck_configuration/router.py index cfb31c9f030..ce80acafb2a 100644 --- a/robot-server/robot_server/deck_configuration/router.py +++ b/robot-server/robot_server/deck_configuration/router.py @@ -6,6 +6,7 @@ import fastapi from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY +from server_utils.fastapi_utils.light_router import LightRouter from opentrons_shared_data.deck.types import DeckDefinitionV5 @@ -21,7 +22,7 @@ from .store import DeckConfigurationStore -router = fastapi.APIRouter() +router = LightRouter() @PydanticResponse.wrap_route( @@ -78,12 +79,12 @@ async def put_deck_configuration( # noqa: D103 if len(validation_errors) == 0: success_data = await store.set(request=request_body.data, last_modified_at=now) return await PydanticResponse.create( - content=SimpleBody.construct(data=success_data) + content=SimpleBody.model_construct(data=success_data) ) else: error_data = validation_mapping.map_out(validation_errors) return await PydanticResponse.create( - content=ErrorBody.construct(errors=error_data), + content=ErrorBody.model_construct(errors=error_data), status_code=HTTP_422_UNPROCESSABLE_ENTITY, ) @@ -111,5 +112,5 @@ async def get_deck_configuration( # noqa: D103 ], ) -> PydanticResponse[SimpleBody[models.DeckConfigurationResponse]]: return await PydanticResponse.create( - content=SimpleBody.construct(data=await store.get()) + content=SimpleBody.model_construct(data=await store.get()) ) diff --git a/robot-server/robot_server/deck_configuration/store.py b/robot-server/robot_server/deck_configuration/store.py index 159013e3504..4a383dd0a3d 100644 --- a/robot-server/robot_server/deck_configuration/store.py +++ b/robot-server/robot_server/deck_configuration/store.py @@ -132,7 +132,7 @@ async def get_for_cli(deck_type: DeckType, path: Path) -> bytes: return serialize_deck_configuration(from_storage[0], from_storage[1]) else: default_as_http_response = _get_default(deck_type) - default_as_http_request = models.DeckConfigurationRequest.construct( + default_as_http_request = models.DeckConfigurationRequest.model_construct( cutoutFixtures=default_as_http_response.cutoutFixtures ) storable_default = _http_types_to_storage_types( @@ -162,21 +162,21 @@ def _storage_types_to_http_types( ) -> models.DeckConfigurationResponse: storage_cutout_fixtures, last_modified_at = storage_val http_cutout_fixtures = [ - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutFixtureId=storage_element.cutout_fixture_id, cutoutId=storage_element.cutout_id, opentronsModuleSerialNumber=storage_element.opentrons_module_serial_number, ) for storage_element in storage_cutout_fixtures ] - return models.DeckConfigurationResponse.construct( + return models.DeckConfigurationResponse.model_construct( cutoutFixtures=http_cutout_fixtures, lastModifiedAt=last_modified_at, ) def _get_default(deck_type: DeckType) -> models.DeckConfigurationResponse: - return models.DeckConfigurationResponse.construct( + return models.DeckConfigurationResponse.model_construct( cutoutFixtures=defaults.for_deck_definition(deck_type.value).cutoutFixtures, lastModifiedAt=None, ) diff --git a/robot-server/robot_server/error_recovery/settings/router.py b/robot-server/robot_server/error_recovery/settings/router.py index 4fdfeee5498..d3c4467a301 100644 --- a/robot-server/robot_server/error_recovery/settings/router.py +++ b/robot-server/robot_server/error_recovery/settings/router.py @@ -4,13 +4,14 @@ from typing import Annotated import fastapi +from server_utils.fastapi_utils.light_router import LightRouter from robot_server.service.json_api import PydanticResponse, RequestModel, SimpleBody from .models import RequestData, ResponseData from .store import ErrorRecoverySettingStore, get_error_recovery_setting_store -router = fastapi.APIRouter() +router = LightRouter() _PATH = "/errorRecovery/settings" @@ -62,5 +63,7 @@ async def _get_current_response( ) -> PydanticResponse[SimpleBody[ResponseData]]: is_enabled = store.get_is_enabled() return await PydanticResponse.create( - SimpleBody.construct(data=ResponseData.construct(enabled=is_enabled)) + SimpleBody.model_construct( + data=ResponseData.model_construct(enabled=is_enabled) + ) ) diff --git a/robot-server/robot_server/errors/error_responses.py b/robot-server/robot_server/errors/error_responses.py index 410fa9d46ab..82752660692 100644 --- a/robot-server/robot_server/errors/error_responses.py +++ b/robot-server/robot_server/errors/error_responses.py @@ -1,6 +1,5 @@ """JSON API errors and response models.""" from pydantic import BaseModel, Field -from pydantic.generics import GenericModel from typing import Any, Dict, Generic, Optional, Sequence, TypeVar, Type from robot_server.service.json_api import BaseResponseBody, ResourceLinks @@ -26,27 +25,24 @@ class BaseErrorBody(BaseResponseBody): def as_error(self, status_code: int) -> ApiError: """Serialize the response as an API error to raise in a handler.""" - return ApiError( - status_code=status_code, - content=self.dict(), - ) + return ApiError(status_code=status_code, content=self.model_dump()) class ErrorSource(BaseModel): """An object containing references to the source of the error.""" pointer: Optional[str] = Field( - None, + default=None, description=( "A JSON Pointer [RFC6901] to the associated entity in the request document." ), ) parameter: Optional[str] = Field( - None, + default=None, description="a string indicating which URI query parameter caused the error.", ) header: Optional[str] = Field( - None, + default=None, description="A string indicating which header caused the error.", ) @@ -96,18 +92,18 @@ def get_some_model(): ), ) source: Optional[ErrorSource] = Field( - None, + default=None, description="An object containing references to the source of the error.", ) meta: Optional[Dict[str, Any]] = Field( - None, + default=None, description=( "An object containing non-standard information about this " "occurrence of the error" ), ) errorCode: str = Field( - ErrorCodes.GENERAL_ERROR.value.code, + default=ErrorCodes.GENERAL_ERROR.value.code, description=("The Opentrons error code associated with the error"), ) @@ -175,12 +171,12 @@ def from_exc( ) -class ErrorBody(BaseErrorBody, GenericModel, Generic[ErrorDetailsT]): +class ErrorBody(BaseErrorBody, Generic[ErrorDetailsT]): """A response body for a single error.""" errors: Sequence[ErrorDetailsT] = Field(..., description="Error details.") links: Optional[ResourceLinks] = Field( - None, + default=None, description=( "Links that leads to further details about " "this particular occurrence of the problem." @@ -188,12 +184,12 @@ class ErrorBody(BaseErrorBody, GenericModel, Generic[ErrorDetailsT]): ) -class MultiErrorResponse(BaseErrorBody, GenericModel, Generic[ErrorDetailsT]): +class MultiErrorResponse(BaseErrorBody, Generic[ErrorDetailsT]): """An response body for multiple errors.""" errors: Sequence[ErrorDetailsT] = Field(..., description="Error details.") links: Optional[ResourceLinks] = Field( - None, + default=None, description=( "Links that leads to further details about " "this particular occurrence of the problem." diff --git a/robot-server/robot_server/errors/global_errors.py b/robot-server/robot_server/errors/global_errors.py index 73e460854ba..5f7d73eb234 100644 --- a/robot-server/robot_server/errors/global_errors.py +++ b/robot-server/robot_server/errors/global_errors.py @@ -53,7 +53,7 @@ def from_exc( ) -> "FirmwareUpdateRequired": """Build a FirmwareUpdateRequired from a specific exception. Preserves metadata.""" parent_inst = ErrorDetails.from_exc(exc, **supplemental_kwargs) - inst = FirmwareUpdateRequired(**parent_inst.dict()) + inst = FirmwareUpdateRequired(**parent_inst.model_dump()) if not inst.meta: inst.meta = {"update_url": "/subsystems/update"} else: diff --git a/robot-server/robot_server/health/models.py b/robot-server/robot_server/health/models.py index ce8a0c2a56f..c0b01385402 100644 --- a/robot-server/robot_server/health/models.py +++ b/robot-server/robot_server/health/models.py @@ -16,7 +16,7 @@ class HealthLinks(BaseModel): " or refer to the OpenAPI specification of the `/logs` endpoint, instead." ), examples=["/logs/api.log"], - deprecated=True, + json_schema_extra={"deprecated": True}, ) serialLog: str = Field( ..., @@ -26,7 +26,7 @@ class HealthLinks(BaseModel): " or refer to the OpenAPI specification of the `/logs` endpoint, instead." ), examples=["/logs/serial.log"], - deprecated=True, + json_schema_extra={"deprecated": True}, ) serverLog: str = Field( ..., @@ -36,10 +36,10 @@ class HealthLinks(BaseModel): " or refer to the OpenAPI specification of the `/logs` endpoint, instead." ), examples=["/logs/server.log"], - deprecated=True, + json_schema_extra={"deprecated": True}, ) oddLog: typing.Optional[str] = Field( - None, + default=None, description=( "The path to the on-device display app logs endpoint" " (only present on the Opentrons Flex)." @@ -47,7 +47,7 @@ class HealthLinks(BaseModel): " or refer to the OpenAPI specification of the `/logs` endpoint, instead." ), examples=["/logs/touchscreen.log"], - deprecated=True, + json_schema_extra={"deprecated": True}, ) apiSpec: str = Field( ..., @@ -103,16 +103,16 @@ class Health(BaseResponseBody): ..., description="The system's maximum supported Protocol API version, " "in the format `[major_version, minor_version]`", - min_items=2, - max_items=2, + min_length=2, + max_length=2, examples=[[2, 8]], ) minimum_protocol_api_version: typing.List[int] = Field( ..., description="The system's minimum supported Protocol API version, " "in the format `[major_version, minor_version]`", - min_items=2, - max_items=2, + min_length=2, + max_length=2, examples=[[2, 0]], ) robot_serial: typing.Optional[str] = Field( diff --git a/robot-server/robot_server/health/router.py b/robot-server/robot_server/health/router.py index a4ca84bd2c1..20f02f0803b 100644 --- a/robot-server/robot_server/health/router.py +++ b/robot-server/robot_server/health/router.py @@ -122,6 +122,10 @@ def _system_version_or_fallback() -> str: "description": "Robot motor controller is not ready", } }, + # response_model_exclude_none=True preserves behavior from older FastAPI and/or + # Pydantic versions. It's unclear exactly what changed and why this is only + # necessary for this endpoint in particular. + response_model_exclude_none=True, ) async def get_health( hardware: Annotated[HardwareControlAPI, Depends(get_hardware)], diff --git a/robot-server/robot_server/instruments/instrument_models.py b/robot-server/robot_server/instruments/instrument_models.py index f6a8ed0f82f..544ff774e54 100644 --- a/robot-server/robot_server/instruments/instrument_models.py +++ b/robot-server/robot_server/instruments/instrument_models.py @@ -5,7 +5,6 @@ from typing import Optional, TypeVar, Union, Generic, Dict, List from datetime import datetime from pydantic import BaseModel, Field -from pydantic.generics import GenericModel from opentrons.calibration_storage.types import SourceType @@ -41,7 +40,7 @@ class InconsistentCalibrationFailure(BaseModel): limit: float -class _GenericInstrument(GenericModel, Generic[InstrumentModelT, InstrumentDataT]): +class _GenericInstrument(BaseModel, Generic[InstrumentModelT, InstrumentDataT]): """Base instrument response.""" mount: str = Field(..., description="The mount this instrument is attached to.") @@ -51,14 +50,15 @@ class _GenericInstrument(GenericModel, Generic[InstrumentModelT, InstrumentDataT instrumentModel: InstrumentModelT = Field(..., description="Instrument model.") serialNumber: str = Field(..., description="Instrument hardware serial number.") subsystem: Optional[SubSystem] = Field( - None, + default=None, description="The subsystem corresponding to this instrument.", ) ok: Literal[True] = Field( ..., description="Whether this instrument is OK and ready to go" ) firmwareVersion: Optional[str] = Field( - None, description="The firmware version of this instrument (if applicable)" + default=None, + description="The firmware version of this instrument (if applicable)", ) data: InstrumentDataT @@ -77,7 +77,7 @@ class GripperData(BaseModel): jawState: str = Field(..., description="Gripper Jaw state.") calibratedOffset: Optional[InstrumentCalibrationData] = Field( - None, description="Calibrated gripper offset." + default=None, description="Calibrated gripper offset." ) @@ -88,7 +88,7 @@ class PipetteData(BaseModel): min_volume: float = Field(..., description="Minimum pipette volume.") max_volume: float = Field(..., description="Maximum pipette volume.") calibratedOffset: Optional[InstrumentCalibrationData] = Field( - None, description="Calibrated pipette offset." + default=None, description="Calibrated pipette offset." ) @@ -109,7 +109,7 @@ class Pipette(_GenericInstrument[PipetteModel, PipetteData]): instrumentName: PipetteName instrumentModel: PipetteModel data: PipetteData - state: Optional[PipetteState] + state: Optional[PipetteState] = None class Gripper(_GenericInstrument[GripperModelStr, GripperData]): diff --git a/robot-server/robot_server/instruments/router.py b/robot-server/robot_server/instruments/router.py index a9a3e3bbed3..d0cc4a212ed 100644 --- a/robot-server/robot_server/instruments/router.py +++ b/robot-server/robot_server/instruments/router.py @@ -1,7 +1,8 @@ """Instruments routes.""" from typing import Annotated, Optional, Dict, List, cast -from fastapi import APIRouter, status, Depends +from fastapi import status, Depends +from server_utils.fastapi_utils.light_router import LightRouter from opentrons.hardware_control.instruments.ot3.instrument_calibration import ( PipetteOffsetSummary, @@ -50,7 +51,7 @@ from opentrons.hardware_control import OT3HardwareControlAPI -instruments_router = APIRouter() +instruments_router = LightRouter() def _pipette_dict_to_pipette_res( @@ -63,7 +64,7 @@ def _pipette_dict_to_pipette_res( """Convert PipetteDict to Pipette response model.""" if pipette_dict: calibration_data = pipette_offset - return Pipette.construct( + return Pipette.model_construct( firmwareVersion=str(fw_version) if fw_version else None, ok=True, mount=MountType.from_hw_mount(mount).value, @@ -75,7 +76,7 @@ def _pipette_dict_to_pipette_res( channels=pipette_dict["channels"], min_volume=pipette_dict["min_volume"], max_volume=pipette_dict["max_volume"], - calibratedOffset=InstrumentCalibrationData.construct( + calibratedOffset=InstrumentCalibrationData.model_construct( offset=Vec3f( x=calibration_data.offset.x, y=calibration_data.offset.y, @@ -84,9 +85,9 @@ def _pipette_dict_to_pipette_res( source=calibration_data.source, last_modified=calibration_data.last_modified, reasonability_check_failures=[ - InconsistentCalibrationFailure.construct( + InconsistentCalibrationFailure.model_construct( offsets={ - k.name: Vec3f.construct(x=v.x, y=v.y, z=v.z) + k.name: Vec3f.model_construct(x=v.x, y=v.y, z=v.z) for k, v in failure.offsets.items() }, limit=failure.limit, @@ -97,7 +98,7 @@ def _pipette_dict_to_pipette_res( if calibration_data else None, ), - state=PipetteState.parse_obj(pipette_state) if pipette_state else None, + state=PipetteState.model_validate(pipette_state) if pipette_state else None, ) @@ -106,7 +107,7 @@ def _gripper_dict_to_gripper_res( ) -> Gripper: """Convert GripperDict to Gripper response model.""" calibration_data = gripper_dict["calibration_offset"] - return Gripper.construct( + return Gripper.model_construct( firmwareVersion=str(fw_version) if fw_version else None, ok=True, mount=MountType.EXTENSION.value, @@ -115,7 +116,7 @@ def _gripper_dict_to_gripper_res( subsystem=SubSystem.from_hw(HWSubSystem.of_mount(OT3Mount.GRIPPER)), data=GripperData( jawState=gripper_dict["state"].name.lower(), - calibratedOffset=InstrumentCalibrationData.construct( + calibratedOffset=InstrumentCalibrationData.model_construct( offset=Vec3f( x=calibration_data.offset.x, y=calibration_data.offset.y, @@ -219,7 +220,7 @@ async def _get_attached_instruments_ot3( await hardware.cache_instruments(skip_if_would_block=True) response_data = await _get_instrument_data(hardware) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=response_data, meta=MultiBodyMeta(cursor=0, totalLength=len(response_data)), ), @@ -243,7 +244,7 @@ async def _get_attached_instruments_ot2( if pipette_dict ] return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=response_data, meta=MultiBodyMeta(cursor=0, totalLength=len(response_data)), ), diff --git a/robot-server/robot_server/labware_offsets/models.py b/robot-server/robot_server/labware_offsets/models.py index 8c6dd5760f6..fcc3d2f2200 100644 --- a/robot-server/robot_server/labware_offsets/models.py +++ b/robot-server/robot_server/labware_offsets/models.py @@ -1,8 +1,7 @@ """Request/response models for the `/labwareOffsets` endpoints.""" -from typing import Literal, Type -from typing_extensions import Self +from typing import Literal from robot_server.errors.error_responses import ErrorDetails @@ -14,6 +13,6 @@ class LabwareOffsetNotFound(ErrorDetails): title: str = "Labware Offset Not Found" @classmethod - def build(cls: Type[Self], bad_offset_id: str) -> Self: + def build(cls, bad_offset_id: str) -> "LabwareOffsetNotFound": """Return an error with a standard message.""" - return cls.construct(detail=f'No offset found with ID "{bad_offset_id}".') + return cls.model_construct(detail=f'No offset found with ID "{bad_offset_id}".') diff --git a/robot-server/robot_server/labware_offsets/router.py b/robot-server/robot_server/labware_offsets/router.py index fb017fc1457..6aca03e4b18 100644 --- a/robot-server/robot_server/labware_offsets/router.py +++ b/robot-server/robot_server/labware_offsets/router.py @@ -6,6 +6,8 @@ from typing import Annotated, Literal import fastapi +from server_utils.fastapi_utils.light_router import LightRouter + from opentrons.protocol_engine import LabwareOffset, LabwareOffsetCreate, ModuleModel from opentrons.types import DeckSlotName @@ -24,12 +26,12 @@ from .fastapi_dependencies import get_labware_offset_store -router = fastapi.APIRouter(prefix="/labwareOffsets") +router = LightRouter() @PydanticResponse.wrap_route( router.post, - path="", + path="/labwareOffsets", summary="Store a labware offset", description=textwrap.dedent( """\ @@ -47,7 +49,7 @@ async def post_labware_offset( # noqa: D103 new_offset_created_at: Annotated[datetime, fastapi.Depends(get_current_time)], request_body: Annotated[RequestModel[LabwareOffsetCreate], fastapi.Body()], ) -> PydanticResponse[SimpleBody[LabwareOffset]]: - new_offset = LabwareOffset.construct( + new_offset = LabwareOffset.model_construct( id=new_offset_id, createdAt=new_offset_created_at, definitionUri=request_body.data.definitionUri, @@ -56,14 +58,14 @@ async def post_labware_offset( # noqa: D103 ) store.add(new_offset) return await PydanticResponse.create( - content=SimpleBody.construct(data=new_offset), + content=SimpleBody.model_construct(data=new_offset), status_code=201, ) @PydanticResponse.wrap_route( router.get, - path="", + path="/labwareOffsets", summary="Search for labware offsets", description=( "Get a filtered list of all the labware offsets currently stored on the robot." @@ -142,14 +144,14 @@ async def get_labware_offsets( # noqa: D103 location_module_model_filter=location_module_model, ) - meta = MultiBodyMeta.construct( + meta = MultiBodyMeta.model_construct( # todo(mm, 2024-12-06): Update this when pagination is supported. cursor=0, totalLength=len(result_data), ) return await PydanticResponse.create( - SimpleMultiBody[LabwareOffset].construct( + SimpleMultiBody[LabwareOffset].model_construct( data=result_data, meta=meta, ) @@ -158,7 +160,7 @@ async def get_labware_offsets( # noqa: D103 @PydanticResponse.wrap_route( router.delete, - path="/{id}", + path="/labwareOffsets/{id}", summary="Delete a single labware offset", description="Delete a single labware offset. The deleted offset is returned.", ) @@ -174,16 +176,18 @@ async def delete_labware_offset( # noqa: D103 except LabwareOffsetNotFoundError as e: raise LabwareOffsetNotFound.build(bad_offset_id=e.bad_offset_id).as_error(404) else: - return await PydanticResponse.create(SimpleBody.construct(data=deleted_offset)) + return await PydanticResponse.create( + SimpleBody.model_construct(data=deleted_offset) + ) @PydanticResponse.wrap_route( router.delete, - path="", + path="/labwareOffsets", summary="Delete all labware offsets", ) async def delete_all_labware_offsets( # noqa: D103 store: Annotated[LabwareOffsetStore, fastapi.Depends(get_labware_offset_store)] ) -> PydanticResponse[SimpleEmptyBody]: store.delete_all() - return await PydanticResponse.create(SimpleEmptyBody.construct()) + return await PydanticResponse.create(SimpleEmptyBody.model_construct()) diff --git a/robot-server/robot_server/maintenance_runs/maintenance_run_data_manager.py b/robot-server/robot_server/maintenance_runs/maintenance_run_data_manager.py index c3346e33351..dfc76945f81 100644 --- a/robot-server/robot_server/maintenance_runs/maintenance_run_data_manager.py +++ b/robot-server/robot_server/maintenance_runs/maintenance_run_data_manager.py @@ -24,7 +24,7 @@ def _build_run( created_at: datetime, state_summary: Optional[StateSummary], ) -> MaintenanceRun: - state_summary = state_summary or StateSummary.construct( + state_summary = state_summary or StateSummary.model_construct( status=EngineStatus.IDLE, errors=[], labware=[], @@ -37,7 +37,7 @@ def _build_run( liquidClasses=[], hasEverEnteredErrorRecovery=False, ) - return MaintenanceRun.construct( + return MaintenanceRun.model_construct( id=run_id, createdAt=created_at, status=state_summary.status, diff --git a/robot-server/robot_server/maintenance_runs/router/__init__.py b/robot-server/robot_server/maintenance_runs/router/__init__.py index 11cdd30950c..b017a0a6adf 100644 --- a/robot-server/robot_server/maintenance_runs/router/__init__.py +++ b/robot-server/robot_server/maintenance_runs/router/__init__.py @@ -1,12 +1,12 @@ """Maintenance Runs router.""" -from fastapi import APIRouter +from server_utils.fastapi_utils.light_router import LightRouter from .base_router import base_router from .commands_router import commands_router from .labware_router import labware_router -maintenance_runs_router = APIRouter() +maintenance_runs_router = LightRouter() maintenance_runs_router.include_router(base_router) maintenance_runs_router.include_router(commands_router) diff --git a/robot-server/robot_server/maintenance_runs/router/base_router.py b/robot-server/robot_server/maintenance_runs/router/base_router.py index 0e9abc62553..3dd2060c2cd 100644 --- a/robot-server/robot_server/maintenance_runs/router/base_router.py +++ b/robot-server/robot_server/maintenance_runs/router/base_router.py @@ -8,8 +8,9 @@ from typing import Annotated, Optional, Callable from typing_extensions import Literal -from fastapi import APIRouter, Depends, status +from fastapi import Depends, status from pydantic import BaseModel, Field +from server_utils.fastapi_utils.light_router import LightRouter from robot_server.errors.error_responses import ErrorDetails, ErrorBody from robot_server.service.dependencies import get_current_time, get_unique_id @@ -42,7 +43,7 @@ from robot_server.service.notifications import get_pe_notify_publishers log = logging.getLogger(__name__) -base_router = APIRouter() +base_router = LightRouter() # TODO (spp, 2023-04-10): move all error types from maintenance & regular runs @@ -188,7 +189,7 @@ async def create_run( log.info(f'Created an empty run "{run_id}"".') return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_201_CREATED, ) @@ -221,11 +222,11 @@ async def get_current_run( data = run_data_manager.get(current_run_id) links = AllRunsLinks( - current=ResourceLink.construct(href=f"/maintenance_runs/{current_run_id}") + current=ResourceLink.model_construct(href=f"/maintenance_runs/{current_run_id}") ) return await PydanticResponse.create( - content=Body.construct(data=data, links=links), + content=Body.model_construct(data=data, links=links), status_code=status.HTTP_200_OK, ) @@ -249,7 +250,7 @@ async def get_run( run_data: Data of the run specified in the runId url parameter. """ return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_200_OK, ) @@ -285,6 +286,6 @@ async def remove_run( raise RunNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleEmptyBody.construct(), + content=SimpleEmptyBody.model_construct(), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/maintenance_runs/router/commands_router.py b/robot-server/robot_server/maintenance_runs/router/commands_router.py index afc5e03779b..0bb3d64c192 100644 --- a/robot-server/robot_server/maintenance_runs/router/commands_router.py +++ b/robot-server/robot_server/maintenance_runs/router/commands_router.py @@ -3,7 +3,8 @@ from typing import Annotated, Optional, Union from typing_extensions import Final, Literal -from fastapi import APIRouter, Depends, Query, status +from fastapi import Depends, Query, status +from server_utils.fastapi_utils.light_router import LightRouter from opentrons.protocol_engine import ( CommandPointer, @@ -17,10 +18,10 @@ MultiBody, MultiBodyMeta, PydanticResponse, + RequestModel, ) from robot_server.robot.control.dependencies import require_estop_in_good_state from robot_server.runs.command_models import ( - RequestModelWithCommandCreate, CommandCollectionLinks, CommandLink, CommandLinkMeta, @@ -39,7 +40,7 @@ _DEFAULT_COMMAND_LIST_LENGTH: Final = 20 -commands_router = APIRouter() +commands_router = LightRouter() class CommandNotFound(ErrorDetails): @@ -99,7 +100,7 @@ async def get_current_run_from_url( }, ) async def create_run_command( - request_body: RequestModelWithCommandCreate, + request_body: RequestModel[pe_commands.CommandCreate], run_orchestrator_store: Annotated[ MaintenanceRunOrchestratorStore, Depends(get_maintenance_run_orchestrator_store) ], @@ -155,7 +156,7 @@ async def create_run_command( # TODO(mc, 2022-05-26): increment the HTTP API version so that default # behavior is to pass through `command_intent` without overriding it command_intent = pe_commands.CommandIntent.SETUP - command_create = request_body.data.copy(update={"intent": command_intent}) + command_create = request_body.data.model_copy(update={"intent": command_intent}) command = await run_orchestrator_store.add_command_and_wait_for_interval( request=command_create, wait_until_complete=waitUntilComplete, timeout=timeout ) @@ -163,7 +164,7 @@ async def create_run_command( response_data = run_orchestrator_store.get_command(command.id) return await PydanticResponse.create( - content=SimpleBody.construct(data=response_data), + content=SimpleBody.model_construct(data=response_data), status_code=status.HTTP_201_CREATED, ) @@ -228,7 +229,7 @@ async def get_run_commands( recovery_target_command = run_data_manager.get_recovery_target_command(run_id=runId) data = [ - RunCommandSummary.construct( + RunCommandSummary.model_construct( id=c.id, key=c.key, commandType=c.commandType, @@ -249,13 +250,13 @@ async def get_run_commands( totalLength=command_slice.total_length, ) - links = CommandCollectionLinks.construct( + links = CommandCollectionLinks.model_construct( current=_make_command_link(runId, current_command), currentlyRecoveringFrom=_make_command_link(runId, recovery_target_command), ) return await PydanticResponse.create( - content=MultiBody.construct(data=data, meta=meta, links=links), + content=MultiBody.model_construct(data=data, meta=meta, links=links), status_code=status.HTTP_200_OK, ) @@ -297,7 +298,7 @@ async def get_run_command( raise CommandNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=command), + content=SimpleBody.model_construct(data=command), status_code=status.HTTP_200_OK, ) @@ -306,7 +307,7 @@ def _make_command_link( run_id: str, command_pointer: Optional[CommandPointer] ) -> Optional[CommandLink]: return ( - CommandLink.construct( + CommandLink.model_construct( href=f"/maintenance_runs/{run_id}/commands/{command_pointer.command_id}", meta=CommandLinkMeta( runId=run_id, diff --git a/robot-server/robot_server/maintenance_runs/router/labware_router.py b/robot-server/robot_server/maintenance_runs/router/labware_router.py index 72fc09d911a..100fafe910a 100644 --- a/robot-server/robot_server/maintenance_runs/router/labware_router.py +++ b/robot-server/robot_server/maintenance_runs/router/labware_router.py @@ -1,7 +1,9 @@ """Router for /maintenance_runs endpoints dealing with labware offsets and definitions.""" from typing import Annotated import logging -from fastapi import APIRouter, Depends, status + +from fastapi import Depends, status +from server_utils.fastapi_utils.light_router import LightRouter from opentrons.protocol_engine import LabwareOffsetCreate, LabwareOffset from opentrons.protocols.models import LabwareDefinition @@ -15,7 +17,7 @@ from .base_router import RunNotFound, RunNotIdle, get_run_data_from_url log = logging.getLogger(__name__) -labware_router = APIRouter() +labware_router = LightRouter() @PydanticResponse.wrap_route( @@ -54,7 +56,7 @@ async def add_labware_offset( log.info(f'Added labware offset "{added_offset.id}"' f' to run "{run.id}".') return await PydanticResponse.create( - content=SimpleBody.construct(data=added_offset), + content=SimpleBody.model_construct(data=added_offset), status_code=status.HTTP_201_CREATED, ) @@ -93,8 +95,8 @@ async def add_labware_definition( log.info(f'Added labware definition "{uri}"' f' to run "{run.id}".') return PydanticResponse( - content=SimpleBody.construct( - data=LabwareDefinitionSummary.construct(definitionUri=uri) + content=SimpleBody.model_construct( + data=LabwareDefinitionSummary.model_construct(definitionUri=uri) ), status_code=status.HTTP_201_CREATED, ) diff --git a/robot-server/robot_server/modules/module_models.py b/robot-server/robot_server/modules/module_models.py index f9acaf59a8b..7b6da4925ac 100644 --- a/robot-server/robot_server/modules/module_models.py +++ b/robot-server/robot_server/modules/module_models.py @@ -1,7 +1,6 @@ """Request and response models for /modules endpoints.""" from datetime import datetime from pydantic import BaseModel, Field -from pydantic.generics import GenericModel from typing import Generic, List, Optional, TypeVar, Union from typing_extensions import Literal @@ -72,7 +71,7 @@ class UsbPort(BaseModel): ) -class _GenericModule(GenericModel, Generic[ModuleT, ModuleModelT, ModuleDataT]): +class _GenericModule(BaseModel, Generic[ModuleT, ModuleModelT, ModuleDataT]): """Base module response.""" id: str = Field( diff --git a/robot-server/robot_server/modules/router.py b/robot-server/robot_server/modules/router.py index 1f630d9bdb6..8bbf8a6ae7a 100644 --- a/robot-server/robot_server/modules/router.py +++ b/robot-server/robot_server/modules/router.py @@ -1,7 +1,9 @@ """Modules routes.""" -from fastapi import APIRouter, Depends, status from typing import Annotated, List, Dict +from fastapi import Depends, status +from server_utils.fastapi_utils.light_router import LightRouter + from opentrons.hardware_control import HardwareControlAPI from opentrons.hardware_control.modules import module_calibration from opentrons.protocol_engine.types import Vec3f @@ -21,7 +23,7 @@ from .module_identifier import ModuleIdentifier from .module_data_mapper import ModuleDataMapper -modules_router = APIRouter() +modules_router = LightRouter() @PydanticResponse.wrap_route( @@ -67,7 +69,7 @@ async def get_attached_modules( module_identity=module_identity, live_data=mod.live_data, usb_port=mod.usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f( x=calibrated.offset.x, y=calibrated.offset.y, @@ -83,7 +85,7 @@ async def get_attached_modules( ) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=response_data, meta=MultiBodyMeta(cursor=0, totalLength=len(response_data)), ), diff --git a/robot-server/robot_server/persistence/_migrations/_up_to_3_worker.py b/robot-server/robot_server/persistence/_migrations/_up_to_3_worker.py index 7e94476d87a..eb916a0a6f7 100644 --- a/robot-server/robot_server/persistence/_migrations/_up_to_3_worker.py +++ b/robot-server/robot_server/persistence/_migrations/_up_to_3_worker.py @@ -86,11 +86,7 @@ def migrate_commands_for_run( ) parsed_commands: typing.Iterable[commands.Command] = ( - pydantic.parse_obj_as( - commands.Command, # type: ignore[arg-type] - c, - ) - for c in old_commands + commands.CommandAdapter.validate_python(c) for c in old_commands ) new_command_rows = [ diff --git a/robot-server/robot_server/persistence/_migrations/up_to_2.py b/robot-server/robot_server/persistence/_migrations/up_to_2.py index 69e9cc57875..2e6ba069b36 100644 --- a/robot-server/robot_server/persistence/_migrations/up_to_2.py +++ b/robot-server/robot_server/persistence/_migrations/up_to_2.py @@ -215,11 +215,11 @@ def _migrate_data_1_to_2(transaction: sqlalchemy.engine.Connection) -> None: f"Migrating analysis {index+1}/{len(rows_needing_migration)}, {row.id}..." ) - v1_completed_analysis = CompletedAnalysis.parse_obj( + v1_completed_analysis = CompletedAnalysis.model_validate( _legacy_pickle.loads(row.completed_analysis) ) - v2_completed_analysis_as_document = v1_completed_analysis.json( + v2_completed_analysis_as_document = v1_completed_analysis.model_dump_json( # by_alias and exclude_none should match how # FastAPI + Pydantic + our customizations serialize these objects # over the `GET /protocols/:id/analyses/:id` endpoint. diff --git a/robot-server/robot_server/persistence/_migrations/up_to_3.py b/robot-server/robot_server/persistence/_migrations/up_to_3.py index 73053d10391..b4382545dbf 100644 --- a/robot-server/robot_server/persistence/_migrations/up_to_3.py +++ b/robot-server/robot_server/persistence/_migrations/up_to_3.py @@ -23,7 +23,6 @@ from typing import List from opentrons.protocol_engine import StateSummary -import pydantic import sqlalchemy from ..pydantic import pydantic_to_json @@ -138,9 +137,7 @@ def _migrate_run_table_excluding_commands( new_state_summary = ( None if old_row.state_summary is None - else pydantic_to_json( - pydantic.parse_obj_as(StateSummary, old_state_summary) - ) + else pydantic_to_json(StateSummary.model_validate(old_state_summary)) ) dest_transaction.execute( insert_new_run, diff --git a/robot-server/robot_server/persistence/pydantic.py b/robot-server/robot_server/persistence/pydantic.py index c56312ec166..a8eb14b0916 100644 --- a/robot-server/robot_server/persistence/pydantic.py +++ b/robot-server/robot_server/persistence/pydantic.py @@ -1,16 +1,17 @@ """Store Pydantic objects in the SQL database.""" import json -from typing import Type, TypeVar, List, Sequence -from pydantic import BaseModel, parse_raw_as, parse_obj_as +from typing import Type, TypeVar, Sequence, overload +from pydantic import BaseModel, TypeAdapter _BaseModelT = TypeVar("_BaseModelT", bound=BaseModel) +_TypeAdapterArgT = TypeVar("_TypeAdapterArgT") def pydantic_to_json(obj: BaseModel) -> str: """Serialize a Pydantic object for storing in the SQL database.""" - return obj.json( + return obj.model_dump_json( # by_alias and exclude_none should match how # FastAPI + Pydantic + our customizations serialize these objects by_alias=True, @@ -20,14 +21,28 @@ def pydantic_to_json(obj: BaseModel) -> str: def pydantic_list_to_json(obj_list: Sequence[BaseModel]) -> str: """Serialize a list of Pydantic objects for storing in the SQL database.""" - return json.dumps([obj.dict(by_alias=True, exclude_none=True) for obj in obj_list]) + return json.dumps( + [obj.model_dump(by_alias=True, exclude_none=True) for obj in obj_list] + ) +@overload def json_to_pydantic(model: Type[_BaseModelT], json_str: str) -> _BaseModelT: - """Parse a Pydantic object stored in the SQL database.""" - return parse_raw_as(model, json_str) + ... + +@overload +def json_to_pydantic( + model: TypeAdapter[_TypeAdapterArgT], json_str: str +) -> _TypeAdapterArgT: + ... -def json_to_pydantic_list(model: Type[_BaseModelT], json_str: str) -> List[_BaseModelT]: - """Parse a list of Pydantic objects stored in the SQL database.""" - return [parse_obj_as(model, obj_dict) for obj_dict in json.loads(json_str)] + +def json_to_pydantic( + model: Type[_BaseModelT] | TypeAdapter[_TypeAdapterArgT], json_str: str +) -> _BaseModelT | _TypeAdapterArgT: + """Parse a Pydantic object stored in the SQL database.""" + if isinstance(model, TypeAdapter): + return model.validate_json(json_str) + else: + return model.model_validate_json(json_str) diff --git a/robot-server/robot_server/protocols/analysis_store.py b/robot-server/robot_server/protocols/analysis_store.py index be69601466f..d03c8cabb31 100644 --- a/robot-server/robot_server/protocols/analysis_store.py +++ b/robot-server/robot_server/protocols/analysis_store.py @@ -195,7 +195,7 @@ async def update( else: result = AnalysisResult.OK - completed_analysis = CompletedAnalysis.construct( + completed_analysis = CompletedAnalysis.model_construct( id=analysis_id, result=result, robotType=robot_type, @@ -237,7 +237,7 @@ async def save_initialization_failed_analysis( errors: List[ErrorOccurrence], ) -> None: """Commit the failed analysis to store.""" - completed_analysis = CompletedAnalysis.construct( + completed_analysis = CompletedAnalysis.model_construct( id=analysis_id, result=AnalysisResult.NOT_OK, robotType=robot_type, @@ -305,7 +305,9 @@ def get_summaries_by_protocol(self, protocol_id: str) -> List[AnalysisSummary]: protocol_id=protocol_id ) completed_analysis_summaries = [ - AnalysisSummary.construct(id=analysis_id, status=AnalysisStatus.COMPLETED) + AnalysisSummary.model_construct( + id=analysis_id, status=AnalysisStatus.COMPLETED + ) for analysis_id in completed_analysis_ids ] @@ -455,7 +457,7 @@ def add( protocol_id not in self._analysis_ids_by_protocol_id ), "Protocol must not already have a pending analysis." - new_pending_analysis = PendingAnalysis.construct( + new_pending_analysis = PendingAnalysis.model_construct( id=analysis_id, runTimeParameters=run_time_parameters, ) diff --git a/robot-server/robot_server/protocols/protocol_models.py b/robot-server/robot_server/protocols/protocol_models.py index c7841f25776..c20bd1ae5ed 100644 --- a/robot-server/robot_server/protocols/protocol_models.py +++ b/robot-server/robot_server/protocols/protocol_models.py @@ -1,7 +1,7 @@ """Protocol file models.""" from datetime import datetime -from pydantic import BaseModel, Extra, Field +from pydantic import ConfigDict, BaseModel, Field from typing import Any, List, Optional from enum import Enum @@ -49,13 +49,7 @@ class Metadata(BaseModel): this should be considered an exception to the rule. """ - # todo(mm, 2021-09-17): Revise these docs after specifying - # metadata more. github.com/Opentrons/opentrons/issues/8334 - - class Config: - """Tell Pydantic that metadata objects can have arbitrary fields.""" - - extra = Extra.allow + model_config = ConfigDict(extra="allow") class Protocol(ResourceModel): diff --git a/robot-server/robot_server/protocols/router.py b/robot-server/robot_server/protocols/router.py index ff6521b70d6..574f9621004 100644 --- a/robot-server/robot_server/protocols/router.py +++ b/robot-server/robot_server/protocols/router.py @@ -15,7 +15,6 @@ from opentrons.util.performance_helpers import TrackingFunctions from fastapi import ( - APIRouter, Depends, File, HTTPException, @@ -26,6 +25,7 @@ ) from fastapi.responses import PlainTextResponse from pydantic import BaseModel, Field +from server_utils.fastapi_utils.light_router import LightRouter from opentrons.protocol_reader import ( ProtocolReader, @@ -151,7 +151,7 @@ class ProtocolLinks(BaseModel): ) -protocols_router = APIRouter() +protocols_router = LightRouter() @PydanticResponse.wrap_route( @@ -364,13 +364,13 @@ async def _get_cached_protocol_analysis() -> PydanticResponse[ status.HTTP_503_SERVICE_UNAVAILABLE ) from error - data = Protocol.construct( + data = Protocol.model_construct( id=cached_protocol_id, createdAt=resource.created_at, protocolKind=resource.protocol_kind, protocolType=resource.source.config.protocol_type, robotType=resource.source.robot_type, - metadata=Metadata.parse_obj(resource.source.metadata), + metadata=Metadata.model_validate(resource.source.metadata), analysisSummaries=analysis_summaries, key=resource.protocol_key, files=[ @@ -385,7 +385,7 @@ async def _get_cached_protocol_analysis() -> PydanticResponse[ ) return await PydanticResponse.create( - content=SimpleBody.construct(data=data), + content=SimpleBody.model_construct(data=data), # not returning a 201 because we're not actually creating a new resource status_code=status.HTTP_200_OK, ) @@ -443,7 +443,7 @@ async def _get_cached_protocol_analysis() -> PydanticResponse[ protocolKind=protocol_kind, protocolType=source.config.protocol_type, robotType=source.robot_type, - metadata=Metadata.parse_obj(source.metadata), + metadata=Metadata.model_validate(source.metadata), analysisSummaries=analysis_summaries, key=key, files=[ProtocolFile(name=f.path.name, role=f.role) for f in source.files], @@ -452,7 +452,7 @@ async def _get_cached_protocol_analysis() -> PydanticResponse[ log.info(f'Created protocol "{protocol_id}" and started analysis "{analysis_id}".') return await PydanticResponse.create( - content=SimpleBody.construct(data=data), + content=SimpleBody.model_construct(data=data), status_code=status.HTTP_201_CREATED, ) @@ -549,13 +549,13 @@ async def get_protocols( """ protocol_resources = protocol_store.get_all() data = [ - Protocol.construct( + Protocol.model_construct( id=r.protocol_id, createdAt=r.created_at, protocolKind=r.protocol_kind, protocolType=r.source.config.protocol_type, robotType=r.source.robot_type, - metadata=Metadata.parse_obj(r.source.metadata), + metadata=Metadata.model_validate(r.source.metadata), analysisSummaries=analysis_store.get_summaries_by_protocol(r.protocol_id), key=r.protocol_key, files=[ProtocolFile(name=f.path.name, role=f.role) for f in r.source.files], @@ -566,7 +566,7 @@ async def get_protocols( meta = MultiBodyMeta(cursor=0, totalLength=len(data)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=data, meta=meta), + content=SimpleMultiBody.model_construct(data=data, meta=meta), status_code=status.HTTP_200_OK, ) @@ -597,7 +597,7 @@ async def get_protocol_ids( meta = MultiBodyMeta(cursor=0, totalLength=len(protocol_ids)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=protocol_ids, meta=meta) + content=SimpleMultiBody.model_construct(data=protocol_ids, meta=meta) ) @@ -630,13 +630,13 @@ async def get_protocol_by_id( analyses = analysis_store.get_summaries_by_protocol(protocol_id=protocolId) referencing_run_ids = protocol_store.get_referencing_run_ids(protocolId) - data = Protocol.construct( + data = Protocol.model_construct( id=protocolId, createdAt=resource.created_at, protocolKind=resource.protocol_kind, protocolType=resource.source.config.protocol_type, robotType=resource.source.robot_type, - metadata=Metadata.parse_obj(resource.source.metadata), + metadata=Metadata.model_validate(resource.source.metadata), analysisSummaries=analyses, key=resource.protocol_key, files=[ @@ -644,15 +644,15 @@ async def get_protocol_by_id( ], ) - links = ProtocolLinks.construct( + links = ProtocolLinks.model_construct( referencingRuns=[ - RunLink.construct(id=run_id, href=f"/runs/{run_id}") + RunLink.model_construct(id=run_id, href=f"/runs/{run_id}") for run_id in referencing_run_ids ] ) return await PydanticResponse.create( - content=Body.construct( + content=Body.model_construct( data=data, links=links, ), @@ -690,7 +690,7 @@ async def delete_protocol_by_id( raise ProtocolUsedByRun(detail=str(e)).as_error(status.HTTP_409_CONFLICT) from e return await PydanticResponse.create( - content=SimpleEmptyBody.construct(), + content=SimpleEmptyBody.model_construct(), status_code=status.HTTP_200_OK, ) @@ -772,7 +772,7 @@ async def create_protocol_analysis( status.HTTP_503_SERVICE_UNAVAILABLE ) from error return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=analysis_summaries, meta=MultiBodyMeta(cursor=0, totalLength=len(analysis_summaries)), ), @@ -813,7 +813,7 @@ async def get_protocol_analyses( analyses = await analysis_store.get_by_protocol(protocolId) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=analyses, meta=MultiBodyMeta(cursor=0, totalLength=len(analyses)), ) @@ -859,7 +859,9 @@ async def get_protocol_analysis_by_id( status.HTTP_404_NOT_FOUND ) from error - return await PydanticResponse.create(content=SimpleBody.construct(data=analysis)) + return await PydanticResponse.create( + content=SimpleBody.model_construct(data=analysis) + ) @protocols_router.get( @@ -951,7 +953,7 @@ async def get_protocol_data_files( data_files = await protocol_store.get_referenced_data_files(protocolId) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=data_files, meta=MultiBodyMeta(cursor=0, totalLength=len(data_files)) ) ) diff --git a/robot-server/robot_server/robot/calibration/check/models.py b/robot-server/robot_server/robot/calibration/check/models.py index 0574a51aced..4b7fbf783a8 100644 --- a/robot-server/robot_server/robot/calibration/check/models.py +++ b/robot-server/robot_server/robot/calibration/check/models.py @@ -1,7 +1,7 @@ from typing import Any, Dict, Optional, List from typing_extensions import Literal from functools import partial -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field from ..helper_classes import RequiredLabware, AttachedPipette @@ -14,8 +14,8 @@ Field, ..., description="An offset vector in deck coordinates (x, y, z)", - min_items=3, - max_items=3, + min_length=3, + max_length=3, ) @@ -75,9 +75,9 @@ class TipComparisonMap(BaseModel): class ComparisonStatePerCalibration(BaseModel): - tipLength: Optional[TipComparisonMap] - pipetteOffset: Optional[PipetteOffsetComparisonMap] - deck: Optional[DeckComparisonMap] + tipLength: Optional[TipComparisonMap] = None + pipetteOffset: Optional[PipetteOffsetComparisonMap] = None + deck: Optional[DeckComparisonMap] = None class ComparisonStatePerPipette(BaseModel): @@ -123,10 +123,9 @@ class CalibrationCheckSessionStatus(BaseModel): supportedCommands: List[str] = Field( ..., description="A list of supported commands for this user flow" ) - - class Config: - arbitrary_types_allowed = True - schema_extra = { + model_config = ConfigDict( + arbitrary_types_allowed=True, + json_schema_extra={ "examples": [ { "instruments": [ @@ -154,4 +153,5 @@ class Config: }, } ] - } + }, + ) diff --git a/robot-server/robot_server/robot/calibration/deck/models.py b/robot-server/robot_server/robot/calibration/deck/models.py index 6a444e31682..7e1f1fa5fee 100644 --- a/robot-server/robot_server/robot/calibration/deck/models.py +++ b/robot-server/robot_server/robot/calibration/deck/models.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field from typing import List from ..helper_classes import AttachedPipette, RequiredLabware @@ -15,9 +15,8 @@ class DeckCalibrationSessionStatus(BaseModel): supportedCommands: List[str] = Field( ..., description="A list of supported commands for this user flow" ) - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "example": [ { "instrument": { @@ -41,3 +40,4 @@ class Config: } ] } + ) diff --git a/robot-server/robot_server/robot/calibration/pipette_offset/models.py b/robot-server/robot_server/robot/calibration/pipette_offset/models.py index d6aa245943f..26d70046d1f 100644 --- a/robot-server/robot_server/robot/calibration/pipette_offset/models.py +++ b/robot-server/robot_server/robot/calibration/pipette_offset/models.py @@ -1,5 +1,5 @@ from typing import Optional, List -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field from ..helper_classes import AttachedPipette, RequiredLabware, NextSteps @@ -23,9 +23,8 @@ class PipetteOffsetCalibrationSessionStatus(BaseModel): nextSteps: Optional[NextSteps] = Field( None, description="Next Available Steps in Session" ) - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "examples": [ { "instrument": { @@ -51,3 +50,4 @@ class Config: } ] } + ) diff --git a/robot-server/robot_server/robot/calibration/tip_length/models.py b/robot-server/robot_server/robot/calibration/tip_length/models.py index 0149b0a65ee..30d698abb5d 100644 --- a/robot-server/robot_server/robot/calibration/tip_length/models.py +++ b/robot-server/robot_server/robot/calibration/tip_length/models.py @@ -1,5 +1,5 @@ from typing import Optional, List -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field from ..helper_classes import AttachedPipette, RequiredLabware, NextSteps @@ -18,9 +18,8 @@ class TipCalibrationSessionStatus(BaseModel): supportedCommands: List[str] = Field( ..., description="A list of supported commands for this user flow" ) - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "examples": [ { "instrument": { @@ -53,3 +52,4 @@ class Config: } ] } + ) diff --git a/robot-server/robot_server/robot/calibration/util.py b/robot-server/robot_server/robot/calibration/util.py index 60192aaa74b..dbb2ea5236b 100644 --- a/robot-server/robot_server/robot/calibration/util.py +++ b/robot-server/robot_server/robot/calibration/util.py @@ -13,7 +13,7 @@ from opentrons.types import Point, Location from robot_server.service.errors import RobotServerError -from ...service.session.models.command_definitions import CommandDefinition +from robot_server.service.session.models.command_definitions import CommandDefinition from .constants import ( STATE_WILDCARD, MOVE_TO_REF_POINT_SAFETY_BUFFER, diff --git a/robot-server/robot_server/robot/control/router.py b/robot-server/robot_server/robot/control/router.py index 35910748115..037ef1dc96a 100644 --- a/robot-server/robot_server/robot/control/router.py +++ b/robot-server/robot_server/robot/control/router.py @@ -1,7 +1,9 @@ """Router for /robot/control endpoints.""" -from fastapi import APIRouter, status, Depends from typing import Annotated, TYPE_CHECKING +from fastapi import status, Depends +from server_utils.fastapi_utils.light_router import LightRouter + from opentrons_shared_data.robot.types import RobotType from opentrons_shared_data.robot.types import RobotTypeEnum from robot_server.hardware import get_robot_type @@ -22,19 +24,19 @@ if TYPE_CHECKING: from opentrons.hardware_control.ot3api import OT3API # noqa: F401 -control_router = APIRouter() +control_router = LightRouter() async def _get_estop_status_response( estop_handler: EstopHandler, ) -> PydanticResponse[SimpleBody[EstopStatusModel]]: """Helper to generate the current Estop Status as a response model.""" - data = EstopStatusModel.construct( + data = EstopStatusModel.model_construct( status=estop_handler.get_state(), leftEstopPhysicalStatus=estop_handler.get_left_physical_status(), rightEstopPhysicalStatus=estop_handler.get_right_physical_status(), ) - return await PydanticResponse.create(content=SimpleBody.construct(data=data)) + return await PydanticResponse.create(content=SimpleBody.model_construct(data=data)) @PydanticResponse.wrap_route( @@ -91,8 +93,8 @@ async def get_door_status( door_required: Annotated[bool, Depends(get_door_switch_required)], ) -> PydanticResponse[SimpleBody[DoorStatusModel]]: return await PydanticResponse.create( - content=SimpleBody.construct( - data=DoorStatusModel.construct( + content=SimpleBody.model_construct( + data=DoorStatusModel.model_construct( status=DoorState.from_hw_physical_status(hardware.door_state), doorRequiredClosedForProtocol=door_required, ) diff --git a/robot-server/robot_server/robot/router.py b/robot-server/robot_server/robot/router.py index 9fff861d4a6..6c6dd6acc05 100644 --- a/robot-server/robot_server/robot/router.py +++ b/robot-server/robot_server/robot/router.py @@ -1,8 +1,8 @@ """Router for /robot endpoints.""" -from fastapi import APIRouter +from server_utils.fastapi_utils.light_router import LightRouter from .control.router import control_router -robot_router = APIRouter() +robot_router = LightRouter() robot_router.include_router(router=control_router) diff --git a/robot-server/robot_server/router.py b/robot-server/robot_server/router.py index 49d835d7eb9..ab5af067637 100644 --- a/robot-server/robot_server/router.py +++ b/robot-server/robot_server/router.py @@ -1,5 +1,7 @@ """Application routes.""" -from fastapi import APIRouter, Depends, status +from fastapi import Depends, status +from server_utils.fastapi_utils.light_router import LightRouter + from .constants import V1_TAG from .errors.error_responses import LegacyErrorResponse @@ -26,24 +28,31 @@ from .subsystems.router import subsystems_router from .system.router import system_router -router = APIRouter() +router = LightRouter() # Legacy routes router.include_router( router=legacy_routes, tags=[V1_TAG], + # todo(mm, 2024-12-19): This `responses` setting is preventing us from + # porting legacy_routes from fastapi.APIRouter to our LightRouter. + # Either teach LightRouter how to handle `responses` or stop doing + # a custom 422 response on these endpoints. responses={ status.HTTP_422_UNPROCESSABLE_ENTITY: { "model": LegacyErrorResponse, } }, ) - router.include_router( router=health_router, tags=["Health", V1_TAG], dependencies=[Depends(check_version_header)], responses={ + # todo(mm, 2024-12-19): This `responses` setting is preventing us from + # porting health_router from fastapi.APIRouter to our LightRouter. + # Either teach LightRouter how to handle `responses` or stop doing + # a custom 422 response on these endpoints. status.HTTP_422_UNPROCESSABLE_ENTITY: { "model": LegacyErrorResponse, } diff --git a/robot-server/robot_server/runs/command_models.py b/robot-server/robot_server/runs/command_models.py index 5da1038f470..dc19b4026c1 100644 --- a/robot-server/robot_server/runs/command_models.py +++ b/robot-server/robot_server/runs/command_models.py @@ -8,21 +8,6 @@ from pydantic import BaseModel, Field -from opentrons.protocol_engine import commands as pe_commands - -from robot_server.service.json_api.request import RequestModel - - -class RequestModelWithCommandCreate(RequestModel[pe_commands.CommandCreate]): - """Equivalent to RequestModel[CommandCreate]. - - This works around a Pydantic v<2 bug where RequestModel[CommandCreate] - doesn't parse using the CommandCreate union discriminator. - https://github.com/pydantic/pydantic/issues/3782 - """ - - data: pe_commands.CommandCreate - class CommandLinkMeta(BaseModel): """Metadata about a command resource referenced in `links`.""" diff --git a/robot-server/robot_server/runs/router/__init__.py b/robot-server/robot_server/runs/router/__init__.py index d7a1640edb8..fa8be0cee67 100644 --- a/robot-server/robot_server/runs/router/__init__.py +++ b/robot-server/robot_server/runs/router/__init__.py @@ -1,5 +1,5 @@ """Runs router.""" -from fastapi import APIRouter +from server_utils.fastapi_utils.light_router import LightRouter from .base_router import base_router from .commands_router import commands_router @@ -7,7 +7,7 @@ from .labware_router import labware_router from .error_recovery_policy_router import error_recovery_policy_router -runs_router = APIRouter() +runs_router = LightRouter() runs_router.include_router(base_router) runs_router.include_router(commands_router) diff --git a/robot-server/robot_server/runs/router/actions_router.py b/robot-server/robot_server/runs/router/actions_router.py index c7c24162cfb..0bfb16185c7 100644 --- a/robot-server/robot_server/runs/router/actions_router.py +++ b/robot-server/robot_server/runs/router/actions_router.py @@ -1,10 +1,12 @@ """Router for /runs actions endpoints.""" import logging -from fastapi import APIRouter, Depends, status from datetime import datetime from typing import Annotated, Literal, Union +from fastapi import Depends, status +from server_utils.fastapi_utils.light_router import LightRouter + from robot_server.errors.error_responses import ErrorDetails, ErrorBody from robot_server.service.dependencies import get_current_time, get_unique_id from robot_server.service.json_api import RequestModel, SimpleBody, PydanticResponse @@ -37,7 +39,7 @@ ) log = logging.getLogger(__name__) -actions_router = APIRouter() +actions_router = LightRouter() class RunActionNotAllowed(ErrorDetails): @@ -155,6 +157,6 @@ async def create_run_action( raise RunNotFound.from_exc(e).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=action), + content=SimpleBody.model_construct(data=action), status_code=status.HTTP_201_CREATED, ) diff --git a/robot-server/robot_server/runs/router/base_router.py b/robot-server/robot_server/runs/router/base_router.py index 4fb4be2b401..8e478c08de9 100644 --- a/robot-server/robot_server/runs/router/base_router.py +++ b/robot-server/robot_server/runs/router/base_router.py @@ -8,8 +8,10 @@ from textwrap import dedent from typing import Annotated, Callable, Final, Literal, Optional, Union -from fastapi import APIRouter, Depends, status, Query +from fastapi import Depends, status, Query from pydantic import BaseModel, Field +from server_utils.fastapi_utils.light_router import LightRouter + from opentrons_shared_data.errors import ErrorCodes from opentrons_shared_data.robot.types import RobotTypeEnum @@ -87,7 +89,7 @@ from robot_server.service.notifications import get_pe_notify_publishers log = logging.getLogger(__name__) -base_router = APIRouter() +base_router = LightRouter() _DEFAULT_COMMAND_ERROR_LIST_LENGTH: Final = 20 @@ -288,7 +290,7 @@ async def create_run( # noqa: C901 log.info(f'Created protocol run "{run_id}" from protocol "{protocol_id}".') return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_201_CREATED, ) @@ -328,13 +330,13 @@ async def get_runs( current_run_id = run_data_manager.current_run_id meta = MultiBodyMeta(cursor=0, totalLength=len(data)) links = AllRunsLinks( - current=ResourceLink.construct(href=f"/runs/{current_run_id}") + current=ResourceLink.model_construct(href=f"/runs/{current_run_id}") if current_run_id is not None else None ) return await PydanticResponse.create( - content=MultiBody.construct(data=data, links=links, meta=meta), + content=MultiBody.model_construct(data=data, links=links, meta=meta), status_code=status.HTTP_200_OK, ) @@ -358,7 +360,7 @@ async def get_run( run_data: Data of the run specified in the runId url parameter. """ return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_200_OK, ) @@ -393,7 +395,7 @@ async def remove_run( raise RunNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleEmptyBody.construct(), + content=SimpleEmptyBody.model_construct(), status_code=status.HTTP_200_OK, ) @@ -433,7 +435,7 @@ async def update_run( raise RunNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_200_OK, ) @@ -509,7 +511,7 @@ async def get_run_commands_error( ) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=command_error_slice.commands_errors, meta=meta, ), @@ -554,7 +556,7 @@ async def get_current_state( # noqa: C901 active_nozzle_maps = run_data_manager.get_nozzle_maps(run_id=runId) nozzle_layouts = { - pipetteId: ActiveNozzleLayout.construct( + pipetteId: ActiveNozzleLayout.model_construct( startingNozzle=nozzle_map.starting_nozzle, activeNozzles=nozzle_map.active_nozzles, config=NozzleLayoutConfig(nozzle_map.configuration.value.lower()), @@ -563,7 +565,7 @@ async def get_current_state( # noqa: C901 } tip_states = { - pipette_id: TipState.construct(hasTip=has_tip) + pipette_id: TipState.model_construct(hasTip=has_tip) for pipette_id, has_tip in run_data_manager.get_tip_attached( run_id=runId ).items() @@ -625,8 +627,8 @@ async def get_current_state( # noqa: C901 break last_completed_command = run_data_manager.get_last_completed_command(run_id=runId) - links = CurrentStateLinks.construct( - lastCompleted=CommandLinkNoMeta.construct( + links = CurrentStateLinks.model_construct( + lastCompleted=CommandLinkNoMeta.model_construct( id=last_completed_command.command_id, href=f"/runs/{runId}/commands/{last_completed_command.command_id}", ) @@ -635,8 +637,8 @@ async def get_current_state( # noqa: C901 ) return await PydanticResponse.create( - content=Body.construct( - data=RunCurrentState.construct( + content=Body.model_construct( + data=RunCurrentState.model_construct( estopEngaged=estop_engaged, activeNozzleLayouts=nozzle_layouts, tipStates=tip_states, diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 577606a1446..b2cbb79b2bb 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -2,7 +2,8 @@ import textwrap from typing import Annotated, Final, Literal, Optional, Union -from fastapi import APIRouter, Depends, Query, status +from fastapi import Depends, Query, status +from server_utils.fastapi_utils.light_router import LightRouter from opentrons.protocol_engine import ( CommandPointer, @@ -17,11 +18,11 @@ MultiBodyMeta, PydanticResponse, SimpleMultiBody, + RequestModel, ) from robot_server.robot.control.dependencies import require_estop_in_good_state from ..command_models import ( - RequestModelWithCommandCreate, CommandCollectionLinks, CommandLink, CommandLinkMeta, @@ -44,7 +45,7 @@ _DEFAULT_COMMAND_LIST_LENGTH: Final = 20 -commands_router = APIRouter() +commands_router = LightRouter() class CommandNotFound(ErrorDetails): @@ -155,7 +156,7 @@ async def get_current_run_from_url( }, ) async def create_run_command( - request_body: RequestModelWithCommandCreate, + request_body: RequestModel[pe_commands.CommandCreate], run_orchestrator_store: Annotated[ RunOrchestratorStore, Depends(get_run_orchestrator_store) ], @@ -221,7 +222,7 @@ async def create_run_command( # TODO(mc, 2022-05-26): increment the HTTP API version so that default # behavior is to pass through `command_intent` without overriding it command_intent = request_body.data.intent or pe_commands.CommandIntent.SETUP - command_create = request_body.data.copy(update={"intent": command_intent}) + command_create = request_body.data.model_copy(update={"intent": command_intent}) try: command = await run_orchestrator_store.add_command_and_wait_for_interval( @@ -241,7 +242,7 @@ async def create_run_command( response_data = run_orchestrator_store.get_command(command.id) return await PydanticResponse.create( - content=SimpleBody.construct(data=response_data), + content=SimpleBody.model_construct(data=response_data), status_code=status.HTTP_201_CREATED, ) @@ -315,7 +316,7 @@ async def get_run_commands( recovery_target_command = run_data_manager.get_recovery_target_command(run_id=runId) data = [ - RunCommandSummary.construct( + RunCommandSummary.model_construct( id=c.id, key=c.key, commandType=c.commandType, @@ -337,13 +338,13 @@ async def get_run_commands( totalLength=command_slice.total_length, ) - links = CommandCollectionLinks.construct( + links = CommandCollectionLinks.model_construct( current=_make_command_link(runId, current_command), currentlyRecoveringFrom=_make_command_link(runId, recovery_target_command), ) return await PydanticResponse.create( - content=MultiBody.construct(data=data, meta=meta, links=links), + content=MultiBody.model_construct(data=data, meta=meta, links=links), status_code=status.HTTP_200_OK, ) @@ -401,7 +402,7 @@ async def get_run_commands_as_pre_serialized_list( status.HTTP_503_SERVICE_UNAVAILABLE ) from e return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=commands, meta=MultiBodyMeta(cursor=0, totalLength=len(commands)) ) ) @@ -442,7 +443,7 @@ async def get_run_command( raise CommandNotFound.from_exc(e).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=command), + content=SimpleBody.model_construct(data=command), status_code=status.HTTP_200_OK, ) @@ -451,7 +452,7 @@ def _make_command_link( run_id: str, command_pointer: Optional[CommandPointer] ) -> Optional[CommandLink]: return ( - CommandLink.construct( + CommandLink.model_construct( href=f"/runs/{run_id}/commands/{command_pointer.command_id}", meta=CommandLinkMeta( runId=run_id, diff --git a/robot-server/robot_server/runs/router/error_recovery_policy_router.py b/robot-server/robot_server/runs/router/error_recovery_policy_router.py index 4653d564244..2b1c786b9d4 100644 --- a/robot-server/robot_server/runs/router/error_recovery_policy_router.py +++ b/robot-server/robot_server/runs/router/error_recovery_policy_router.py @@ -4,7 +4,8 @@ from textwrap import dedent from typing import Annotated -from fastapi import status, APIRouter, Depends +from fastapi import status, Depends +from server_utils.fastapi_utils.light_router import LightRouter from robot_server.errors.error_responses import ErrorBody from robot_server.service.json_api.request import RequestModel @@ -20,7 +21,7 @@ from ..error_recovery_models import ErrorRecoveryPolicy -error_recovery_policy_router = APIRouter() +error_recovery_policy_router = LightRouter() @PydanticResponse.wrap_route( @@ -59,7 +60,7 @@ async def put_error_recovery_policy( raise RunStopped(detail=str(e)).as_error(status.HTTP_409_CONFLICT) from e return await PydanticResponse.create( - content=SimpleEmptyBody.construct(), + content=SimpleEmptyBody.model_construct(), status_code=status.HTTP_200_OK, ) @@ -90,8 +91,8 @@ async def get_error_recovery_policy( raise RunStopped(detail=str(e)).as_error(status.HTTP_409_CONFLICT) from e return await PydanticResponse.create( - content=SimpleBody.construct( - data=ErrorRecoveryPolicy.construct(policyRules=rules) + content=SimpleBody.model_construct( + data=ErrorRecoveryPolicy.model_construct(policyRules=rules) ), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/runs/router/labware_router.py b/robot-server/robot_server/runs/router/labware_router.py index 16924fd4ae8..06c16f8b71a 100644 --- a/robot-server/robot_server/runs/router/labware_router.py +++ b/robot-server/robot_server/runs/router/labware_router.py @@ -1,13 +1,16 @@ """Router for /runs endpoints dealing with labware offsets and definitions.""" + import logging from typing import Annotated, Union -from fastapi import APIRouter, Depends, status +from fastapi import Depends, status from opentrons_shared_data.labware.labware_definition import ( LabwareDefinition as SD_LabwareDefinition, ) +from server_utils.fastapi_utils.light_router import LightRouter + from opentrons.protocol_engine import LabwareOffsetCreate, LabwareOffset from opentrons.protocols.models import LabwareDefinition @@ -25,7 +28,7 @@ from .base_router import RunNotFound, RunStopped, RunNotIdle, get_run_data_from_url log = logging.getLogger(__name__) -labware_router = APIRouter() +labware_router = LightRouter() @PydanticResponse.wrap_route( @@ -69,7 +72,7 @@ async def add_labware_offset( log.info(f'Added labware offset "{added_offset.id}"' f' to run "{run.id}".') return await PydanticResponse.create( - content=SimpleBody.construct(data=added_offset), + content=SimpleBody.model_construct(data=added_offset), status_code=status.HTTP_201_CREATED, ) @@ -113,8 +116,8 @@ async def add_labware_definition( log.info(f'Added labware definition "{uri}"' f' to run "{run.id}".') return PydanticResponse( - content=SimpleBody.construct( - data=LabwareDefinitionSummary.construct(definitionUri=uri) + content=SimpleBody.model_construct( + data=LabwareDefinitionSummary.model_construct(definitionUri=uri) ), status_code=status.HTTP_201_CREATED, ) @@ -155,6 +158,6 @@ async def get_run_loaded_labware_definitions( raise RunStopped(detail=str(e)).as_error(status.HTTP_409_CONFLICT) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=labware_definitions), + content=SimpleBody.model_construct(data=labware_definitions), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/runs/run_data_manager.py b/robot-server/robot_server/runs/run_data_manager.py index 9999e040523..fa937f7cb68 100644 --- a/robot-server/robot_server/runs/run_data_manager.py +++ b/robot-server/robot_server/runs/run_data_manager.py @@ -49,7 +49,7 @@ def _build_run( # such that this default summary object is not needed if run_resource.ok and isinstance(state_summary, StateSummary): - return Run.construct( + return Run.model_construct( id=run_resource.run_id, protocolId=run_resource.protocol_id, createdAt=run_resource.created_at, @@ -72,7 +72,7 @@ def _build_run( errors: List[EnumeratedError] = [] if isinstance(state_summary, BadStateSummary): - state = StateSummary.construct( + state = StateSummary.model_construct( status=EngineStatus.STOPPED, errors=[], labware=[], @@ -109,7 +109,7 @@ def _build_run( AssertionError("Logic error in parsing invalid run.") ) - return BadRun.construct( + return BadRun.model_construct( dataError=run_loading_error, id=run_resource.run_id, protocolId=run_resource.protocol_id, diff --git a/robot-server/robot_server/runs/run_store.py b/robot-server/robot_server/runs/run_store.py index 0148f20058b..3094b73b8ba 100644 --- a/robot-server/robot_server/runs/run_store.py +++ b/robot-server/robot_server/runs/run_store.py @@ -1,4 +1,5 @@ """Runs' on-db store.""" + import logging from collections import defaultdict from dataclasses import dataclass @@ -7,8 +8,8 @@ from typing import Dict, List, Optional, Literal, Union import sqlalchemy +from pydantic import TypeAdapter, ValidationError from sqlalchemy import and_ -from pydantic import ValidationError from opentrons.util.helpers import utc_now from opentrons.protocol_engine import ( @@ -19,7 +20,7 @@ CommandErrorSlice, CommandStatus, ) -from opentrons.protocol_engine.commands import Command +from opentrons.protocol_engine.commands import Command, CommandAdapter from opentrons.protocol_engine.types import RunTimeParameter from opentrons_shared_data.errors.exceptions import ( @@ -38,7 +39,6 @@ from robot_server.persistence.pydantic import ( json_to_pydantic, pydantic_to_json, - json_to_pydantic_list, pydantic_list_to_json, ) from robot_server.protocols.protocol_store import ProtocolNotFoundError @@ -51,6 +51,8 @@ _CACHE_ENTRIES = 32 +_rtp_list_adapter = TypeAdapter(list[RunTimeParameter]) + @dataclass(frozen=True) class RunResource: @@ -433,7 +435,7 @@ def get_run_time_parameters(self, run_id: str) -> List[RunTimeParameter]: try: return ( - json_to_pydantic_list(RunTimeParameter, row.run_time_parameters) # type: ignore[arg-type] + json_to_pydantic(_rtp_list_adapter, row.run_time_parameters) if row.run_time_parameters is not None else [] ) @@ -515,8 +517,7 @@ def get_commands_slice( slice_result = transaction.execute(select_slice).all() sliced_commands: List[Command] = [ - json_to_pydantic(Command, row.command) # type: ignore[arg-type] - for row in slice_result + _parse_command(row.command) for row in slice_result ] return CommandSlice( @@ -680,7 +681,7 @@ def get_command(self, run_id: str, command_id: str) -> Command: if command is None: raise CommandNotFoundError(command_id=command_id) - return json_to_pydantic(Command, command) # type: ignore[arg-type] + return _parse_command(command) def remove(self, run_id: str) -> None: """Remove a run by its unique identifier. @@ -829,6 +830,11 @@ def _convert_state_to_sql_values( } +def _parse_command(json_str: str) -> Command: + """Parse a JSON string from the database into a `Command`.""" + return json_to_pydantic(CommandAdapter, json_str) + + def _convert_commands_status_to_sql_command_status( status: CommandStatus, ) -> CommandStatusSQLEnum: diff --git a/robot-server/robot_server/service/errors.py b/robot-server/robot_server/service/errors.py index 94a8d758563..8503b767258 100644 --- a/robot-server/robot_server/service/errors.py +++ b/robot-server/robot_server/service/errors.py @@ -79,7 +79,7 @@ def __init__( *wrapped_details, ), links=links, - ).dict(exclude_none=True) + ).model_dump(exclude_none=True) super().__init__( status_code=definition.status_code, diff --git a/robot-server/robot_server/service/json_api/request.py b/robot-server/robot_server/service/json_api/request.py index 9f716cb66e3..26ad5c882f6 100644 --- a/robot-server/robot_server/service/json_api/request.py +++ b/robot-server/robot_server/service/json_api/request.py @@ -1,12 +1,11 @@ from typing import Generic, TypeVar -from pydantic import Field -from pydantic.generics import GenericModel +from pydantic import BaseModel, Field RequestDataT = TypeVar("RequestDataT") -class RequestModel(GenericModel, Generic[RequestDataT]): +class RequestModel(BaseModel, Generic[RequestDataT]): """ """ data: RequestDataT = Field(..., description="the document’s 'primary data'") diff --git a/robot-server/robot_server/service/json_api/response.py b/robot-server/robot_server/service/json_api/response.py index 8764d8edd53..9b815e75fbf 100644 --- a/robot-server/robot_server/service/json_api/response.py +++ b/robot-server/robot_server/service/json_api/response.py @@ -11,9 +11,8 @@ ParamSpec, Callable, ) +from typing_extensions import get_args, override from pydantic import Field, BaseModel -from pydantic.generics import GenericModel -from pydantic.typing import get_args from fastapi.responses import JSONResponse from fastapi.dependencies.utils import get_typed_return_annotation from .resource_links import ResourceLinks as DeprecatedResourceLinks @@ -41,29 +40,50 @@ class BaseResponseBody(BaseModel): JSON responses adhere to the server's generated OpenAPI Spec. """ - def dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: + @override + def model_dump(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: """Always exclude `None` when serializing to an object. - The OpenAPI spec marks `Optional` BaseModel fields as omittable, but - not nullable. This `dict` method override ensures that `null` is never - returned in a response, which would violate the spec. + With Pydantic v1, the OpenAPI spec described `Optional`(i.e., possibly + `None`-valued) fields as omittable, but not nullable. This did not match + Pydantic's actual serialization behavior, which serialized Python `None` to + JSON `null` by default. This method override fixed the mismatch by making + Pydantic omit the field from serialization instead. + + With Pydantic v2, the OpenAPI spec does describe `Optional` fields as nullable, + matching Pydantic's serialization behavior. We therefore no longer need this + override to make them match. However, removing this override and changing + serialization behavior at this point would risk breaking things on the client. """ kwargs["exclude_none"] = True + return super().model_dump(*args, **kwargs) + + @override + def dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: + """See notes in `model_dump()`.""" + kwargs["exclude_none"] = True return super().dict(*args, **kwargs) + @override + def model_dump_json(self, *args: Any, **kwargs: Any) -> str: + """See notes in `.model_dump()`.""" + kwargs["exclude_none"] = True + return super().model_dump_json(*args, **kwargs) + + @override def json(self, *args: Any, **kwargs: Any) -> str: - """See notes in `.dict()`.""" + """See notes in `.model_dump()`.""" kwargs["exclude_none"] = True - return super().json(*args, **kwargs) + return super().model_dump_json(*args, **kwargs) -class SimpleBody(BaseResponseBody, GenericModel, Generic[ResponseDataT]): +class SimpleBody(BaseResponseBody, Generic[ResponseDataT]): """A response that returns a single resource.""" data: ResponseDataT = Field(..., description=DESCRIPTION_DATA) -class Body(BaseResponseBody, GenericModel, Generic[ResponseDataT, ResponseLinksT]): +class Body(BaseResponseBody, Generic[ResponseDataT, ResponseLinksT]): """A response that returns a single resource and stateful links.""" data: ResponseDataT = Field(..., description=DESCRIPTION_DATA) @@ -74,7 +94,7 @@ class SimpleEmptyBody(BaseResponseBody): """A response that returns no data and no links.""" -class EmptyBody(BaseResponseBody, GenericModel, Generic[ResponseLinksT]): +class EmptyBody(BaseResponseBody, Generic[ResponseLinksT]): """A response that returns no data except stateful links.""" links: ResponseLinksT = Field(..., description=DESCRIPTION_LINKS) @@ -94,7 +114,7 @@ class MultiBodyMeta(BaseModel): ) -class SimpleMultiBody(BaseResponseBody, GenericModel, Generic[ResponseDataT]): +class SimpleMultiBody(BaseResponseBody, Generic[ResponseDataT]): """A response that returns multiple resources.""" data: Sequence[ResponseDataT] = Field(..., description=DESCRIPTION_DATA) @@ -104,8 +124,8 @@ class SimpleMultiBody(BaseResponseBody, GenericModel, Generic[ResponseDataT]): # non-validating classmethod is taken from the type of this member, and there we really # want the arguments to be Sequence so they can accept narrower subtypes. For instance, # if you define a function as returning SimpleMultiBody[Union[A, B]], you should really - # be able to do return SimpleMultiBody.construct([A(), A(), A()]) or even - # SimpleMultiBody[Union[A, B]].construct([A(), A(), A()]). However, because construct's + # be able to do return SimpleMultiBody.model_construct([A(), A(), A()]) or even + # SimpleMultiBody[Union[A, B]].model_construct([A(), A(), A()]). However, because construct's # params are defined based on the dataclass fields, the only way to get the arguments # to be covariant is to make data the covariant Sequence protocol. meta: MultiBodyMeta = Field( @@ -114,11 +134,7 @@ class SimpleMultiBody(BaseResponseBody, GenericModel, Generic[ResponseDataT]): ) -class MultiBody( - BaseResponseBody, - GenericModel, - Generic[ResponseDataT, ResponseLinksT], -): +class MultiBody(BaseResponseBody, Generic[ResponseDataT, ResponseLinksT]): """A response that returns multiple resources and stateful links.""" data: List[ResponseDataT] = Field(..., description=DESCRIPTION_DATA) @@ -225,7 +241,7 @@ async def create( def render(self, content: ResponseBodyT) -> bytes: """Render the response body to JSON bytes.""" - return content.json().encode(self.charset) + return content.model_dump_json().encode(self.charset) # TODO(mc, 2021-12-09): remove this model @@ -240,7 +256,7 @@ class DeprecatedResponseDataModel(BaseModel): # TODO(mc, 2021-12-09): remove this model -class DeprecatedResponseModel(GenericModel, Generic[ResponseDataT]): +class DeprecatedResponseModel(BaseModel, Generic[ResponseDataT]): """A response that returns a single resource and stateful links. This deprecated response model may serialize `Optional` fields to `null`, @@ -259,7 +275,7 @@ class DeprecatedResponseModel(GenericModel, Generic[ResponseDataT]): # TODO(mc, 2021-12-09): remove this model class DeprecatedMultiResponseModel( - GenericModel, + BaseModel, Generic[ResponseDataT], ): """A response that returns multiple resources and stateful links. diff --git a/robot-server/robot_server/service/labware/models.py b/robot-server/robot_server/service/labware/models.py index 97222e635cf..89d140ced61 100644 --- a/robot-server/robot_server/service/labware/models.py +++ b/robot-server/robot_server/service/labware/models.py @@ -2,7 +2,7 @@ from datetime import datetime from functools import partial -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field from robot_server.service.json_api import ( DeprecatedResponseDataModel, @@ -73,9 +73,8 @@ class LabwareCalibration(DeprecatedResponseDataModel): definitionHash: str = Field( ..., description="The sha256 hash of key labware definition details" ) - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "examples": [ { "calibrationData": { @@ -111,6 +110,7 @@ class Config: }, ] } + ) MultipleCalibrationsResponse = DeprecatedMultiResponseModel[LabwareCalibration] diff --git a/robot-server/robot_server/service/labware/router.py b/robot-server/robot_server/service/labware/router.py index a90ed1ca867..6d44f2faeee 100644 --- a/robot-server/robot_server/service/labware/router.py +++ b/robot-server/robot_server/service/labware/router.py @@ -6,7 +6,8 @@ from typing import Annotated, Optional from typing_extensions import Literal, NoReturn -from fastapi import APIRouter, Depends, status +from fastapi import Depends, status +from server_utils.fastapi_utils.light_router import LightRouter from opentrons_shared_data.errors import ErrorCodes from robot_server.errors.error_responses import ErrorDetails, ErrorBody @@ -15,7 +16,7 @@ from robot_server.service.errors import RobotServerError, CommonErrorDef -router = APIRouter() +router = LightRouter() class LabwareCalibrationEndpointsRemoved(ErrorDetails): diff --git a/robot-server/robot_server/service/legacy/models/control.py b/robot-server/robot_server/service/legacy/models/control.py index 923ba29eb30..c375ed9d949 100644 --- a/robot-server/robot_server/service/legacy/models/control.py +++ b/robot-server/robot_server/service/legacy/models/control.py @@ -3,7 +3,7 @@ from enum import Enum from opentrons import types -from pydantic import BaseModel, Field, root_validator +from pydantic import model_validator, ConfigDict, BaseModel, Field class MotionTarget(str, Enum): @@ -28,8 +28,8 @@ class HomeTarget(str, Enum): Field, ..., description="A point in deck coordinates (x, y, z)", - min_items=3, - max_items=3, + min_length=3, + max_length=3, ) @@ -51,9 +51,8 @@ class RobotPositions(BaseModel): class RobotPositionsResponse(BaseModel): positions: RobotPositions - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "example": { "positions": { "change_pipette": { @@ -65,6 +64,7 @@ class Config: } } } + ) class Mount(str, Enum): @@ -95,7 +95,8 @@ class RobotMoveTarget(BaseModel): "if target is pipette", ) - @root_validator(pre=True) + @model_validator(mode="before") + @classmethod def root_validator(cls, values): points = values.get("point", []) target = values.get("target") @@ -108,8 +109,8 @@ def root_validator(cls, values): ) return values - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "examples": [ { "target": "mount", @@ -124,6 +125,7 @@ class Config: }, ] } + ) class RobotHomeTarget(BaseModel): @@ -141,17 +143,19 @@ class RobotHomeTarget(BaseModel): " in that case)", ) - @root_validator(pre=True) + @model_validator(mode="before") + @classmethod def root_validate(cls, values): # Make sure that mount is present if target is pipette if values.get("target") == HomeTarget.pipette.value and not values.get("mount"): raise ValueError("mount must be specified if target is pipette") return values - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "examples": [{"target": "robot"}, {"target": "pipette", "mount": "right"}] } + ) class RobotLightState(BaseModel): diff --git a/robot-server/robot_server/service/legacy/models/deck_calibration.py b/robot-server/robot_server/service/legacy/models/deck_calibration.py index a1db8eef866..f424ee4fb07 100644 --- a/robot-server/robot_server/service/legacy/models/deck_calibration.py +++ b/robot-server/robot_server/service/legacy/models/deck_calibration.py @@ -31,19 +31,19 @@ class InstrumentOffset(BaseModel): single: Offset = Field( ..., - deprecated=True, description=( "This will always be `[0, 0, 0]`." " Use the `GET /calibration/pipette_offset` endpoint instead." ), + json_schema_extra={"deprecated": True}, ) multi: Offset = Field( ..., - deprecated=True, description=( "This will always be `[0, 0, 0]`." " Use the `GET /calibration/pipette_offset` endpoint instead." ), + json_schema_extra={"deprecated": True}, ) @@ -78,7 +78,7 @@ class DeckCalibrationData(BaseModel): " was used in this calibration." " This is deprecated because it was prone to bugs where semantically identical" " definitions had different hashes.", - deprecated=True, + json_schema_extra={"deprecated": True}, ) source: typing.Optional[SourceType] = Field( None, description="The calibration source" diff --git a/robot-server/robot_server/service/legacy/models/modules.py b/robot-server/robot_server/service/legacy/models/modules.py index 992c109591a..e15419b7397 100644 --- a/robot-server/robot_server/service/legacy/models/modules.py +++ b/robot-server/robot_server/service/legacy/models/modules.py @@ -1,5 +1,5 @@ import typing -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field class TemperatureModuleLiveData(BaseModel): @@ -168,9 +168,8 @@ class Modules(BaseModel): """A list of all attached modules and the status of each one""" modules: typing.List[Module] - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "examples": [ {"modules": []}, { @@ -270,6 +269,7 @@ class Config: }, ] } + ) class ModuleSerial(BaseModel): @@ -288,9 +288,11 @@ class SerialCommand(BaseModel): args: typing.Optional[typing.List[typing.Any]] = Field( None, description="The ordered args list for the call" ) - - class Config: - schema_extra = {"examples": [{"command_type": "set_Temperature", "args": [60]}]} + model_config = ConfigDict( + json_schema_extra={ + "examples": [{"command_type": "set_Temperature", "args": [60]}] + } + ) class SerialCommandResponse(BaseModel): @@ -300,6 +302,6 @@ class SerialCommandResponse(BaseModel): returnValue: typing.Optional[str] = Field( None, description="The return value from the call" ) - - class Config: - schema_extra = {"examples": [{"message": "Success", "returnValue": None}]} + model_config = ConfigDict( + json_schema_extra={"examples": [{"message": "Success", "returnValue": None}]} + ) diff --git a/robot-server/robot_server/service/legacy/models/motors.py b/robot-server/robot_server/service/legacy/models/motors.py index b27b4260131..2242185d6cc 100644 --- a/robot-server/robot_server/service/legacy/models/motors.py +++ b/robot-server/robot_server/service/legacy/models/motors.py @@ -1,7 +1,7 @@ from enum import Enum import typing -from pydantic import BaseModel, Field, validator +from pydantic import field_validator, BaseModel, Field from opentrons.hardware_control import types @@ -38,8 +38,10 @@ class EngagedMotors(BaseModel): z_r: EngagedMotor p_l: EngagedMotor p_r: EngagedMotor - q: typing.Optional[EngagedMotor] # Optional since OT2 doesn't have these axes - g: typing.Optional[EngagedMotor] + q: typing.Optional[ + EngagedMotor + ] = None # Optional since OT2 doesn't have these axes + g: typing.Optional[EngagedMotor] = None class Axes(BaseModel): @@ -47,6 +49,7 @@ class Axes(BaseModel): axes: typing.List[MotorName] - @validator("axes", pre=True) + @field_validator("axes", mode="before") + @classmethod def lower_case_motor_name(cls, v): return [m.lower() for m in v] diff --git a/robot-server/robot_server/service/legacy/models/networking.py b/robot-server/robot_server/service/legacy/models/networking.py index 5b3351fdb3f..53a666719f7 100644 --- a/robot-server/robot_server/service/legacy/models/networking.py +++ b/robot-server/robot_server/service/legacy/models/networking.py @@ -1,7 +1,14 @@ import typing from enum import Enum -from pydantic import BaseModel, Field, SecretStr, validator, root_validator +from pydantic import ( + field_validator, + model_validator, + ConfigDict, + BaseModel, + Field, + SecretStr, +) from opentrons.system import wifi @@ -53,9 +60,8 @@ class NetworkingStatus(BaseModel): description="Per-interface networking status. Properties are " "named for network interfaces", ) - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "example": { "status": "full", "interfaces": { @@ -76,6 +82,7 @@ class Config: }, } } + ) class NetworkingSecurityType(str, Enum): @@ -111,9 +118,8 @@ class WifiNetworks(BaseModel): """The list of networks""" list: typing.List[WifiNetworkFull] - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "example": { "list": [ { @@ -126,6 +132,7 @@ class Config: ] } } + ) class WifiConfiguration(BaseModel): @@ -141,7 +148,7 @@ class WifiConfiguration(BaseModel): "`false` (default if key is not " "present) otherwise.", ) - securityType: typing.Optional[NetworkingSecurityType] + securityType: typing.Optional[NetworkingSecurityType] = None psk: typing.Optional[SecretStr] = Field( None, @@ -160,10 +167,11 @@ class WifiConfiguration(BaseModel): 'and it may also have `"anonymousIdentity"` and ' '`"caCert"` properties, both of which are identified' " as present but not required.", - required=["eapType"], + json_schema_extra={"required": ["eapType"]}, ) - @validator("eapConfig") + @field_validator("eapConfig") + @classmethod def eap_config_validate(cls, v): """Custom validator for the eapConfig field""" if v is not None: @@ -176,7 +184,8 @@ def eap_config_validate(cls, v): return v - @root_validator(pre=True) + @model_validator(mode="before") + @classmethod def validate_configuration(cls, values): """Validate the configuration""" security_type = values.get("securityType") @@ -199,8 +208,8 @@ def validate_configuration(cls, values): raise ValueError("If securityType is wpa-eap, eapConfig must be specified") return values - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "examples": [ {"ssid": "linksys"}, { @@ -225,6 +234,7 @@ class Config: }, ] } + ) class WifiConfigurationResponse(BaseModel): @@ -257,7 +267,7 @@ class WifiKeyFile(BaseModel): class AddWifiKeyFileResponse(WifiKeyFile): """Response to add wifi key file""" - message: typing.Optional[str] + message: typing.Optional[str] = None class WifiKeyFiles(BaseModel): @@ -266,9 +276,8 @@ class WifiKeyFiles(BaseModel): wifi_keys: typing.List[WifiKeyFile] = Field( [], alias="keys", description="A list of keys in the system" ) - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "example": { "keys": [ { @@ -279,6 +288,7 @@ class Config: ] } } + ) class EapConfigOptionType(str, Enum): @@ -327,9 +337,8 @@ class EapOptions(BaseModel): """An object describing all supported EAP variants and their parameters""" options: typing.List[EapVariant] - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "example": { "options": [ { @@ -365,3 +374,4 @@ class Config: ] } } + ) diff --git a/robot-server/robot_server/service/legacy/models/pipettes.py b/robot-server/robot_server/service/legacy/models/pipettes.py index 3c43ac3fec4..cff8c01307f 100644 --- a/robot-server/robot_server/service/legacy/models/pipettes.py +++ b/robot-server/robot_server/service/legacy/models/pipettes.py @@ -1,6 +1,6 @@ import typing -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field class AttachedPipette(BaseModel): @@ -36,9 +36,8 @@ class PipettesByMount(BaseModel): left: AttachedPipette right: AttachedPipette - - class Config: - schema_extra = { + model_config = ConfigDict( + json_schema_extra={ "example": { "left": { "model": "p300_single_v1.5", @@ -58,3 +57,4 @@ class Config: }, } } + ) diff --git a/robot-server/robot_server/service/legacy/models/settings.py b/robot-server/robot_server/service/legacy/models/settings.py index f77977dbe3a..980610f8ef5 100644 --- a/robot-server/robot_server/service/legacy/models/settings.py +++ b/robot-server/robot_server/service/legacy/models/settings.py @@ -3,7 +3,7 @@ from typing import Optional, List, Dict, Any, Union -from pydantic import BaseModel, Field, create_model, validator +from pydantic import field_validator, BaseModel, Field, create_model from opentrons_shared_data.pipette import model_constants from opentrons.config.reset import ResetOptionId @@ -17,7 +17,7 @@ class AdvancedSetting(BaseModel): ..., description="The ID by which the property used to be known; not" " useful now and may contain spaces or hyphens", - deprecated=True, + json_schema_extra={"deprecated": True}, ) title: str = Field( ..., @@ -99,7 +99,8 @@ class LogLevel(BaseModel): None, description="The value to set (conforming to Python log levels)" ) - @validator("log_level", pre=True) + @field_validator("log_level", mode="before") + @classmethod def lower_case_log_keys(cls, value): return value if value is None else LogLevels(value.lower(), None) @@ -136,7 +137,7 @@ class PipetteSettingsField(BaseModel): units: Optional[str] = Field( None, description="The physical units this value is in (e.g. mm, uL)" ) - type: Optional[PipetteSettingsFieldType] + type: Optional[PipetteSettingsFieldType] = None min: float = Field(..., description="The minimum acceptable value of the property") max: float = Field(..., description="The maximum acceptable value of the property") default: float = Field(..., description="The default value of the property") @@ -184,11 +185,9 @@ class BasePipetteSettingFields(BaseModel): class PipetteSettings(BaseModel): - info: PipetteSettingsInfo - setting_fields: PipetteSettingsFields # type: ignore - class Config: - fields = {"setting_fields": "fields"} + info: PipetteSettingsInfo + setting_fields: PipetteSettingsFields = Field(..., alias="fields") # type: ignore MultiPipetteSettings = Dict[str, PipetteSettings] @@ -208,7 +207,8 @@ class PipetteSettingsUpdate(BaseModel): None, alias="fields" ) - @validator("setting_fields") + @field_validator("setting_fields") + @classmethod def validate_fields(cls, v): """A validator to ensure that values for mutable configs are floats and booleans for quirks.""" diff --git a/robot-server/robot_server/service/legacy/routers/networking.py b/robot-server/robot_server/service/legacy/routers/networking.py index b47cf283ddf..ea3c4543ea8 100644 --- a/robot-server/robot_server/service/legacy/routers/networking.py +++ b/robot-server/robot_server/service/legacy/routers/networking.py @@ -90,7 +90,7 @@ async def get_wifi_networks( "letting the system decide when to do a rescan." ), ), - ] = False + ] = False, ) -> WifiNetworks: networks = await nmcli.available_ssids(rescan) return WifiNetworks(list=[WifiNetworkFull(**n) for n in networks]) @@ -190,7 +190,7 @@ async def post_wifi_key(key: UploadFile = File(...)): else: # We return a JSONResponse because we want the 200 status code. response.message = "Key file already present" - return JSONResponse(content=response.dict()) + return JSONResponse(content=response.model_dump()) @router.delete( @@ -210,7 +210,7 @@ async def delete_wifi_key( description="The ID of key to delete, as determined by a previous" " call to GET /wifi/keys", ), - ] + ], ) -> V1BasicResponse: """Delete wifi key handler""" deleted_file = wifi.remove_key(key_uuid) @@ -274,4 +274,4 @@ async def post_wifi_disconnect(wifi_ssid: WifiNetwork): ) else: stat = status.HTTP_500_INTERNAL_SERVER_ERROR - return JSONResponse(status_code=stat, content=result.dict()) + return JSONResponse(status_code=stat, content=result.model_dump()) diff --git a/robot-server/robot_server/service/legacy/routers/settings.py b/robot-server/robot_server/service/legacy/routers/settings.py index f1c7c77dc72..12589748283 100644 --- a/robot-server/robot_server/service/legacy/routers/settings.py +++ b/robot-server/robot_server/service/legacy/routers/settings.py @@ -424,7 +424,7 @@ def _pipette_settings_from_mutable_configs( # TODO(mc, 2020-09-17): s/fields/setting_fields (?) # need model and name? - return PipetteSettings( # type: ignore[call-arg] + return PipetteSettings( info=PipetteSettingsInfo( name=cast(str, mutable_configs.get("name", "")), model=cast(str, mutable_configs.get("model", "")), diff --git a/robot-server/robot_server/service/notifications/notification_client.py b/robot-server/robot_server/service/notifications/notification_client.py index 052bb272cf9..1727ee0c880 100644 --- a/robot-server/robot_server/service/notifications/notification_client.py +++ b/robot-server/robot_server/service/notifications/notification_client.py @@ -91,8 +91,8 @@ def publish_advise_refetch( Args: topic: The topic to publish the message on. """ - message = NotifyRefetchBody.construct() - payload = message.json() + message = NotifyRefetchBody.model_construct() + payload = message.model_dump_json() self._client.publish( topic=topic, payload=payload, @@ -109,8 +109,8 @@ def publish_advise_unsubscribe( Args: topic: The topic to publish the message on. """ - message = NotifyUnsubscribeBody.construct() - payload = message.json() + message = NotifyUnsubscribeBody.model_construct() + payload = message.model_dump_json() self._client.publish( topic=topic, payload=payload, diff --git a/robot-server/robot_server/service/pipette_offset/models.py b/robot-server/robot_server/service/pipette_offset/models.py index 401afc29273..f5b7af12452 100644 --- a/robot-server/robot_server/service/pipette_offset/models.py +++ b/robot-server/robot_server/service/pipette_offset/models.py @@ -31,7 +31,7 @@ class PipetteOffsetCalibration(DeprecatedResponseDataModel): pipette: str = Field(..., description="The pipette ID") mount: str = Field(..., description="The pipette mount") offset: typing.List[float] = Field( - ..., description="The pipette offset vector", max_items=3, min_items=3 + ..., description="The pipette offset vector", max_length=3, min_length=3 ) tiprack: str = Field( ..., diff --git a/robot-server/robot_server/service/session/command_execution/callable_executor.py b/robot-server/robot_server/service/session/command_execution/callable_executor.py index 39a62cbe3ae..2503ff952e3 100644 --- a/robot-server/robot_server/service/session/command_execution/callable_executor.py +++ b/robot-server/robot_server/service/session/command_execution/callable_executor.py @@ -26,7 +26,7 @@ async def execute(self, command: Command) -> CompletedCommand: with duration() as time_it: name_arg = command.request.command data = command.request.data - data_arg = data.dict() if data else {} + data_arg = data.model_dump() if data else {} await self._callable(name_arg, data_arg) diff --git a/robot-server/robot_server/service/session/models/command.py b/robot-server/robot_server/service/session/models/command.py index 6c33f219e05..0f68eb996e0 100644 --- a/robot-server/robot_server/service/session/models/command.py +++ b/robot-server/robot_server/service/session/models/command.py @@ -22,7 +22,6 @@ from typing_extensions import Literal from pydantic import BaseModel, Field -from pydantic.generics import GenericModel from opentrons.util.helpers import utc_now from opentrons.protocol_engine import commands @@ -70,7 +69,7 @@ class CommandStatus(str, Enum): class SessionCommandRequest( - GenericModel, typing.Generic[CommandT, RequestDataT, ResponseDataT] + BaseModel, typing.Generic[CommandT, RequestDataT, ResponseDataT] ): """A session command request.""" @@ -101,7 +100,6 @@ def make_response( class SessionCommandResponse( DeprecatedResponseDataModel, - GenericModel, typing.Generic[CommandT, RequestDataT, ResponseDataT], ): """A session command response.""" @@ -110,8 +108,8 @@ class SessionCommandResponse( data: RequestDataT status: CommandStatus createdAt: datetime = Field(default_factory=utc_now) - startedAt: typing.Optional[datetime] - completedAt: typing.Optional[datetime] + startedAt: typing.Optional[datetime] = None + completedAt: typing.Optional[datetime] = None result: typing.Optional[ResponseDataT] = None diff --git a/robot-server/robot_server/service/session/models/common.py b/robot-server/robot_server/service/session/models/common.py index add30e47d3e..f61f7d0a5bd 100644 --- a/robot-server/robot_server/service/session/models/common.py +++ b/robot-server/robot_server/service/session/models/common.py @@ -22,4 +22,4 @@ class EmptyModel(BaseModel): class JogPosition(BaseModel): - vector: OffsetVector = Field(..., min_items=3, max_items=3) + vector: OffsetVector = Field(..., min_length=3, max_length=3) diff --git a/robot-server/robot_server/service/session/models/session.py b/robot-server/robot_server/service/session/models/session.py index 4587c99f046..7724865e9e4 100644 --- a/robot-server/robot_server/service/session/models/session.py +++ b/robot-server/robot_server/service/session/models/session.py @@ -52,7 +52,7 @@ class SessionCreateAttributes(BaseModel): class SessionCreateAttributesNoParams(SessionCreateAttributes): """The base model of request that has no createParams.""" - createParams: typing.Optional[BaseModel] + createParams: typing.Optional[BaseModel] = None class CalibrationCheckCreateAttributes(SessionCreateAttributesNoParams): diff --git a/robot-server/robot_server/service/session/router.py b/robot-server/robot_server/service/session/router.py index 3b9da7ba88a..83f5a5578ab 100644 --- a/robot-server/robot_server/service/session/router.py +++ b/robot-server/robot_server/service/session/router.py @@ -169,7 +169,7 @@ async def session_command_execute_handler( log.debug(f"Command result: {command_result}") - return CommandResponse( + return CommandResponse.model_construct( data=command_result, links=get_valid_session_links(sessionId, router) ) diff --git a/robot-server/robot_server/service/tip_length/models.py b/robot-server/robot_server/service/tip_length/models.py index 2ff8f81b5ef..eca3757be5c 100644 --- a/robot-server/robot_server/service/tip_length/models.py +++ b/robot-server/robot_server/service/tip_length/models.py @@ -24,7 +24,7 @@ class TipLengthCalibration(DeprecatedResponseDataModel): " This is deprecated because it was prone to bugs where semantically identical" " definitions had different hashes." " Use `uri` instead.", - deprecated=True, + json_schema_extra={"deprecated": True}, ) pipette: str = Field(..., description="The pipette ID") lastModified: datetime = Field( diff --git a/robot-server/robot_server/settings.py b/robot-server/robot_server/settings.py index 40b0ed663bb..d67e1040fef 100644 --- a/robot-server/robot_server/settings.py +++ b/robot-server/robot_server/settings.py @@ -4,10 +4,11 @@ from functools import lru_cache from pathlib import Path -from pydantic import BaseSettings, Field +from pydantic import Field from dotenv import load_dotenv from opentrons.config import infer_config_base_dir +from pydantic_settings import BaseSettings, SettingsConfigDict log = logging.getLogger(__name__) @@ -28,9 +29,7 @@ class Environment(BaseSettings): """Environment related settings""" dot_env_path: Path = infer_config_base_dir() / "robot.env" - - class Config: - env_prefix = "OT_ROBOT_SERVER_" + model_config = SettingsConfigDict(env_prefix="OT_ROBOT_SERVER_") # If you update this, also update the generated settings_schema.json. @@ -94,6 +93,8 @@ class RobotServerSettings(BaseSettings): ), ) + model_config = SettingsConfigDict(env_prefix="OT_ROBOT_SERVER_") + maximum_quick_transfer_protocols: int = Field( default=20, gt=0, @@ -110,6 +111,3 @@ class RobotServerSettings(BaseSettings): "The maximum number of uploaded data files to allow before auto-deleting old ones." ), ) - - class Config: - env_prefix = "OT_ROBOT_SERVER_" diff --git a/robot-server/robot_server/subsystems/router.py b/robot-server/robot_server/subsystems/router.py index e64e7390063..950cf0505e2 100644 --- a/robot-server/robot_server/subsystems/router.py +++ b/robot-server/robot_server/subsystems/router.py @@ -3,9 +3,11 @@ from datetime import datetime from typing import Annotated, Optional, TYPE_CHECKING -from fastapi import APIRouter, status, Depends, Response, Request +from fastapi import status, Depends, Response, Request from typing_extensions import Literal +from server_utils.fastapi_utils.light_router import LightRouter + from robot_server.service.json_api import ( SimpleMultiBody, PydanticResponse, @@ -45,7 +47,7 @@ if TYPE_CHECKING: from opentrons.hardware_control.ot3api import OT3API # noqa: F401 -subsystems_router = APIRouter() +subsystems_router = LightRouter() def status_route_for(subsystem: SubSystem) -> str: @@ -122,7 +124,7 @@ async def get_attached_subsystems( """Return all subsystems currently present on the machine.""" hardware = get_ot3_hardware(thread_manager) data = [ - PresentSubsystem.construct( + PresentSubsystem.model_construct( name=SubSystem.from_hw(subsystem_id), ok=subsystem_details.ok, current_fw_version=str(subsystem_details.current_fw_version), @@ -134,7 +136,7 @@ async def get_attached_subsystems( ] meta = MultiBodyMeta(cursor=0, totalLength=len(data)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=data, meta=meta) + content=SimpleMultiBody.model_construct(data=data, meta=meta) ) @@ -164,8 +166,8 @@ async def get_attached_subsystem( status.HTTP_404_NOT_FOUND ) return await PydanticResponse.create( - content=SimpleBody.construct( - data=PresentSubsystem.construct( + content=SimpleBody.model_construct( + data=PresentSubsystem.model_construct( name=subsystem, ok=subsystem_status.ok, current_fw_version=str(subsystem_status.current_fw_version), @@ -197,7 +199,7 @@ async def get_subsystem_updates( """Return all currently-running firmware update process summaries.""" handles = await update_manager.all_ongoing_processes() data = [ - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=handle.process_details.update_id, subsystem=handle.process_details.subsystem, updateStatus=handle.cached_state, @@ -207,7 +209,7 @@ async def get_subsystem_updates( ] meta = MultiBodyMeta(cursor=0, totalLength=len(data)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=data, meta=meta) + content=SimpleMultiBody.model_construct(data=data, meta=meta) ) @@ -238,8 +240,8 @@ async def get_subsystem_update( ) from e progress = await handle.get_progress() return await PydanticResponse.create( - content=SimpleBody.construct( - data=UpdateProgressData.construct( + content=SimpleBody.model_construct( + data=UpdateProgressData.model_construct( id=handle.process_details.update_id, createdAt=handle.process_details.created_at, subsystem=handle.process_details.subsystem, @@ -282,7 +284,7 @@ async def get_update_processes( ] meta = MultiBodyMeta(cursor=0, totalLength=len(data)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=data, meta=meta) + content=SimpleMultiBody.model_construct(data=data, meta=meta) ) @@ -306,8 +308,8 @@ async def get_update_process( raise IDNotFound(detail=id).as_error(status.HTTP_404_NOT_FOUND) from e progress = await handle.get_progress() return await PydanticResponse.create( - content=SimpleBody.construct( - data=UpdateProgressData.construct( + content=SimpleBody.model_construct( + data=UpdateProgressData.model_construct( id=handle.process_details.update_id, subsystem=handle.process_details.subsystem, createdAt=handle.process_details.created_at, @@ -375,8 +377,8 @@ async def begin_subsystem_update( ) progress = await summary.get_progress() return await PydanticResponse.create( - content=SimpleBody.construct( - data=UpdateProgressData.construct( + content=SimpleBody.model_construct( + data=UpdateProgressData.model_construct( id=summary.process_details.update_id, createdAt=summary.process_details.created_at, subsystem=subsystem, diff --git a/robot-server/robot_server/system/router.py b/robot-server/robot_server/system/router.py index fab9b598ba3..2735dfca01d 100644 --- a/robot-server/robot_server/system/router.py +++ b/robot-server/robot_server/system/router.py @@ -5,7 +5,8 @@ - /system/time: allows the client to read & update robot system time """ from datetime import datetime -from fastapi import APIRouter + +from server_utils.fastapi_utils.light_router import LightRouter from robot_server.service.json_api.resource_links import ResourceLinkKey, ResourceLink @@ -13,7 +14,7 @@ from .time_utils import get_system_time, set_system_time -system_router = APIRouter() +system_router = LightRouter() """Router for /system endpoints.""" diff --git a/robot-server/scripts/spec_generator.py b/robot-server/scripts/spec_generator.py index 8f2a807c771..f3253f2471b 100755 --- a/robot-server/scripts/spec_generator.py +++ b/robot-server/scripts/spec_generator.py @@ -21,7 +21,7 @@ def write_api_spec(output: TextIOBase) -> None: spec_dict = get_openapi( title="Opentrons HTTP API Spec", - version=API_VERSION, + version=str(API_VERSION), description=( "This OpenAPI spec describes the HTTP API for Opentrons " "robots. It may be retrieved from a robot on port 31950 at " @@ -34,7 +34,7 @@ def write_api_spec(output: TextIOBase) -> None: json.dump(spec_dict, output) -def _run_cmdline() -> None: +def _run_cmdline() -> int: parser = ArgumentParser( description="Generate a static openapi spec. Note: robot-server must be importable when you run this." ) @@ -45,12 +45,8 @@ def _run_cmdline() -> None: help="Where to write the file (will be json)", ) args = parser.parse_args() - try: - write_api_spec(args.output) - return 0 - except Exception as e: - sys.stderr.write(str(e) + "\n") - return -1 + write_api_spec(args.output) + return 0 if __name__ == "__main__": diff --git a/robot-server/setup.py b/robot-server/setup.py index 7fcdabade82..1866f478dbc 100755 --- a/robot-server/setup.py +++ b/robot-server/setup.py @@ -56,10 +56,11 @@ def get_version(): f"opentrons-shared-data=={VERSION}", f"server-utils=={VERSION}", "anyio==3.7.1", - "fastapi==0.99.1", + "fastapi==0.100.0", "python-dotenv==1.0.1", "python-multipart==0.0.6", - "pydantic==1.10.12", + "pydantic==2.9.0", + "pydantic-settings==2.4.0", "typing-extensions>=4.0.0,<5", "uvicorn==0.27.0.post1", "wsproto==1.2.0", diff --git a/robot-server/tests/commands/test_router.py b/robot-server/tests/commands/test_router.py index 4ccc1a1acc1..978594a6917 100644 --- a/robot-server/tests/commands/test_router.py +++ b/robot-server/tests/commands/test_router.py @@ -11,10 +11,9 @@ from opentrons.protocol_engine.errors import CommandDoesNotExistError from opentrons.protocol_runner import RunOrchestrator -from robot_server.service.json_api import MultiBodyMeta +from robot_server.service.json_api import MultiBodyMeta, RequestModel from robot_server.errors.error_responses import ApiError from robot_server.commands.router import ( - RequestModelWithStatelessCommandCreate, create_command, get_commands_list, get_command, @@ -58,7 +57,7 @@ def _stub_queued_command_state(*_a: object, **_k: object) -> pe_commands.Command ).then_do(_stub_queued_command_state) result = await create_command( - RequestModelWithStatelessCommandCreate(data=command_create), + RequestModel(data=command_create), waitUntilComplete=False, timeout=42, orchestrator=run_orchestrator, @@ -99,7 +98,7 @@ async def test_create_command_wait_for_complete( decoy.when(run_orchestrator.get_command("abc123")).then_return(completed_command) result = await create_command( - RequestModelWithStatelessCommandCreate(data=command_create), + RequestModel(data=command_create), waitUntilComplete=True, timeout=42, orchestrator=run_orchestrator, diff --git a/robot-server/tests/conftest.py b/robot-server/tests/conftest.py index aed43626a2b..125e695a134 100644 --- a/robot-server/tests/conftest.py +++ b/robot-server/tests/conftest.py @@ -398,3 +398,24 @@ def sql_engine(tmp_path: Path) -> Generator[SQLEngine, None, None]: with sql_engine_ctx(db_file_path) as engine: metadata.create_all(engine) yield engine + + +def datetime_to_zulu_iso8601(dt: datetime) -> str: + """Serialize a datetime to an ISO8601 string. + + If the timezone is UTC, represent that with "Z", which matches what Pydantic does, + instead instead of with "+00:00", which is Python's default. + + e.g. "2024-12-10T19:40:55.984327Z" vs. "2024-12-10T19:40:55.984327+00:00". + """ + return dt.isoformat().replace("+00:00", "Z") + + +# todo(mm, 2024-12-10): +# In Python 3.11+, we can replace this with just datetime.fromisoformat(). +def zulu_iso8601_to_datetime(iso8601_str: str) -> datetime: + """Parse an ISO8601 datetime string with a "Z" timezone. + + See `datetime_to_zulu_iso8601()`. + """ + return datetime.fromisoformat(iso8601_str.replace("Z", "+00:00")) diff --git a/robot-server/tests/errors/test_exception_handlers.py b/robot-server/tests/errors/test_exception_handlers.py index eff6b5e041c..45cbf27321e 100644 --- a/robot-server/tests/errors/test_exception_handlers.py +++ b/robot-server/tests/errors/test_exception_handlers.py @@ -22,13 +22,7 @@ class Item(BaseModel): @pytest.fixture def app() -> FastAPI: """Get a FastAPI app with our exception handlers.""" - app = FastAPI() - - # TODO(mc, 2021-05-10): upgrade to FastAPI > 0.61.2 to use `exception_handlers` arg - # see https://github.com/tiangolo/fastapi/pull/1924 - for exc_cls, handler in exception_handlers.items(): - app.add_exception_handler(exc_cls, handler) - + app = FastAPI(exception_handlers=exception_handlers) return app @@ -159,21 +153,23 @@ def create_item(item: Item) -> Item: "errorCode": "4000", "id": "InvalidRequest", "title": "Invalid Request", - "detail": "field required", + "detail": "Field required", "source": {"pointer": "/string_field"}, }, { "errorCode": "4000", "id": "InvalidRequest", "title": "Invalid Request", - "detail": "value is not a valid integer", + "detail": "Input should be a valid integer, unable to parse " + "string as an integer", "source": {"pointer": "/int_field"}, }, { "errorCode": "4000", "id": "InvalidRequest", "title": "Invalid Request", - "detail": "value could not be parsed to a boolean", + "detail": "Input should be a valid boolean, unable to interpret " + "input", "source": {"pointer": "/array_field/0"}, }, ] @@ -196,7 +192,8 @@ def get_item(count: int) -> Item: "errorCode": "4000", "id": "InvalidRequest", "title": "Invalid Request", - "detail": "value is not a valid integer", + "detail": "Input should be a valid integer, unable to parse " + "string as an integer", "source": {"parameter": "count"}, }, ] @@ -219,7 +216,7 @@ def get_item(header_name: str = Header(...)) -> Item: "errorCode": "4000", "id": "InvalidRequest", "title": "Invalid Request", - "detail": "field required", + "detail": "Field required", "source": {"header": "header-name"}, }, ] @@ -242,8 +239,9 @@ def create_item_legacy(item: Item) -> Item: assert response.json() == { "errorCode": "4000", "message": ( - "body.string_field: none is not an allowed value; " - "body.int_field: value is not a valid integer; " - "body.array_field.0: value could not be parsed to a boolean" + "body.string_field: Input should be a valid string; " + "body.int_field: Input should be a valid integer, unable to parse " + "string as an integer; body.array_field.0: Input should be a valid " + "boolean, unable to interpret input" ), } diff --git a/robot-server/tests/instruments/test_router.py b/robot-server/tests/instruments/test_router.py index fe401828284..9989d6b0409 100644 --- a/robot-server/tests/instruments/test_router.py +++ b/robot-server/tests/instruments/test_router.py @@ -212,7 +212,7 @@ async def rehearse_instrument_retrievals(skip_if_would_block: bool = False) -> N result = await get_attached_instruments(hardware=ot3_hardware_api) assert result.content.data == [ - Pipette.construct( + Pipette.model_construct( ok=True, mount="left", instrumentType="pipette", @@ -234,7 +234,7 @@ async def rehearse_instrument_retrievals(skip_if_would_block: bool = False) -> N ), state=PipetteState(tip_detected=True), ), - Pipette.construct( + Pipette.model_construct( ok=True, mount="right", firmwareVersion="11", @@ -256,7 +256,7 @@ async def rehearse_instrument_retrievals(skip_if_would_block: bool = False) -> N ), state=PipetteState(tip_detected=False), ), - Gripper.construct( + Gripper.model_construct( ok=True, mount="extension", firmwareVersion="11", @@ -305,7 +305,7 @@ async def test_get_ot2_instruments( decoy.verify(await ot2_hardware_api.cache_instruments(), times=0) assert result2.status_code == 200 assert result2.content.data == [ - Pipette.construct( + Pipette.model_construct( ok=True, mount="right", instrumentType="pipette", @@ -351,7 +351,7 @@ async def test_get_96_channel_instruments( decoy.when(ot3_hardware_api.get_instrument_offset(OT3Mount.RIGHT)).then_return(None) assert result2.status_code == 200 assert result2.content.data == [ - Pipette.construct( + Pipette.model_construct( ok=True, mount="left", instrumentType="pipette", diff --git a/robot-server/tests/integration/http_api/runs/test_persistence.py b/robot-server/tests/integration/http_api/runs/test_persistence.py index 943f644e8d3..84b5f28aeb6 100644 --- a/robot-server/tests/integration/http_api/runs/test_persistence.py +++ b/robot-server/tests/integration/http_api/runs/test_persistence.py @@ -6,6 +6,7 @@ import anyio import pytest +from tests.conftest import zulu_iso8601_to_datetime from tests.integration.dev_server import DevServer from tests.integration.robot_client import RobotClient @@ -298,13 +299,13 @@ async def test_runs_completed_started_at_persist_via_actions_router( get_run_response = await client.get_run(run_id=run_id) run_data = get_run_response.json()["data"] - assert datetime.fromisoformat(run_data["startedAt"]).timestamp() == pytest.approx( + assert zulu_iso8601_to_datetime(run_data["startedAt"]).timestamp() == pytest.approx( expected_started_at.timestamp(), abs=2 ) - assert datetime.fromisoformat(run_data["completedAt"]).timestamp() == pytest.approx( - expected_completed_at.timestamp(), abs=2 - ) + assert zulu_iso8601_to_datetime( + run_data["completedAt"] + ).timestamp() == pytest.approx(expected_completed_at.timestamp(), abs=2) # make sure the times are in order assert run_data["startedAt"] < run_data["completedAt"] @@ -332,6 +333,6 @@ async def test_runs_completed_filled_started_at_none_persist( run_data = get_run_response.json()["data"] assert "startedAt" not in run_data - assert datetime.fromisoformat(run_data["completedAt"]).timestamp() == pytest.approx( - expected_completed_at.timestamp(), abs=2 - ) + assert zulu_iso8601_to_datetime( + run_data["completedAt"] + ).timestamp() == pytest.approx(expected_completed_at.timestamp(), abs=2) diff --git a/robot-server/tests/integration/robot_client.py b/robot-server/tests/integration/robot_client.py index 7e6b70c09f6..bda888507bb 100644 --- a/robot-server/tests/integration/robot_client.py +++ b/robot-server/tests/integration/robot_client.py @@ -10,7 +10,7 @@ from httpx import Response -_STARTUP_WAIT = 20 +_STARTUP_WAIT = 40 _SHUTDOWN_WAIT = 20 _RUN_POLL_INTERVAL = 0.1 diff --git a/robot-server/tests/integration/system/test_system_time.tavern.yaml b/robot-server/tests/integration/system/test_system_time.tavern.yaml index 1b78ea1d1f5..bffb3760cc8 100644 --- a/robot-server/tests/integration/system/test_system_time.tavern.yaml +++ b/robot-server/tests/integration/system/test_system_time.tavern.yaml @@ -34,14 +34,6 @@ stages: id: 'time' response: status_code: 422 - json: - errors: - - id: 'InvalidRequest' - title: 'Invalid Request' - detail: 'field required' - errorCode: '4000' - source: - pointer: '/data/systemTime' - name: System Time PUT request on a dev server raises error request: url: '{ot2_server_base_url}/system/time' diff --git a/robot-server/tests/integration/test_identify.tavern.yaml b/robot-server/tests/integration/test_identify.tavern.yaml index b753ef84db7..1ad773c420e 100644 --- a/robot-server/tests/integration/test_identify.tavern.yaml +++ b/robot-server/tests/integration/test_identify.tavern.yaml @@ -7,7 +7,7 @@ stages: - name: Identify a robot by flashing the lights request: method: POST - url: "{ot2_server_base_url}/identify" + url: '{ot2_server_base_url}/identify' params: seconds: 5 response: @@ -23,9 +23,6 @@ stages: - name: Attempt to send identify request without parameters request: method: POST - url: "{ot2_server_base_url}/identify" + url: '{ot2_server_base_url}/identify' response: status_code: 422 - json: - message: "query.seconds: field required" - errorCode: "4000" diff --git a/robot-server/tests/integration/test_settings_log_level.tavern.yaml b/robot-server/tests/integration/test_settings_log_level.tavern.yaml index 0a7c4e6bcf4..9604a821d87 100644 --- a/robot-server/tests/integration/test_settings_log_level.tavern.yaml +++ b/robot-server/tests/integration/test_settings_log_level.tavern.yaml @@ -11,16 +11,16 @@ marks: - warning - error stages: - - name: Set log_level to acceptable values + - name: Set log_level to acceptable values request: method: POST - url: "{ot2_server_base_url}/settings/log_level/local" - json: - log_level: "{log_level}" + url: '{ot2_server_base_url}/settings/log_level/local' + json: + log_level: '{log_level}' response: status_code: 200 json: - message: "log_level set to {log_level}" + message: 'log_level set to {log_level}' --- # Incorect Log Level Requests test_name: POST Set log level to invalid value @@ -28,31 +28,25 @@ marks: - usefixtures: - ot2_server_base_url stages: - - name: Set log_level to error + - name: Set log_level to error request: method: POST - url: "{ot2_server_base_url}/settings/log_level/local" - json: + url: '{ot2_server_base_url}/settings/log_level/local' + json: log_level: bad_level response: status_code: 422 - json: - message: "body.log_level: '{tavern.request_vars.json.log_level}' is not a valid LogLevels" - errorCode: '4000' --- test_name: POST Set log level to nothing marks: - usefixtures: - ot2_server_base_url stages: - - name: Set log_level to nothing + - name: Set log_level to nothing request: method: POST - url: "{ot2_server_base_url}/settings/log_level/local" - json: + url: '{ot2_server_base_url}/settings/log_level/local' + json: log_level: Null response: status_code: 422 - json: - message: "log_level must be set" - errorCode: '4000' diff --git a/robot-server/tests/integration/test_settings_reset_options.tavern.yaml b/robot-server/tests/integration/test_settings_reset_options.tavern.yaml index e6436d2a352..2f6ce05f120 100644 --- a/robot-server/tests/integration/test_settings_reset_options.tavern.yaml +++ b/robot-server/tests/integration/test_settings_reset_options.tavern.yaml @@ -195,6 +195,3 @@ stages: doesNotExist: true response: status_code: 422 - json: - message: !re_search 'value is not a valid enumeration member' - errorCode: '4000' diff --git a/robot-server/tests/maintenance_runs/router/test_commands_router.py b/robot-server/tests/maintenance_runs/router/test_commands_router.py index 19415f080f9..84bf5a490c5 100644 --- a/robot-server/tests/maintenance_runs/router/test_commands_router.py +++ b/robot-server/tests/maintenance_runs/router/test_commands_router.py @@ -13,7 +13,7 @@ from opentrons.protocol_engine.errors import CommandDoesNotExistError from robot_server.errors.error_responses import ApiError -from robot_server.service.json_api import MultiBodyMeta +from robot_server.service.json_api import MultiBodyMeta, RequestModel from robot_server.maintenance_runs.maintenance_run_orchestrator_store import ( MaintenanceRunOrchestratorStore, @@ -31,7 +31,6 @@ get_current_run_from_url, ) from robot_server.runs.command_models import ( - RequestModelWithCommandCreate, CommandCollectionLinks, CommandLink, CommandLinkMeta, @@ -109,7 +108,7 @@ async def test_create_run_command( result = await create_run_command( run_id="run-id", - request_body=RequestModelWithCommandCreate(data=command_request), + request_body=RequestModel(data=command_request), waitUntilComplete=False, run_orchestrator_store=mock_maintenance_run_orchestrator_store, timeout=None, @@ -151,7 +150,7 @@ async def test_create_run_command_blocking_completion( result = await create_run_command( run_id="run-id", - request_body=RequestModelWithCommandCreate(data=command_request), + request_body=RequestModel(data=command_request), waitUntilComplete=True, timeout=999, run_orchestrator_store=mock_maintenance_run_orchestrator_store, diff --git a/robot-server/tests/maintenance_runs/router/test_labware_router.py b/robot-server/tests/maintenance_runs/router/test_labware_router.py index 4e5ae1152f2..d23204aa2d2 100644 --- a/robot-server/tests/maintenance_runs/router/test_labware_router.py +++ b/robot-server/tests/maintenance_runs/router/test_labware_router.py @@ -46,7 +46,7 @@ def run() -> MaintenanceRun: @pytest.fixture() def labware_definition(minimal_labware_def: LabwareDefDict) -> LabwareDefinition: """Create a labware definition fixture.""" - return LabwareDefinition.parse_obj(minimal_labware_def) + return LabwareDefinition.model_validate(minimal_labware_def) async def test_add_labware_offset( diff --git a/robot-server/tests/maintenance_runs/test_engine_store.py b/robot-server/tests/maintenance_runs/test_engine_store.py index bf01c653df1..ed9987f5e77 100644 --- a/robot-server/tests/maintenance_runs/test_engine_store.py +++ b/robot-server/tests/maintenance_runs/test_engine_store.py @@ -109,7 +109,7 @@ async def test_create_engine_with_labware_offsets( ) assert result.labwareOffsets == [ - pe_types.LabwareOffset.construct( + pe_types.LabwareOffset.model_construct( id=matchers.IsA(str), createdAt=matchers.IsA(datetime), definitionUri="namespace/load_name/version", diff --git a/robot-server/tests/maintenance_runs/test_run_data_manager.py b/robot-server/tests/maintenance_runs/test_run_data_manager.py index 07bc9c2e476..634eaab6ce5 100644 --- a/robot-server/tests/maintenance_runs/test_run_data_manager.py +++ b/robot-server/tests/maintenance_runs/test_run_data_manager.py @@ -1,4 +1,5 @@ """Tests for RunDataManager.""" + import pytest from datetime import datetime from decoy import Decoy @@ -62,13 +63,17 @@ def engine_state_summary() -> StateSummary: """Get a StateSummary value object.""" return StateSummary( status=EngineStatus.IDLE, - errors=[ErrorOccurrence.construct(id="some-error-id")], # type: ignore[call-arg] + errors=[ErrorOccurrence.model_construct(id="some-error-id")], # type: ignore[call-arg] hasEverEnteredErrorRecovery=False, - labware=[LoadedLabware.construct(id="some-labware-id")], # type: ignore[call-arg] - labwareOffsets=[LabwareOffset.construct(id="some-labware-offset-id")], # type: ignore[call-arg] - pipettes=[LoadedPipette.construct(id="some-pipette-id")], # type: ignore[call-arg] - modules=[LoadedModule.construct(id="some-module-id")], # type: ignore[call-arg] - liquids=[Liquid(id="some-liquid-id", displayName="liquid", description="desc")], + labware=[LoadedLabware.model_construct(id="some-labware-id")], # type: ignore[call-arg] + labwareOffsets=[LabwareOffset.model_construct(id="some-labware-offset-id")], # type: ignore[call-arg] + pipettes=[LoadedPipette.model_construct(id="some-pipette-id")], # type: ignore[call-arg] + modules=[LoadedModule.model_construct(id="some-module-id")], # type: ignore[call-arg] + liquids=[ + Liquid.model_construct( + id="some-liquid-id", displayName="liquid", description="desc" + ) + ], liquidClasses=[], wells=[], ) diff --git a/robot-server/tests/modules/test_module_data_mapper.py b/robot-server/tests/modules/test_module_data_mapper.py index 62fa54e9a49..9fef6e607f4 100644 --- a/robot-server/tests/modules/test_module_data_mapper.py +++ b/robot-server/tests/modules/test_module_data_mapper.py @@ -138,7 +138,7 @@ def test_maps_magnetic_module_data( has_available_update=True, live_data=input_data, usb_port=hardware_usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), ) @@ -213,7 +213,7 @@ def test_maps_temperature_module_data( has_available_update=True, live_data=input_data, usb_port=hardware_usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), ) @@ -321,7 +321,7 @@ def test_maps_thermocycler_module_data( has_available_update=True, live_data=input_data, usb_port=hardware_usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), ) @@ -426,7 +426,7 @@ def test_maps_heater_shaker_module_data( has_available_update=True, live_data=input_data, usb_port=hardware_usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), ) diff --git a/robot-server/tests/modules/test_router.py b/robot-server/tests/modules/test_router.py index 287041f17cf..f63f46f7c27 100644 --- a/robot-server/tests/modules/test_router.py +++ b/robot-server/tests/modules/test_router.py @@ -99,7 +99,7 @@ async def test_get_modules_maps_data_and_id( hubPort=None, path="/dev/null", ), - moduleOffset=ModuleCalibrationData.construct( + moduleOffset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), data=MagneticModuleData( @@ -171,7 +171,7 @@ async def test_get_modules_maps_data_and_id( port_group=PortGroup.UNKNOWN, hub_port=None, ), - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f( x=calibration_offset.offset.x, y=calibration_offset.offset.y, diff --git a/robot-server/tests/persistence/test_pydantic.py b/robot-server/tests/persistence/test_pydantic.py new file mode 100644 index 00000000000..ea4ea1887a8 --- /dev/null +++ b/robot-server/tests/persistence/test_pydantic.py @@ -0,0 +1,35 @@ +"""Unit tests for `robot_server.persistence.pydantic`.""" + + +from pydantic import BaseModel, Field, TypeAdapter + +from robot_server.persistence import pydantic as subject + + +class _DummyModel(BaseModel): + field: str + aliasedField: str = Field(alias="aliasedFieldAlias") + + +def test_round_trip() -> None: + """Test Python->JSON->Python round trips.""" + original = _DummyModel.model_construct(field="hello", aliasedField="world") + after_round_trip = subject.json_to_pydantic( + _DummyModel, subject.pydantic_to_json(original) + ) + assert after_round_trip == original + + original_list = [original] * 10 + after_round_trip_list = subject.json_to_pydantic( + TypeAdapter(list[_DummyModel]), subject.pydantic_list_to_json(original_list) + ) + assert after_round_trip_list == original_list + + +def test_field_aliases() -> None: + """The JSON should contain field aliases, not the Python attribute names.""" + original = _DummyModel.model_construct(field="hello", aliasedField="world") + json = subject.pydantic_to_json(original) + json_list = subject.pydantic_list_to_json([original]) + assert '"aliasedFieldAlias"' in json + assert '"aliasedFieldAlias"' in json_list diff --git a/robot-server/tests/protocols/test_protocol_analyzer.py b/robot-server/tests/protocols/test_protocol_analyzer.py index 5f581afebb4..39f0feda73b 100644 --- a/robot-server/tests/protocols/test_protocol_analyzer.py +++ b/robot-server/tests/protocols/test_protocol_analyzer.py @@ -246,7 +246,7 @@ async def test_analyze_updates_pending_on_error( raised_exception = Exception("You got me!!") - error_occurrence = pe_errors.ErrorOccurrence.construct( + error_occurrence = pe_errors.ErrorOccurrence.model_construct( id="internal-error", createdAt=datetime(year=2023, month=3, day=3), errorType="EnumeratedError", diff --git a/robot-server/tests/protocols/test_protocols_router.py b/robot-server/tests/protocols/test_protocols_router.py index 0ae2c591ebd..56005162e93 100644 --- a/robot-server/tests/protocols/test_protocols_router.py +++ b/robot-server/tests/protocols/test_protocols_router.py @@ -392,7 +392,7 @@ async def test_get_protocol_by_id( key="dummy-key-111", ) - assert result.content.links == ProtocolLinks.construct(referencingRuns=[]) + assert result.content.links == ProtocolLinks.model_construct(referencingRuns=[]) assert result.status_code == 200 diff --git a/robot-server/tests/runs/router/test_base_router.py b/robot-server/tests/runs/router/test_base_router.py index ee9b291cc4a..0350bb4d0b0 100644 --- a/robot-server/tests/runs/router/test_base_router.py +++ b/robot-server/tests/runs/router/test_base_router.py @@ -1,4 +1,5 @@ """Tests for base /runs routes.""" + from opentrons.hardware_control import HardwareControlAPI from opentrons_shared_data.robot.types import RobotTypeEnum import pytest @@ -776,7 +777,7 @@ async def test_get_run_commands_errors_raises_no_run( @pytest.mark.parametrize( "error_list, expected_cursor_result", - [([], 0), ([pe_errors.ErrorOccurrence.construct(id="error-id")], 1)], + [([], 0), ([pe_errors.ErrorOccurrence.model_construct(id="error-id")], 1)], ) async def test_get_run_commands_errors_defualt_cursor( decoy: Decoy, @@ -864,7 +865,7 @@ async def test_get_current_state_success( ) assert result.status_code == 200 - assert result.content.data == RunCurrentState.construct( + assert result.content.data == RunCurrentState.model_construct( estopEngaged=False, activeNozzleLayouts={ "mock-pipette-id": ActiveNozzleLayout( @@ -874,6 +875,7 @@ async def test_get_current_state_success( ) }, tipStates={"mock-pipette-id": TipState(hasTip=True)}, + placeLabwareState=None, ) assert result.content.links == CurrentStateLinks( lastCompleted=CommandLinkNoMeta( diff --git a/robot-server/tests/runs/router/test_commands_router.py b/robot-server/tests/runs/router/test_commands_router.py index e7045fe6287..50d58993c6c 100644 --- a/robot-server/tests/runs/router/test_commands_router.py +++ b/robot-server/tests/runs/router/test_commands_router.py @@ -13,10 +13,9 @@ ) from robot_server.errors.error_responses import ApiError -from robot_server.service.json_api import MultiBodyMeta +from robot_server.service.json_api import MultiBodyMeta, RequestModel from robot_server.runs.command_models import ( - RequestModelWithCommandCreate, CommandCollectionLinks, CommandLink, CommandLinkMeta, @@ -129,7 +128,7 @@ def _stub_queued_command_state(*_a: object, **_k: object) -> pe_commands.Command result = await create_run_command( run_id="run-id", - request_body=RequestModelWithCommandCreate(data=command_request), + request_body=RequestModel(data=command_request), waitUntilComplete=False, run_orchestrator_store=mock_run_orchestrator_store, failedCommandId=None, @@ -163,7 +162,7 @@ async def test_create_command_with_failed_command_raises( with pytest.raises(ApiError): await create_run_command( run_id="run-id", - request_body=RequestModelWithCommandCreate(data=command_create), + request_body=RequestModel(data=command_create), run_orchestrator_store=mock_run_orchestrator_store, failedCommandId="123", check_estop=True, @@ -204,7 +203,7 @@ async def test_create_run_command_blocking_completion( result = await create_run_command( run_id="run-id", - request_body=RequestModelWithCommandCreate(data=command_request), + request_body=RequestModel(data=command_request), waitUntilComplete=True, timeout=999, run_orchestrator_store=mock_run_orchestrator_store, @@ -238,7 +237,7 @@ async def test_add_conflicting_setup_command( with pytest.raises(ApiError) as exc_info: await create_run_command( run_id="run-id", - request_body=RequestModelWithCommandCreate(data=command_request), + request_body=RequestModel(data=command_request), run_orchestrator_store=mock_run_orchestrator_store, failedCommandId=None, check_estop=True, @@ -273,7 +272,7 @@ async def test_add_command_to_stopped_engine( with pytest.raises(ApiError) as exc_info: await create_run_command( run_id="run-id", - request_body=RequestModelWithCommandCreate(data=command_request), + request_body=RequestModel(data=command_request), run_orchestrator_store=mock_run_orchestrator_store, failedCommandId=None, check_estop=True, diff --git a/robot-server/tests/runs/router/test_labware_router.py b/robot-server/tests/runs/router/test_labware_router.py index 1252d983efb..25f80048df6 100644 --- a/robot-server/tests/runs/router/test_labware_router.py +++ b/robot-server/tests/runs/router/test_labware_router.py @@ -1,4 +1,5 @@ """Tests for /runs routes dealing with labware offsets and definitions.""" + import pytest from datetime import datetime from decoy import Decoy @@ -49,7 +50,7 @@ def run() -> Run: @pytest.fixture() def labware_definition(minimal_labware_def: LabwareDefDict) -> LabwareDefinition: """Create a labware definition fixture.""" - return LabwareDefinition.parse_obj(minimal_labware_def) + return LabwareDefinition.model_validate(minimal_labware_def) async def test_add_labware_offset( @@ -92,7 +93,7 @@ async def test_add_labware_offset_not_current( run: Run, ) -> None: """It should 409 if the run is not current.""" - not_current_run = run.copy(update={"current": False}) + not_current_run = run.model_copy(update={"current": False}) labware_offset_request = pe_types.LabwareOffsetCreate( definitionUri="namespace_1/load_name_1/123", @@ -141,7 +142,7 @@ async def test_add_labware_definition_not_current( labware_definition: LabwareDefinition, ) -> None: """It should 409 if the run is not current.""" - not_current_run = run.copy(update={"current": False}) + not_current_run = run.model_copy(update={"current": False}) with pytest.raises(ApiError) as exc_info: await add_labware_definition( @@ -162,8 +163,8 @@ async def test_get_run_labware_definition( mock_run_data_manager.get_run_loaded_labware_definitions(run_id="run-id") ).then_return( [ - SD_LabwareDefinition.construct(namespace="test_1"), # type: ignore[call-arg] - SD_LabwareDefinition.construct(namespace="test_2"), # type: ignore[call-arg] + SD_LabwareDefinition.model_construct(namespace="test_1"), # type: ignore[call-arg] + SD_LabwareDefinition.model_construct(namespace="test_2"), # type: ignore[call-arg] ] ) @@ -172,7 +173,7 @@ async def test_get_run_labware_definition( ) assert result.content.data == [ - SD_LabwareDefinition.construct(namespace="test_1"), # type: ignore[call-arg] - SD_LabwareDefinition.construct(namespace="test_2"), # type: ignore[call-arg] + SD_LabwareDefinition.model_construct(namespace="test_1"), # type: ignore[call-arg] + SD_LabwareDefinition.model_construct(namespace="test_2"), # type: ignore[call-arg] ] assert result.status_code == 200 diff --git a/robot-server/tests/runs/test_error_recovery_mapping.py b/robot-server/tests/runs/test_error_recovery_mapping.py index 8b75ff99aad..6cab88243e3 100644 --- a/robot-server/tests/runs/test_error_recovery_mapping.py +++ b/robot-server/tests/runs/test_error_recovery_mapping.py @@ -116,9 +116,9 @@ def test_create_error_recovery_policy_defined_error( @pytest.mark.parametrize("enabled", [True, False]) def test_enabled_boolean(enabled: bool) -> None: """enabled=False should override any rules and always fail the run.""" - command = LiquidProbe.construct() # type: ignore[call-arg] + command = LiquidProbe.model_construct() # type: ignore[call-arg] error_data = DefinedErrorData[LiquidNotFoundError]( - public=LiquidNotFoundError.construct() # type: ignore[call-arg] + public=LiquidNotFoundError.model_construct() # type: ignore[call-arg] ) rules = [ @@ -160,9 +160,9 @@ def test_enabled_on_flex_disabled_on_ot2( robot_type: RobotType, expect_error_recovery_to_be_enabled: bool ) -> None: """On OT-2s, the run should always fail regardless of any input rules.""" - command = LiquidProbe.construct() # type: ignore[call-arg] + command = LiquidProbe.model_construct() # type: ignore[call-arg] error_data = DefinedErrorData[LiquidNotFoundError]( - public=LiquidNotFoundError.construct() # type: ignore[call-arg] + public=LiquidNotFoundError.model_construct() # type: ignore[call-arg] ) rules = [ diff --git a/robot-server/tests/runs/test_run_controller.py b/robot-server/tests/runs/test_run_controller.py index 1aa17ba9932..8b417aff1b2 100644 --- a/robot-server/tests/runs/test_run_controller.py +++ b/robot-server/tests/runs/test_run_controller.py @@ -111,7 +111,7 @@ def command_annotations() -> List[CommandAnnotation]: def protocol_commands() -> List[pe_commands.Command]: """Get a StateSummary value object.""" return [ - pe_commands.WaitForResume.construct( # type: ignore[call-arg] + pe_commands.WaitForResume.model_construct( # type: ignore[call-arg] params=pe_commands.WaitForResumeParams(message="hello world") ) ] diff --git a/robot-server/tests/runs/test_run_data_manager.py b/robot-server/tests/runs/test_run_data_manager.py index a56c8dbc705..8d139b4edff 100644 --- a/robot-server/tests/runs/test_run_data_manager.py +++ b/robot-server/tests/runs/test_run_data_manager.py @@ -1,4 +1,5 @@ """Tests for RunDataManager.""" + from datetime import datetime from typing import Optional, List, Dict from unittest.mock import sentinel @@ -98,13 +99,17 @@ def engine_state_summary() -> StateSummary: """Get a StateSummary value object.""" return StateSummary( status=EngineStatus.IDLE, - errors=[ErrorOccurrence.construct(id="some-error-id")], # type: ignore[call-arg] + errors=[ErrorOccurrence.model_construct(id="some-error-id")], # type: ignore[call-arg] hasEverEnteredErrorRecovery=False, - labware=[LoadedLabware.construct(id="some-labware-id")], # type: ignore[call-arg] - labwareOffsets=[LabwareOffset.construct(id="some-labware-offset-id")], # type: ignore[call-arg] - pipettes=[LoadedPipette.construct(id="some-pipette-id")], # type: ignore[call-arg] - modules=[LoadedModule.construct(id="some-module-id")], # type: ignore[call-arg] - liquids=[Liquid(id="some-liquid-id", displayName="liquid", description="desc")], + labware=[LoadedLabware.model_construct(id="some-labware-id")], # type: ignore[call-arg] + labwareOffsets=[LabwareOffset.model_construct(id="some-labware-offset-id")], # type: ignore[call-arg] + pipettes=[LoadedPipette.model_construct(id="some-pipette-id")], # type: ignore[call-arg] + modules=[LoadedModule.model_construct(id="some-module-id")], # type: ignore[call-arg] + liquids=[ + Liquid.model_construct( + id="some-liquid-id", displayName="liquid", description="desc" + ) + ], liquidClasses=[], wells=[], ) @@ -513,13 +518,17 @@ async def test_get_all_runs( """It should get all runs, including current and historical.""" current_run_data = StateSummary( status=EngineStatus.IDLE, - errors=[ErrorOccurrence.construct(id="current-error-id")], # type: ignore[call-arg] + errors=[ErrorOccurrence.model_construct(id="current-error-id")], # type: ignore[call-arg] hasEverEnteredErrorRecovery=False, - labware=[LoadedLabware.construct(id="current-labware-id")], # type: ignore[call-arg] - labwareOffsets=[LabwareOffset.construct(id="current-labware-offset-id")], # type: ignore[call-arg] - pipettes=[LoadedPipette.construct(id="current-pipette-id")], # type: ignore[call-arg] - modules=[LoadedModule.construct(id="current-module-id")], # type: ignore[call-arg] - liquids=[Liquid(id="some-liquid-id", displayName="liquid", description="desc")], + labware=[LoadedLabware.model_construct(id="current-labware-id")], # type: ignore[call-arg] + labwareOffsets=[LabwareOffset.model_construct(id="current-labware-offset-id")], # type: ignore[call-arg] + pipettes=[LoadedPipette.model_construct(id="current-pipette-id")], # type: ignore[call-arg] + modules=[LoadedModule.model_construct(id="current-module-id")], # type: ignore[call-arg] + liquids=[ + Liquid.model_construct( + id="some-liquid-id", displayName="liquid", description="desc" + ) + ], liquidClasses=[], wells=[], ) @@ -534,12 +543,12 @@ async def test_get_all_runs( historical_run_data = StateSummary( status=EngineStatus.STOPPED, - errors=[ErrorOccurrence.construct(id="old-error-id")], # type: ignore[call-arg] + errors=[ErrorOccurrence.model_construct(id="old-error-id")], # type: ignore[call-arg] hasEverEnteredErrorRecovery=False, - labware=[LoadedLabware.construct(id="old-labware-id")], # type: ignore[call-arg] - labwareOffsets=[LabwareOffset.construct(id="old-labware-offset-id")], # type: ignore[call-arg] - pipettes=[LoadedPipette.construct(id="old-pipette-id")], # type: ignore[call-arg] - modules=[LoadedModule.construct(id="old-module-id")], # type: ignore[call-arg] + labware=[LoadedLabware.model_construct(id="old-labware-id")], # type: ignore[call-arg] + labwareOffsets=[LabwareOffset.model_construct(id="old-labware-offset-id")], # type: ignore[call-arg] + pipettes=[LoadedPipette.model_construct(id="old-pipette-id")], # type: ignore[call-arg] + modules=[LoadedModule.model_construct(id="old-module-id")], # type: ignore[call-arg] liquids=[], liquidClasses=[], wells=[], @@ -958,9 +967,7 @@ def test_get_commands_errors_slice_historical_run( mock_run_store: RunStore, ) -> None: """Should get a sliced command error list from engine store.""" - expected_commands_errors_result = [ - ErrorOccurrence.construct(id="error-id") # type: ignore[call-arg] - ] + expected_commands_errors_result = [ErrorOccurrence.model_construct(id="error-id")] # type: ignore[call-arg] command_error_slice = CommandErrorSlice( cursor=1, total_length=3, commands_errors=expected_commands_errors_result @@ -985,7 +992,7 @@ def test_get_commands_errors_slice_current_run( ) -> None: """Should get a sliced command error list from engine store.""" expected_commands_errors_result = [ - ErrorOccurrence.construct(id="error-id") # type: ignore[call-arg] + ErrorOccurrence.model_construct(id="error-id") # type: ignore[call-arg] ] command_error_slice = CommandErrorSlice( @@ -1257,16 +1264,16 @@ async def test_get_current_run_labware_definition( mock_run_orchestrator_store.get_loaded_labware_definitions() ).then_return( [ - LabwareDefinition.construct(namespace="test_1"), # type: ignore[call-arg] - LabwareDefinition.construct(namespace="test_2"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="test_1"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="test_2"), # type: ignore[call-arg] ] ) result = subject.get_run_loaded_labware_definitions(run_id="run-id") assert result == [ - LabwareDefinition.construct(namespace="test_1"), # type: ignore[call-arg] - LabwareDefinition.construct(namespace="test_2"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="test_1"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="test_2"), # type: ignore[call-arg] ] diff --git a/robot-server/tests/runs/test_run_orchestrator_store.py b/robot-server/tests/runs/test_run_orchestrator_store.py index 1774215acfd..b0f8354e494 100644 --- a/robot-server/tests/runs/test_run_orchestrator_store.py +++ b/robot-server/tests/runs/test_run_orchestrator_store.py @@ -120,7 +120,7 @@ async def test_create_engine_with_labware_offsets( ) assert result.labwareOffsets == [ - pe_types.LabwareOffset.construct( + pe_types.LabwareOffset.model_construct( id=matchers.IsA(str), createdAt=matchers.IsA(datetime), definitionUri="namespace/load_name/version", diff --git a/robot-server/tests/runs/test_run_store.py b/robot-server/tests/runs/test_run_store.py index ab8e5f10fdf..400e5ef6a06 100644 --- a/robot-server/tests/runs/test_run_store.py +++ b/robot-server/tests/runs/test_run_store.py @@ -1,4 +1,5 @@ """Tests for robot_server.runs.run_store.""" + from datetime import datetime, timezone from pathlib import Path from typing import List, Optional, Type @@ -121,7 +122,7 @@ def protocol_commands_errors() -> List[pe_commands.Command]: params=pe_commands.WaitForResumeParams(message="hello world"), result=pe_commands.WaitForResumeResult(), intent=pe_commands.CommandIntent.PROTOCOL, - error=ErrorOccurrence.construct( + error=ErrorOccurrence.model_construct( id="error-id", createdAt=datetime(2024, 1, 1), errorType="blah-blah", @@ -136,7 +137,7 @@ def protocol_commands_errors() -> List[pe_commands.Command]: params=pe_commands.WaitForResumeParams(message="hey world"), result=pe_commands.WaitForResumeResult(), intent=pe_commands.CommandIntent.PROTOCOL, - error=ErrorOccurrence.construct( + error=ErrorOccurrence.model_construct( id="error-id-2", createdAt=datetime(2024, 1, 1), errorType="blah-blah", @@ -241,10 +242,10 @@ def run_time_parameters() -> List[pe_types.RunTimeParameter]: @pytest.fixture def invalid_state_summary() -> StateSummary: """Should fail pydantic validation.""" - analysis_error = pe_errors.ErrorOccurrence.construct( + analysis_error = pe_errors.ErrorOccurrence.model_construct( id="error-id", # Invalid value here should fail analysis - createdAt=MountType.LEFT, # type: ignore + createdAt=MountType.LEFT, # type: ignore[arg-type] errorType="BadError", detail="oh no", ) @@ -737,7 +738,7 @@ def test_get_run_time_parameters_invalid( state_summary: StateSummary, ) -> None: """It should return an empty list if there invalid parameters.""" - bad_parameters = [pe_types.BooleanParameter.construct(foo="bar")] # type: ignore[call-arg] + bad_parameters = [pe_types.BooleanParameter.model_construct(foo="bar")] # type: ignore[call-arg] subject.insert( run_id="run-id", protocol_id=None, @@ -993,12 +994,12 @@ def test_get_all_commands_as_preserialized_list( run_id="run-id", include_fixit_commands=True ) assert result == [ - '{"id": "pause-1", "createdAt": "2021-01-01T00:00:00", "commandType": "waitForResume",' - ' "key": "command-key", "status": "succeeded", "params": {"message": "hello world"}, "result": {}, "intent": "protocol"}', - '{"id": "pause-2", "createdAt": "2022-02-02T00:00:00", "commandType": "waitForResume",' - ' "key": "command-key", "status": "succeeded", "params": {"message": "hey world"}, "result": {}, "intent": "protocol"}', - '{"id": "pause-3", "createdAt": "2023-03-03T00:00:00", "commandType": "waitForResume", "key": "command-key", "status": "succeeded", "params": {"message": "sup world"}, "result": {}}', - '{"id": "fixit-pause-1", "createdAt": "2021-01-01T00:00:00", "commandType": "waitForResume", "key": "command-key", "status": "succeeded", "params": {"message": "hello world"}, "result": {}, "intent": "fixit"}', + '{"id":"pause-1","createdAt":"2021-01-01T00:00:00","commandType":"waitForResume",' + '"key":"command-key","status":"succeeded","params":{"message":"hello world"},"result":{},"intent":"protocol"}', + '{"id":"pause-2","createdAt":"2022-02-02T00:00:00","commandType":"waitForResume",' + '"key":"command-key","status":"succeeded","params":{"message":"hey world"},"result":{},"intent":"protocol"}', + '{"id":"pause-3","createdAt":"2023-03-03T00:00:00","commandType":"waitForResume","key":"command-key","status":"succeeded","params":{"message":"sup world"},"result":{}}', + '{"id":"fixit-pause-1","createdAt":"2021-01-01T00:00:00","commandType":"waitForResume","key":"command-key","status":"succeeded","params":{"message":"hello world"},"result":{},"intent":"fixit"}', ] @@ -1023,9 +1024,9 @@ def test_get_all_commands_as_preserialized_list_no_fixit( run_id="run-id", include_fixit_commands=False ) assert result == [ - '{"id": "pause-1", "createdAt": "2021-01-01T00:00:00", "commandType": "waitForResume",' - ' "key": "command-key", "status": "succeeded", "params": {"message": "hello world"}, "result": {}, "intent": "protocol"}', - '{"id": "pause-2", "createdAt": "2022-02-02T00:00:00", "commandType": "waitForResume",' - ' "key": "command-key", "status": "succeeded", "params": {"message": "hey world"}, "result": {}, "intent": "protocol"}', - '{"id": "pause-3", "createdAt": "2023-03-03T00:00:00", "commandType": "waitForResume", "key": "command-key", "status": "succeeded", "params": {"message": "sup world"}, "result": {}}', + '{"id":"pause-1","createdAt":"2021-01-01T00:00:00","commandType":"waitForResume",' + '"key":"command-key","status":"succeeded","params":{"message":"hello world"},"result":{},"intent":"protocol"}', + '{"id":"pause-2","createdAt":"2022-02-02T00:00:00","commandType":"waitForResume",' + '"key":"command-key","status":"succeeded","params":{"message":"hey world"},"result":{},"intent":"protocol"}', + '{"id":"pause-3","createdAt":"2023-03-03T00:00:00","commandType":"waitForResume","key":"command-key","status":"succeeded","params":{"message":"sup world"},"result":{}}', ] diff --git a/robot-server/tests/service/json_api/test_request.py b/robot-server/tests/service/json_api/test_request.py index 446c7e32339..8c2012b158b 100644 --- a/robot-server/tests/service/json_api/test_request.py +++ b/robot-server/tests/service/json_api/test_request.py @@ -9,38 +9,44 @@ def test_attributes_as_dict(): DictRequest = RequestModel[dict] obj_to_validate = {"data": {"some_data": 1}} - my_request_obj = DictRequest.parse_obj(obj_to_validate) - assert my_request_obj.dict() == {"data": {"some_data": 1}} + my_request_obj = DictRequest.model_validate(obj_to_validate) + assert my_request_obj.model_dump() == {"data": {"some_data": 1}} def test_attributes_as_item_model(): ItemRequest = RequestModel[ItemModel] obj_to_validate = {"data": {"name": "apple", "quantity": 10, "price": 1.20}} - my_request_obj = ItemRequest.parse_obj(obj_to_validate) - assert my_request_obj.dict() == obj_to_validate + my_request_obj = ItemRequest.model_validate(obj_to_validate) + assert my_request_obj.model_dump() == obj_to_validate def test_attributes_as_item_model_empty_dict(): ItemRequest = RequestModel[ItemModel] obj_to_validate: Dict[str, Any] = {"data": {}} with raises(ValidationError) as e: - ItemRequest.parse_obj(obj_to_validate) + ItemRequest.model_validate(obj_to_validate) assert e.value.errors() == [ { "loc": ("data", "name"), - "msg": "field required", - "type": "value_error.missing", + "msg": "Field required", + "type": "missing", + "input": {}, + "url": "https://errors.pydantic.dev/2.9/v/missing", }, { "loc": ("data", "quantity"), - "msg": "field required", - "type": "value_error.missing", + "msg": "Field required", + "type": "missing", + "input": {}, + "url": "https://errors.pydantic.dev/2.9/v/missing", }, { "loc": ("data", "price"), - "msg": "field required", - "type": "value_error.missing", + "msg": "Field required", + "type": "missing", + "input": {}, + "url": "https://errors.pydantic.dev/2.9/v/missing", }, ] @@ -49,13 +55,15 @@ def test_attributes_required(): MyRequest = RequestModel[dict] obj_to_validate = {"data": None} with raises(ValidationError) as e: - MyRequest.parse_obj(obj_to_validate) + MyRequest.model_validate(obj_to_validate) assert e.value.errors() == [ { "loc": ("data",), - "msg": "none is not an allowed value", - "type": "type_error.none.not_allowed", + "msg": "Input should be a valid dictionary", + "input": None, + "url": "https://errors.pydantic.dev/2.9/v/dict_type", + "type": "dict_type", }, ] @@ -64,13 +72,15 @@ def test_data_required(): MyRequest = RequestModel[dict] obj_to_validate = {"data": None} with raises(ValidationError) as e: - MyRequest.parse_obj(obj_to_validate) + MyRequest.model_validate(obj_to_validate) assert e.value.errors() == [ { "loc": ("data",), - "msg": "none is not an allowed value", - "type": "type_error.none.not_allowed", + "msg": "Input should be a valid dictionary", + "input": None, + "url": "https://errors.pydantic.dev/2.9/v/dict_type", + "type": "dict_type", }, ] @@ -80,7 +90,7 @@ def test_request_with_id(): obj_to_validate = { "data": {"type": "item", "attributes": {}, "id": "abc123"}, } - my_request_obj = MyRequest.parse_obj(obj_to_validate) - assert my_request_obj.dict() == { + my_request_obj = MyRequest.model_validate(obj_to_validate) + assert my_request_obj.model_dump() == { "data": {"type": "item", "attributes": {}, "id": "abc123"}, } diff --git a/robot-server/tests/service/json_api/test_resource_links.py b/robot-server/tests/service/json_api/test_resource_links.py index 5505dfeac21..581c018892a 100644 --- a/robot-server/tests/service/json_api/test_resource_links.py +++ b/robot-server/tests/service/json_api/test_resource_links.py @@ -14,8 +14,8 @@ def test_follows_structure(): "self": {"href": "/items/1", "meta": None}, } } - validated = ThingWithLink.parse_obj(structure_to_validate) - assert validated.dict() == structure_to_validate + validated = ThingWithLink.model_validate(structure_to_validate) + assert validated.model_dump() == structure_to_validate def test_must_be_self_key_with_string_value(): @@ -25,7 +25,13 @@ def test_must_be_self_key_with_string_value(): } } with raises(ValidationError) as e: - ThingWithLink.parse_obj(invalid_structure_to_validate) + ThingWithLink.model_validate(invalid_structure_to_validate) assert e.value.errors() == [ - {"loc": ("links",), "msg": "field required", "type": "value_error.missing"} + { + "loc": ("links",), + "msg": "Field required", + "type": "missing", + "input": {"invalid": {"key": "value"}}, + "url": "https://errors.pydantic.dev/2.9/v/missing", + } ] diff --git a/robot-server/tests/service/json_api/test_response.py b/robot-server/tests/service/json_api/test_response.py index 6952468229b..05e01f45b6c 100644 --- a/robot-server/tests/service/json_api/test_response.py +++ b/robot-server/tests/service/json_api/test_response.py @@ -126,4 +126,4 @@ class ResponseSpec(NamedTuple): @pytest.mark.parametrize(ResponseSpec._fields, RESPONSE_SPECS) def test_response_to_dict(subject: BaseModel, expected: Dict[str, Any]) -> None: - assert subject.dict() == expected + assert subject.model_dump() == expected diff --git a/robot-server/tests/service/legacy/models/test_control.py b/robot-server/tests/service/legacy/models/test_control.py index 058ae5c80e4..931d7c0811a 100644 --- a/robot-server/tests/service/legacy/models/test_control.py +++ b/robot-server/tests/service/legacy/models/test_control.py @@ -11,12 +11,12 @@ def test_robot_home_target(): def test_robot_move_target_points_too_few(): - with pytest.raises(ValueError, match="ensure this value has at least 3 items"): + with pytest.raises(ValueError, match="List should have at least 3 items"): control.RobotMoveTarget(target=control.MotionTarget.pipette, point=[1, 2]) def test_robot_move_target_points_too_many(): - with pytest.raises(ValueError, match="ensure this value has at most 3 items"): + with pytest.raises(ValueError, match="List should have at most 3 items"): control.RobotMoveTarget(target=control.MotionTarget.pipette, point=[1, 2, 3, 4]) diff --git a/robot-server/tests/service/legacy/routers/test_settings.py b/robot-server/tests/service/legacy/routers/test_settings.py index 6c9ae8adb56..4e8ee53b138 100644 --- a/robot-server/tests/service/legacy/routers/test_settings.py +++ b/robot-server/tests/service/legacy/routers/test_settings.py @@ -146,6 +146,7 @@ def test_receive_attached_pipette_settings( pip_types.PipetteModelType.p20, pip_types.PipetteChannelType.EIGHT_CHANNEL, pip_types.PipetteVersionType(3, 5), + pip_types.PipetteOEMType.OT, ), pipette_serial_number="P12345", pipette_override_path="nope", @@ -598,7 +599,7 @@ def test_reset_invalid_option( assert resp.status_code == 422 body = resp.json() assert "message" in body - assert "not a valid enumeration member" in body["message"] + assert "Input should be" in body["message"] @pytest.fixture() diff --git a/robot-server/tests/service/session/models/test_command.py b/robot-server/tests/service/session/models/test_command.py index 67d99f409ac..695638262a4 100644 --- a/robot-server/tests/service/session/models/test_command.py +++ b/robot-server/tests/service/session/models/test_command.py @@ -15,7 +15,7 @@ ) def test_empty(command_def: command_definitions.CommandDefinition): """Test creation of empty command request and response.""" - request = command.CommandRequest.parse_obj( + request = command.CommandRequest.model_validate( {"data": {"command": command_def.value, "data": {}}} ) assert request.data.command == command_def @@ -57,6 +57,6 @@ def test_empty(command_def: command_definitions.CommandDefinition): def test_requires_data(command_def: command_definitions.CommandDefinition): """Test creation of command requiring data will fail with empty body.""" with pytest.raises(ValidationError): - command.CommandRequest.parse_obj( + command.CommandRequest.model_validate( {"data": {"command": command_def.value, "data": {}}} ) diff --git a/robot-server/tests/subsystems/test_router.py b/robot-server/tests/subsystems/test_router.py index c77e291736c..8b6357a0c1e 100644 --- a/robot-server/tests/subsystems/test_router.py +++ b/robot-server/tests/subsystems/test_router.py @@ -72,7 +72,7 @@ def thread_manager(decoy: Decoy, ot3_hardware_api: "OT3API") -> ThreadManagedHar from opentrons.hardware_control.ot3api import OT3API except ImportError: pytest.skip("Cannot run on OT-2 (for now)") - manager = decoy.mock(cls=ThreadManagedHardware) # type: ignore[misc] + manager = decoy.mock(cls=ThreadManagedHardware) decoy.when(manager.wrapped()).then_return(ot3_hardware_api) decoy.when(manager.wraps_instance(OT3API)).then_return(True) return cast(ThreadManagedHardware, manager) @@ -104,7 +104,7 @@ def _build_attached_subsystems( def _build_subsystem_data( subsystem: SubSystem, state: SubSystemState ) -> PresentSubsystem: - return PresentSubsystem.construct( + return PresentSubsystem.model_construct( name=subsystem, ok=state.ok, current_fw_version=str(state.current_fw_version), @@ -230,25 +230,25 @@ async def test_get_subsystem_updates_with_some( response = await get_subsystem_updates(update_manager) assert response.content.data == [ - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=x_process_details.update_id, createdAt=x_process_details.created_at, subsystem=x_process_details.subsystem, updateStatus=x_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=y_process_details.update_id, createdAt=y_process_details.created_at, subsystem=y_process_details.subsystem, updateStatus=y_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=head_process_details.update_id, createdAt=head_process_details.created_at, subsystem=head_process_details.subsystem, updateStatus=head_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=pipette_process_details.update_id, createdAt=pipette_process_details.created_at, subsystem=pipette_process_details.subsystem, @@ -284,7 +284,7 @@ async def test_get_subsystem_update_succeeds( await update_manager.get_ongoing_update_process_handle_by_subsystem(subsystem) ).then_return(handle) response = await get_subsystem_update(subsystem, update_manager) - assert response.content.data == UpdateProgressData.construct( + assert response.content.data == UpdateProgressData.model_construct( id=details.update_id, createdAt=details.created_at, subsystem=details.subsystem, @@ -329,7 +329,7 @@ async def test_get_subsystem_update_error( await update_manager.get_ongoing_update_process_handle_by_subsystem(subsystem) ).then_return(handle) response = await get_subsystem_update(subsystem, update_manager) - assert response.content.data == UpdateProgressData.construct( + assert response.content.data == UpdateProgressData.model_construct( id=details.update_id, createdAt=details.created_at, subsystem=details.subsystem, @@ -386,25 +386,25 @@ async def test_get_all_updates_some( ) response = await get_update_processes(update_manager) assert response.content.data == [ - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=x_process_details.update_id, createdAt=x_process_details.created_at, subsystem=x_process_details.subsystem, updateStatus=x_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=y_process_details.update_id, createdAt=y_process_details.created_at, subsystem=y_process_details.subsystem, updateStatus=y_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=head_process_details.update_id, createdAt=head_process_details.created_at, subsystem=head_process_details.subsystem, updateStatus=head_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=pipette_process_details.update_id, createdAt=pipette_process_details.created_at, subsystem=pipette_process_details.subsystem, @@ -526,7 +526,7 @@ async def test_begin_update( headers["Location"] == f"http://127.0.0.1:31950/subsystems/updates/current/{subsystem.value}" ) - assert response_data.content.data == UpdateProgressData.construct( + assert response_data.content.data == UpdateProgressData.model_construct( id=update_id, createdAt=created_at, subsystem=subsystem, diff --git a/robot-server/tests/system/test_system_router.py b/robot-server/tests/system/test_system_router.py index 68d2e765460..4b3f32402e3 100644 --- a/robot-server/tests/system/test_system_router.py +++ b/robot-server/tests/system/test_system_router.py @@ -1,13 +1,17 @@ """Tests for the /system router.""" +from typing import Iterator + import pytest from mock import MagicMock, patch from datetime import datetime, timezone from starlette.testclient import TestClient -from typing import Iterator +from pydantic import TypeAdapter from robot_server.service.json_api import ResourceLink, ResourceLinks, ResourceLinkKey from robot_server.system import errors, router +from tests.conftest import datetime_to_zulu_iso8601 + @pytest.fixture def mock_system_time() -> datetime: @@ -22,10 +26,13 @@ def mock_set_system_time(mock_system_time: datetime) -> Iterator[MagicMock]: yield p +ResourceLinksAdapter: TypeAdapter[ResourceLinks] = TypeAdapter(ResourceLinks) + + @pytest.fixture def response_links() -> ResourceLinks: """Get expected /system/time resource links.""" - return {ResourceLinkKey.self: ResourceLink(href="/system/time")} + return {ResourceLinkKey.self.value: ResourceLink(href="/system/time")} def test_raise_system_synchronized_error( @@ -103,7 +110,10 @@ def test_set_system_time( }, ) assert response.json() == { - "data": {"systemTime": mock_system_time.isoformat(), "id": "time"}, - "links": response_links, + "data": { + "systemTime": datetime_to_zulu_iso8601(mock_system_time), + "id": "time", + }, + "links": ResourceLinksAdapter.dump_python(response_links), } assert response.status_code == 200 diff --git a/scripts/eslint-plugin-opentrons/lib/index.js b/scripts/eslint-plugin-opentrons/lib/index.js index 08e970bedf9..654b6681d0c 100644 --- a/scripts/eslint-plugin-opentrons/lib/index.js +++ b/scripts/eslint-plugin-opentrons/lib/index.js @@ -4,4 +4,6 @@ module.exports.rules = { 'no-imports-up-the-tree-of-life': require('./rules/no-imports-up-the-tree-of-life'), 'no-imports-across-applications': require('./rules/no-imports-across-applications'), + 'no-margins-in-css': require('./rules/no-margins-in-css'), + 'no-margins-inline': require('./rules/no-margins-inline'), } diff --git a/scripts/eslint-plugin-opentrons/lib/rules/no-margins-in-css.js b/scripts/eslint-plugin-opentrons/lib/rules/no-margins-in-css.js new file mode 100644 index 00000000000..e3a24bbd5ef --- /dev/null +++ b/scripts/eslint-plugin-opentrons/lib/rules/no-margins-in-css.js @@ -0,0 +1,46 @@ +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Disallow the use of margin-related properties in css-in-js', + category: 'Best Practices', + recommended: false, + }, + messages: { + noMarginInCssInJs: "Avoid using '{{property}}' in css-in-js.", + }, + }, + create(context) { + return { + // Check for CSS-in-JS template literals + TaggedTemplateExpression(node) { + const forbiddenMargins = [ + 'margin', + // 'margin-top', + // 'margin-left', + // 'margin-right', + // 'margin-bottom', + ] + + if (node.tag.type === 'Identifier' && node.tag.name === 'css') { + const templateLiteral = node.quasi + templateLiteral.quasis.forEach(quasi => { + const text = quasi.value.raw + forbiddenMargins.forEach(property => { + const regex = new RegExp(`\\b${property}\\b`, 'i') + if (regex.test(text)) { + context.report({ + node: quasi, + messageId: 'noMarginInCssInJs', + data: { + property, + }, + }) + } + }) + }) + } + }, + } + }, +} diff --git a/scripts/eslint-plugin-opentrons/lib/rules/no-margins-inline.js b/scripts/eslint-plugin-opentrons/lib/rules/no-margins-inline.js new file mode 100644 index 00000000000..265ce8472da --- /dev/null +++ b/scripts/eslint-plugin-opentrons/lib/rules/no-margins-inline.js @@ -0,0 +1,52 @@ +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Disallow the use of margin-related styles', + category: 'Best Practices', + recommended: false, + }, + messages: { + noMarginInline: "Avoid using '{{property}}' in your components.", + }, + schema: [], + }, + create(context) { + const forbiddenMargins = [ + 'margin', + 'marginLeft', + 'marginRight', + 'marginTop', + 'marginBottom', + 'marginX', + 'marginY', + ] + + return { + // Existing visitor for object properties + Property(node) { + if (forbiddenMargins.includes(node.key.name || node.key.value)) { + context.report({ + node: node.key, + messageId: 'noMarginInline', + data: { + property: node.key.name || node.key.value, + }, + }) + } + }, + // New visitor for JSX attributes + JSXAttribute(node) { + if (forbiddenMargins.includes(node.name.name)) { + context.report({ + node: node.name, + messageId: 'noMarginInline', + data: { + property: node.name.name, + }, + }) + } + }, + } + }, +} diff --git a/server-utils/Pipfile b/server-utils/Pipfile index b54d6962543..537a0a670f7 100755 --- a/server-utils/Pipfile +++ b/server-utils/Pipfile @@ -19,7 +19,7 @@ pytest-lazy-fixture = "==0.6.3" pytest-xdist = "~=2.5.0" requests = "==2.31.0" mock = "==5.1.0" -mypy = "==1.8.0" +mypy = "==1.11.0" flake8 = "==7.0.0" flake8-annotations = "==3.0.1" flake8-docstrings = "~=1.7.0" diff --git a/server-utils/Pipfile.lock b/server-utils/Pipfile.lock index c9060279ba2..768034d5cb4 100644 --- a/server-utils/Pipfile.lock +++ b/server-utils/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "5c9bee178723885363cdb4a226e6c1b7988731c23ae83fdde921d4ebb510090e" + "sha256": "6410a533fa68be2f8ba9ee2d77fd5f5d63653019d1a256ee8026ac52536d022e" }, "pipfile-spec": 6, "requires": { @@ -27,6 +27,14 @@ } }, "develop": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "anyio": { "hashes": [ "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", @@ -45,11 +53,11 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "black": { "hashes": [ @@ -83,11 +91,11 @@ }, "certifi": { "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" ], "markers": "python_version >= '3.6'", - "version": "==2024.2.2" + "version": "==2024.8.30" }, "charset-normalizer": { "hashes": [ @@ -206,61 +214,81 @@ "toml" ], "hashes": [ - "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61", - "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1", - "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7", - "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7", - "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75", - "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd", - "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35", - "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04", - "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6", - "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042", - "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166", - "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1", - "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d", - "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c", - "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66", - "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70", - "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1", - "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676", - "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630", - "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a", - "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74", - "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad", - "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19", - "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6", - "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448", - "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018", - "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218", - "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756", - "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54", - "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45", - "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628", - "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968", - "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d", - "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25", - "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60", - "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950", - "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06", - "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295", - "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b", - "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c", - "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc", - "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74", - "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1", - "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee", - "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011", - "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156", - "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766", - "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5", - "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581", - "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016", - "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c", - "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3" + "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", + "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", + "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", + "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989", + "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", + "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", + "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", + "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", + "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", + "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", + "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", + "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", + "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", + "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", + "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", + "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8", + "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", + "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", + "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", + "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", + "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", + "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", + "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", + "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", + "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", + "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0", + "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", + "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", + "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", + "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", + "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", + "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", + "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", + "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", + "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de", + "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", + "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", + "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569", + "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", + "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", + "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", + "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", + "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", + "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6", + "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", + "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", + "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", + "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", + "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", + "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b", + "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", + "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", + "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3", + "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", + "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", + "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", + "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", + "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", + "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", + "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", + "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", + "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", + "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7", + "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", + "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", + "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", + "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", + "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", + "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a", + "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", + "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", + "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc" ], "markers": "python_version >= '3.8'", - "version": "==7.4.1" + "version": "==7.6.1" }, "decoy": { "hashes": [ @@ -273,27 +301,27 @@ }, "exceptiongroup": { "hashes": [ - "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", - "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.0" + "version": "==1.2.2" }, "execnet": { "hashes": [ - "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", - "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" + "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", + "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.2" + "markers": "python_version >= '3.8'", + "version": "==2.1.1" }, "fastapi": { "hashes": [ - "sha256:976df7bab51ac7beda9f68c4513b8c4490b5c1135c72aafd0a5ee4023ec5282e", - "sha256:ac78f717cd80d657bd183f94d33b9bda84aa376a46a9dab513586b8eef1dc6fc" + "sha256:271662daf986da8fa98dc2b7c7f61c4abdfdccfb4786d79ed8b2878f172c6d5f", + "sha256:acb5f941ea8215663283c10018323ba7ea737c571b67fc7e88e9469c7eb1d12e" ], "markers": "python_version >= '3.7'", - "version": "==0.99.1" + "version": "==0.100.0" }, "flake8": { "hashes": [ @@ -392,37 +420,37 @@ }, "mypy": { "hashes": [ - "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", - "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", - "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", - "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", - "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", - "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", - "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", - "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", - "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", - "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", - "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", - "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", - "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", - "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", - "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", - "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", - "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", - "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", - "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", - "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", - "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", - "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", - "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", - "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", - "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", - "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", - "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.8.0" + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -434,11 +462,11 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", + "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==24.1" }, "pathspec": { "hashes": [ @@ -450,19 +478,19 @@ }, "platformdirs": { "hashes": [ - "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", - "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" + "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", + "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" ], "markers": "python_version >= '3.8'", - "version": "==4.2.0" + "version": "==4.2.2" }, "pluggy": { "hashes": [ - "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", - "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" ], "markers": "python_version >= '3.8'", - "version": "==1.4.0" + "version": "==1.5.0" }, "py": { "hashes": [ @@ -482,45 +510,106 @@ }, "pydantic": { "hashes": [ - "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303", - "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe", - "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47", - "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494", - "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33", - "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86", - "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d", - "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c", - "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a", - "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565", - "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb", - "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62", - "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62", - "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0", - "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523", - "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d", - "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405", - "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f", - "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b", - "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718", - "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed", - "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb", - "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5", - "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc", - "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942", - "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe", - "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246", - "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350", - "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303", - "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09", - "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33", - "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8", - "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a", - "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1", - "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6", - "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d" + "sha256:c7a8a9fdf7d100afa49647eae340e2d23efa382466a8d177efcd1381e9be5598", + "sha256:f66a7073abd93214a20c5f7b32d56843137a7a2e70d02111f3be287035c45370" ], - "markers": "python_version >= '3.7'", - "version": "==1.10.12" + "markers": "python_version >= '3.8'", + "version": "==2.9.0" + }, + "pydantic-core": { + "hashes": [ + "sha256:0102e49ac7d2df3379ef8d658d3bc59d3d769b0bdb17da189b75efa861fc07b4", + "sha256:0123655fedacf035ab10c23450163c2f65a4174f2bb034b188240a6cf06bb123", + "sha256:043ef8469f72609c4c3a5e06a07a1f713d53df4d53112c6d49207c0bd3c3bd9b", + "sha256:0448b81c3dfcde439551bb04a9f41d7627f676b12701865c8a2574bcea034437", + "sha256:05b366fb8fe3d8683b11ac35fa08947d7b92be78ec64e3277d03bd7f9b7cda79", + "sha256:07049ec9306ec64e955b2e7c40c8d77dd78ea89adb97a2013d0b6e055c5ee4c5", + "sha256:084414ffe9a85a52940b49631321d636dadf3576c30259607b75516d131fecd0", + "sha256:086c5db95157dc84c63ff9d96ebb8856f47ce113c86b61065a066f8efbe80acf", + "sha256:12625e69b1199e94b0ae1c9a95d000484ce9f0182f9965a26572f054b1537e44", + "sha256:16b25a4a120a2bb7dab51b81e3d9f3cde4f9a4456566c403ed29ac81bf49744f", + "sha256:19f1352fe4b248cae22a89268720fc74e83f008057a652894f08fa931e77dced", + "sha256:1a2ab4f410f4b886de53b6bddf5dd6f337915a29dd9f22f20f3099659536b2f6", + "sha256:1c7b81beaf7c7ebde978377dc53679c6cba0e946426fc7ade54251dfe24a7604", + "sha256:1cf842265a3a820ebc6388b963ead065f5ce8f2068ac4e1c713ef77a67b71f7c", + "sha256:1eb37f7d6a8001c0f86dc8ff2ee8d08291a536d76e49e78cda8587bb54d8b329", + "sha256:23af245b8f2f4ee9e2c99cb3f93d0e22fb5c16df3f2f643f5a8da5caff12a653", + "sha256:257d6a410a0d8aeb50b4283dea39bb79b14303e0fab0f2b9d617701331ed1515", + "sha256:276ae78153a94b664e700ac362587c73b84399bd1145e135287513442e7dfbc7", + "sha256:2b1a195efd347ede8bcf723e932300292eb13a9d2a3c1f84eb8f37cbbc905b7f", + "sha256:329a721253c7e4cbd7aad4a377745fbcc0607f9d72a3cc2102dd40519be75ed2", + "sha256:358331e21a897151e54d58e08d0219acf98ebb14c567267a87e971f3d2a3be59", + "sha256:3649bd3ae6a8ebea7dc381afb7f3c6db237fc7cebd05c8ac36ca8a4187b03b30", + "sha256:3713dc093d5048bfaedbba7a8dbc53e74c44a140d45ede020dc347dda18daf3f", + "sha256:3ef71ec876fcc4d3bbf2ae81961959e8d62f8d74a83d116668409c224012e3af", + "sha256:41ae8537ad371ec018e3c5da0eb3f3e40ee1011eb9be1da7f965357c4623c501", + "sha256:4a801c5e1e13272e0909c520708122496647d1279d252c9e6e07dac216accc41", + "sha256:4c83c64d05ffbbe12d4e8498ab72bdb05bcc1026340a4a597dc647a13c1605ec", + "sha256:4cebb9794f67266d65e7e4cbe5dcf063e29fc7b81c79dc9475bd476d9534150e", + "sha256:5668b3173bb0b2e65020b60d83f5910a7224027232c9f5dc05a71a1deac9f960", + "sha256:56e6a12ec8d7679f41b3750ffa426d22b44ef97be226a9bab00a03365f217b2b", + "sha256:582871902e1902b3c8e9b2c347f32a792a07094110c1bca6c2ea89b90150caac", + "sha256:5c8aa40f6ca803f95b1c1c5aeaee6237b9e879e4dfb46ad713229a63651a95fb", + "sha256:5d813fd871b3d5c3005157622ee102e8908ad6011ec915a18bd8fde673c4360e", + "sha256:5dd0ec5f514ed40e49bf961d49cf1bc2c72e9b50f29a163b2cc9030c6742aa73", + "sha256:5f3cf3721eaf8741cffaf092487f1ca80831202ce91672776b02b875580e174a", + "sha256:6294907eaaccf71c076abdd1c7954e272efa39bb043161b4b8aa1cd76a16ce43", + "sha256:64d094ea1aa97c6ded4748d40886076a931a8bf6f61b6e43e4a1041769c39dd2", + "sha256:6650a7bbe17a2717167e3e23c186849bae5cef35d38949549f1c116031b2b3aa", + "sha256:67b6655311b00581914aba481729971b88bb8bc7996206590700a3ac85e457b8", + "sha256:6b06c5d4e8701ac2ba99a2ef835e4e1b187d41095a9c619c5b185c9068ed2a49", + "sha256:6ce883906810b4c3bd90e0ada1f9e808d9ecf1c5f0b60c6b8831d6100bcc7dd6", + "sha256:6db09153d8438425e98cdc9a289c5fade04a5d2128faff8f227c459da21b9703", + "sha256:6f80fba4af0cb1d2344869d56430e304a51396b70d46b91a55ed4959993c0589", + "sha256:743e5811b0c377eb830150d675b0847a74a44d4ad5ab8845923d5b3a756d8100", + "sha256:753294d42fb072aa1775bfe1a2ba1012427376718fa4c72de52005a3d2a22178", + "sha256:7568f682c06f10f30ef643a1e8eec4afeecdafde5c4af1b574c6df079e96f96c", + "sha256:7706e15cdbf42f8fab1e6425247dfa98f4a6f8c63746c995d6a2017f78e619ae", + "sha256:785e7f517ebb9890813d31cb5d328fa5eda825bb205065cde760b3150e4de1f7", + "sha256:7a05c0240f6c711eb381ac392de987ee974fa9336071fb697768dfdb151345ce", + "sha256:7ce7eaf9a98680b4312b7cebcdd9352531c43db00fca586115845df388f3c465", + "sha256:7ce8e26b86a91e305858e018afc7a6e932f17428b1eaa60154bd1f7ee888b5f8", + "sha256:7d0324a35ab436c9d768753cbc3c47a865a2cbc0757066cb864747baa61f6ece", + "sha256:7e9b24cca4037a561422bf5dc52b38d390fb61f7bfff64053ce1b72f6938e6b2", + "sha256:810ca06cca91de9107718dc83d9ac4d2e86efd6c02cba49a190abcaf33fb0472", + "sha256:820f6ee5c06bc868335e3b6e42d7ef41f50dfb3ea32fbd523ab679d10d8741c0", + "sha256:82764c0bd697159fe9947ad59b6db6d7329e88505c8f98990eb07e84cc0a5d81", + "sha256:8ae65fdfb8a841556b52935dfd4c3f79132dc5253b12c0061b96415208f4d622", + "sha256:8d5b0ff3218858859910295df6953d7bafac3a48d5cd18f4e3ed9999efd2245f", + "sha256:95d6bf449a1ac81de562d65d180af5d8c19672793c81877a2eda8fde5d08f2fd", + "sha256:964c7aa318da542cdcc60d4a648377ffe1a2ef0eb1e996026c7f74507b720a78", + "sha256:96ef39add33ff58cd4c112cbac076726b96b98bb8f1e7f7595288dcfb2f10b57", + "sha256:a6612c2a844043e4d10a8324c54cdff0042c558eef30bd705770793d70b224aa", + "sha256:a8031074a397a5925d06b590121f8339d34a5a74cfe6970f8a1124eb8b83f4ac", + "sha256:aab9e522efff3993a9e98ab14263d4e20211e62da088298089a03056980a3e69", + "sha256:ae579143826c6f05a361d9546446c432a165ecf1c0b720bbfd81152645cb897d", + "sha256:ae90b9e50fe1bd115b24785e962b51130340408156d34d67b5f8f3fa6540938e", + "sha256:b18cf68255a476b927910c6873d9ed00da692bb293c5b10b282bd48a0afe3ae2", + "sha256:b7efb12e5071ad8d5b547487bdad489fbd4a5a35a0fc36a1941517a6ad7f23e0", + "sha256:c4d9f15ffe68bcd3898b0ad7233af01b15c57d91cd1667f8d868e0eacbfe3f87", + "sha256:c53100c8ee5a1e102766abde2158077d8c374bee0639201f11d3032e3555dfbc", + "sha256:c57e493a0faea1e4c38f860d6862ba6832723396c884fbf938ff5e9b224200e2", + "sha256:c8319e0bd6a7b45ad76166cc3d5d6a36c97d0c82a196f478c3ee5346566eebfd", + "sha256:caffda619099cfd4f63d48462f6aadbecee3ad9603b4b88b60cb821c1b258576", + "sha256:cc0c316fba3ce72ac3ab7902a888b9dc4979162d320823679da270c2d9ad0cad", + "sha256:cdd02a08205dc90238669f082747612cb3c82bd2c717adc60f9b9ecadb540f80", + "sha256:d50ac34835c6a4a0d456b5db559b82047403c4317b3bc73b3455fefdbdc54b0a", + "sha256:d6b9dd6aa03c812017411734e496c44fef29b43dba1e3dd1fa7361bbacfc1354", + "sha256:da3131ef2b940b99106f29dfbc30d9505643f766704e14c5d5e504e6a480c35e", + "sha256:da43cbe593e3c87d07108d0ebd73771dc414488f1f91ed2e204b0370b94b37ac", + "sha256:dd59638025160056687d598b054b64a79183f8065eae0d3f5ca523cde9943940", + "sha256:e1895e949f8849bc2757c0dbac28422a04be031204df46a56ab34bcf98507342", + "sha256:e1a79ad49f346aa1a2921f31e8dbbab4d64484823e813a002679eaa46cba39e1", + "sha256:e460475719721d59cd54a350c1f71c797c763212c836bf48585478c5514d2854", + "sha256:e64ffaf8f6e17ca15eb48344d86a7a741454526f3a3fa56bc493ad9d7ec63936", + "sha256:e6e3ccebdbd6e53474b0bb7ab8b88e83c0cfe91484b25e058e581348ee5a01a5", + "sha256:e758d271ed0286d146cf7c04c539a5169a888dd0b57026be621547e756af55bc", + "sha256:f087879f1ffde024dd2788a30d55acd67959dcf6c431e9d3682d1c491a0eb474", + "sha256:f477d26183e94eaafc60b983ab25af2a809a1b48ce4debb57b343f671b7a90b6", + "sha256:fc535cb898ef88333cf317777ecdfe0faac1c2a3187ef7eb061b6f7ecf7e6bae" + ], + "markers": "python_version >= '3.8'", + "version": "==2.23.2" }, "pydocstyle": { "hashes": [ @@ -549,12 +638,12 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:2143d9d9375bf372a73260e4114541485e84fca350b0b6b92674ca56ff5f7ea2", - "sha256:b0079dfac14b60cd1ce4691fbfb1748fe939db7d0234b5aba97197d10fbe0fef" + "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", + "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.23.4" + "version": "==0.23.8" }, "pytest-cov": { "hashes": [ @@ -655,11 +744,11 @@ }, "sniffio": { "hashes": [ - "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", - "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384" + "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" ], "markers": "python_version >= '3.7'", - "version": "==1.3.0" + "version": "==1.3.1" }, "snowballstemmer": { "hashes": [ @@ -747,37 +836,45 @@ }, "types-mock": { "hashes": [ - "sha256:13ca379d5710ccb3f18f69ade5b08881874cb83383d8fb49b1d4dac9d5c5d090", - "sha256:3d116955495935b0bcba14954b38d97e507cd43eca3e3700fc1b8e4f5c6bf2c7" + "sha256:5281a645d72e827d70043e3cc144fe33b1c003db084f789dc203aa90e812a5a4", + "sha256:d586a01d39ad919d3ddcd73de6cde73ca7f3c69707219f722d1b8d7733641ad7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==5.1.0.20240106" + "version": "==5.1.0.20240425" }, "types-requests": { "hashes": [ - "sha256:03a28ce1d7cd54199148e043b2079cdded22d6795d19a2c2a6791a4b2b5e2eb5", - "sha256:9592a9a4cb92d6d75d9b491a41477272b710e021011a2a3061157e2fb1f1a5d1" + "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1", + "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.31.0.20240125" + "version": "==2.31.0.20240406" }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.12.2" + }, + "tzdata": { + "hashes": [ + "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", + "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.1" }, "urllib3": { "hashes": [ - "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20", - "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224" + "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", + "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168" ], "markers": "python_version >= '3.8'", - "version": "==2.2.0" + "version": "==2.2.2" }, "uvicorn": { "hashes": [ diff --git a/server-utils/pytest.ini b/server-utils/pytest.ini index 49f04412746..a2cc2091144 100644 --- a/server-utils/pytest.ini +++ b/server-utils/pytest.ini @@ -1,3 +1,9 @@ [pytest] addopts = --color=yes --strict-markers asyncio_mode = auto + +filterwarnings = + # Pydantic's shims for its legacy v1 methods (e.g. `BaseModel.construct()`) + # are not type-checked properly. Forbid them, so we're forced to use their newer + # v2 replacements which are type-checked (e.g. ``BaseModel.model_construct()`) + error::pydantic.PydanticDeprecatedSince20 diff --git a/server-utils/server_utils/fastapi_utils/light_router.py b/server-utils/server_utils/fastapi_utils/light_router.py new file mode 100644 index 00000000000..82d82893151 --- /dev/null +++ b/server-utils/server_utils/fastapi_utils/light_router.py @@ -0,0 +1,306 @@ +"""See the `LightRouter` class.""" + +from __future__ import annotations + +import dataclasses +import enum +import typing +import typing_extensions + +import fastapi + + +_FASTAPI_ROUTE_METHOD_NAMES = { + "get", + "put", + "post", + "delete", + "options", + "head", + "patch", + "trace", +} + +if typing.TYPE_CHECKING: + # This is some chicanery so that @router.get(...), @router.post(...), etc. give us + # type-checking and autocomplete that exactly match the regular FastAPI version. + # These methods have a lot of parameters with complicated types and it would be + # a bear to manually keep them in sync with FastAPI. + + _P = typing.ParamSpec("_P") + _ReturnT = typing.TypeVar("_ReturnT") + + # `_CallableLike(FastAPI.foo)` produces a callable with the same signature + # as `FastAPI.foo()`. + class _CallableLike(typing.Generic[_P, _ReturnT]): + def __init__( + self, + method_to_mimic: typing.Callable[ + typing.Concatenate[ + fastapi.FastAPI, # The `self` parameter, which we throw away. + _P, # The actual args and kwargs, which we preserve. + ], + _ReturnT, + ], + ) -> None: + raise NotImplementedError("This is only for type-checking, not runtime.") + + def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _ReturnT: + raise NotImplementedError("This is only for type-checking, not runtime.") + + class _FastAPIRouteMethods: + get: typing.Final = _CallableLike(fastapi.FastAPI.get) + put: typing.Final = _CallableLike(fastapi.FastAPI.put) + post: typing.Final = _CallableLike(fastapi.FastAPI.post) + delete: typing.Final = _CallableLike(fastapi.FastAPI.delete) + options: typing.Final = _CallableLike(fastapi.FastAPI.options) + head: typing.Final = _CallableLike(fastapi.FastAPI.head) + patch: typing.Final = _CallableLike(fastapi.FastAPI.patch) + trace: typing.Final = _CallableLike(fastapi.FastAPI.trace) + +else: + + class _FastAPIRouteMethods: + pass + + +class LightRouter(_FastAPIRouteMethods): + """A lightweight drop-in replacement for `fastapi.APIRouter`. + + Use it like `fastapi.APIRouter`: + + foo_router = LightRouter() + + @router.get("/foo/{id}") + def get_health(id: str) -> Response: + ... + + bar_router = ... + + root_router = LightRouter() + root_router.include_router(foo_router) + root_router.include_router(bar_router) + + app = fastapi.FastAPI() + root_router.install_on_app(app) + + Rationale: + + With FastAPI's standard `FastAPI` and `APIRouter` classes, the `.include_router()` + method has a lot of overhead, accounting for something like 30-40% of + robot-server's startup time, which is multiple minutes long at the time of writing. + (https://github.com/pydantic/pydantic/issues/6768#issuecomment-1644532429) + + We could avoid the overhead by adding endpoints directly to the top-level FastAPI + app, "flat," instead of using `.include_router()`. But that would be bad for code + organization; we want to keep our tree of sub-routers. So this class reimplements + the important parts of `fastapi.APIRouter`, so we can keep our router tree, but + in a lighter-weight way. + + When you call `@router.get()` or `router.include_router()` on this class, it appends + to a lightweight internal structure and completely avoids slow calls into FastAPI. + Later on, when you do `router.install_on_app()`, everything in the tree is added to + the FastAPI app. + """ + + def __init__(self) -> None: + self._routes: list[_Endpoint | _IncludedRouter] = [] + + def __getattr__(self, name: str) -> object: + """Supply the optimized version of `@router.get()`, `@router.post()`, etc. + + See the FastAPI docs for usage details. + """ + if name in _FASTAPI_ROUTE_METHOD_NAMES: + return _EndpointCaptor(method_name=name, on_capture=self._routes.append) + else: + raise AttributeError(name=name) + + def include_router( + self, + router: LightRouter | fastapi.APIRouter, + **kwargs: typing_extensions.Unpack[_RouterIncludeKwargs], + ) -> None: + """The optimized version of `fastapi.APIRouter.include_router()`. + + See the FastAPI docs for argument details. + """ # noqa: D402 + self._routes.append(_IncludedRouter(router=router, inclusion_kwargs=kwargs)) + + def install_on_app( + self, + app: fastapi.FastAPI, + **kwargs: typing_extensions.Unpack[_RouterIncludeKwargs], + ) -> None: + """The optimized version of `fastapi.FastAPI.include_router()`. + + See the FastAPI docs for argument details.. + """ + for route in self._routes: + if isinstance(route, _IncludedRouter): + router = route.router + combined_kwargs = _merge_kwargs(kwargs, route.inclusion_kwargs) + if isinstance(router, fastapi.APIRouter): + app.include_router(router, **combined_kwargs) + elif isinstance(route.router, LightRouter): + router.install_on_app(app, **combined_kwargs) + else: + typing_extensions.assert_type(route, _Endpoint) + combined_kwargs = _merge_kwargs( + kwargs, + route.kwargs, # type: ignore[arg-type] + ) + fastapi_method = getattr(app, route.method_name) + fastapi_decorator = fastapi_method(*route.args, **combined_kwargs) + fastapi_decorator(route.function) + + +class _RouterIncludeKwargs(typing.TypedDict): + """The keyword arguments of FastAPI's `.include_router()` method. + + (At least the arguments that we actually use, anyway.) + """ + + # Arguments with defaults should be annotated as `NotRequired`. + # For example, `foo: str | None = None` becomes `NotRequired[str | None]`. + + tags: typing_extensions.NotRequired[list[str | enum.Enum] | None] + responses: typing_extensions.NotRequired[ + dict[int | str, dict[str, typing.Any]] | None + ] + dependencies: typing_extensions.NotRequired[ + typing.Sequence[ + # FastAPI does not publicly expose the type of the result of a + # Depends(...) call, so this needs to be Any. + typing.Any + ] + | None + ] + + +def _merge_kwargs( + from_parent: _RouterIncludeKwargs, from_child: _RouterIncludeKwargs +) -> _RouterIncludeKwargs: + """Merge kwargs from different levels of a FastAPI router tree. + + FastAPI keyword arguments can be specified at multiple levels in the router tree. + For example, the top-level router, subrouters, and finally the endpoint function + can each specify their own `tags`. The different levels need to be merged + carefully and in argument-specific ways if we want to match FastAPI behavior. + For example, the final `tags` value should be the concatenation of the values + from all levels. + """ + merge_result: _RouterIncludeKwargs = {} + remaining_from_parent = from_parent.copy() + remaining_from_child = from_child.copy() + + # When we know how to merge a specific argument's values, do so. + # This takes care to leave the argument unset if it's unset in both parent and + # child, in order to leave the defaulting up to FastAPI. + if "tags" in remaining_from_parent or "tags" in remaining_from_child: + merge_result["tags"] = [ + *(remaining_from_parent.get("tags") or []), + *(remaining_from_child.get("tags") or []), + ] + remaining_from_parent.pop("tags", None) + remaining_from_child.pop("tags", None) + + # For any argument whose values we don't know how to merge, we can just pass it + # along opaquely, as long as the parent and child aren't both trying to set it. + # + # If the parent and child *are* both trying to set it, then we have a problem. + # It would likely be wrong to arbitrarily choose one to override the other, + # so we can only raise an error. + colliding_keys = set(remaining_from_parent.keys()).intersection( + remaining_from_child.keys() + ) + if not colliding_keys: + merge_result.update(remaining_from_parent) + merge_result.update(remaining_from_child) + else: + a_collisions: dict[object, object] = { + k: remaining_from_parent[k] for k in colliding_keys # type: ignore[literal-required] + } + b_collisions: dict[object, object] = { + k: remaining_from_child[k] for k in colliding_keys # type: ignore[literal-required] + } + raise NotImplementedError( + f"These FastAPI keyword arguments appear at different levels " + f"in the router tree, and we don't know how to merge their values:\n" + f"{a_collisions}\n{b_collisions}\n" + f"Modify {__name__} to handle the merge, or avoid the problem by " + f"setting the argument at only one level of the router tree." + ) + + return merge_result + + +@dataclasses.dataclass +class _IncludedRouter: + router: fastapi.APIRouter | LightRouter + inclusion_kwargs: _RouterIncludeKwargs + + +_DecoratedFunctionT = typing.TypeVar( + "_DecoratedFunctionT", bound=typing.Callable[..., object] +) + + +class _EndpointCaptor: + """A callable that pretends to be a FastAPI path operation decorator. + + `method_name` is the FastAPI method to pretend to be, e.g. "get" or "post". + + Supposing you have an `_EndpointCaptor` named `get`, when this whole enchilada + happens: + + @get("/foo/{id}", description="blah blah") + def get_some_endpoint(id: str) -> Response: + ... + + Then information about the whole enchilada is sent to the `on_capture` callback. + """ + + def __init__( + self, + method_name: str, + on_capture: typing.Callable[[_Endpoint], None], + ) -> None: + self._method_name = method_name + self._on_capture = on_capture + + def __call__( + self, *fastapi_decorator_args: object, **fastapi_decorator_kwargs: object + ) -> typing.Callable[[_DecoratedFunctionT], _DecoratedFunctionT]: + def decorate(decorated_function: _DecoratedFunctionT) -> _DecoratedFunctionT: + self._on_capture( + _Endpoint( + method_name=self._method_name, + args=fastapi_decorator_args, + kwargs=fastapi_decorator_kwargs, + function=decorated_function, + ) + ) + return decorated_function + + return decorate + + +@dataclasses.dataclass +class _Endpoint: + """Information about an endpoint that's been added to a router.""" + + method_name: str + """The name of the method on the FastAPI class, e.g. "get".""" + + args: tuple[object, ...] + """The positional arguments passed to the FastAPI method, e.g. the URL path.""" + + kwargs: dict[str, object] + """The keyword arguments passed to the FastAPI method, e.g. `description`.""" + + function: typing.Callable[..., object] + """The function actually implementing the logic of the endpoint. + + (The "path operation function", in FastAPI terms.) + """ diff --git a/server-utils/setup.py b/server-utils/setup.py index 41cab81de03..13e87a15c99 100755 --- a/server-utils/setup.py +++ b/server-utils/setup.py @@ -52,10 +52,10 @@ def get_version(): PACKAGES = find_packages(where=".", exclude=["tests.*", "tests"]) INSTALL_REQUIRES = [ "anyio==3.7.1", - "fastapi==0.99.1", + "fastapi==0.100.0", "python-dotenv==1.0.1", "python-multipart==0.0.6", - "pydantic==1.10.12", + "pydantic>2.0.0,<3", "typing-extensions>=4.0.0,<5", "uvicorn==0.27.0.post1", "wsproto==1.2.0", diff --git a/setup-vitest.ts b/setup-vitest.mts similarity index 100% rename from setup-vitest.ts rename to setup-vitest.mts diff --git a/shared-data/command/schemas/10.json b/shared-data/command/schemas/10.json index aced561bdff..fe43bffd92f 100644 --- a/shared-data/command/schemas/10.json +++ b/shared-data/command/schemas/10.json @@ -1774,6 +1774,7 @@ "p1000_single_gen2", "p1000_single_flex", "p1000_multi_flex", + "p1000_multi_em_flex", "p1000_96", "p200_96" ], diff --git a/shared-data/command/schemas/11.json b/shared-data/command/schemas/11.json index 955dff87690..ec0d854e5f9 100644 --- a/shared-data/command/schemas/11.json +++ b/shared-data/command/schemas/11.json @@ -1,6369 +1,5880 @@ { - "title": "CreateCommandUnion", - "description": "Model that validates a union of all CommandCreate models.", - "discriminator": { - "propertyName": "commandType", - "mapping": { - "airGapInPlace": "#/definitions/AirGapInPlaceCreate", - "aspirate": "#/definitions/AspirateCreate", - "aspirateInPlace": "#/definitions/AspirateInPlaceCreate", - "comment": "#/definitions/CommentCreate", - "configureForVolume": "#/definitions/ConfigureForVolumeCreate", - "configureNozzleLayout": "#/definitions/ConfigureNozzleLayoutCreate", - "custom": "#/definitions/CustomCreate", - "dispense": "#/definitions/DispenseCreate", - "dispenseInPlace": "#/definitions/DispenseInPlaceCreate", - "blowout": "#/definitions/BlowOutCreate", - "blowOutInPlace": "#/definitions/BlowOutInPlaceCreate", - "dropTip": "#/definitions/DropTipCreate", - "dropTipInPlace": "#/definitions/DropTipInPlaceCreate", - "home": "#/definitions/HomeCreate", - "retractAxis": "#/definitions/RetractAxisCreate", - "loadLabware": "#/definitions/LoadLabwareCreate", - "reloadLabware": "#/definitions/ReloadLabwareCreate", - "loadLiquid": "#/definitions/LoadLiquidCreate", - "loadLiquidClass": "#/definitions/LoadLiquidClassCreate", - "loadModule": "#/definitions/LoadModuleCreate", - "loadPipette": "#/definitions/LoadPipetteCreate", - "moveLabware": "#/definitions/MoveLabwareCreate", - "moveRelative": "#/definitions/MoveRelativeCreate", - "moveToCoordinates": "#/definitions/MoveToCoordinatesCreate", - "moveToWell": "#/definitions/MoveToWellCreate", - "moveToAddressableArea": "#/definitions/MoveToAddressableAreaCreate", - "moveToAddressableAreaForDropTip": "#/definitions/MoveToAddressableAreaForDropTipCreate", - "prepareToAspirate": "#/definitions/PrepareToAspirateCreate", - "waitForResume": "#/definitions/WaitForResumeCreate", - "pause": "#/definitions/WaitForResumeCreate", - "waitForDuration": "#/definitions/WaitForDurationCreate", - "pickUpTip": "#/definitions/PickUpTipCreate", - "savePosition": "#/definitions/SavePositionCreate", - "setRailLights": "#/definitions/SetRailLightsCreate", - "touchTip": "#/definitions/TouchTipCreate", - "setStatusBar": "#/definitions/SetStatusBarCreate", - "verifyTipPresence": "#/definitions/VerifyTipPresenceCreate", - "getTipPresence": "#/definitions/GetTipPresenceCreate", - "getNextTip": "#/definitions/GetNextTipCreate", - "liquidProbe": "#/definitions/LiquidProbeCreate", - "tryLiquidProbe": "#/definitions/TryLiquidProbeCreate", - "heaterShaker/waitForTemperature": "#/definitions/opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureCreate", - "heaterShaker/setTargetTemperature": "#/definitions/opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureCreate", - "heaterShaker/deactivateHeater": "#/definitions/DeactivateHeaterCreate", - "heaterShaker/setAndWaitForShakeSpeed": "#/definitions/SetAndWaitForShakeSpeedCreate", - "heaterShaker/deactivateShaker": "#/definitions/DeactivateShakerCreate", - "heaterShaker/openLabwareLatch": "#/definitions/OpenLabwareLatchCreate", - "heaterShaker/closeLabwareLatch": "#/definitions/CloseLabwareLatchCreate", - "magneticModule/disengage": "#/definitions/DisengageCreate", - "magneticModule/engage": "#/definitions/EngageCreate", - "temperatureModule/setTargetTemperature": "#/definitions/opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureCreate", - "temperatureModule/waitForTemperature": "#/definitions/opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureCreate", - "temperatureModule/deactivate": "#/definitions/DeactivateTemperatureCreate", - "thermocycler/setTargetBlockTemperature": "#/definitions/SetTargetBlockTemperatureCreate", - "thermocycler/waitForBlockTemperature": "#/definitions/WaitForBlockTemperatureCreate", - "thermocycler/setTargetLidTemperature": "#/definitions/SetTargetLidTemperatureCreate", - "thermocycler/waitForLidTemperature": "#/definitions/WaitForLidTemperatureCreate", - "thermocycler/deactivateBlock": "#/definitions/DeactivateBlockCreate", - "thermocycler/deactivateLid": "#/definitions/DeactivateLidCreate", - "thermocycler/openLid": "#/definitions/opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidCreate", - "thermocycler/closeLid": "#/definitions/opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidCreate", - "thermocycler/runProfile": "#/definitions/RunProfileCreate", - "thermocycler/runExtendedProfile": "#/definitions/RunExtendedProfileCreate", - "absorbanceReader/closeLid": "#/definitions/opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidCreate", - "absorbanceReader/openLid": "#/definitions/opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidCreate", - "absorbanceReader/initialize": "#/definitions/InitializeCreate", - "absorbanceReader/read": "#/definitions/ReadAbsorbanceCreate", - "calibration/calibrateGripper": "#/definitions/CalibrateGripperCreate", - "calibration/calibratePipette": "#/definitions/CalibratePipetteCreate", - "calibration/calibrateModule": "#/definitions/CalibrateModuleCreate", - "calibration/moveToMaintenancePosition": "#/definitions/MoveToMaintenancePositionCreate", - "unsafe/blowOutInPlace": "#/definitions/UnsafeBlowOutInPlaceCreate", - "unsafe/dropTipInPlace": "#/definitions/UnsafeDropTipInPlaceCreate", - "unsafe/updatePositionEstimators": "#/definitions/UpdatePositionEstimatorsCreate", - "unsafe/engageAxes": "#/definitions/UnsafeEngageAxesCreate", - "unsafe/ungripLabware": "#/definitions/UnsafeUngripLabwareCreate", - "unsafe/placeLabware": "#/definitions/UnsafePlaceLabwareCreate", - "robot/moveAxesRelative": "#/definitions/MoveAxesRelativeCreate", - "robot/moveAxesTo": "#/definitions/MoveAxesToCreate", - "robot/moveTo": "#/definitions/MoveToCreate", - "robot/openGripperJaw": "#/definitions/openGripperJawCreate", - "robot/closeGripperJaw": "#/definitions/closeGripperJawCreate" - } - }, - "oneOf": [ - { - "$ref": "#/definitions/AirGapInPlaceCreate" - }, - { - "$ref": "#/definitions/AspirateCreate" - }, - { - "$ref": "#/definitions/AspirateInPlaceCreate" - }, - { - "$ref": "#/definitions/CommentCreate" - }, - { - "$ref": "#/definitions/ConfigureForVolumeCreate" - }, - { - "$ref": "#/definitions/ConfigureNozzleLayoutCreate" - }, - { - "$ref": "#/definitions/CustomCreate" - }, - { - "$ref": "#/definitions/DispenseCreate" - }, - { - "$ref": "#/definitions/DispenseInPlaceCreate" - }, - { - "$ref": "#/definitions/BlowOutCreate" - }, - { - "$ref": "#/definitions/BlowOutInPlaceCreate" - }, - { - "$ref": "#/definitions/DropTipCreate" - }, - { - "$ref": "#/definitions/DropTipInPlaceCreate" - }, - { - "$ref": "#/definitions/HomeCreate" - }, - { - "$ref": "#/definitions/RetractAxisCreate" - }, - { - "$ref": "#/definitions/LoadLabwareCreate" - }, - { - "$ref": "#/definitions/ReloadLabwareCreate" - }, - { - "$ref": "#/definitions/LoadLiquidCreate" - }, - { - "$ref": "#/definitions/LoadLiquidClassCreate" - }, - { - "$ref": "#/definitions/LoadModuleCreate" - }, - { - "$ref": "#/definitions/LoadPipetteCreate" - }, - { - "$ref": "#/definitions/MoveLabwareCreate" - }, - { - "$ref": "#/definitions/MoveRelativeCreate" - }, - { - "$ref": "#/definitions/MoveToCoordinatesCreate" - }, - { - "$ref": "#/definitions/MoveToWellCreate" - }, - { - "$ref": "#/definitions/MoveToAddressableAreaCreate" - }, - { - "$ref": "#/definitions/MoveToAddressableAreaForDropTipCreate" - }, - { - "$ref": "#/definitions/PrepareToAspirateCreate" - }, - { - "$ref": "#/definitions/WaitForResumeCreate" - }, - { - "$ref": "#/definitions/WaitForDurationCreate" - }, - { - "$ref": "#/definitions/PickUpTipCreate" - }, - { - "$ref": "#/definitions/SavePositionCreate" - }, - { - "$ref": "#/definitions/SetRailLightsCreate" - }, - { - "$ref": "#/definitions/TouchTipCreate" - }, - { - "$ref": "#/definitions/SetStatusBarCreate" - }, - { - "$ref": "#/definitions/VerifyTipPresenceCreate" - }, - { - "$ref": "#/definitions/GetTipPresenceCreate" - }, - { - "$ref": "#/definitions/GetNextTipCreate" - }, - { - "$ref": "#/definitions/LiquidProbeCreate" - }, - { - "$ref": "#/definitions/TryLiquidProbeCreate" - }, - { - "$ref": "#/definitions/opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureCreate" - }, - { - "$ref": "#/definitions/opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureCreate" - }, - { - "$ref": "#/definitions/DeactivateHeaterCreate" - }, - { - "$ref": "#/definitions/SetAndWaitForShakeSpeedCreate" - }, - { - "$ref": "#/definitions/DeactivateShakerCreate" - }, - { - "$ref": "#/definitions/OpenLabwareLatchCreate" - }, - { - "$ref": "#/definitions/CloseLabwareLatchCreate" - }, - { - "$ref": "#/definitions/DisengageCreate" - }, - { - "$ref": "#/definitions/EngageCreate" - }, - { - "$ref": "#/definitions/opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureCreate" - }, - { - "$ref": "#/definitions/opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureCreate" - }, - { - "$ref": "#/definitions/DeactivateTemperatureCreate" - }, - { - "$ref": "#/definitions/SetTargetBlockTemperatureCreate" - }, - { - "$ref": "#/definitions/WaitForBlockTemperatureCreate" - }, - { - "$ref": "#/definitions/SetTargetLidTemperatureCreate" - }, - { - "$ref": "#/definitions/WaitForLidTemperatureCreate" - }, - { - "$ref": "#/definitions/DeactivateBlockCreate" - }, - { - "$ref": "#/definitions/DeactivateLidCreate" - }, - { - "$ref": "#/definitions/opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidCreate" - }, - { - "$ref": "#/definitions/opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidCreate" - }, - { - "$ref": "#/definitions/RunProfileCreate" - }, - { - "$ref": "#/definitions/RunExtendedProfileCreate" - }, - { - "$ref": "#/definitions/opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidCreate" - }, - { - "$ref": "#/definitions/opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidCreate" - }, - { - "$ref": "#/definitions/InitializeCreate" - }, - { - "$ref": "#/definitions/ReadAbsorbanceCreate" - }, - { - "$ref": "#/definitions/CalibrateGripperCreate" - }, - { - "$ref": "#/definitions/CalibratePipetteCreate" - }, - { - "$ref": "#/definitions/CalibrateModuleCreate" - }, - { - "$ref": "#/definitions/MoveToMaintenancePositionCreate" - }, - { - "$ref": "#/definitions/UnsafeBlowOutInPlaceCreate" - }, - { - "$ref": "#/definitions/UnsafeDropTipInPlaceCreate" - }, - { - "$ref": "#/definitions/UpdatePositionEstimatorsCreate" - }, - { - "$ref": "#/definitions/UnsafeEngageAxesCreate" - }, - { - "$ref": "#/definitions/UnsafeUngripLabwareCreate" - }, - { - "$ref": "#/definitions/UnsafePlaceLabwareCreate" - }, - { - "$ref": "#/definitions/MoveAxesRelativeCreate" - }, - { - "$ref": "#/definitions/MoveAxesToCreate" - }, - { - "$ref": "#/definitions/MoveToCreate" - }, - { - "$ref": "#/definitions/openGripperJawCreate" + "$defs": { + "AddressableAreaLocation": { + "description": "The location of something place in an addressable area. This is a superset of deck slots.", + "properties": { + "addressableAreaName": { + "description": "The name of the addressable area that you want to use. Valid values are the `id`s of `addressableArea`s in the [deck definition](https://github.com/Opentrons/opentrons/tree/edge/shared-data/deck).", + "title": "Addressableareaname", + "type": "string" + } + }, + "required": ["addressableAreaName"], + "title": "AddressableAreaLocation", + "type": "object" }, - { - "$ref": "#/definitions/closeGripperJawCreate" - } - ], - "definitions": { - "AirGapInPlaceParams": { - "title": "AirGapInPlaceParams", - "description": "Payload required to air gap in place.", - "type": "object", + "AddressableOffsetVector": { + "description": "Offset, in deck coordinates, from nominal to actual position of an addressable area.", "properties": { - "flowRate": { - "title": "Flowrate", - "description": "Speed in \u00b5L/s configured for the pipette", - "exclusiveMinimum": 0, + "x": { + "title": "X", "type": "number" }, - "volume": { - "title": "Volume", - "description": "The amount of liquid to aspirate, in \u00b5L. Must not be greater than the remaining available amount, which depends on the pipette (see `loadPipette`), its configuration (see `configureForVolume`), the tip (see `pickUpTip`), and the amount you've aspirated so far. There is some tolerance for floating point rounding errors.", - "minimum": 0, + "y": { + "title": "Y", "type": "number" }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" + "z": { + "title": "Z", + "type": "number" } }, - "required": ["flowRate", "volume", "pipetteId"] - }, - "CommandIntent": { - "title": "CommandIntent", - "description": "Run intent for a given command.\n\nProps:\n PROTOCOL: the command is part of the protocol run itself.\n SETUP: the command is part of the setup phase of a run.", - "enum": ["protocol", "setup", "fixit"], - "type": "string" + "required": ["x", "y", "z"], + "title": "AddressableOffsetVector", + "type": "object" }, "AirGapInPlaceCreate": { - "title": "AirGapInPlaceCreate", "description": "AirGapInPlace command request model.", - "type": "object", "properties": { "commandType": { - "title": "Commandtype", + "const": "airGapInPlace", "default": "airGapInPlace", "enum": ["airGapInPlace"], + "title": "Commandtype", "type": "string" }, - "params": { - "$ref": "#/definitions/AirGapInPlaceParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/AirGapInPlaceParams" } }, - "required": ["params"] - }, - "WellOrigin": { - "title": "WellOrigin", - "description": "Origin of WellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well\n MENISCUS: the meniscus-center of the well", - "enum": ["top", "bottom", "center", "meniscus"], - "type": "string" + "required": ["params"], + "title": "AirGapInPlaceCreate", + "type": "object" }, - "WellOffset": { - "title": "WellOffset", - "description": "An offset vector in (x, y, z).", - "type": "object", + "AirGapInPlaceParams": { + "description": "Payload required to air gap in place.", "properties": { - "x": { - "title": "X", - "default": 0, + "flowRate": { + "description": "Speed in \u00b5L/s configured for the pipette", + "exclusiveMinimum": 0.0, + "title": "Flowrate", "type": "number" }, - "y": { - "title": "Y", - "default": 0, - "type": "number" + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" }, - "z": { - "title": "Z", - "default": 0, + "volume": { + "description": "The amount of liquid to aspirate, in \u00b5L. Must not be greater than the remaining available amount, which depends on the pipette (see `loadPipette`), its configuration (see `configureForVolume`), the tip (see `pickUpTip`), and the amount you've aspirated so far. There is some tolerance for floating point rounding errors.", + "minimum": 0.0, + "title": "Volume", "type": "number" } - } + }, + "required": ["flowRate", "volume", "pipetteId"], + "title": "AirGapInPlaceParams", + "type": "object" }, - "LiquidHandlingWellLocation": { - "title": "LiquidHandlingWellLocation", - "description": "A relative location in reference to a well's location.\n\nTo be used with commands that handle liquids.", - "type": "object", + "AllNozzleLayoutConfiguration": { + "description": "All basemodel to represent a reset to the nozzle configuration. Sending no parameters resets to default.", "properties": { - "origin": { - "default": "top", - "allOf": [ - { - "$ref": "#/definitions/WellOrigin" - } - ] - }, - "offset": { - "$ref": "#/definitions/WellOffset" - }, - "volumeOffset": { - "title": "Volumeoffset", - "description": "A volume of liquid, in \u00b5L, to offset the z-axis offset. When \"operationVolume\" is specified, this volume is pulled from the command volume parameter.", - "default": 0.0, - "anyOf": [ - { - "type": "number" - }, - { - "enum": ["operationVolume"], - "type": "string" - } - ] - } - } - }, - "AspirateParams": { - "title": "AspirateParams", - "description": "Parameters required to aspirate from a specific well.", - "type": "object", - "properties": { - "labwareId": { - "title": "Labwareid", - "description": "Identifier of labware to use.", - "type": "string" - }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", - "type": "string" - }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to perform the operation", - "allOf": [ - { - "$ref": "#/definitions/LiquidHandlingWellLocation" - } - ] - }, - "flowRate": { - "title": "Flowrate", - "description": "Speed in \u00b5L/s configured for the pipette", - "exclusiveMinimum": 0, - "type": "number" - }, - "volume": { - "title": "Volume", - "description": "The amount of liquid to aspirate, in \u00b5L. Must not be greater than the remaining available amount, which depends on the pipette (see `loadPipette`), its configuration (see `configureForVolume`), the tip (see `pickUpTip`), and the amount you've aspirated so far. There is some tolerance for floating point rounding errors.", - "minimum": 0, - "type": "number" - }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", + "style": { + "const": "ALL", + "default": "ALL", + "enum": ["ALL"], + "title": "Style", "type": "string" } }, - "required": ["labwareId", "wellName", "flowRate", "volume", "pipetteId"] + "title": "AllNozzleLayoutConfiguration", + "type": "object" }, "AspirateCreate": { - "title": "AspirateCreate", "description": "Create aspirate command request model.", - "type": "object", "properties": { "commandType": { - "title": "Commandtype", + "const": "aspirate", "default": "aspirate", "enum": ["aspirate"], + "title": "Commandtype", "type": "string" }, - "params": { - "$ref": "#/definitions/AspirateParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" - } - }, - "required": ["params"] - }, - "AspirateInPlaceParams": { - "title": "AspirateInPlaceParams", - "description": "Payload required to aspirate in place.", - "type": "object", - "properties": { - "flowRate": { - "title": "Flowrate", - "description": "Speed in \u00b5L/s configured for the pipette", - "exclusiveMinimum": 0, - "type": "number" - }, - "volume": { - "title": "Volume", - "description": "The amount of liquid to aspirate, in \u00b5L. Must not be greater than the remaining available amount, which depends on the pipette (see `loadPipette`), its configuration (see `configureForVolume`), the tip (see `pickUpTip`), and the amount you've aspirated so far. There is some tolerance for floating point rounding errors.", - "minimum": 0, - "type": "number" }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" + "params": { + "$ref": "#/$defs/AspirateParams" } }, - "required": ["flowRate", "volume", "pipetteId"] + "required": ["params"], + "title": "AspirateCreate", + "type": "object" }, "AspirateInPlaceCreate": { - "title": "AspirateInPlaceCreate", "description": "AspirateInPlace command request model.", - "type": "object", "properties": { "commandType": { - "title": "Commandtype", + "const": "aspirateInPlace", "default": "aspirateInPlace", "enum": ["aspirateInPlace"], + "title": "Commandtype", "type": "string" }, - "params": { - "$ref": "#/definitions/AspirateInPlaceParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" - } - }, - "required": ["params"] - }, - "CommentParams": { - "title": "CommentParams", - "description": "Payload required to annotate execution with a comment.", - "type": "object", - "properties": { - "message": { - "title": "Message", - "description": "A user-facing message", - "type": "string" - } - }, - "required": ["message"] - }, - "CommentCreate": { - "title": "CommentCreate", - "description": "Comment command request model.", - "type": "object", - "properties": { - "commandType": { - "title": "Commandtype", - "default": "comment", - "enum": ["comment"], + "title": "Key", "type": "string" }, "params": { - "$ref": "#/definitions/CommentParams" - }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] - }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" + "$ref": "#/$defs/AspirateInPlaceParams" } }, - "required": ["params"] + "required": ["params"], + "title": "AspirateInPlaceCreate", + "type": "object" }, - "ConfigureForVolumeParams": { - "title": "ConfigureForVolumeParams", - "description": "Parameters required to configure volume for a specific pipette.", - "type": "object", + "AspirateInPlaceParams": { + "description": "Payload required to aspirate in place.", "properties": { + "flowRate": { + "description": "Speed in \u00b5L/s configured for the pipette", + "exclusiveMinimum": 0.0, + "title": "Flowrate", + "type": "number" + }, "pipetteId": { - "title": "Pipetteid", "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" }, "volume": { + "description": "The amount of liquid to aspirate, in \u00b5L. Must not be greater than the remaining available amount, which depends on the pipette (see `loadPipette`), its configuration (see `configureForVolume`), the tip (see `pickUpTip`), and the amount you've aspirated so far. There is some tolerance for floating point rounding errors.", + "minimum": 0.0, "title": "Volume", - "description": "Amount of liquid in uL. Must be at least 0 and no greater than a pipette-specific maximum volume.", - "minimum": 0, "type": "number" - }, - "tipOverlapNotAfterVersion": { - "title": "Tipoverlapnotafterversion", - "description": "A version of tip overlap data to not exceed. The highest-versioned tip overlap data that does not exceed this version will be used. Versions are expressed as vN where N is an integer, counting up from v0. If None, the current highest version will be used.", - "type": "string" } }, - "required": ["pipetteId", "volume"] + "required": ["flowRate", "volume", "pipetteId"], + "title": "AspirateInPlaceParams", + "type": "object" }, - "ConfigureForVolumeCreate": { - "title": "ConfigureForVolumeCreate", - "description": "Configure for volume command creation request model.", - "type": "object", + "AspirateParams": { + "description": "Parameters required to aspirate from a specific well.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "configureForVolume", - "enum": ["configureForVolume"], + "flowRate": { + "description": "Speed in \u00b5L/s configured for the pipette", + "exclusiveMinimum": 0.0, + "title": "Flowrate", + "type": "number" + }, + "labwareId": { + "description": "Identifier of labware to use.", + "title": "Labwareid", "type": "string" }, - "params": { - "$ref": "#/definitions/ConfigureForVolumeParams" + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "volume": { + "description": "The amount of liquid to aspirate, in \u00b5L. Must not be greater than the remaining available amount, which depends on the pipette (see `loadPipette`), its configuration (see `configureForVolume`), the tip (see `pickUpTip`), and the amount you've aspirated so far. There is some tolerance for floating point rounding errors.", + "minimum": 0.0, + "title": "Volume", + "type": "number" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" - } - }, - "required": ["params"] - }, - "AllNozzleLayoutConfiguration": { - "title": "AllNozzleLayoutConfiguration", - "description": "All basemodel to represent a reset to the nozzle configuration. Sending no parameters resets to default.", - "type": "object", - "properties": { - "style": { - "title": "Style", - "default": "ALL", - "enum": ["ALL"], - "type": "string" - } - } - }, - "SingleNozzleLayoutConfiguration": { - "title": "SingleNozzleLayoutConfiguration", - "description": "Minimum information required for a new nozzle configuration.", - "type": "object", - "properties": { - "style": { - "title": "Style", - "default": "SINGLE", - "enum": ["SINGLE"], - "type": "string" + "wellLocation": { + "$ref": "#/$defs/LiquidHandlingWellLocation", + "description": "Relative well location at which to perform the operation" }, - "primaryNozzle": { - "title": "Primarynozzle", - "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": ["A1", "H1", "A12", "H12"], + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", "type": "string" } }, - "required": ["primaryNozzle"] + "required": ["labwareId", "wellName", "flowRate", "volume", "pipetteId"], + "title": "AspirateParams", + "type": "object" }, - "RowNozzleLayoutConfiguration": { - "title": "RowNozzleLayoutConfiguration", - "description": "Minimum information required for a new nozzle configuration.", - "type": "object", + "AspirateProperties": { + "description": "Properties specific to the aspirate function.", "properties": { - "style": { - "title": "Style", - "default": "ROW", - "enum": ["ROW"], - "type": "string" + "correctionByVolume": { + "description": "Settings for volume correction keyed by by target aspiration volume, representing additional volume the plunger should move to accurately hit target volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Correctionbyvolume", + "type": "array" }, - "primaryNozzle": { - "title": "Primarynozzle", - "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": ["A1", "H1", "A12", "H12"], - "type": "string" + "delay": { + "$ref": "#/$defs/DelayProperties", + "description": "Delay settings after an aspirate" + }, + "flowRateByVolume": { + "description": "Settings for flow rate keyed by target aspiration volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Flowratebyvolume", + "type": "array" + }, + "mix": { + "$ref": "#/$defs/MixProperties", + "description": "Mixing settings for before an aspirate" + }, + "offset": { + "$ref": "#/$defs/Coordinate", + "description": "Relative offset for aspiration." + }, + "positionReference": { + "$ref": "#/$defs/PositionReference", + "description": "Position reference for aspiration." + }, + "preWet": { + "description": "Whether to perform a pre-wet action.", + "title": "Prewet", + "type": "boolean" + }, + "retract": { + "$ref": "#/$defs/RetractAspirate", + "description": "Pipette retract settings after an aspirate." + }, + "submerge": { + "$ref": "#/$defs/Submerge", + "description": "Submerge settings for aspirate." } }, - "required": ["primaryNozzle"] + "required": [ + "submerge", + "retract", + "positionReference", + "offset", + "flowRateByVolume", + "correctionByVolume", + "preWet", + "mix", + "delay" + ], + "title": "AspirateProperties", + "type": "object" }, - "ColumnNozzleLayoutConfiguration": { - "title": "ColumnNozzleLayoutConfiguration", - "description": "Information required for nozzle configurations of type ROW and COLUMN.", - "type": "object", + "BlowOutCreate": { + "description": "Create blow-out command request model.", "properties": { - "style": { - "title": "Style", - "default": "COLUMN", - "enum": ["COLUMN"], + "commandType": { + "const": "blowout", + "default": "blowout", + "enum": ["blowout"], + "title": "Commandtype", "type": "string" }, - "primaryNozzle": { - "title": "Primarynozzle", - "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": ["A1", "H1", "A12", "H12"], + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/BlowOutParams" } }, - "required": ["primaryNozzle"] + "required": ["params"], + "title": "BlowOutCreate", + "type": "object" }, - "QuadrantNozzleLayoutConfiguration": { - "title": "QuadrantNozzleLayoutConfiguration", - "description": "Information required for nozzle configurations of type QUADRANT.", - "type": "object", + "BlowOutInPlaceCreate": { + "description": "BlowOutInPlace command request model.", "properties": { - "style": { - "title": "Style", - "default": "QUADRANT", - "enum": ["QUADRANT"], + "commandType": { + "const": "blowOutInPlace", + "default": "blowOutInPlace", + "enum": ["blowOutInPlace"], + "title": "Commandtype", "type": "string" }, - "primaryNozzle": { - "title": "Primarynozzle", - "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": ["A1", "H1", "A12", "H12"], - "type": "string" + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" }, - "frontRightNozzle": { - "title": "Frontrightnozzle", - "description": "The front right nozzle in your configuration.", - "pattern": "[A-Z]\\d{1,2}", + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" }, - "backLeftNozzle": { - "title": "Backleftnozzle", - "description": "The back left nozzle in your configuration.", - "pattern": "[A-Z]\\d{1,2}", - "type": "string" + "params": { + "$ref": "#/$defs/BlowOutInPlaceParams" } }, - "required": ["primaryNozzle", "frontRightNozzle", "backLeftNozzle"] + "required": ["params"], + "title": "BlowOutInPlaceCreate", + "type": "object" }, - "ConfigureNozzleLayoutParams": { - "title": "ConfigureNozzleLayoutParams", - "description": "Parameters required to configure the nozzle layout for a specific pipette.", - "type": "object", + "BlowOutInPlaceParams": { + "description": "Payload required to blow-out in place.", "properties": { + "flowRate": { + "description": "Speed in \u00b5L/s configured for the pipette", + "exclusiveMinimum": 0.0, + "title": "Flowrate", + "type": "number" + }, "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", "title": "Pipetteid", + "type": "string" + } + }, + "required": ["flowRate", "pipetteId"], + "title": "BlowOutInPlaceParams", + "type": "object" + }, + "BlowOutParams": { + "description": "Payload required to blow-out a specific well.", + "properties": { + "flowRate": { + "description": "Speed in \u00b5L/s configured for the pipette", + "exclusiveMinimum": 0.0, + "title": "Flowrate", + "type": "number" + }, + "labwareId": { + "description": "Identifier of labware to use.", + "title": "Labwareid", + "type": "string" + }, + "pipetteId": { "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" }, - "configurationParams": { - "title": "Configurationparams", + "wellLocation": { + "$ref": "#/$defs/WellLocation", + "description": "Relative well location at which to perform the operation" + }, + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", + "type": "string" + } + }, + "required": ["labwareId", "wellName", "flowRate", "pipetteId"], + "title": "BlowOutParams", + "type": "object" + }, + "BlowoutLocation": { + "description": "Location for blowout during a transfer function.", + "enum": ["source", "destination", "trash"], + "title": "BlowoutLocation", + "type": "string" + }, + "BlowoutParams": { + "description": "Parameters for blowout.", + "properties": { + "flowRate": { "anyOf": [ { - "$ref": "#/definitions/AllNozzleLayoutConfiguration" - }, - { - "$ref": "#/definitions/SingleNozzleLayoutConfiguration" - }, - { - "$ref": "#/definitions/RowNozzleLayoutConfiguration" - }, - { - "$ref": "#/definitions/ColumnNozzleLayoutConfiguration" + "minimum": 0, + "type": "integer" }, { - "$ref": "#/definitions/QuadrantNozzleLayoutConfiguration" + "minimum": 0.0, + "type": "number" } - ] + ], + "description": "Flow rate for blow out, in microliters per second.", + "title": "Flowrate" + }, + "location": { + "$ref": "#/$defs/BlowoutLocation", + "description": "Location well or trash entity for blow out." } }, - "required": ["pipetteId", "configurationParams"] + "required": ["location", "flowRate"], + "title": "BlowoutParams", + "type": "object" }, - "ConfigureNozzleLayoutCreate": { - "title": "ConfigureNozzleLayoutCreate", - "description": "Configure nozzle layout creation request model.", - "type": "object", + "BlowoutProperties": { + "description": "Blowout properties.", + "properties": { + "enable": { + "description": "Whether blow-out is enabled.", + "title": "Enable", + "type": "boolean" + }, + "params": { + "$ref": "#/$defs/BlowoutParams", + "description": "Parameters for the blowout function.", + "title": "Params" + } + }, + "required": ["enable"], + "title": "BlowoutProperties", + "type": "object" + }, + "CalibrateGripperCreate": { + "description": "A request to create a `calibrateGripper` command.", "properties": { "commandType": { + "const": "calibration/calibrateGripper", + "default": "calibration/calibrateGripper", + "enum": ["calibration/calibrateGripper"], "title": "Commandtype", - "default": "configureNozzleLayout", - "enum": ["configureNozzleLayout"], "type": "string" }, - "params": { - "$ref": "#/definitions/ConfigureNozzleLayoutParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/CalibrateGripperParams" } }, - "required": ["params"] + "required": ["params"], + "title": "CalibrateGripperCreate", + "type": "object" }, - "CustomParams": { - "title": "CustomParams", - "description": "Payload used by a custom command.", - "type": "object", - "properties": {} + "CalibrateGripperParams": { + "description": "Parameters for a `calibrateGripper` command.", + "properties": { + "jaw": { + "$ref": "#/$defs/CalibrateGripperParamsJaw", + "description": "Which of the gripper's jaws to use to measure its offset. The robot will assume that a human operator has already attached the capacitive probe to the jaw and none is attached to the other jaw." + }, + "otherJawOffset": { + "$ref": "#/$defs/Vec3f", + "description": "If an offset for the other probe is already found, then specifying it here will enable the CalibrateGripper command to complete the calibration process by calculating the total offset and saving it to disk. If this param is not specified then the command will only find and return the offset for the specified probe.", + "title": "Otherjawoffset" + } + }, + "required": ["jaw"], + "title": "CalibrateGripperParams", + "type": "object" }, - "CustomCreate": { - "title": "CustomCreate", - "description": "A request to create a custom command.", - "type": "object", + "CalibrateGripperParamsJaw": { + "enum": ["front", "rear"], + "title": "CalibrateGripperParamsJaw", + "type": "string" + }, + "CalibrateModuleCreate": { + "description": "Create calibrate-module command request model.", "properties": { "commandType": { + "const": "calibration/calibrateModule", + "default": "calibration/calibrateModule", + "enum": ["calibration/calibrateModule"], "title": "Commandtype", - "default": "custom", - "enum": ["custom"], "type": "string" }, - "params": { - "$ref": "#/definitions/CustomParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/CalibrateModuleParams" } }, - "required": ["params"] + "required": ["params"], + "title": "CalibrateModuleCreate", + "type": "object" }, - "DispenseParams": { - "title": "DispenseParams", - "description": "Payload required to dispense to a specific well.", - "type": "object", + "CalibrateModuleParams": { + "description": "Payload required to calibrate-module.", "properties": { "labwareId": { + "description": "The unique id of module calibration adapter labware.", "title": "Labwareid", - "description": "Identifier of labware to use.", - "type": "string" - }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", "type": "string" }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to perform the operation", - "allOf": [ - { - "$ref": "#/definitions/LiquidHandlingWellLocation" - } - ] - }, - "flowRate": { - "title": "Flowrate", - "description": "Speed in \u00b5L/s configured for the pipette", - "exclusiveMinimum": 0, - "type": "number" - }, - "volume": { - "title": "Volume", - "description": "The amount of liquid to dispense, in \u00b5L. Must not be greater than the currently aspirated volume. There is some tolerance for floating point rounding errors.", - "minimum": 0, - "type": "number" - }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", + "moduleId": { + "description": "The unique id of module to calibrate.", + "title": "Moduleid", "type": "string" }, - "pushOut": { - "title": "Pushout", - "description": "push the plunger a small amount farther than necessary for accurate low-volume dispensing", - "type": "number" + "mount": { + "$ref": "#/$defs/MountType", + "description": "The instrument mount used to calibrate the module." } }, - "required": ["labwareId", "wellName", "flowRate", "volume", "pipetteId"] + "required": ["moduleId", "labwareId", "mount"], + "title": "CalibrateModuleParams", + "type": "object" }, - "DispenseCreate": { - "title": "DispenseCreate", - "description": "Create dispense command request model.", - "type": "object", + "CalibratePipetteCreate": { + "description": "Create calibrate-pipette command request model.", "properties": { "commandType": { + "const": "calibration/calibratePipette", + "default": "calibration/calibratePipette", + "enum": ["calibration/calibratePipette"], "title": "Commandtype", - "default": "dispense", - "enum": ["dispense"], "type": "string" }, - "params": { - "$ref": "#/definitions/DispenseParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/CalibratePipetteParams" } }, - "required": ["params"] + "required": ["params"], + "title": "CalibratePipetteCreate", + "type": "object" }, - "DispenseInPlaceParams": { - "title": "DispenseInPlaceParams", - "description": "Payload required to dispense in place.", - "type": "object", + "CalibratePipetteParams": { + "description": "Payload required to calibrate-pipette.", "properties": { - "flowRate": { - "title": "Flowrate", - "description": "Speed in \u00b5L/s configured for the pipette", - "exclusiveMinimum": 0, - "type": "number" - }, - "volume": { - "title": "Volume", - "description": "The amount of liquid to dispense, in \u00b5L. Must not be greater than the currently aspirated volume. There is some tolerance for floating point rounding errors.", - "minimum": 0, - "type": "number" - }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" - }, - "pushOut": { - "title": "Pushout", - "description": "push the plunger a small amount farther than necessary for accurate low-volume dispensing", - "type": "number" + "mount": { + "$ref": "#/$defs/MountType", + "description": "Instrument mount to calibrate." } }, - "required": ["flowRate", "volume", "pipetteId"] + "required": ["mount"], + "title": "CalibratePipetteParams", + "type": "object" }, - "DispenseInPlaceCreate": { - "title": "DispenseInPlaceCreate", - "description": "DispenseInPlace command request model.", - "type": "object", + "CloseLabwareLatchCreate": { + "description": "A request to create a Heater-Shaker's close latch command.", "properties": { "commandType": { + "const": "heaterShaker/closeLabwareLatch", + "default": "heaterShaker/closeLabwareLatch", + "enum": ["heaterShaker/closeLabwareLatch"], "title": "Commandtype", - "default": "dispenseInPlace", - "enum": ["dispenseInPlace"], "type": "string" }, - "params": { - "$ref": "#/definitions/DispenseInPlaceParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/CloseLabwareLatchParams" } }, - "required": ["params"] + "required": ["params"], + "title": "CloseLabwareLatchCreate", + "type": "object" }, - "WellLocation": { - "title": "WellLocation", - "description": "A relative location in reference to a well's location.", - "type": "object", + "CloseLabwareLatchParams": { + "description": "Input parameters to close a Heater-Shaker Module's labware latch.", "properties": { - "origin": { - "default": "top", - "allOf": [ - { - "$ref": "#/definitions/WellOrigin" - } - ] - }, - "offset": { - "$ref": "#/definitions/WellOffset" - }, - "volumeOffset": { - "title": "Volumeoffset", - "description": "A volume of liquid, in \u00b5L, to offset the z-axis offset.", - "default": 0.0, - "type": "number" + "moduleId": { + "description": "Unique ID of the Heater-Shaker Module.", + "title": "Moduleid", + "type": "string" } - } + }, + "required": ["moduleId"], + "title": "CloseLabwareLatchParams", + "type": "object" }, - "BlowOutParams": { - "title": "BlowOutParams", - "description": "Payload required to blow-out a specific well.", - "type": "object", + "ColumnNozzleLayoutConfiguration": { + "description": "Information required for nozzle configurations of type ROW and COLUMN.", "properties": { - "labwareId": { - "title": "Labwareid", - "description": "Identifier of labware to use.", - "type": "string" - }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", + "primaryNozzle": { + "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", + "enum": ["A1", "H1", "A12", "H12"], + "title": "Primarynozzle", "type": "string" }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to perform the operation", - "allOf": [ - { - "$ref": "#/definitions/WellLocation" - } - ] - }, - "flowRate": { - "title": "Flowrate", - "description": "Speed in \u00b5L/s configured for the pipette", - "exclusiveMinimum": 0, - "type": "number" - }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", + "style": { + "const": "COLUMN", + "default": "COLUMN", + "enum": ["COLUMN"], + "title": "Style", "type": "string" } }, - "required": ["labwareId", "wellName", "flowRate", "pipetteId"] + "required": ["primaryNozzle"], + "title": "ColumnNozzleLayoutConfiguration", + "type": "object" }, - "BlowOutCreate": { - "title": "BlowOutCreate", - "description": "Create blow-out command request model.", - "type": "object", + "CommandIntent": { + "description": "Run intent for a given command.\n\nProps:\n PROTOCOL: the command is part of the protocol run itself.\n SETUP: the command is part of the setup phase of a run.", + "enum": ["protocol", "setup", "fixit"], + "title": "CommandIntent", + "type": "string" + }, + "CommentCreate": { + "description": "Comment command request model.", "properties": { "commandType": { + "const": "comment", + "default": "comment", + "enum": ["comment"], "title": "Commandtype", - "default": "blowout", - "enum": ["blowout"], "type": "string" }, - "params": { - "$ref": "#/definitions/BlowOutParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/CommentParams" } }, - "required": ["params"] + "required": ["params"], + "title": "CommentCreate", + "type": "object" }, - "BlowOutInPlaceParams": { - "title": "BlowOutInPlaceParams", - "description": "Payload required to blow-out in place.", - "type": "object", + "CommentParams": { + "description": "Payload required to annotate execution with a comment.", "properties": { - "flowRate": { - "title": "Flowrate", - "description": "Speed in \u00b5L/s configured for the pipette", - "exclusiveMinimum": 0, - "type": "number" - }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", + "message": { + "description": "A user-facing message", + "title": "Message", "type": "string" } }, - "required": ["flowRate", "pipetteId"] + "required": ["message"], + "title": "CommentParams", + "type": "object" }, - "BlowOutInPlaceCreate": { - "title": "BlowOutInPlaceCreate", - "description": "BlowOutInPlace command request model.", - "type": "object", + "ConfigureForVolumeCreate": { + "description": "Configure for volume command creation request model.", "properties": { "commandType": { + "const": "configureForVolume", + "default": "configureForVolume", + "enum": ["configureForVolume"], "title": "Commandtype", - "default": "blowOutInPlace", - "enum": ["blowOutInPlace"], "type": "string" }, - "params": { - "$ref": "#/definitions/BlowOutInPlaceParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" - } - }, - "required": ["params"] - }, - "DropTipWellOrigin": { - "title": "DropTipWellOrigin", - "description": "The origin of a DropTipWellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well\n DEFAULT: the default drop-tip location of the well,\n based on pipette configuration and length of the tip.", - "enum": ["top", "bottom", "center", "default"], - "type": "string" - }, - "DropTipWellLocation": { - "title": "DropTipWellLocation", - "description": "Like WellLocation, but for dropping tips.\n\nUnlike a typical WellLocation, the location for a drop tip\ndefaults to location based on the tip length rather than the well's top.", - "type": "object", - "properties": { - "origin": { - "default": "default", - "allOf": [ - { - "$ref": "#/definitions/DropTipWellOrigin" - } - ] }, - "offset": { - "$ref": "#/definitions/WellOffset" + "params": { + "$ref": "#/$defs/ConfigureForVolumeParams" } - } + }, + "required": ["params"], + "title": "ConfigureForVolumeCreate", + "type": "object" }, - "DropTipParams": { - "title": "DropTipParams", - "description": "Payload required to drop a tip in a specific well.", - "type": "object", + "ConfigureForVolumeParams": { + "description": "Parameters required to configure volume for a specific pipette.", "properties": { "pipetteId": { - "title": "Pipetteid", "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" }, - "labwareId": { - "title": "Labwareid", - "description": "Identifier of labware to use.", - "type": "string" - }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", + "tipOverlapNotAfterVersion": { + "description": "A version of tip overlap data to not exceed. The highest-versioned tip overlap data that does not exceed this version will be used. Versions are expressed as vN where N is an integer, counting up from v0. If None, the current highest version will be used.", + "title": "Tipoverlapnotafterversion", "type": "string" }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to drop the tip.", - "allOf": [ - { - "$ref": "#/definitions/DropTipWellLocation" - } - ] - }, - "homeAfter": { - "title": "Homeafter", - "description": "Whether to home this pipette's plunger after dropping the tip. You should normally leave this unspecified to let the robot choose a safe default depending on its hardware.", - "type": "boolean" - }, - "alternateDropLocation": { - "title": "Alternatedroplocation", - "description": "Whether to alternate location where tip is dropped within the labware. If True, this command will ignore the wellLocation provided and alternate between dropping tips at two predetermined locations inside the specified labware well. If False, the tip will be dropped at the top center of the well.", - "default": false, - "type": "boolean" + "volume": { + "description": "Amount of liquid in uL. Must be at least 0 and no greater than a pipette-specific maximum volume.", + "minimum": 0.0, + "title": "Volume", + "type": "number" } }, - "required": ["pipetteId", "labwareId", "wellName"] + "required": ["pipetteId", "volume"], + "title": "ConfigureForVolumeParams", + "type": "object" }, - "DropTipCreate": { - "title": "DropTipCreate", - "description": "Drop tip command creation request model.", - "type": "object", + "ConfigureNozzleLayoutCreate": { + "description": "Configure nozzle layout creation request model.", "properties": { "commandType": { + "const": "configureNozzleLayout", + "default": "configureNozzleLayout", + "enum": ["configureNozzleLayout"], "title": "Commandtype", - "default": "dropTip", - "enum": ["dropTip"], "type": "string" }, - "params": { - "$ref": "#/definitions/DropTipParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/ConfigureNozzleLayoutParams" } }, - "required": ["params"] + "required": ["params"], + "title": "ConfigureNozzleLayoutCreate", + "type": "object" }, - "DropTipInPlaceParams": { - "title": "DropTipInPlaceParams", - "description": "Payload required to drop a tip in place.", - "type": "object", + "ConfigureNozzleLayoutParams": { + "description": "Parameters required to configure the nozzle layout for a specific pipette.", "properties": { + "configurationParams": { + "anyOf": [ + { + "$ref": "#/$defs/AllNozzleLayoutConfiguration" + }, + { + "$ref": "#/$defs/SingleNozzleLayoutConfiguration" + }, + { + "$ref": "#/$defs/RowNozzleLayoutConfiguration" + }, + { + "$ref": "#/$defs/ColumnNozzleLayoutConfiguration" + }, + { + "$ref": "#/$defs/QuadrantNozzleLayoutConfiguration" + } + ], + "title": "Configurationparams" + }, "pipetteId": { - "title": "Pipetteid", "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" + } + }, + "required": ["pipetteId", "configurationParams"], + "title": "ConfigureNozzleLayoutParams", + "type": "object" + }, + "Coordinate": { + "description": "Three-dimensional coordinates.", + "properties": { + "x": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ], + "title": "X" }, - "homeAfter": { - "title": "Homeafter", - "description": "Whether to home this pipette's plunger after dropping the tip. You should normally leave this unspecified to let the robot choose a safe default depending on its hardware.", - "type": "boolean" + "y": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ], + "title": "Y" + }, + "z": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ], + "title": "Z" } }, - "required": ["pipetteId"] + "required": ["x", "y", "z"], + "title": "Coordinate", + "type": "object" }, - "DropTipInPlaceCreate": { - "title": "DropTipInPlaceCreate", - "description": "Drop tip in place command creation request model.", - "type": "object", + "CustomCreate": { + "description": "A request to create a custom command.", "properties": { "commandType": { + "const": "custom", + "default": "custom", + "enum": ["custom"], "title": "Commandtype", - "default": "dropTipInPlace", - "enum": ["dropTipInPlace"], "type": "string" }, - "params": { - "$ref": "#/definitions/DropTipInPlaceParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/CustomParams" } }, - "required": ["params"] - }, - "MotorAxis": { - "title": "MotorAxis", - "description": "Motor axis on which to issue a home command.", - "enum": [ - "x", - "y", - "leftZ", - "rightZ", - "leftPlunger", - "rightPlunger", - "extensionZ", - "extensionJaw", - "axis96ChannelCam" - ], - "type": "string" - }, - "MountType": { - "title": "MountType", - "description": "An enumeration.", - "enum": ["left", "right", "extension"], - "type": "string" + "required": ["params"], + "title": "CustomCreate", + "type": "object" }, - "HomeParams": { - "title": "HomeParams", - "description": "Payload required for a Home command.", - "type": "object", - "properties": { - "axes": { - "description": "Axes to return to their home positions. If omitted, will home all motors. Extra axes may be implicitly homed to ensure accurate homing of the explicitly specified axes.", - "type": "array", - "items": { - "$ref": "#/definitions/MotorAxis" - } - }, - "skipIfMountPositionOk": { - "description": "If this parameter is provided, the gantry will only be homed if the specified mount has an invalid position. If omitted, the homing action will be executed unconditionally.", - "allOf": [ - { - "$ref": "#/definitions/MountType" - } - ] - } - } + "CustomParams": { + "additionalProperties": true, + "description": "Payload used by a custom command.", + "properties": {}, + "title": "CustomParams", + "type": "object" }, - "HomeCreate": { - "title": "HomeCreate", - "description": "Data to create a Home command.", - "type": "object", + "DeactivateBlockCreate": { + "description": "A request to create a Thermocycler's deactivate block command.", "properties": { "commandType": { + "const": "thermocycler/deactivateBlock", + "default": "thermocycler/deactivateBlock", + "enum": ["thermocycler/deactivateBlock"], "title": "Commandtype", - "default": "home", - "enum": ["home"], "type": "string" }, - "params": { - "$ref": "#/definitions/HomeParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/DeactivateBlockParams" } }, - "required": ["params"] + "required": ["params"], + "title": "DeactivateBlockCreate", + "type": "object" }, - "RetractAxisParams": { - "title": "RetractAxisParams", - "description": "Payload required for a Retract Axis command.", - "type": "object", + "DeactivateBlockParams": { + "description": "Input parameters to unset a Thermocycler's target block temperature.", "properties": { - "axis": { - "description": "Axis to retract to its home position as quickly as safely possible. The difference between retracting an axis and homing an axis using the home command is that a home will always probe the limit switch and will work as the first motion command a robot will need to execute; On the other hand, retraction will rely on this previously determined home position to move to it as fast as safely possible. So on the Flex, it will move (fast) the axis to the previously recorded home position and on the OT2, it will move (fast) the axis a safe distance from the previously recorded home position, and then slowly approach the limit switch.", - "allOf": [ - { - "$ref": "#/definitions/MotorAxis" - } - ] + "moduleId": { + "description": "Unique ID of the Thermocycler.", + "title": "Moduleid", + "type": "string" } }, - "required": ["axis"] + "required": ["moduleId"], + "title": "DeactivateBlockParams", + "type": "object" }, - "RetractAxisCreate": { - "title": "RetractAxisCreate", - "description": "Data to create a Retract Axis command.", - "type": "object", + "DeactivateHeaterCreate": { + "description": "A request to create a Heater-Shaker's deactivate heater command.", "properties": { "commandType": { + "const": "heaterShaker/deactivateHeater", + "default": "heaterShaker/deactivateHeater", + "enum": ["heaterShaker/deactivateHeater"], "title": "Commandtype", - "default": "retractAxis", - "enum": ["retractAxis"], "type": "string" }, - "params": { - "$ref": "#/definitions/RetractAxisParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/DeactivateHeaterParams" } }, - "required": ["params"] - }, - "DeckSlotName": { - "title": "DeckSlotName", - "description": "Deck slot identifiers.", - "enum": [ - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12", - "A1", - "A2", - "A3", - "B1", - "B2", - "B3", - "C1", - "C2", - "C3", - "D1", - "D2", - "D3" - ] - }, - "DeckSlotLocation": { - "title": "DeckSlotLocation", - "description": "The location of something placed in a single deck slot.", - "type": "object", - "properties": { - "slotName": { - "description": "A slot on the robot's deck.\n\nThe plain numbers like `\"5\"` are for the OT-2, and the coordinates like `\"C2\"` are for the Flex.\n\nWhen you provide one of these values, you can use either style. It will automatically be converted to match the robot.\n\nWhen one of these values is returned, it will always match the robot.", - "allOf": [ - { - "$ref": "#/definitions/DeckSlotName" - } - ] - } - }, - "required": ["slotName"] + "required": ["params"], + "title": "DeactivateHeaterCreate", + "type": "object" }, - "ModuleLocation": { - "title": "ModuleLocation", - "description": "The location of something placed atop a hardware module.", - "type": "object", + "DeactivateHeaterParams": { + "description": "Input parameters to unset a Heater-Shaker's target temperature.", "properties": { "moduleId": { + "description": "Unique ID of the Heater-Shaker Module.", "title": "Moduleid", - "description": "The ID of a loaded module from a prior `loadModule` command.", "type": "string" } }, - "required": ["moduleId"] + "required": ["moduleId"], + "title": "DeactivateHeaterParams", + "type": "object" }, - "OnLabwareLocation": { - "title": "OnLabwareLocation", - "description": "The location of something placed atop another labware.", - "type": "object", + "DeactivateLidCreate": { + "description": "A request to create a Thermocycler's deactivate lid command.", "properties": { - "labwareId": { - "title": "Labwareid", - "description": "The ID of a loaded Labware from a prior `loadLabware` command.", + "commandType": { + "const": "thermocycler/deactivateLid", + "default": "thermocycler/deactivateLid", + "enum": ["thermocycler/deactivateLid"], + "title": "Commandtype", "type": "string" - } - }, - "required": ["labwareId"] - }, - "AddressableAreaLocation": { - "title": "AddressableAreaLocation", - "description": "The location of something place in an addressable area. This is a superset of deck slots.", - "type": "object", - "properties": { - "addressableAreaName": { - "title": "Addressableareaname", - "description": "The name of the addressable area that you want to use. Valid values are the `id`s of `addressableArea`s in the [deck definition](https://github.com/Opentrons/opentrons/tree/edge/shared-data/deck).", - "type": "string" - } - }, - "required": ["addressableAreaName"] - }, - "LoadLabwareParams": { - "title": "LoadLabwareParams", - "description": "Payload required to load a labware into a slot.", - "type": "object", - "properties": { - "location": { - "title": "Location", - "description": "Location the labware should be loaded into.", - "anyOf": [ - { - "$ref": "#/definitions/DeckSlotLocation" - }, - { - "$ref": "#/definitions/ModuleLocation" - }, - { - "$ref": "#/definitions/OnLabwareLocation" - }, - { - "enum": ["offDeck"], - "type": "string" - }, - { - "$ref": "#/definitions/AddressableAreaLocation" - } - ] - }, - "loadName": { - "title": "Loadname", - "description": "Name used to reference a labware definition.", - "type": "string" - }, - "namespace": { - "title": "Namespace", - "description": "The namespace the labware definition belongs to.", - "type": "string" - }, - "version": { - "title": "Version", - "description": "The labware definition version.", - "type": "integer" - }, - "labwareId": { - "title": "Labwareid", - "description": "An optional ID to assign to this labware. If None, an ID will be generated.", - "type": "string" - }, - "displayName": { - "title": "Displayname", - "description": "An optional user-specified display name or label for this labware.", - "type": "string" - } - }, - "required": ["location", "loadName", "namespace", "version"] - }, - "LoadLabwareCreate": { - "title": "LoadLabwareCreate", - "description": "Load labware command creation request.", - "type": "object", - "properties": { - "commandType": { - "title": "Commandtype", - "default": "loadLabware", - "enum": ["loadLabware"], - "type": "string" - }, - "params": { - "$ref": "#/definitions/LoadLabwareParams" }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/DeactivateLidParams" } }, - "required": ["params"] + "required": ["params"], + "title": "DeactivateLidCreate", + "type": "object" }, - "ReloadLabwareParams": { - "title": "ReloadLabwareParams", - "description": "Payload required to load a labware into a slot.", - "type": "object", + "DeactivateLidParams": { + "description": "Input parameters to unset a Thermocycler's target lid temperature.", "properties": { - "labwareId": { - "title": "Labwareid", - "description": "The already-loaded labware instance to update.", + "moduleId": { + "description": "Unique ID of the Thermocycler.", + "title": "Moduleid", "type": "string" } }, - "required": ["labwareId"] + "required": ["moduleId"], + "title": "DeactivateLidParams", + "type": "object" }, - "ReloadLabwareCreate": { - "title": "ReloadLabwareCreate", - "description": "Reload labware command creation request.", - "type": "object", + "DeactivateShakerCreate": { + "description": "A request to create a Heater-Shaker's deactivate shaker command.", "properties": { "commandType": { + "const": "heaterShaker/deactivateShaker", + "default": "heaterShaker/deactivateShaker", + "enum": ["heaterShaker/deactivateShaker"], "title": "Commandtype", - "default": "reloadLabware", - "enum": ["reloadLabware"], "type": "string" }, - "params": { - "$ref": "#/definitions/ReloadLabwareParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/DeactivateShakerParams" } }, - "required": ["params"] + "required": ["params"], + "title": "DeactivateShakerCreate", + "type": "object" }, - "LoadLiquidParams": { - "title": "LoadLiquidParams", - "description": "Payload required to load a liquid into a well.", - "type": "object", + "DeactivateShakerParams": { + "description": "Input parameters to deactivate shaker for a Heater-Shaker Module.", "properties": { - "liquidId": { - "title": "Liquidid", - "description": "Unique identifier of the liquid to load. If this is the sentinel value EMPTY, all values of volumeByWell must be 0.", - "anyOf": [ - { - "type": "string" - }, - { - "enum": ["EMPTY"], - "type": "string" - } - ] - }, - "labwareId": { - "title": "Labwareid", - "description": "Unique identifier of labware to load liquid into.", + "moduleId": { + "description": "Unique ID of the Heater-Shaker Module.", + "title": "Moduleid", "type": "string" - }, - "volumeByWell": { - "title": "Volumebywell", - "description": "Volume of liquid, in \u00b5L, loaded into each well by name, in this labware. If the liquid id is the sentinel value EMPTY, all volumes must be 0.", - "type": "object", - "additionalProperties": { - "type": "number" - } } }, - "required": ["liquidId", "labwareId", "volumeByWell"] + "required": ["moduleId"], + "title": "DeactivateShakerParams", + "type": "object" }, - "LoadLiquidCreate": { - "title": "LoadLiquidCreate", - "description": "Load liquid command creation request.", - "type": "object", + "DeactivateTemperatureCreate": { + "description": "A request to deactivate a Temperature Module.", "properties": { "commandType": { + "const": "temperatureModule/deactivate", + "default": "temperatureModule/deactivate", + "enum": ["temperatureModule/deactivate"], "title": "Commandtype", - "default": "loadLiquid", - "enum": ["loadLiquid"], "type": "string" }, - "params": { - "$ref": "#/definitions/LoadLiquidParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/DeactivateTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "DeactivateTemperatureCreate", + "type": "object" }, - "PositionReference": { - "title": "PositionReference", - "description": "Positional reference for liquid handling operations.", - "enum": ["well-bottom", "well-top", "well-center", "liquid-meniscus"] + "DeactivateTemperatureParams": { + "description": "Input parameters to deactivate a Temperature Module.", + "properties": { + "moduleId": { + "description": "Unique ID of the Temperature Module.", + "title": "Moduleid", + "type": "string" + } + }, + "required": ["moduleId"], + "title": "DeactivateTemperatureParams", + "type": "object" }, - "Coordinate": { - "title": "Coordinate", - "description": "Three-dimensional coordinates.", - "type": "object", + "DeckPoint": { + "description": "Coordinates of a point in deck space.", "properties": { "x": { "title": "X", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] + "type": "number" }, "y": { "title": "Y", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] + "type": "number" }, "z": { "title": "Z", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] + "type": "number" + } + }, + "required": ["x", "y", "z"], + "title": "DeckPoint", + "type": "object" + }, + "DeckSlotLocation": { + "description": "The location of something placed in a single deck slot.", + "properties": { + "slotName": { + "$ref": "#/$defs/DeckSlotName", + "description": "A slot on the robot's deck.\n\nThe plain numbers like `\"5\"` are for the OT-2, and the coordinates like `\"C2\"` are for the Flex.\n\nWhen you provide one of these values, you can use either style. It will automatically be converted to match the robot.\n\nWhen one of these values is returned, it will always match the robot." } }, - "required": ["x", "y", "z"] + "required": ["slotName"], + "title": "DeckSlotLocation", + "type": "object" + }, + "DeckSlotName": { + "description": "Deck slot identifiers.", + "enum": [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "A1", + "A2", + "A3", + "B1", + "B2", + "B3", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3" + ], + "title": "DeckSlotName", + "type": "string" }, "DelayParams": { - "title": "DelayParams", "description": "Parameters for delay.", - "type": "object", "properties": { "duration": { - "title": "Duration", - "description": "Duration of delay, in seconds.", "anyOf": [ { - "type": "integer", - "minimum": 0 + "minimum": 0, + "type": "integer" }, { - "type": "number", - "minimum": 0.0 + "minimum": 0.0, + "type": "number" } - ] + ], + "description": "Duration of delay, in seconds.", + "title": "Duration" } }, - "required": ["duration"] + "required": ["duration"], + "title": "DelayParams", + "type": "object" }, "DelayProperties": { - "title": "DelayProperties", "description": "Shared properties for delay..", - "type": "object", "properties": { "enable": { - "title": "Enable", "description": "Whether delay is enabled.", + "title": "Enable", "type": "boolean" }, "params": { - "title": "Params", + "$ref": "#/$defs/DelayParams", "description": "Parameters for the delay function.", - "allOf": [ - { - "$ref": "#/definitions/DelayParams" - } - ] + "title": "Params" } }, - "required": ["enable"] + "required": ["enable"], + "title": "DelayProperties", + "type": "object" }, - "Submerge": { - "title": "Submerge", - "description": "Shared properties for the submerge function before aspiration or dispense.", - "type": "object", + "DisengageCreate": { + "description": "A request to create a Magnetic Module disengage command.", "properties": { - "positionReference": { - "description": "Position reference for submerge.", - "allOf": [ - { - "$ref": "#/definitions/PositionReference" - } - ] + "commandType": { + "const": "magneticModule/disengage", + "default": "magneticModule/disengage", + "enum": ["magneticModule/disengage"], + "title": "Commandtype", + "type": "string" }, - "offset": { - "title": "Offset", - "description": "Relative offset for submerge.", - "allOf": [ - { - "$ref": "#/definitions/Coordinate" - } - ] + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" }, - "speed": { - "title": "Speed", - "description": "Speed of submerging, in millimeters per second.", - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" }, - "delay": { - "title": "Delay", - "description": "Delay settings for submerge.", - "allOf": [ - { - "$ref": "#/definitions/DelayProperties" - } - ] + "params": { + "$ref": "#/$defs/DisengageParams" } }, - "required": ["positionReference", "offset", "speed", "delay"] + "required": ["params"], + "title": "DisengageCreate", + "type": "object" }, - "LiquidClassTouchTipParams": { - "title": "LiquidClassTouchTipParams", - "description": "Parameters for touch-tip.", - "type": "object", + "DisengageParams": { + "description": "Input data to disengage a Magnetic Module's magnets.", "properties": { - "zOffset": { - "title": "Zoffset", - "description": "Offset from the top of the well for touch-tip, in millimeters.", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] - }, - "mmToEdge": { - "title": "Mmtoedge", - "description": "Offset away from the the well edge, in millimeters.", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] - }, - "speed": { - "title": "Speed", - "description": "Touch-tip speed, in millimeters per second.", - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] + "moduleId": { + "description": "The ID of the Magnetic Module whose magnets you want to disengage, from a prior `loadModule` command.", + "title": "Moduleid", + "type": "string" } }, - "required": ["zOffset", "mmToEdge", "speed"] + "required": ["moduleId"], + "title": "DisengageParams", + "type": "object" }, - "TouchTipProperties": { - "title": "TouchTipProperties", - "description": "Shared properties for the touch-tip function.", - "type": "object", + "DispenseCreate": { + "description": "Create dispense command request model.", "properties": { - "enable": { - "title": "Enable", - "description": "Whether touch-tip is enabled.", - "type": "boolean" + "commandType": { + "const": "dispense", + "default": "dispense", + "enum": ["dispense"], + "title": "Commandtype", + "type": "string" + }, + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" }, "params": { - "title": "Params", - "description": "Parameters for the touch-tip function.", - "allOf": [ - { - "$ref": "#/definitions/LiquidClassTouchTipParams" - } - ] + "$ref": "#/$defs/DispenseParams" } }, - "required": ["enable"] + "required": ["params"], + "title": "DispenseCreate", + "type": "object" }, - "RetractAspirate": { - "title": "RetractAspirate", - "description": "Shared properties for the retract function after aspiration.", - "type": "object", + "DispenseInPlaceCreate": { + "description": "DispenseInPlace command request model.", "properties": { - "positionReference": { - "description": "Position reference for retract after aspirate.", - "allOf": [ - { - "$ref": "#/definitions/PositionReference" - } - ] + "commandType": { + "const": "dispenseInPlace", + "default": "dispenseInPlace", + "enum": ["dispenseInPlace"], + "title": "Commandtype", + "type": "string" }, - "offset": { - "title": "Offset", - "description": "Relative offset for retract after aspirate.", - "allOf": [ - { - "$ref": "#/definitions/Coordinate" - } - ] + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" }, - "speed": { - "title": "Speed", - "description": "Speed of retraction, in millimeters per second.", - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" }, - "airGapByVolume": { - "title": "Airgapbyvolume", - "description": "Settings for air gap keyed by target aspiration volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - } - ] - } + "params": { + "$ref": "#/$defs/DispenseInPlaceParams" + } + }, + "required": ["params"], + "title": "DispenseInPlaceCreate", + "type": "object" + }, + "DispenseInPlaceParams": { + "description": "Payload required to dispense in place.", + "properties": { + "flowRate": { + "description": "Speed in \u00b5L/s configured for the pipette", + "exclusiveMinimum": 0.0, + "title": "Flowrate", + "type": "number" }, - "touchTip": { - "title": "Touchtip", - "description": "Touch tip settings for retract after aspirate.", - "allOf": [ - { - "$ref": "#/definitions/TouchTipProperties" - } - ] + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" }, - "delay": { - "title": "Delay", - "description": "Delay settings for retract after aspirate.", - "allOf": [ - { - "$ref": "#/definitions/DelayProperties" - } - ] + "pushOut": { + "description": "push the plunger a small amount farther than necessary for accurate low-volume dispensing", + "title": "Pushout", + "type": "number" + }, + "volume": { + "description": "The amount of liquid to dispense, in \u00b5L. Must not be greater than the currently aspirated volume. There is some tolerance for floating point rounding errors.", + "minimum": 0.0, + "title": "Volume", + "type": "number" } }, - "required": [ - "positionReference", - "offset", - "speed", - "airGapByVolume", - "touchTip", - "delay" - ] + "required": ["flowRate", "volume", "pipetteId"], + "title": "DispenseInPlaceParams", + "type": "object" }, - "MixParams": { - "title": "MixParams", - "description": "Parameters for mix.", - "type": "object", + "DispenseParams": { + "description": "Payload required to dispense to a specific well.", "properties": { - "repetitions": { - "title": "Repetitions", - "description": "Number of mixing repetitions.", - "minimum": 0, - "type": "integer" + "flowRate": { + "description": "Speed in \u00b5L/s configured for the pipette", + "exclusiveMinimum": 0.0, + "title": "Flowrate", + "type": "number" + }, + "labwareId": { + "description": "Identifier of labware to use.", + "title": "Labwareid", + "type": "string" + }, + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" + }, + "pushOut": { + "description": "push the plunger a small amount farther than necessary for accurate low-volume dispensing", + "title": "Pushout", + "type": "number" }, "volume": { + "description": "The amount of liquid to dispense, in \u00b5L. Must not be greater than the currently aspirated volume. There is some tolerance for floating point rounding errors.", + "minimum": 0.0, "title": "Volume", - "description": "Volume used for mixing, in microliters.", - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] + "type": "number" + }, + "wellLocation": { + "$ref": "#/$defs/LiquidHandlingWellLocation", + "description": "Relative well location at which to perform the operation" + }, + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", + "type": "string" } }, - "required": ["repetitions", "volume"] + "required": ["labwareId", "wellName", "flowRate", "volume", "pipetteId"], + "title": "DispenseParams", + "type": "object" }, - "MixProperties": { - "title": "MixProperties", - "description": "Mixing properties.", - "type": "object", + "DropTipCreate": { + "description": "Drop tip command creation request model.", "properties": { - "enable": { - "title": "Enable", - "description": "Whether mix is enabled.", - "type": "boolean" + "commandType": { + "const": "dropTip", + "default": "dropTip", + "enum": ["dropTip"], + "title": "Commandtype", + "type": "string" + }, + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" }, "params": { - "title": "Params", - "description": "Parameters for the mix function.", - "allOf": [ - { - "$ref": "#/definitions/MixParams" - } - ] + "$ref": "#/$defs/DropTipParams" } }, - "required": ["enable"] + "required": ["params"], + "title": "DropTipCreate", + "type": "object" }, - "AspirateProperties": { - "title": "AspirateProperties", - "description": "Properties specific to the aspirate function.", - "type": "object", + "DropTipInPlaceCreate": { + "description": "Drop tip in place command creation request model.", "properties": { - "submerge": { - "title": "Submerge", - "description": "Submerge settings for aspirate.", - "allOf": [ - { - "$ref": "#/definitions/Submerge" - } - ] - }, - "retract": { - "title": "Retract", - "description": "Pipette retract settings after an aspirate.", - "allOf": [ - { - "$ref": "#/definitions/RetractAspirate" - } - ] - }, - "positionReference": { - "description": "Position reference for aspiration.", - "allOf": [ - { - "$ref": "#/definitions/PositionReference" - } - ] - }, - "offset": { - "title": "Offset", - "description": "Relative offset for aspiration.", - "allOf": [ - { - "$ref": "#/definitions/Coordinate" - } - ] - }, - "flowRateByVolume": { - "title": "Flowratebyvolume", - "description": "Settings for flow rate keyed by target aspiration volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - } - ] - } - }, - "correctionByVolume": { - "title": "Correctionbyvolume", - "description": "Settings for volume correction keyed by by target aspiration volume, representing additional volume the plunger should move to accurately hit target volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] - } - ] - } + "commandType": { + "const": "dropTipInPlace", + "default": "dropTipInPlace", + "enum": ["dropTipInPlace"], + "title": "Commandtype", + "type": "string" }, - "preWet": { - "title": "Prewet", - "description": "Whether to perform a pre-wet action.", - "type": "boolean" + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" }, - "mix": { - "title": "Mix", - "description": "Mixing settings for before an aspirate", - "allOf": [ - { - "$ref": "#/definitions/MixProperties" - } - ] + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" }, - "delay": { - "title": "Delay", - "description": "Delay settings after an aspirate", - "allOf": [ - { - "$ref": "#/definitions/DelayProperties" - } - ] + "params": { + "$ref": "#/$defs/DropTipInPlaceParams" } }, - "required": [ - "submerge", - "retract", - "positionReference", - "offset", - "flowRateByVolume", - "correctionByVolume", - "preWet", - "mix", - "delay" - ] - }, - "BlowoutLocation": { - "title": "BlowoutLocation", - "description": "Location for blowout during a transfer function.", - "enum": ["source", "destination", "trash"] + "required": ["params"], + "title": "DropTipInPlaceCreate", + "type": "object" }, - "BlowoutParams": { - "title": "BlowoutParams", - "description": "Parameters for blowout.", - "type": "object", + "DropTipInPlaceParams": { + "description": "Payload required to drop a tip in place.", "properties": { - "location": { - "description": "Location well or trash entity for blow out.", - "allOf": [ - { - "$ref": "#/definitions/BlowoutLocation" - } - ] + "homeAfter": { + "description": "Whether to home this pipette's plunger after dropping the tip. You should normally leave this unspecified to let the robot choose a safe default depending on its hardware.", + "title": "Homeafter", + "type": "boolean" }, - "flowRate": { - "title": "Flowrate", - "description": "Flow rate for blow out, in microliters per second.", - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" } }, - "required": ["location", "flowRate"] + "required": ["pipetteId"], + "title": "DropTipInPlaceParams", + "type": "object" }, - "BlowoutProperties": { - "title": "BlowoutProperties", - "description": "Blowout properties.", - "type": "object", + "DropTipParams": { + "description": "Payload required to drop a tip in a specific well.", "properties": { - "enable": { - "title": "Enable", - "description": "Whether blow-out is enabled.", + "alternateDropLocation": { + "description": "Whether to alternate location where tip is dropped within the labware. If True, this command will ignore the wellLocation provided and alternate between dropping tips at two predetermined locations inside the specified labware well. If False, the tip will be dropped at the top center of the well.", + "title": "Alternatedroplocation", "type": "boolean" }, - "params": { - "title": "Params", - "description": "Parameters for the blowout function.", - "allOf": [ - { - "$ref": "#/definitions/BlowoutParams" - } - ] + "homeAfter": { + "description": "Whether to home this pipette's plunger after dropping the tip. You should normally leave this unspecified to let the robot choose a safe default depending on its hardware.", + "title": "Homeafter", + "type": "boolean" + }, + "labwareId": { + "description": "Identifier of labware to use.", + "title": "Labwareid", + "type": "string" + }, + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" + }, + "wellLocation": { + "$ref": "#/$defs/DropTipWellLocation", + "description": "Relative well location at which to drop the tip." + }, + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", + "type": "string" } }, - "required": ["enable"] + "required": ["pipetteId", "labwareId", "wellName"], + "title": "DropTipParams", + "type": "object" }, - "RetractDispense": { - "title": "RetractDispense", - "description": "Shared properties for the retract function after dispense.", - "type": "object", + "DropTipWellLocation": { + "description": "Like WellLocation, but for dropping tips.\n\nUnlike a typical WellLocation, the location for a drop tip\ndefaults to location based on the tip length rather than the well's top.", "properties": { - "positionReference": { - "description": "Position reference for retract after dispense.", - "allOf": [ - { - "$ref": "#/definitions/PositionReference" - } - ] - }, "offset": { - "title": "Offset", - "description": "Relative offset for retract after dispense.", - "allOf": [ - { - "$ref": "#/definitions/Coordinate" - } - ] - }, - "speed": { - "title": "Speed", - "description": "Speed of retraction, in millimeters per second.", - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - "airGapByVolume": { - "title": "Airgapbyvolume", - "description": "Settings for air gap keyed by target aspiration volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - } - ] - } - }, - "blowout": { - "title": "Blowout", - "description": "Blowout properties for retract after dispense.", - "allOf": [ - { - "$ref": "#/definitions/BlowoutProperties" - } - ] - }, - "touchTip": { - "title": "Touchtip", - "description": "Touch tip settings for retract after dispense.", - "allOf": [ - { - "$ref": "#/definitions/TouchTipProperties" - } - ] + "$ref": "#/$defs/WellOffset" }, - "delay": { - "title": "Delay", - "description": "Delay settings for retract after dispense.", - "allOf": [ - { - "$ref": "#/definitions/DelayProperties" - } - ] + "origin": { + "$ref": "#/$defs/DropTipWellOrigin", + "default": "default" } }, - "required": [ - "positionReference", - "offset", - "speed", - "airGapByVolume", - "blowout", - "touchTip", - "delay" - ] + "title": "DropTipWellLocation", + "type": "object" }, - "SingleDispenseProperties": { - "title": "SingleDispenseProperties", - "description": "Properties specific to the single-dispense function.", - "type": "object", + "DropTipWellOrigin": { + "description": "The origin of a DropTipWellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well\n DEFAULT: the default drop-tip location of the well,\n based on pipette configuration and length of the tip.", + "enum": ["top", "bottom", "center", "default"], + "title": "DropTipWellOrigin", + "type": "string" + }, + "EngageCreate": { + "description": "A request to create a Magnetic Module engage command.", "properties": { - "submerge": { - "title": "Submerge", - "description": "Submerge settings for single dispense.", - "allOf": [ - { - "$ref": "#/definitions/Submerge" - } - ] - }, - "retract": { - "title": "Retract", - "description": "Pipette retract settings after a single dispense.", - "allOf": [ - { - "$ref": "#/definitions/RetractDispense" - } - ] + "commandType": { + "const": "magneticModule/engage", + "default": "magneticModule/engage", + "enum": ["magneticModule/engage"], + "title": "Commandtype", + "type": "string" }, - "positionReference": { - "description": "Position reference for single dispense.", - "allOf": [ - { - "$ref": "#/definitions/PositionReference" - } - ] + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" }, - "offset": { - "title": "Offset", - "description": "Relative offset for single dispense.", - "allOf": [ - { - "$ref": "#/definitions/Coordinate" - } - ] - }, - "flowRateByVolume": { - "title": "Flowratebyvolume", - "description": "Settings for flow rate keyed by target dispense volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - } - ] - } - }, - "correctionByVolume": { - "title": "Correctionbyvolume", - "description": "Settings for volume correction keyed by by target dispense volume, representing additional volume the plunger should move to accurately hit target volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] - } - ] - } - }, - "mix": { - "title": "Mix", - "description": "Mixing settings for after a dispense", - "allOf": [ - { - "$ref": "#/definitions/MixProperties" - } - ] - }, - "pushOutByVolume": { - "title": "Pushoutbyvolume", - "description": "Settings for pushout keyed by target dispense volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - } - ] - } - }, - "delay": { - "title": "Delay", - "description": "Delay after dispense, in seconds.", - "allOf": [ - { - "$ref": "#/definitions/DelayProperties" - } - ] - } - }, - "required": [ - "submerge", - "retract", - "positionReference", - "offset", - "flowRateByVolume", - "correctionByVolume", - "mix", - "pushOutByVolume", - "delay" - ] - }, - "MultiDispenseProperties": { - "title": "MultiDispenseProperties", - "description": "Properties specific to the multi-dispense function.", - "type": "object", - "properties": { - "submerge": { - "title": "Submerge", - "description": "Submerge settings for multi-dispense.", - "allOf": [ - { - "$ref": "#/definitions/Submerge" - } - ] - }, - "retract": { - "title": "Retract", - "description": "Pipette retract settings after a multi-dispense.", - "allOf": [ - { - "$ref": "#/definitions/RetractDispense" - } - ] - }, - "positionReference": { - "description": "Position reference for multi-dispense.", - "allOf": [ - { - "$ref": "#/definitions/PositionReference" - } - ] - }, - "offset": { - "title": "Offset", - "description": "Relative offset for single multi-dispense.", - "allOf": [ - { - "$ref": "#/definitions/Coordinate" - } - ] - }, - "flowRateByVolume": { - "title": "Flowratebyvolume", - "description": "Settings for flow rate keyed by target dispense volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - } - ] - } - }, - "correctionByVolume": { - "title": "Correctionbyvolume", - "description": "Settings for volume correction keyed by by target dispense volume, representing additional volume the plunger should move to accurately hit target volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] - } - ] - } - }, - "conditioningByVolume": { - "title": "Conditioningbyvolume", - "description": "Settings for conditioning volume keyed by target dispense volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - } - ] - } - }, - "disposalByVolume": { - "title": "Disposalbyvolume", - "description": "Settings for disposal volume keyed by target dispense volume.", - "type": "array", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - }, - { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "number", - "minimum": 0.0 - } - ] - } - ] - } - }, - "delay": { - "title": "Delay", - "description": "Delay settings after each dispense", - "allOf": [ - { - "$ref": "#/definitions/DelayProperties" - } - ] - } - }, - "required": [ - "submerge", - "retract", - "positionReference", - "offset", - "flowRateByVolume", - "correctionByVolume", - "conditioningByVolume", - "disposalByVolume", - "delay" - ] - }, - "LiquidClassRecord": { - "title": "LiquidClassRecord", - "description": "LiquidClassRecord is our internal representation of an (immutable) liquid class.\n\nConceptually, a liquid class record is the tuple (name, pipette, tip, transfer properties).\nWe consider two liquid classes to be the same if every entry in that tuple is the same; and liquid\nclasses are different if any entry in the tuple is different.\n\nThis class defines the tuple via inheritance so that we can reuse the definitions from shared_data.", - "type": "object", - "properties": { - "tiprack": { - "title": "Tiprack", - "description": "The name of tiprack whose tip will be used when handling this specific liquid class with this pipette", - "type": "string" - }, - "aspirate": { - "title": "Aspirate", - "description": "Aspirate parameters for this tip type.", - "allOf": [ - { - "$ref": "#/definitions/AspirateProperties" - } - ] - }, - "singleDispense": { - "title": "Singledispense", - "description": "Single dispense parameters for this tip type.", - "allOf": [ - { - "$ref": "#/definitions/SingleDispenseProperties" - } - ] - }, - "multiDispense": { - "title": "Multidispense", - "description": "Optional multi-dispense parameters for this tip type.", - "allOf": [ - { - "$ref": "#/definitions/MultiDispenseProperties" - } - ] - }, - "liquidClassName": { - "title": "Liquidclassname", - "description": "Identifier for the liquid of this liquid class, e.g. glycerol50.", + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" }, - "pipetteModel": { - "title": "Pipettemodel", - "description": "Identifier for the pipette of this liquid class.", - "type": "string" + "params": { + "$ref": "#/$defs/EngageParams" } }, - "required": [ - "tiprack", - "aspirate", - "singleDispense", - "liquidClassName", - "pipetteModel" - ] + "required": ["params"], + "title": "EngageCreate", + "type": "object" }, - "LoadLiquidClassParams": { - "title": "LoadLiquidClassParams", - "description": "The liquid class transfer properties to store.", - "type": "object", + "EngageParams": { + "description": "Input data to engage a Magnetic Module.", "properties": { - "liquidClassId": { - "title": "Liquidclassid", - "description": "Unique identifier for the liquid class to store. If you do not supply a liquidClassId, we will generate one.", - "type": "string" + "height": { + "description": "How high, in millimeters, to raise the magnets.\n\nZero means the tops of the magnets are level with the ledge that the labware rests on. This will be slightly above the magnets' minimum height, the hardware home position. Negative values are allowed, to put the magnets below the ledge.\n\nUnits are always true millimeters. This is unlike certain labware definitions, engage commands in the Python Protocol API, and engage commands in older versions of the JSON protocol schema. Take care to convert properly.", + "title": "Height", + "type": "number" }, - "liquidClassRecord": { - "title": "Liquidclassrecord", - "description": "The liquid class to store.", - "allOf": [ - { - "$ref": "#/definitions/LiquidClassRecord" - } - ] + "moduleId": { + "description": "The ID of the Magnetic Module whose magnets you want to raise, from a prior `loadModule` command.", + "title": "Moduleid", + "type": "string" } }, - "required": ["liquidClassRecord"] + "required": ["moduleId", "height"], + "title": "EngageParams", + "type": "object" }, - "LoadLiquidClassCreate": { - "title": "LoadLiquidClassCreate", - "description": "Load Liquid Class command creation request.", - "type": "object", + "GetNextTipCreate": { + "description": "Get next tip command creation request model.", "properties": { "commandType": { + "const": "getNextTip", + "default": "getNextTip", + "enum": ["getNextTip"], "title": "Commandtype", - "default": "loadLiquidClass", - "enum": ["loadLiquidClass"], "type": "string" }, - "params": { - "$ref": "#/definitions/LoadLiquidClassParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/GetNextTipParams" } }, - "required": ["params"] - }, - "ModuleModel": { - "title": "ModuleModel", - "description": "All available modules' models.", - "enum": [ - "temperatureModuleV1", - "temperatureModuleV2", - "magneticModuleV1", - "magneticModuleV2", - "thermocyclerModuleV1", - "thermocyclerModuleV2", - "heaterShakerModuleV1", - "magneticBlockV1", - "absorbanceReaderV1" - ], - "type": "string" + "required": ["params"], + "title": "GetNextTipCreate", + "type": "object" }, - "LoadModuleParams": { - "title": "LoadModuleParams", - "description": "Payload required to load a module.", - "type": "object", + "GetNextTipParams": { + "description": "Payload needed to resolve the next available tip.", "properties": { - "model": { - "description": "The model name of the module to load.\n\nProtocol Engine will look for a connected module that either exactly matches this one, or is compatible.\n\n For example, if you request a `temperatureModuleV1` here, Protocol Engine might load a `temperatureModuleV1` or a `temperatureModuleV2`.\n\n The model that it finds connected will be available through `result.model`.", - "allOf": [ - { - "$ref": "#/definitions/ModuleModel" - } - ] + "labwareIds": { + "description": "Labware ID(s) of tip racks to resolve next available tip(s) from Labware IDs will be resolved sequentially", + "items": { + "type": "string" + }, + "title": "Labwareids", + "type": "array" }, - "location": { - "title": "Location", - "description": "The location into which this module should be loaded.\n\nFor the Thermocycler Module, which occupies multiple deck slots, this should be the front-most occupied slot (normally slot 7).", - "allOf": [ - { - "$ref": "#/definitions/DeckSlotLocation" - } - ] + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" }, - "moduleId": { - "title": "Moduleid", - "description": "An optional ID to assign to this module. If None, an ID will be generated.", + "startingTipWell": { + "description": "Name of starting tip rack 'well'. This only applies to the first tip rack in the list provided in labwareIDs", + "title": "Startingtipwell", "type": "string" } }, - "required": ["model", "location"] + "required": ["pipetteId", "labwareIds"], + "title": "GetNextTipParams", + "type": "object" }, - "LoadModuleCreate": { - "title": "LoadModuleCreate", - "description": "The model for a creation request for a load module command.", - "type": "object", + "GetTipPresenceCreate": { + "description": "GetTipPresence command creation request model.", "properties": { "commandType": { + "const": "getTipPresence", + "default": "getTipPresence", + "enum": ["getTipPresence"], "title": "Commandtype", - "default": "loadModule", - "enum": ["loadModule"], "type": "string" }, - "params": { - "$ref": "#/definitions/LoadModuleParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/GetTipPresenceParams" } }, - "required": ["params"] - }, - "PipetteNameType": { - "title": "PipetteNameType", - "description": "Pipette load name values.", - "enum": [ - "p10_single", - "p10_multi", - "p20_single_gen2", - "p20_multi_gen2", - "p50_single", - "p50_multi", - "p50_single_flex", - "p50_multi_flex", - "p300_single", - "p300_multi", - "p300_single_gen2", - "p300_multi_gen2", - "p1000_single", - "p1000_single_gen2", - "p1000_single_flex", - "p1000_multi_flex", - "p1000_multi_em", - "p1000_96", - "p200_96" - ], - "type": "string" + "required": ["params"], + "title": "GetTipPresenceCreate", + "type": "object" }, - "LoadPipetteParams": { - "title": "LoadPipetteParams", - "description": "Payload needed to load a pipette on to a mount.", - "type": "object", + "GetTipPresenceParams": { + "description": "Payload required for a GetTipPresence command.", "properties": { - "pipetteName": { - "description": "The load name of the pipette to be required.", - "allOf": [ - { - "$ref": "#/definitions/PipetteNameType" - } - ] - }, - "mount": { - "description": "The mount the pipette should be present on.", - "allOf": [ - { - "$ref": "#/definitions/MountType" - } - ] - }, "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", "title": "Pipetteid", - "description": "An optional ID to assign to this pipette. If None, an ID will be generated.", + "type": "string" + } + }, + "required": ["pipetteId"], + "title": "GetTipPresenceParams", + "type": "object" + }, + "HomeCreate": { + "description": "Data to create a Home command.", + "properties": { + "commandType": { + "const": "home", + "default": "home", + "enum": ["home"], + "title": "Commandtype", "type": "string" }, - "tipOverlapNotAfterVersion": { - "title": "Tipoverlapnotafterversion", - "description": "A version of tip overlap data to not exceed. The highest-versioned tip overlap data that does not exceed this version will be used. Versions are expressed as vN where N is an integer, counting up from v0. If None, the current highest version will be used.", + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" }, - "liquidPresenceDetection": { - "title": "Liquidpresencedetection", - "description": "Enable liquid presence detection for this pipette. Defaults to False.", - "type": "boolean" + "params": { + "$ref": "#/$defs/HomeParams" } }, - "required": ["pipetteName", "mount"] + "required": ["params"], + "title": "HomeCreate", + "type": "object" }, - "LoadPipetteCreate": { - "title": "LoadPipetteCreate", - "description": "Load pipette command creation request model.", - "type": "object", + "HomeParams": { + "description": "Payload required for a Home command.", + "properties": { + "axes": { + "description": "Axes to return to their home positions. If omitted, will home all motors. Extra axes may be implicitly homed to ensure accurate homing of the explicitly specified axes.", + "items": { + "$ref": "#/$defs/MotorAxis" + }, + "title": "Axes", + "type": "array" + }, + "skipIfMountPositionOk": { + "$ref": "#/$defs/MountType", + "description": "If this parameter is provided, the gantry will only be homed if the specified mount has an invalid position. If omitted, the homing action will be executed unconditionally.", + "title": "Skipifmountpositionok" + } + }, + "title": "HomeParams", + "type": "object" + }, + "InitializeCreate": { + "description": "A request to execute an Absorbance Reader measurement.", "properties": { "commandType": { + "const": "absorbanceReader/initialize", + "default": "absorbanceReader/initialize", + "enum": ["absorbanceReader/initialize"], "title": "Commandtype", - "default": "loadPipette", - "enum": ["loadPipette"], "type": "string" }, - "params": { - "$ref": "#/definitions/LoadPipetteParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" + }, + "params": { + "$ref": "#/$defs/InitializeParams" + } + }, + "required": ["params"], + "title": "InitializeCreate", + "type": "object" + }, + "InitializeParams": { + "description": "Input parameters to initialize an absorbance reading.", + "properties": { + "measureMode": { + "description": "Initialize single or multi measurement mode.", + "enum": ["single", "multi"], + "title": "Measuremode", "type": "string" + }, + "moduleId": { + "description": "Unique ID of the absorbance reader.", + "title": "Moduleid", + "type": "string" + }, + "referenceWavelength": { + "description": "Optional reference wavelength in nm.", + "title": "Referencewavelength", + "type": "integer" + }, + "sampleWavelengths": { + "description": "Sample wavelengths in nm.", + "items": { + "type": "integer" + }, + "title": "Samplewavelengths", + "type": "array" } }, - "required": ["params"] + "required": ["moduleId", "measureMode", "sampleWavelengths"], + "title": "InitializeParams", + "type": "object" + }, + "InstrumentSensorId": { + "description": "Primary and secondary sensor ids.", + "enum": ["primary", "secondary", "both"], + "title": "InstrumentSensorId", + "type": "string" }, "LabwareMovementStrategy": { - "title": "LabwareMovementStrategy", "description": "Strategy to use for labware movement.", "enum": ["usingGripper", "manualMoveWithPause", "manualMoveWithoutPause"], + "title": "LabwareMovementStrategy", "type": "string" }, "LabwareOffsetVector": { - "title": "LabwareOffsetVector", "description": "Offset, in deck coordinates from nominal to actual position.", - "type": "object", "properties": { "x": { "title": "X", "type": "number" }, - "y": { - "title": "Y", - "type": "number" + "y": { + "title": "Y", + "type": "number" + }, + "z": { + "title": "Z", + "type": "number" + } + }, + "required": ["x", "y", "z"], + "title": "LabwareOffsetVector", + "type": "object" + }, + "LiquidClassRecord": { + "description": "LiquidClassRecord is our internal representation of an (immutable) liquid class.\n\nConceptually, a liquid class record is the tuple (name, pipette, tip, transfer properties).\nWe consider two liquid classes to be the same if every entry in that tuple is the same; and liquid\nclasses are different if any entry in the tuple is different.\n\nThis class defines the tuple via inheritance so that we can reuse the definitions from shared_data.", + "properties": { + "aspirate": { + "$ref": "#/$defs/AspirateProperties", + "description": "Aspirate parameters for this tip type." + }, + "liquidClassName": { + "description": "Identifier for the liquid of this liquid class, e.g. glycerol50.", + "title": "Liquidclassname", + "type": "string" + }, + "multiDispense": { + "$ref": "#/$defs/MultiDispenseProperties", + "description": "Optional multi-dispense parameters for this tip type.", + "title": "Multidispense" }, - "z": { - "title": "Z", - "type": "number" + "pipetteModel": { + "description": "Identifier for the pipette of this liquid class.", + "title": "Pipettemodel", + "type": "string" + }, + "singleDispense": { + "$ref": "#/$defs/SingleDispenseProperties", + "description": "Single dispense parameters for this tip type." + }, + "tiprack": { + "description": "The name of tiprack whose tip will be used when handling this specific liquid class with this pipette", + "title": "Tiprack", + "type": "string" } }, - "required": ["x", "y", "z"] + "required": [ + "tiprack", + "aspirate", + "singleDispense", + "liquidClassName", + "pipetteModel" + ], + "title": "LiquidClassRecord", + "type": "object" }, - "MoveLabwareParams": { - "title": "MoveLabwareParams", - "description": "Input parameters for a ``moveLabware`` command.", - "type": "object", + "LiquidClassTouchTipParams": { + "description": "Parameters for touch-tip.", "properties": { - "labwareId": { - "title": "Labwareid", - "description": "The ID of the labware to move.", - "type": "string" - }, - "newLocation": { - "title": "Newlocation", - "description": "Where to move the labware.", + "mmToEdge": { "anyOf": [ { - "$ref": "#/definitions/DeckSlotLocation" - }, - { - "$ref": "#/definitions/ModuleLocation" + "type": "integer" }, { - "$ref": "#/definitions/OnLabwareLocation" - }, + "type": "number" + } + ], + "description": "Offset away from the the well edge, in millimeters.", + "title": "Mmtoedge" + }, + "speed": { + "anyOf": [ { - "enum": ["offDeck"], - "type": "string" + "minimum": 0, + "type": "integer" }, { - "$ref": "#/definitions/AddressableAreaLocation" + "minimum": 0.0, + "type": "number" } - ] + ], + "description": "Touch-tip speed, in millimeters per second.", + "title": "Speed" }, - "strategy": { - "description": "Whether to use the gripper to perform the labware movement or to perform a manual movement with an option to pause.", - "allOf": [ + "zOffset": { + "anyOf": [ { - "$ref": "#/definitions/LabwareMovementStrategy" - } - ] - }, - "pickUpOffset": { - "title": "Pickupoffset", - "description": "Offset to use when picking up labware. Experimental param, subject to change", - "allOf": [ + "type": "integer" + }, { - "$ref": "#/definitions/LabwareOffsetVector" + "type": "number" } - ] + ], + "description": "Offset from the top of the well for touch-tip, in millimeters.", + "title": "Zoffset" + } + }, + "required": ["zOffset", "mmToEdge", "speed"], + "title": "LiquidClassTouchTipParams", + "type": "object" + }, + "LiquidHandlingWellLocation": { + "description": "A relative location in reference to a well's location.\n\nTo be used with commands that handle liquids.", + "properties": { + "offset": { + "$ref": "#/$defs/WellOffset" }, - "dropOffset": { - "title": "Dropoffset", - "description": "Offset to use when dropping off labware. Experimental param, subject to change", - "allOf": [ + "origin": { + "$ref": "#/$defs/WellOrigin", + "default": "top" + }, + "volumeOffset": { + "anyOf": [ { - "$ref": "#/definitions/LabwareOffsetVector" + "type": "number" + }, + { + "const": "operationVolume", + "enum": ["operationVolume"], + "type": "string" } - ] + ], + "default": 0.0, + "description": "A volume of liquid, in \u00b5L, to offset the z-axis offset. When \"operationVolume\" is specified, this volume is pulled from the command volume parameter.", + "title": "Volumeoffset" } }, - "required": ["labwareId", "newLocation", "strategy"] + "title": "LiquidHandlingWellLocation", + "type": "object" }, - "MoveLabwareCreate": { - "title": "MoveLabwareCreate", - "description": "A request to create a ``moveLabware`` command.", - "type": "object", + "LiquidProbeCreate": { + "description": "The request model for a `liquidProbe` command.", "properties": { "commandType": { + "const": "liquidProbe", + "default": "liquidProbe", + "enum": ["liquidProbe"], "title": "Commandtype", - "default": "moveLabware", - "enum": ["moveLabware"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveLabwareParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/LiquidProbeParams" } }, - "required": ["params"] - }, - "MovementAxis": { - "title": "MovementAxis", - "description": "Axis on which to issue a relative movement.", - "enum": ["x", "y", "z"], - "type": "string" + "required": ["params"], + "title": "LiquidProbeCreate", + "type": "object" }, - "MoveRelativeParams": { - "title": "MoveRelativeParams", - "description": "Payload required for a MoveRelative command.", - "type": "object", + "LiquidProbeParams": { + "description": "Parameters required for a `liquidProbe` command.", "properties": { + "labwareId": { + "description": "Identifier of labware to use.", + "title": "Labwareid", + "type": "string" + }, "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", "title": "Pipetteid", - "description": "Pipette to move.", "type": "string" }, - "axis": { - "description": "Axis along which to move.", - "allOf": [ - { - "$ref": "#/definitions/MovementAxis" - } - ] + "wellLocation": { + "$ref": "#/$defs/WellLocation", + "description": "Relative well location at which to perform the operation" }, - "distance": { - "title": "Distance", - "description": "Distance to move in millimeters. A positive number will move towards the right (x), back (y), top (z) of the deck.", - "type": "number" + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", + "type": "string" } }, - "required": ["pipetteId", "axis", "distance"] + "required": ["labwareId", "wellName", "pipetteId"], + "title": "LiquidProbeParams", + "type": "object" }, - "MoveRelativeCreate": { - "title": "MoveRelativeCreate", - "description": "Data to create a MoveRelative command.", - "type": "object", + "LoadLabwareCreate": { + "description": "Load labware command creation request.", "properties": { "commandType": { + "const": "loadLabware", + "default": "loadLabware", + "enum": ["loadLabware"], "title": "Commandtype", - "default": "moveRelative", - "enum": ["moveRelative"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveRelativeParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/LoadLabwareParams" } }, - "required": ["params"] + "required": ["params"], + "title": "LoadLabwareCreate", + "type": "object" }, - "DeckPoint": { - "title": "DeckPoint", - "description": "Coordinates of a point in deck space.", - "type": "object", + "LoadLabwareParams": { + "description": "Payload required to load a labware into a slot.", "properties": { - "x": { - "title": "X", - "type": "number" + "displayName": { + "description": "An optional user-specified display name or label for this labware.", + "title": "Displayname", + "type": "string" }, - "y": { - "title": "Y", - "type": "number" + "labwareId": { + "description": "An optional ID to assign to this labware. If None, an ID will be generated.", + "title": "Labwareid", + "type": "string" }, - "z": { - "title": "Z", - "type": "number" + "loadName": { + "description": "Name used to reference a labware definition.", + "title": "Loadname", + "type": "string" + }, + "location": { + "anyOf": [ + { + "$ref": "#/$defs/DeckSlotLocation" + }, + { + "$ref": "#/$defs/ModuleLocation" + }, + { + "$ref": "#/$defs/OnLabwareLocation" + }, + { + "const": "offDeck", + "enum": ["offDeck"], + "type": "string" + }, + { + "$ref": "#/$defs/AddressableAreaLocation" + } + ], + "description": "Location the labware should be loaded into.", + "title": "Location" + }, + "namespace": { + "description": "The namespace the labware definition belongs to.", + "title": "Namespace", + "type": "string" + }, + "version": { + "description": "The labware definition version.", + "title": "Version", + "type": "integer" } }, - "required": ["x", "y", "z"] + "required": ["location", "loadName", "namespace", "version"], + "title": "LoadLabwareParams", + "type": "object" }, - "MoveToCoordinatesParams": { - "title": "MoveToCoordinatesParams", - "description": "Payload required to move a pipette to coordinates.", - "type": "object", + "LoadLiquidClassCreate": { + "description": "Load Liquid Class command creation request.", "properties": { - "minimumZHeight": { - "title": "Minimumzheight", - "description": "Optional minimal Z margin in mm. If this is larger than the API's default safe Z margin, it will make the arc higher. If it's smaller, it will have no effect.", - "type": "number" + "commandType": { + "const": "loadLiquidClass", + "default": "loadLiquidClass", + "enum": ["loadLiquidClass"], + "title": "Commandtype", + "type": "string" }, - "forceDirect": { - "title": "Forcedirect", - "description": "If true, moving from one labware/well to another will not arc to the default safe z, but instead will move directly to the specified location. This will also force the `minimumZHeight` param to be ignored. A 'direct' movement is in X/Y/Z simultaneously.", - "default": false, - "type": "boolean" + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" }, - "speed": { - "title": "Speed", - "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", - "type": "number" + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", + "params": { + "$ref": "#/$defs/LoadLiquidClassParams" + } + }, + "required": ["params"], + "title": "LoadLiquidClassCreate", + "type": "object" + }, + "LoadLiquidClassParams": { + "description": "The liquid class transfer properties to store.", + "properties": { + "liquidClassId": { + "description": "Unique identifier for the liquid class to store. If you do not supply a liquidClassId, we will generate one.", + "title": "Liquidclassid", "type": "string" }, - "coordinates": { - "title": "Coordinates", - "description": "X, Y and Z coordinates in mm from deck's origin location (left-front-bottom corner of work space)", - "allOf": [ - { - "$ref": "#/definitions/DeckPoint" - } - ] + "liquidClassRecord": { + "$ref": "#/$defs/LiquidClassRecord", + "description": "The liquid class to store." } }, - "required": ["pipetteId", "coordinates"] + "required": ["liquidClassRecord"], + "title": "LoadLiquidClassParams", + "type": "object" }, - "MoveToCoordinatesCreate": { - "title": "MoveToCoordinatesCreate", - "description": "Move to coordinates command creation request model.", - "type": "object", + "LoadLiquidCreate": { + "description": "Load liquid command creation request.", "properties": { "commandType": { + "const": "loadLiquid", + "default": "loadLiquid", + "enum": ["loadLiquid"], "title": "Commandtype", - "default": "moveToCoordinates", - "enum": ["moveToCoordinates"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveToCoordinatesParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/LoadLiquidParams" } }, - "required": ["params"] + "required": ["params"], + "title": "LoadLiquidCreate", + "type": "object" }, - "MoveToWellParams": { - "title": "MoveToWellParams", - "description": "Payload required to move a pipette to a specific well.", - "type": "object", + "LoadLiquidParams": { + "description": "Payload required to load a liquid into a well.", "properties": { - "minimumZHeight": { - "title": "Minimumzheight", - "description": "Optional minimal Z margin in mm. If this is larger than the API's default safe Z margin, it will make the arc higher. If it's smaller, it will have no effect.", - "type": "number" - }, - "forceDirect": { - "title": "Forcedirect", - "description": "If true, moving from one labware/well to another will not arc to the default safe z, but instead will move directly to the specified location. This will also force the `minimumZHeight` param to be ignored. A 'direct' movement is in X/Y/Z simultaneously.", - "default": false, - "type": "boolean" - }, - "speed": { - "title": "Speed", - "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", - "type": "number" - }, "labwareId": { + "description": "Unique identifier of labware to load liquid into.", "title": "Labwareid", - "description": "Identifier of labware to use.", - "type": "string" - }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", "type": "string" }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to perform the operation", - "allOf": [ + "liquidId": { + "anyOf": [ + { + "type": "string" + }, { - "$ref": "#/definitions/WellLocation" + "const": "EMPTY", + "enum": ["EMPTY"], + "type": "string" } - ] + ], + "description": "Unique identifier of the liquid to load. If this is the sentinel value EMPTY, all values of volumeByWell must be 0.", + "title": "Liquidid" }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" + "volumeByWell": { + "additionalProperties": { + "type": "number" + }, + "description": "Volume of liquid, in \u00b5L, loaded into each well by name, in this labware. If the liquid id is the sentinel value EMPTY, all volumes must be 0.", + "title": "Volumebywell", + "type": "object" } }, - "required": ["labwareId", "wellName", "pipetteId"] + "required": ["liquidId", "labwareId", "volumeByWell"], + "title": "LoadLiquidParams", + "type": "object" }, - "MoveToWellCreate": { - "title": "MoveToWellCreate", - "description": "Move to well command creation request model.", - "type": "object", + "LoadModuleCreate": { + "description": "The model for a creation request for a load module command.", "properties": { "commandType": { + "const": "loadModule", + "default": "loadModule", + "enum": ["loadModule"], "title": "Commandtype", - "default": "moveToWell", - "enum": ["moveToWell"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveToWellParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/LoadModuleParams" } }, - "required": ["params"] + "required": ["params"], + "title": "LoadModuleCreate", + "type": "object" }, - "AddressableOffsetVector": { - "title": "AddressableOffsetVector", - "description": "Offset, in deck coordinates, from nominal to actual position of an addressable area.", - "type": "object", + "LoadModuleParams": { + "description": "Payload required to load a module.", "properties": { - "x": { - "title": "X", - "type": "number" + "location": { + "$ref": "#/$defs/DeckSlotLocation", + "description": "The location into which this module should be loaded.\n\nFor the Thermocycler Module, which occupies multiple deck slots, this should be the front-most occupied slot (normally slot 7)." }, - "y": { - "title": "Y", - "type": "number" + "model": { + "$ref": "#/$defs/ModuleModel", + "description": "The model name of the module to load.\n\nProtocol Engine will look for a connected module that either exactly matches this one, or is compatible.\n\n For example, if you request a `temperatureModuleV1` here, Protocol Engine might load a `temperatureModuleV1` or a `temperatureModuleV2`.\n\n The model that it finds connected will be available through `result.model`." }, - "z": { - "title": "Z", - "type": "number" + "moduleId": { + "description": "An optional ID to assign to this module. If None, an ID will be generated.", + "title": "Moduleid", + "type": "string" } }, - "required": ["x", "y", "z"] + "required": ["model", "location"], + "title": "LoadModuleParams", + "type": "object" }, - "MoveToAddressableAreaParams": { - "title": "MoveToAddressableAreaParams", - "description": "Payload required to move a pipette to a specific addressable area.\n\nAn *addressable area* is a space in the robot that may or may not be usable depending on how\nthe robot's deck is configured. For example, if a Flex is configured with a waste chute, it will\nhave additional addressable areas representing the opening of the waste chute, where tips and\nlabware can be dropped.\n\nThis moves the pipette so all of its nozzles are centered over the addressable area.\nIf the pipette is currently configured with a partial tip layout, this centering is over all\nthe pipette's physical nozzles, not just the nozzles that are active.\n\nThe z-position will be chosen to put the bottom of the tips---or the bottom of the nozzles,\nif there are no tips---level with the top of the addressable area.\n\nWhen this command is executed, Protocol Engine will make sure the robot's deck is configured\nsuch that the requested addressable area actually exists. For example, if you request\nthe addressable area B4, it will make sure the robot is set up with a B3/B4 staging area slot.\nIf that's not the case, the command will fail.", - "type": "object", + "LoadPipetteCreate": { + "description": "Load pipette command creation request model.", "properties": { - "minimumZHeight": { - "title": "Minimumzheight", - "description": "Optional minimal Z margin in mm. If this is larger than the API's default safe Z margin, it will make the arc higher. If it's smaller, it will have no effect.", - "type": "number" + "commandType": { + "const": "loadPipette", + "default": "loadPipette", + "enum": ["loadPipette"], + "title": "Commandtype", + "type": "string" }, - "forceDirect": { - "title": "Forcedirect", - "description": "If true, moving from one labware/well to another will not arc to the default safe z, but instead will move directly to the specified location. This will also force the `minimumZHeight` param to be ignored. A 'direct' movement is in X/Y/Z simultaneously.", - "default": false, + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" + }, + "params": { + "$ref": "#/$defs/LoadPipetteParams" + } + }, + "required": ["params"], + "title": "LoadPipetteCreate", + "type": "object" + }, + "LoadPipetteParams": { + "description": "Payload needed to load a pipette on to a mount.", + "properties": { + "liquidPresenceDetection": { + "description": "Enable liquid presence detection for this pipette. Defaults to False.", + "title": "Liquidpresencedetection", "type": "boolean" }, - "speed": { - "title": "Speed", - "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", - "type": "number" + "mount": { + "$ref": "#/$defs/MountType", + "description": "The mount the pipette should be present on." }, "pipetteId": { + "description": "An optional ID to assign to this pipette. If None, an ID will be generated.", "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", "type": "string" }, - "addressableAreaName": { - "title": "Addressableareaname", - "description": "The name of the addressable area that you want to use. Valid values are the `id`s of `addressableArea`s in the [deck definition](https://github.com/Opentrons/opentrons/tree/edge/shared-data/deck).", + "pipetteName": { + "$ref": "#/$defs/PipetteNameType", + "description": "The load name of the pipette to be required." + }, + "tipOverlapNotAfterVersion": { + "description": "A version of tip overlap data to not exceed. The highest-versioned tip overlap data that does not exceed this version will be used. Versions are expressed as vN where N is an integer, counting up from v0. If None, the current highest version will be used.", + "title": "Tipoverlapnotafterversion", "type": "string" + } + }, + "required": ["pipetteName", "mount"], + "title": "LoadPipetteParams", + "type": "object" + }, + "MaintenancePosition": { + "description": "Maintenance position options.", + "enum": ["attachPlate", "attachInstrument"], + "title": "MaintenancePosition", + "type": "string" + }, + "MixParams": { + "description": "Parameters for mix.", + "properties": { + "repetitions": { + "description": "Number of mixing repetitions.", + "minimum": 0, + "title": "Repetitions", + "type": "integer" }, - "offset": { - "title": "Offset", - "description": "Relative offset of addressable area to move pipette's critical point.", - "default": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "allOf": [ + "volume": { + "anyOf": [ + { + "type": "integer" + }, { - "$ref": "#/definitions/AddressableOffsetVector" + "type": "number" } - ] - }, - "stayAtHighestPossibleZ": { - "title": "Stayathighestpossiblez", - "description": "If `true`, the pipette will retract to its highest possible height and stay there instead of descending to the destination. `minimumZHeight` will be ignored.", - "default": false, + ], + "description": "Volume used for mixing, in microliters.", + "title": "Volume" + } + }, + "required": ["repetitions", "volume"], + "title": "MixParams", + "type": "object" + }, + "MixProperties": { + "description": "Mixing properties.", + "properties": { + "enable": { + "description": "Whether mix is enabled.", + "title": "Enable", "type": "boolean" + }, + "params": { + "$ref": "#/$defs/MixParams", + "description": "Parameters for the mix function.", + "title": "Params" } }, - "required": ["pipetteId", "addressableAreaName"] + "required": ["enable"], + "title": "MixProperties", + "type": "object" }, - "MoveToAddressableAreaCreate": { - "title": "MoveToAddressableAreaCreate", - "description": "Move to addressable area command creation request model.", - "type": "object", + "ModuleLocation": { + "description": "The location of something placed atop a hardware module.", + "properties": { + "moduleId": { + "description": "The ID of a loaded module from a prior `loadModule` command.", + "title": "Moduleid", + "type": "string" + } + }, + "required": ["moduleId"], + "title": "ModuleLocation", + "type": "object" + }, + "ModuleModel": { + "description": "All available modules' models.", + "enum": [ + "temperatureModuleV1", + "temperatureModuleV2", + "magneticModuleV1", + "magneticModuleV2", + "thermocyclerModuleV1", + "thermocyclerModuleV2", + "heaterShakerModuleV1", + "magneticBlockV1", + "absorbanceReaderV1" + ], + "title": "ModuleModel", + "type": "string" + }, + "MotorAxis": { + "description": "Motor axis on which to issue a home command.", + "enum": [ + "x", + "y", + "leftZ", + "rightZ", + "leftPlunger", + "rightPlunger", + "extensionZ", + "extensionJaw", + "axis96ChannelCam" + ], + "title": "MotorAxis", + "type": "string" + }, + "MountType": { + "enum": ["left", "right", "extension"], + "title": "MountType", + "type": "string" + }, + "MoveAxesRelativeCreate": { + "description": "MoveAxesRelative command request model.", "properties": { "commandType": { + "const": "robot/moveAxesRelative", + "default": "robot/moveAxesRelative", + "enum": ["robot/moveAxesRelative"], "title": "Commandtype", - "default": "moveToAddressableArea", - "enum": ["moveToAddressableArea"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveToAddressableAreaParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/MoveAxesRelativeParams" } }, - "required": ["params"] + "required": ["params"], + "title": "MoveAxesRelativeCreate", + "type": "object" }, - "MoveToAddressableAreaForDropTipParams": { - "title": "MoveToAddressableAreaForDropTipParams", - "description": "Payload required to move a pipette to a specific addressable area.\n\nAn *addressable area* is a space in the robot that may or may not be usable depending on how\nthe robot's deck is configured. For example, if a Flex is configured with a waste chute, it will\nhave additional addressable areas representing the opening of the waste chute, where tips and\nlabware can be dropped.\n\nThis moves the pipette so all of its nozzles are centered over the addressable area.\nIf the pipette is currently configured with a partial tip layout, this centering is over all\nthe pipette's physical nozzles, not just the nozzles that are active.\n\nThe z-position will be chosen to put the bottom of the tips---or the bottom of the nozzles,\nif there are no tips---level with the top of the addressable area.\n\nWhen this command is executed, Protocol Engine will make sure the robot's deck is configured\nsuch that the requested addressable area actually exists. For example, if you request\nthe addressable area B4, it will make sure the robot is set up with a B3/B4 staging area slot.\nIf that's not the case, the command will fail.", - "type": "object", + "MoveAxesRelativeParams": { + "description": "Payload required to move axes relative to position.", "properties": { - "minimumZHeight": { - "title": "Minimumzheight", - "description": "Optional minimal Z margin in mm. If this is larger than the API's default safe Z margin, it will make the arc higher. If it's smaller, it will have no effect.", - "type": "number" - }, - "forceDirect": { - "title": "Forcedirect", - "description": "If true, moving from one labware/well to another will not arc to the default safe z, but instead will move directly to the specified location. This will also force the `minimumZHeight` param to be ignored. A 'direct' movement is in X/Y/Z simultaneously.", - "default": false, - "type": "boolean" + "axis_map": { + "additionalProperties": { + "type": "number" + }, + "description": "A dictionary mapping axes to relative movements in mm.", + "title": "Axis Map", + "type": "object" }, "speed": { + "description": "The max velocity to move the axes at. Will fall to hardware defaults if none provided.", "title": "Speed", - "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", "type": "number" - }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" - }, - "addressableAreaName": { - "title": "Addressableareaname", - "description": "The name of the addressable area that you want to use. Valid values are the `id`s of `addressableArea`s in the [deck definition](https://github.com/Opentrons/opentrons/tree/edge/shared-data/deck).", - "type": "string" - }, - "offset": { - "title": "Offset", - "description": "Relative offset of addressable area to move pipette's critical point.", - "default": { - "x": 0.0, - "y": 0.0, - "z": 0.0 - }, - "allOf": [ - { - "$ref": "#/definitions/AddressableOffsetVector" - } - ] - }, - "alternateDropLocation": { - "title": "Alternatedroplocation", - "description": "Whether to alternate location where tip is dropped within the addressable area. If True, this command will ignore the offset provided and alternate between dropping tips at two predetermined locations inside the specified labware well. If False, the tip will be dropped at the top center of the area.", - "default": false, - "type": "boolean" - }, - "ignoreTipConfiguration": { - "title": "Ignoretipconfiguration", - "description": "Whether to utilize the critical point of the tip configuraiton when moving to an addressable area. If True, this command will ignore the tip configuration and use the center of the entire instrument as the critical point for movement. If False, this command will use the critical point provided by the current tip configuration.", - "default": true, - "type": "boolean" } }, - "required": ["pipetteId", "addressableAreaName"] + "required": ["axis_map"], + "title": "MoveAxesRelativeParams", + "type": "object" }, - "MoveToAddressableAreaForDropTipCreate": { - "title": "MoveToAddressableAreaForDropTipCreate", - "description": "Move to addressable area for drop tip command creation request model.", - "type": "object", + "MoveAxesToCreate": { + "description": "MoveAxesTo command request model.", "properties": { "commandType": { + "const": "robot/moveAxesTo", + "default": "robot/moveAxesTo", + "enum": ["robot/moveAxesTo"], "title": "Commandtype", - "default": "moveToAddressableAreaForDropTip", - "enum": ["moveToAddressableAreaForDropTip"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveToAddressableAreaForDropTipParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/MoveAxesToParams" } }, - "required": ["params"] + "required": ["params"], + "title": "MoveAxesToCreate", + "type": "object" }, - "PrepareToAspirateParams": { - "title": "PrepareToAspirateParams", - "description": "Parameters required to prepare a specific pipette for aspiration.", - "type": "object", + "MoveAxesToParams": { + "description": "Payload required to move axes to absolute position.", "properties": { - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" + "axis_map": { + "additionalProperties": { + "type": "number" + }, + "description": "The specified axes to move to an absolute deck position with.", + "title": "Axis Map", + "type": "object" + }, + "critical_point": { + "additionalProperties": { + "type": "number" + }, + "description": "The critical point to move the mount with.", + "title": "Critical Point", + "type": "object" + }, + "speed": { + "description": "The max velocity to move the axes at. Will fall to hardware defaults if none provided.", + "title": "Speed", + "type": "number" } }, - "required": ["pipetteId"] + "required": ["axis_map"], + "title": "MoveAxesToParams", + "type": "object" }, - "PrepareToAspirateCreate": { - "title": "PrepareToAspirateCreate", - "description": "Prepare for aspirate command creation request model.", - "type": "object", + "MoveLabwareCreate": { + "description": "A request to create a ``moveLabware`` command.", "properties": { "commandType": { + "const": "moveLabware", + "default": "moveLabware", + "enum": ["moveLabware"], "title": "Commandtype", - "default": "prepareToAspirate", - "enum": ["prepareToAspirate"], "type": "string" }, - "params": { - "$ref": "#/definitions/PrepareToAspirateParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/MoveLabwareParams" } }, - "required": ["params"] - }, - "WaitForResumeParams": { - "title": "WaitForResumeParams", - "description": "Payload required to pause the protocol.", - "type": "object", - "properties": { - "message": { - "title": "Message", - "description": "A user-facing message associated with the pause", - "type": "string" - } - } + "required": ["params"], + "title": "MoveLabwareCreate", + "type": "object" }, - "WaitForResumeCreate": { - "title": "WaitForResumeCreate", - "description": "Wait for resume command request model.", - "type": "object", + "MoveLabwareParams": { + "description": "Input parameters for a ``moveLabware`` command.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "waitForResume", - "enum": ["waitForResume", "pause"], - "type": "string" + "dropOffset": { + "$ref": "#/$defs/LabwareOffsetVector", + "description": "Offset to use when dropping off labware. Experimental param, subject to change", + "title": "Dropoffset" }, - "params": { - "$ref": "#/definitions/WaitForResumeParams" + "labwareId": { + "description": "The ID of the labware to move.", + "title": "Labwareid", + "type": "string" }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ + "newLocation": { + "anyOf": [ + { + "$ref": "#/$defs/DeckSlotLocation" + }, + { + "$ref": "#/$defs/ModuleLocation" + }, + { + "$ref": "#/$defs/OnLabwareLocation" + }, + { + "const": "offDeck", + "enum": ["offDeck"], + "type": "string" + }, { - "$ref": "#/definitions/CommandIntent" + "$ref": "#/$defs/AddressableAreaLocation" } - ] + ], + "description": "Where to move the labware.", + "title": "Newlocation" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" - } - }, - "required": ["params"] - }, - "WaitForDurationParams": { - "title": "WaitForDurationParams", - "description": "Payload required to pause the protocol.", - "type": "object", - "properties": { - "seconds": { - "title": "Seconds", - "description": "Duration, in seconds, to wait for.", - "type": "number" + "pickUpOffset": { + "$ref": "#/$defs/LabwareOffsetVector", + "description": "Offset to use when picking up labware. Experimental param, subject to change", + "title": "Pickupoffset" }, - "message": { - "title": "Message", - "description": "A user-facing message associated with the pause", - "type": "string" + "strategy": { + "$ref": "#/$defs/LabwareMovementStrategy", + "description": "Whether to use the gripper to perform the labware movement or to perform a manual movement with an option to pause." } }, - "required": ["seconds"] + "required": ["labwareId", "newLocation", "strategy"], + "title": "MoveLabwareParams", + "type": "object" }, - "WaitForDurationCreate": { - "title": "WaitForDurationCreate", - "description": "Wait for duration command request model.", - "type": "object", + "MoveRelativeCreate": { + "description": "Data to create a MoveRelative command.", "properties": { "commandType": { + "const": "moveRelative", + "default": "moveRelative", + "enum": ["moveRelative"], "title": "Commandtype", - "default": "waitForDuration", - "enum": ["waitForDuration"], "type": "string" }, - "params": { - "$ref": "#/definitions/WaitForDurationParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" - } - }, - "required": ["params"] - }, - "PickUpTipWellOrigin": { - "title": "PickUpTipWellOrigin", - "description": "The origin of a PickUpTipWellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well", - "enum": ["top", "bottom", "center"], - "type": "string" - }, - "PickUpTipWellLocation": { - "title": "PickUpTipWellLocation", - "description": "A relative location in reference to a well's location.\n\nTo be used for picking up tips.", - "type": "object", - "properties": { - "origin": { - "default": "top", - "allOf": [ - { - "$ref": "#/definitions/PickUpTipWellOrigin" - } - ] }, - "offset": { - "$ref": "#/definitions/WellOffset" + "params": { + "$ref": "#/$defs/MoveRelativeParams" } - } + }, + "required": ["params"], + "title": "MoveRelativeCreate", + "type": "object" }, - "PickUpTipParams": { - "title": "PickUpTipParams", - "description": "Payload needed to move a pipette to a specific well.", - "type": "object", + "MoveRelativeParams": { + "description": "Payload required for a MoveRelative command.", "properties": { - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" - }, - "labwareId": { - "title": "Labwareid", - "description": "Identifier of labware to use.", - "type": "string" + "axis": { + "$ref": "#/$defs/MovementAxis", + "description": "Axis along which to move." }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", - "type": "string" + "distance": { + "description": "Distance to move in millimeters. A positive number will move towards the right (x), back (y), top (z) of the deck.", + "title": "Distance", + "type": "number" }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to pick up the tip.", - "allOf": [ - { - "$ref": "#/definitions/PickUpTipWellLocation" - } - ] + "pipetteId": { + "description": "Pipette to move.", + "title": "Pipetteid", + "type": "string" } }, - "required": ["pipetteId", "labwareId", "wellName"] + "required": ["pipetteId", "axis", "distance"], + "title": "MoveRelativeParams", + "type": "object" }, - "PickUpTipCreate": { - "title": "PickUpTipCreate", - "description": "Pick up tip command creation request model.", - "type": "object", + "MoveToAddressableAreaCreate": { + "description": "Move to addressable area command creation request model.", "properties": { "commandType": { + "const": "moveToAddressableArea", + "default": "moveToAddressableArea", + "enum": ["moveToAddressableArea"], "title": "Commandtype", - "default": "pickUpTip", - "enum": ["pickUpTip"], "type": "string" }, - "params": { - "$ref": "#/definitions/PickUpTipParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/MoveToAddressableAreaParams" } }, - "required": ["params"] + "required": ["params"], + "title": "MoveToAddressableAreaCreate", + "type": "object" }, - "SavePositionParams": { - "title": "SavePositionParams", - "description": "Payload needed to save a pipette's current position.", - "type": "object", + "MoveToAddressableAreaForDropTipCreate": { + "description": "Move to addressable area for drop tip command creation request model.", "properties": { - "pipetteId": { - "title": "Pipetteid", - "description": "Unique identifier of the pipette in question.", + "commandType": { + "const": "moveToAddressableAreaForDropTip", + "default": "moveToAddressableAreaForDropTip", + "enum": ["moveToAddressableAreaForDropTip"], + "title": "Commandtype", "type": "string" }, - "positionId": { - "title": "Positionid", - "description": "An optional ID to assign to this command instance. Auto-assigned if not defined.", + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" }, - "failOnNotHomed": { - "title": "Failonnothomed", - "default": true, - "descrption": "Require all axes to be homed before saving position.", - "type": "boolean" + "params": { + "$ref": "#/$defs/MoveToAddressableAreaForDropTipParams" } }, - "required": ["pipetteId"] + "required": ["params"], + "title": "MoveToAddressableAreaForDropTipCreate", + "type": "object" }, - "SavePositionCreate": { - "title": "SavePositionCreate", - "description": "Save position command creation request model.", - "type": "object", + "MoveToAddressableAreaForDropTipParams": { + "description": "Payload required to move a pipette to a specific addressable area.\n\nAn *addressable area* is a space in the robot that may or may not be usable depending on how\nthe robot's deck is configured. For example, if a Flex is configured with a waste chute, it will\nhave additional addressable areas representing the opening of the waste chute, where tips and\nlabware can be dropped.\n\nThis moves the pipette so all of its nozzles are centered over the addressable area.\nIf the pipette is currently configured with a partial tip layout, this centering is over all\nthe pipette's physical nozzles, not just the nozzles that are active.\n\nThe z-position will be chosen to put the bottom of the tips---or the bottom of the nozzles,\nif there are no tips---level with the top of the addressable area.\n\nWhen this command is executed, Protocol Engine will make sure the robot's deck is configured\nsuch that the requested addressable area actually exists. For example, if you request\nthe addressable area B4, it will make sure the robot is set up with a B3/B4 staging area slot.\nIf that's not the case, the command will fail.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "savePosition", - "enum": ["savePosition"], + "addressableAreaName": { + "description": "The name of the addressable area that you want to use. Valid values are the `id`s of `addressableArea`s in the [deck definition](https://github.com/Opentrons/opentrons/tree/edge/shared-data/deck).", + "title": "Addressableareaname", "type": "string" }, - "params": { - "$ref": "#/definitions/SavePositionParams" + "alternateDropLocation": { + "description": "Whether to alternate location where tip is dropped within the addressable area. If True, this command will ignore the offset provided and alternate between dropping tips at two predetermined locations inside the specified labware well. If False, the tip will be dropped at the top center of the area.", + "title": "Alternatedroplocation", + "type": "boolean" }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "forceDirect": { + "default": false, + "description": "If true, moving from one labware/well to another will not arc to the default safe z, but instead will move directly to the specified location. This will also force the `minimumZHeight` param to be ignored. A 'direct' movement is in X/Y/Z simultaneously.", + "title": "Forcedirect", + "type": "boolean" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "ignoreTipConfiguration": { + "description": "Whether to utilize the critical point of the tip configuraiton when moving to an addressable area. If True, this command will ignore the tip configuration and use the center of the entire instrument as the critical point for movement. If False, this command will use the critical point provided by the current tip configuration.", + "title": "Ignoretipconfiguration", + "type": "boolean" + }, + "minimumZHeight": { + "description": "Optional minimal Z margin in mm. If this is larger than the API's default safe Z margin, it will make the arc higher. If it's smaller, it will have no effect.", + "title": "Minimumzheight", + "type": "number" + }, + "offset": { + "$ref": "#/$defs/AddressableOffsetVector", + "default": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "description": "Relative offset of addressable area to move pipette's critical point." + }, + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" + }, + "speed": { + "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", + "title": "Speed", + "type": "number" } }, - "required": ["params"] + "required": ["pipetteId", "addressableAreaName"], + "title": "MoveToAddressableAreaForDropTipParams", + "type": "object" }, - "SetRailLightsParams": { - "title": "SetRailLightsParams", - "description": "Payload required to set the rail lights on or off.", - "type": "object", + "MoveToAddressableAreaParams": { + "description": "Payload required to move a pipette to a specific addressable area.\n\nAn *addressable area* is a space in the robot that may or may not be usable depending on how\nthe robot's deck is configured. For example, if a Flex is configured with a waste chute, it will\nhave additional addressable areas representing the opening of the waste chute, where tips and\nlabware can be dropped.\n\nThis moves the pipette so all of its nozzles are centered over the addressable area.\nIf the pipette is currently configured with a partial tip layout, this centering is over all\nthe pipette's physical nozzles, not just the nozzles that are active.\n\nThe z-position will be chosen to put the bottom of the tips---or the bottom of the nozzles,\nif there are no tips---level with the top of the addressable area.\n\nWhen this command is executed, Protocol Engine will make sure the robot's deck is configured\nsuch that the requested addressable area actually exists. For example, if you request\nthe addressable area B4, it will make sure the robot is set up with a B3/B4 staging area slot.\nIf that's not the case, the command will fail.", "properties": { - "on": { - "title": "On", - "description": "The field that determines if the light is turned off or on.", + "addressableAreaName": { + "description": "The name of the addressable area that you want to use. Valid values are the `id`s of `addressableArea`s in the [deck definition](https://github.com/Opentrons/opentrons/tree/edge/shared-data/deck).", + "title": "Addressableareaname", + "type": "string" + }, + "forceDirect": { + "default": false, + "description": "If true, moving from one labware/well to another will not arc to the default safe z, but instead will move directly to the specified location. This will also force the `minimumZHeight` param to be ignored. A 'direct' movement is in X/Y/Z simultaneously.", + "title": "Forcedirect", + "type": "boolean" + }, + "minimumZHeight": { + "description": "Optional minimal Z margin in mm. If this is larger than the API's default safe Z margin, it will make the arc higher. If it's smaller, it will have no effect.", + "title": "Minimumzheight", + "type": "number" + }, + "offset": { + "$ref": "#/$defs/AddressableOffsetVector", + "default": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "description": "Relative offset of addressable area to move pipette's critical point." + }, + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" + }, + "speed": { + "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", + "title": "Speed", + "type": "number" + }, + "stayAtHighestPossibleZ": { + "default": false, + "description": "If `true`, the pipette will retract to its highest possible height and stay there instead of descending to the destination. `minimumZHeight` will be ignored.", + "title": "Stayathighestpossiblez", "type": "boolean" } }, - "required": ["on"] + "required": ["pipetteId", "addressableAreaName"], + "title": "MoveToAddressableAreaParams", + "type": "object" }, - "SetRailLightsCreate": { - "title": "SetRailLightsCreate", - "description": "setRailLights command request model.", - "type": "object", + "MoveToCoordinatesCreate": { + "description": "Move to coordinates command creation request model.", "properties": { "commandType": { + "const": "moveToCoordinates", + "default": "moveToCoordinates", + "enum": ["moveToCoordinates"], "title": "Commandtype", - "default": "setRailLights", - "enum": ["setRailLights"], "type": "string" }, - "params": { - "$ref": "#/definitions/SetRailLightsParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/MoveToCoordinatesParams" } }, - "required": ["params"] + "required": ["params"], + "title": "MoveToCoordinatesCreate", + "type": "object" }, - "TouchTipParams": { - "title": "TouchTipParams", - "description": "Payload needed to touch a pipette tip the sides of a specific well.", - "type": "object", + "MoveToCoordinatesParams": { + "description": "Payload required to move a pipette to coordinates.", "properties": { - "labwareId": { - "title": "Labwareid", - "description": "Identifier of labware to use.", - "type": "string" + "coordinates": { + "$ref": "#/$defs/DeckPoint", + "description": "X, Y and Z coordinates in mm from deck's origin location (left-front-bottom corner of work space)" }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", - "type": "string" + "forceDirect": { + "default": false, + "description": "If true, moving from one labware/well to another will not arc to the default safe z, but instead will move directly to the specified location. This will also force the `minimumZHeight` param to be ignored. A 'direct' movement is in X/Y/Z simultaneously.", + "title": "Forcedirect", + "type": "boolean" }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to perform the operation", - "allOf": [ - { - "$ref": "#/definitions/WellLocation" - } - ] + "minimumZHeight": { + "description": "Optional minimal Z margin in mm. If this is larger than the API's default safe Z margin, it will make the arc higher. If it's smaller, it will have no effect.", + "title": "Minimumzheight", + "type": "number" }, "pipetteId": { - "title": "Pipetteid", "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" }, - "radius": { - "title": "Radius", - "description": "The proportion of the target well's radius the pipette tip will move towards.", - "default": 1.0, - "type": "number" - }, "speed": { - "title": "Speed", "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", + "title": "Speed", "type": "number" } }, - "required": ["labwareId", "wellName", "pipetteId"] + "required": ["pipetteId", "coordinates"], + "title": "MoveToCoordinatesParams", + "type": "object" }, - "TouchTipCreate": { - "title": "TouchTipCreate", - "description": "Touch tip command creation request model.", - "type": "object", + "MoveToCreate": { + "description": "MoveTo command request model.", "properties": { "commandType": { + "const": "robot/moveTo", + "default": "robot/moveTo", + "enum": ["robot/moveTo"], "title": "Commandtype", - "default": "touchTip", - "enum": ["touchTip"], + "type": "string" + }, + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" }, "params": { - "$ref": "#/definitions/TouchTipParams" + "$ref": "#/$defs/MoveToParams" + } + }, + "required": ["params"], + "title": "MoveToCreate", + "type": "object" + }, + "MoveToMaintenancePositionCreate": { + "description": "Calibration set up position command creation request model.", + "properties": { + "commandType": { + "const": "calibration/moveToMaintenancePosition", + "default": "calibration/moveToMaintenancePosition", + "enum": ["calibration/moveToMaintenancePosition"], + "title": "Commandtype", + "type": "string" }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" + }, + "params": { + "$ref": "#/$defs/MoveToMaintenancePositionParams" + } + }, + "required": ["params"], + "title": "MoveToMaintenancePositionCreate", + "type": "object" + }, + "MoveToMaintenancePositionParams": { + "description": "Calibration set up position command parameters.", + "properties": { + "maintenancePosition": { + "$ref": "#/$defs/MaintenancePosition", + "default": "attachInstrument", + "description": "The position the gantry mount needs to move to." }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" + "mount": { + "$ref": "#/$defs/MountType", + "description": "Gantry mount to move maintenance position." } }, - "required": ["params"] - }, - "StatusBarAnimation": { - "title": "StatusBarAnimation", - "description": "Status Bar animation options.", - "enum": ["idle", "confirm", "updating", "disco", "off"] + "required": ["mount"], + "title": "MoveToMaintenancePositionParams", + "type": "object" }, - "SetStatusBarParams": { - "title": "SetStatusBarParams", - "description": "Payload required to set the status bar to run an animation.", - "type": "object", + "MoveToParams": { + "description": "Payload required to move to a destination position.", "properties": { - "animation": { - "description": "The animation that should be executed on the status bar.", - "allOf": [ - { - "$ref": "#/definitions/StatusBarAnimation" - } - ] + "destination": { + "$ref": "#/$defs/DeckPoint", + "description": "X, Y and Z coordinates in mm from deck's origin location (left-front-bottom corner of work space)" + }, + "mount": { + "$ref": "#/$defs/MountType", + "description": "The mount to move to the destination point." + }, + "speed": { + "description": "The max velocity to move the axes at. Will fall to hardware defaults if none provided.", + "title": "Speed", + "type": "number" } }, - "required": ["animation"] + "required": ["mount", "destination"], + "title": "MoveToParams", + "type": "object" }, - "SetStatusBarCreate": { - "title": "SetStatusBarCreate", - "description": "setStatusBar command request model.", - "type": "object", + "MoveToWellCreate": { + "description": "Move to well command creation request model.", "properties": { "commandType": { + "const": "moveToWell", + "default": "moveToWell", + "enum": ["moveToWell"], "title": "Commandtype", - "default": "setStatusBar", - "enum": ["setStatusBar"], "type": "string" }, - "params": { - "$ref": "#/definitions/SetStatusBarParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/MoveToWellParams" } }, - "required": ["params"] - }, - "TipPresenceStatus": { - "title": "TipPresenceStatus", - "description": "Tip presence status reported by a pipette.", - "enum": ["present", "absent", "unknown"], - "type": "string" - }, - "InstrumentSensorId": { - "title": "InstrumentSensorId", - "description": "Primary and secondary sensor ids.", - "enum": ["primary", "secondary", "both"], - "type": "string" + "required": ["params"], + "title": "MoveToWellCreate", + "type": "object" }, - "VerifyTipPresenceParams": { - "title": "VerifyTipPresenceParams", - "description": "Payload required for a VerifyTipPresence command.", - "type": "object", + "MoveToWellParams": { + "description": "Payload required to move a pipette to a specific well.", "properties": { + "forceDirect": { + "default": false, + "description": "If true, moving from one labware/well to another will not arc to the default safe z, but instead will move directly to the specified location. This will also force the `minimumZHeight` param to be ignored. A 'direct' movement is in X/Y/Z simultaneously.", + "title": "Forcedirect", + "type": "boolean" + }, + "labwareId": { + "description": "Identifier of labware to use.", + "title": "Labwareid", + "type": "string" + }, + "minimumZHeight": { + "description": "Optional minimal Z margin in mm. If this is larger than the API's default safe Z margin, it will make the arc higher. If it's smaller, it will have no effect.", + "title": "Minimumzheight", + "type": "number" + }, "pipetteId": { - "title": "Pipetteid", "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" }, - "expectedState": { - "description": "The expected tip presence status on the pipette.", - "allOf": [ - { - "$ref": "#/definitions/TipPresenceStatus" - } - ] + "speed": { + "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", + "title": "Speed", + "type": "number" }, - "followSingularSensor": { - "description": "The sensor id to follow if the other can be ignored.", - "allOf": [ - { - "$ref": "#/definitions/InstrumentSensorId" - } - ] + "wellLocation": { + "$ref": "#/$defs/WellLocation", + "description": "Relative well location at which to perform the operation" + }, + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", + "type": "string" } }, - "required": ["pipetteId", "expectedState"] + "required": ["labwareId", "wellName", "pipetteId"], + "title": "MoveToWellParams", + "type": "object" }, - "VerifyTipPresenceCreate": { - "title": "VerifyTipPresenceCreate", - "description": "VerifyTipPresence command creation request model.", - "type": "object", + "MovementAxis": { + "description": "Axis on which to issue a relative movement.", + "enum": ["x", "y", "z"], + "title": "MovementAxis", + "type": "string" + }, + "MultiDispenseProperties": { + "description": "Properties specific to the multi-dispense function.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "verifyTipPresence", - "enum": ["verifyTipPresence"], - "type": "string" + "conditioningByVolume": { + "description": "Settings for conditioning volume keyed by target dispense volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Conditioningbyvolume", + "type": "array" }, - "params": { - "$ref": "#/definitions/VerifyTipPresenceParams" + "correctionByVolume": { + "description": "Settings for volume correction keyed by by target dispense volume, representing additional volume the plunger should move to accurately hit target volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Correctionbyvolume", + "type": "array" }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "delay": { + "$ref": "#/$defs/DelayProperties", + "description": "Delay settings after each dispense" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" + "disposalByVolume": { + "description": "Settings for disposal volume keyed by target dispense volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Disposalbyvolume", + "type": "array" + }, + "flowRateByVolume": { + "description": "Settings for flow rate keyed by target dispense volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Flowratebyvolume", + "type": "array" + }, + "offset": { + "$ref": "#/$defs/Coordinate", + "description": "Relative offset for single multi-dispense." + }, + "positionReference": { + "$ref": "#/$defs/PositionReference", + "description": "Position reference for multi-dispense." + }, + "retract": { + "$ref": "#/$defs/RetractDispense", + "description": "Pipette retract settings after a multi-dispense." + }, + "submerge": { + "$ref": "#/$defs/Submerge", + "description": "Submerge settings for multi-dispense." } }, - "required": ["params"] + "required": [ + "submerge", + "retract", + "positionReference", + "offset", + "flowRateByVolume", + "correctionByVolume", + "conditioningByVolume", + "disposalByVolume", + "delay" + ], + "title": "MultiDispenseProperties", + "type": "object" }, - "GetTipPresenceParams": { - "title": "GetTipPresenceParams", - "description": "Payload required for a GetTipPresence command.", - "type": "object", + "OnLabwareLocation": { + "description": "The location of something placed atop another labware.", "properties": { - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", + "labwareId": { + "description": "The ID of a loaded Labware from a prior `loadLabware` command.", + "title": "Labwareid", "type": "string" } }, - "required": ["pipetteId"] + "required": ["labwareId"], + "title": "OnLabwareLocation", + "type": "object" }, - "GetTipPresenceCreate": { - "title": "GetTipPresenceCreate", - "description": "GetTipPresence command creation request model.", - "type": "object", + "OpenLabwareLatchCreate": { + "description": "A request to create a Heater-Shaker's open labware latch command.", "properties": { "commandType": { + "const": "heaterShaker/openLabwareLatch", + "default": "heaterShaker/openLabwareLatch", + "enum": ["heaterShaker/openLabwareLatch"], "title": "Commandtype", - "default": "getTipPresence", - "enum": ["getTipPresence"], "type": "string" }, - "params": { - "$ref": "#/definitions/GetTipPresenceParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/OpenLabwareLatchParams" } }, - "required": ["params"] + "required": ["params"], + "title": "OpenLabwareLatchCreate", + "type": "object" }, - "GetNextTipParams": { - "title": "GetNextTipParams", - "description": "Payload needed to resolve the next available tip.", - "type": "object", + "OpenLabwareLatchParams": { + "description": "Input parameters to open a Heater-Shaker Module's labware latch.", "properties": { - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" - }, - "labwareIds": { - "title": "Labwareids", - "description": "Labware ID(s) of tip racks to resolve next available tip(s) from Labware IDs will be resolved sequentially", - "type": "array", - "items": { - "type": "string" - } - }, - "startingTipWell": { - "title": "Startingtipwell", - "description": "Name of starting tip rack 'well'. This only applies to the first tip rack in the list provided in labwareIDs", + "moduleId": { + "description": "Unique ID of the Heater-Shaker Module.", + "title": "Moduleid", "type": "string" } }, - "required": ["pipetteId", "labwareIds"] + "required": ["moduleId"], + "title": "OpenLabwareLatchParams", + "type": "object" }, - "GetNextTipCreate": { - "title": "GetNextTipCreate", - "description": "Get next tip command creation request model.", - "type": "object", + "PickUpTipCreate": { + "description": "Pick up tip command creation request model.", "properties": { "commandType": { + "const": "pickUpTip", + "default": "pickUpTip", + "enum": ["pickUpTip"], "title": "Commandtype", - "default": "getNextTip", - "enum": ["getNextTip"], "type": "string" }, - "params": { - "$ref": "#/definitions/GetNextTipParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/PickUpTipParams" } }, - "required": ["params"] + "required": ["params"], + "title": "PickUpTipCreate", + "type": "object" }, - "LiquidProbeParams": { - "title": "LiquidProbeParams", - "description": "Parameters required for a `liquidProbe` command.", - "type": "object", + "PickUpTipParams": { + "description": "Payload needed to move a pipette to a specific well.", "properties": { "labwareId": { - "title": "Labwareid", "description": "Identifier of labware to use.", + "title": "Labwareid", "type": "string" }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", - "type": "string" - }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to perform the operation", - "allOf": [ - { - "$ref": "#/definitions/WellLocation" - } - ] - }, "pipetteId": { - "title": "Pipetteid", "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" - } - }, - "required": ["labwareId", "wellName", "pipetteId"] - }, - "LiquidProbeCreate": { - "title": "LiquidProbeCreate", - "description": "The request model for a `liquidProbe` command.", - "type": "object", - "properties": { - "commandType": { - "title": "Commandtype", - "default": "liquidProbe", - "enum": ["liquidProbe"], - "type": "string" - }, - "params": { - "$ref": "#/definitions/LiquidProbeParams" }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "wellLocation": { + "$ref": "#/$defs/PickUpTipWellLocation", + "description": "Relative well location at which to pick up the tip." }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", "type": "string" } }, - "required": ["params"] + "required": ["pipetteId", "labwareId", "wellName"], + "title": "PickUpTipParams", + "type": "object" }, - "TryLiquidProbeParams": { - "title": "TryLiquidProbeParams", - "description": "Parameters required for a `tryLiquidProbe` command.", - "type": "object", + "PickUpTipWellLocation": { + "description": "A relative location in reference to a well's location.\n\nTo be used for picking up tips.", "properties": { - "labwareId": { - "title": "Labwareid", - "description": "Identifier of labware to use.", - "type": "string" - }, - "wellName": { - "title": "Wellname", - "description": "Name of well to use in labware.", - "type": "string" - }, - "wellLocation": { - "title": "Welllocation", - "description": "Relative well location at which to perform the operation", - "allOf": [ - { - "$ref": "#/definitions/WellLocation" - } - ] + "offset": { + "$ref": "#/$defs/WellOffset" }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", - "type": "string" + "origin": { + "$ref": "#/$defs/PickUpTipWellOrigin", + "default": "top" } }, - "required": ["labwareId", "wellName", "pipetteId"] + "title": "PickUpTipWellLocation", + "type": "object" }, - "TryLiquidProbeCreate": { - "title": "TryLiquidProbeCreate", - "description": "The request model for a `tryLiquidProbe` command.", - "type": "object", + "PickUpTipWellOrigin": { + "description": "The origin of a PickUpTipWellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well", + "enum": ["top", "bottom", "center"], + "title": "PickUpTipWellOrigin", + "type": "string" + }, + "PipetteNameType": { + "description": "Pipette load name values.", + "enum": [ + "p10_single", + "p10_multi", + "p20_single_gen2", + "p20_multi_gen2", + "p50_single", + "p50_multi", + "p50_single_flex", + "p50_multi_flex", + "p300_single", + "p300_multi", + "p300_single_gen2", + "p300_multi_gen2", + "p1000_single", + "p1000_single_gen2", + "p1000_single_flex", + "p1000_multi_flex", + "p1000_multi_em_flex", + "p1000_96", + "p200_96" + ], + "title": "PipetteNameType", + "type": "string" + }, + "PositionReference": { + "description": "Positional reference for liquid handling operations.", + "enum": ["well-bottom", "well-top", "well-center", "liquid-meniscus"], + "title": "PositionReference", + "type": "string" + }, + "PrepareToAspirateCreate": { + "description": "Prepare for aspirate command creation request model.", "properties": { "commandType": { + "const": "prepareToAspirate", + "default": "prepareToAspirate", + "enum": ["prepareToAspirate"], "title": "Commandtype", - "default": "tryLiquidProbe", - "enum": ["tryLiquidProbe"], "type": "string" }, - "params": { - "$ref": "#/definitions/TryLiquidProbeParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/PrepareToAspirateParams" } }, - "required": ["params"] + "required": ["params"], + "title": "PrepareToAspirateCreate", + "type": "object" }, - "opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureParams": { - "title": "WaitForTemperatureParams", - "description": "Input parameters to wait for a Heater-Shaker's target temperature.", - "type": "object", + "PrepareToAspirateParams": { + "description": "Parameters required to prepare a specific pipette for aspiration.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Heater-Shaker Module.", + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" - }, - "celsius": { - "title": "Celsius", - "description": "Target temperature in \u00b0C. If not specified, will default to the module's target temperature. Specifying a celsius parameter other than the target temperature could lead to unpredictable behavior and hence is not recommended for use. This parameter can be removed in a future version without prior notice.", - "type": "number" } }, - "required": ["moduleId"] + "required": ["pipetteId"], + "title": "PrepareToAspirateParams", + "type": "object" }, - "opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureCreate": { - "title": "WaitForTemperatureCreate", - "description": "A request to create a Heater-Shaker's wait for temperature command.", - "type": "object", + "ProfileCycle": { + "description": "An individual cycle in a Thermocycler extended profile.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "heaterShaker/waitForTemperature", - "enum": ["heaterShaker/waitForTemperature"], - "type": "string" - }, - "params": { - "$ref": "#/definitions/opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureParams" - }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "repetitions": { + "description": "Number of times to repeat the steps.", + "title": "Repetitions", + "type": "integer" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" + "steps": { + "description": "Steps to repeat.", + "items": { + "$ref": "#/$defs/ProfileStep" + }, + "title": "Steps", + "type": "array" } }, - "required": ["params"] + "required": ["steps", "repetitions"], + "title": "ProfileCycle", + "type": "object" }, - "opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureParams": { - "title": "SetTargetTemperatureParams", - "description": "Input parameters to set a Heater-Shaker's target temperature.", - "type": "object", + "ProfileStep": { + "description": "An individual step in a Thermocycler extended profile.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Heater-Shaker Module.", - "type": "string" - }, "celsius": { - "title": "Celsius", "description": "Target temperature in \u00b0C.", + "title": "Celsius", + "type": "number" + }, + "holdSeconds": { + "description": "Time to hold target temperature in seconds.", + "title": "Holdseconds", "type": "number" } }, - "required": ["moduleId", "celsius"] + "required": ["celsius", "holdSeconds"], + "title": "ProfileStep", + "type": "object" }, - "opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureCreate": { - "title": "SetTargetTemperatureCreate", - "description": "A request to create a Heater-Shaker's set temperature command.", - "type": "object", + "QuadrantNozzleLayoutConfiguration": { + "description": "Information required for nozzle configurations of type QUADRANT.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "heaterShaker/setTargetTemperature", - "enum": ["heaterShaker/setTargetTemperature"], + "backLeftNozzle": { + "description": "The back left nozzle in your configuration.", + "pattern": "[A-Z]\\d{1,2}", + "title": "Backleftnozzle", "type": "string" }, - "params": { - "$ref": "#/definitions/opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureParams" - }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "frontRightNozzle": { + "description": "The front right nozzle in your configuration.", + "pattern": "[A-Z]\\d{1,2}", + "title": "Frontrightnozzle", + "type": "string" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "primaryNozzle": { + "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", + "enum": ["A1", "H1", "A12", "H12"], + "title": "Primarynozzle", "type": "string" - } - }, - "required": ["params"] - }, - "DeactivateHeaterParams": { - "title": "DeactivateHeaterParams", - "description": "Input parameters to unset a Heater-Shaker's target temperature.", - "type": "object", - "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Heater-Shaker Module.", + }, + "style": { + "const": "QUADRANT", + "default": "QUADRANT", + "enum": ["QUADRANT"], + "title": "Style", "type": "string" } }, - "required": ["moduleId"] + "required": ["primaryNozzle", "frontRightNozzle", "backLeftNozzle"], + "title": "QuadrantNozzleLayoutConfiguration", + "type": "object" }, - "DeactivateHeaterCreate": { - "title": "DeactivateHeaterCreate", - "description": "A request to create a Heater-Shaker's deactivate heater command.", - "type": "object", + "ReadAbsorbanceCreate": { + "description": "A request to execute an Absorbance Reader measurement.", "properties": { "commandType": { + "const": "absorbanceReader/read", + "default": "absorbanceReader/read", + "enum": ["absorbanceReader/read"], "title": "Commandtype", - "default": "heaterShaker/deactivateHeater", - "enum": ["heaterShaker/deactivateHeater"], "type": "string" }, - "params": { - "$ref": "#/definitions/DeactivateHeaterParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/ReadAbsorbanceParams" } }, - "required": ["params"] + "required": ["params"], + "title": "ReadAbsorbanceCreate", + "type": "object" }, - "SetAndWaitForShakeSpeedParams": { - "title": "SetAndWaitForShakeSpeedParams", - "description": "Input parameters to set and wait for a shake speed for a Heater-Shaker Module.", - "type": "object", + "ReadAbsorbanceParams": { + "description": "Input parameters for an absorbance reading.", "properties": { + "fileName": { + "description": "Optional file name to use when storing the results of a measurement.", + "title": "Filename", + "type": "string" + }, "moduleId": { + "description": "Unique ID of the Absorbance Reader.", "title": "Moduleid", - "description": "Unique ID of the Heater-Shaker Module.", "type": "string" - }, - "rpm": { - "title": "Rpm", - "description": "Target speed in rotations per minute.", - "type": "number" } }, - "required": ["moduleId", "rpm"] + "required": ["moduleId"], + "title": "ReadAbsorbanceParams", + "type": "object" }, - "SetAndWaitForShakeSpeedCreate": { - "title": "SetAndWaitForShakeSpeedCreate", - "description": "A request to create a Heater-Shaker's set and wait for shake speed command.", - "type": "object", + "ReloadLabwareCreate": { + "description": "Reload labware command creation request.", "properties": { "commandType": { + "const": "reloadLabware", + "default": "reloadLabware", + "enum": ["reloadLabware"], "title": "Commandtype", - "default": "heaterShaker/setAndWaitForShakeSpeed", - "enum": ["heaterShaker/setAndWaitForShakeSpeed"], "type": "string" }, - "params": { - "$ref": "#/definitions/SetAndWaitForShakeSpeedParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/ReloadLabwareParams" } }, - "required": ["params"] + "required": ["params"], + "title": "ReloadLabwareCreate", + "type": "object" }, - "DeactivateShakerParams": { - "title": "DeactivateShakerParams", - "description": "Input parameters to deactivate shaker for a Heater-Shaker Module.", - "type": "object", + "ReloadLabwareParams": { + "description": "Payload required to load a labware into a slot.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Heater-Shaker Module.", + "labwareId": { + "description": "The already-loaded labware instance to update.", + "title": "Labwareid", "type": "string" } }, - "required": ["moduleId"] + "required": ["labwareId"], + "title": "ReloadLabwareParams", + "type": "object" }, - "DeactivateShakerCreate": { - "title": "DeactivateShakerCreate", - "description": "A request to create a Heater-Shaker's deactivate shaker command.", - "type": "object", + "RetractAspirate": { + "description": "Shared properties for the retract function after aspiration.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "heaterShaker/deactivateShaker", - "enum": ["heaterShaker/deactivateShaker"], - "type": "string" + "airGapByVolume": { + "description": "Settings for air gap keyed by target aspiration volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Airgapbyvolume", + "type": "array" }, - "params": { - "$ref": "#/definitions/DeactivateShakerParams" + "delay": { + "$ref": "#/$defs/DelayProperties", + "description": "Delay settings for retract after aspirate." }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ + "offset": { + "$ref": "#/$defs/Coordinate", + "description": "Relative offset for retract after aspirate." + }, + "positionReference": { + "$ref": "#/$defs/PositionReference", + "description": "Position reference for retract after aspirate." + }, + "speed": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, { - "$ref": "#/definitions/CommandIntent" + "minimum": 0.0, + "type": "number" } - ] + ], + "description": "Speed of retraction, in millimeters per second.", + "title": "Speed" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" - } - }, - "required": ["params"] - }, - "OpenLabwareLatchParams": { - "title": "OpenLabwareLatchParams", - "description": "Input parameters to open a Heater-Shaker Module's labware latch.", - "type": "object", - "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Heater-Shaker Module.", - "type": "string" + "touchTip": { + "$ref": "#/$defs/TouchTipProperties", + "description": "Touch tip settings for retract after aspirate." } }, - "required": ["moduleId"] + "required": [ + "positionReference", + "offset", + "speed", + "airGapByVolume", + "touchTip", + "delay" + ], + "title": "RetractAspirate", + "type": "object" }, - "OpenLabwareLatchCreate": { - "title": "OpenLabwareLatchCreate", - "description": "A request to create a Heater-Shaker's open labware latch command.", - "type": "object", + "RetractAxisCreate": { + "description": "Data to create a Retract Axis command.", "properties": { "commandType": { + "const": "retractAxis", + "default": "retractAxis", + "enum": ["retractAxis"], "title": "Commandtype", - "default": "heaterShaker/openLabwareLatch", - "enum": ["heaterShaker/openLabwareLatch"], "type": "string" }, - "params": { - "$ref": "#/definitions/OpenLabwareLatchParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/RetractAxisParams" } }, - "required": ["params"] + "required": ["params"], + "title": "RetractAxisCreate", + "type": "object" }, - "CloseLabwareLatchParams": { - "title": "CloseLabwareLatchParams", - "description": "Input parameters to close a Heater-Shaker Module's labware latch.", - "type": "object", + "RetractAxisParams": { + "description": "Payload required for a Retract Axis command.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Heater-Shaker Module.", - "type": "string" + "axis": { + "$ref": "#/$defs/MotorAxis", + "description": "Axis to retract to its home position as quickly as safely possible. The difference between retracting an axis and homing an axis using the home command is that a home will always probe the limit switch and will work as the first motion command a robot will need to execute; On the other hand, retraction will rely on this previously determined home position to move to it as fast as safely possible. So on the Flex, it will move (fast) the axis to the previously recorded home position and on the OT2, it will move (fast) the axis a safe distance from the previously recorded home position, and then slowly approach the limit switch." } }, - "required": ["moduleId"] + "required": ["axis"], + "title": "RetractAxisParams", + "type": "object" }, - "CloseLabwareLatchCreate": { - "title": "CloseLabwareLatchCreate", - "description": "A request to create a Heater-Shaker's close latch command.", - "type": "object", + "RetractDispense": { + "description": "Shared properties for the retract function after dispense.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "heaterShaker/closeLabwareLatch", - "enum": ["heaterShaker/closeLabwareLatch"], - "type": "string" + "airGapByVolume": { + "description": "Settings for air gap keyed by target aspiration volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Airgapbyvolume", + "type": "array" }, - "params": { - "$ref": "#/definitions/CloseLabwareLatchParams" + "blowout": { + "$ref": "#/$defs/BlowoutProperties", + "description": "Blowout properties for retract after dispense." }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "delay": { + "$ref": "#/$defs/DelayProperties", + "description": "Delay settings for retract after dispense." }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" - } - }, - "required": ["params"] - }, - "DisengageParams": { - "title": "DisengageParams", - "description": "Input data to disengage a Magnetic Module's magnets.", - "type": "object", - "properties": { - "moduleId": { - "title": "Moduleid", - "description": "The ID of the Magnetic Module whose magnets you want to disengage, from a prior `loadModule` command.", - "type": "string" - } - }, - "required": ["moduleId"] - }, - "DisengageCreate": { - "title": "DisengageCreate", - "description": "A request to create a Magnetic Module disengage command.", - "type": "object", - "properties": { - "commandType": { - "title": "Commandtype", - "default": "magneticModule/disengage", - "enum": ["magneticModule/disengage"], - "type": "string" + "offset": { + "$ref": "#/$defs/Coordinate", + "description": "Relative offset for retract after dispense." }, - "params": { - "$ref": "#/definitions/DisengageParams" + "positionReference": { + "$ref": "#/$defs/PositionReference", + "description": "Position reference for retract after dispense." }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ + "speed": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, { - "$ref": "#/definitions/CommandIntent" + "minimum": 0.0, + "type": "number" } - ] + ], + "description": "Speed of retraction, in millimeters per second.", + "title": "Speed" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" + "touchTip": { + "$ref": "#/$defs/TouchTipProperties", + "description": "Touch tip settings for retract after dispense." } }, - "required": ["params"] + "required": [ + "positionReference", + "offset", + "speed", + "airGapByVolume", + "blowout", + "touchTip", + "delay" + ], + "title": "RetractDispense", + "type": "object" }, - "EngageParams": { - "title": "EngageParams", - "description": "Input data to engage a Magnetic Module.", - "type": "object", + "RowNozzleLayoutConfiguration": { + "description": "Minimum information required for a new nozzle configuration.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "The ID of the Magnetic Module whose magnets you want to raise, from a prior `loadModule` command.", + "primaryNozzle": { + "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", + "enum": ["A1", "H1", "A12", "H12"], + "title": "Primarynozzle", "type": "string" }, - "height": { - "title": "Height", - "description": "How high, in millimeters, to raise the magnets.\n\nZero means the tops of the magnets are level with the ledge that the labware rests on. This will be slightly above the magnets' minimum height, the hardware home position. Negative values are allowed, to put the magnets below the ledge.\n\nUnits are always true millimeters. This is unlike certain labware definitions, engage commands in the Python Protocol API, and engage commands in older versions of the JSON protocol schema. Take care to convert properly.", - "type": "number" + "style": { + "const": "ROW", + "default": "ROW", + "enum": ["ROW"], + "title": "Style", + "type": "string" } }, - "required": ["moduleId", "height"] + "required": ["primaryNozzle"], + "title": "RowNozzleLayoutConfiguration", + "type": "object" }, - "EngageCreate": { - "title": "EngageCreate", - "description": "A request to create a Magnetic Module engage command.", - "type": "object", + "RunExtendedProfileCreate": { + "description": "A request to execute a Thermocycler profile run.", "properties": { "commandType": { + "const": "thermocycler/runExtendedProfile", + "default": "thermocycler/runExtendedProfile", + "enum": ["thermocycler/runExtendedProfile"], "title": "Commandtype", - "default": "magneticModule/engage", - "enum": ["magneticModule/engage"], "type": "string" }, - "params": { - "$ref": "#/definitions/EngageParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/RunExtendedProfileParams" } }, - "required": ["params"] + "required": ["params"], + "title": "RunExtendedProfileCreate", + "type": "object" }, - "opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureParams": { - "title": "SetTargetTemperatureParams", - "description": "Input parameters to set a Temperature Module's target temperature.", - "type": "object", + "RunExtendedProfileParams": { + "description": "Input parameters for an individual Thermocycler profile step.", "properties": { + "blockMaxVolumeUl": { + "description": "Amount of liquid in uL of the most-full well in labware loaded onto the thermocycler.", + "title": "Blockmaxvolumeul", + "type": "number" + }, "moduleId": { + "description": "Unique ID of the Thermocycler.", "title": "Moduleid", - "description": "Unique ID of the Temperature Module.", "type": "string" }, - "celsius": { - "title": "Celsius", - "description": "Target temperature in \u00b0C.", - "type": "number" + "profileElements": { + "description": "Elements of the profile. Each can be either a step or a cycle.", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/ProfileStep" + }, + { + "$ref": "#/$defs/ProfileCycle" + } + ] + }, + "title": "Profileelements", + "type": "array" } }, - "required": ["moduleId", "celsius"] + "required": ["moduleId", "profileElements"], + "title": "RunExtendedProfileParams", + "type": "object" }, - "opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureCreate": { - "title": "SetTargetTemperatureCreate", - "description": "A request to create a Temperature Module's set temperature command.", - "type": "object", + "RunProfileCreate": { + "description": "A request to execute a Thermocycler profile run.", "properties": { "commandType": { + "const": "thermocycler/runProfile", + "default": "thermocycler/runProfile", + "enum": ["thermocycler/runProfile"], "title": "Commandtype", - "default": "temperatureModule/setTargetTemperature", - "enum": ["temperatureModule/setTargetTemperature"], "type": "string" }, - "params": { - "$ref": "#/definitions/opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/RunProfileParams" } }, - "required": ["params"] + "required": ["params"], + "title": "RunProfileCreate", + "type": "object" }, - "opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureParams": { - "title": "WaitForTemperatureParams", - "description": "Input parameters to wait for a Temperature Module's target temperature.", - "type": "object", + "RunProfileParams": { + "description": "Input parameters to run a Thermocycler profile.", "properties": { + "blockMaxVolumeUl": { + "description": "Amount of liquid in uL of the most-full well in labware loaded onto the thermocycler.", + "title": "Blockmaxvolumeul", + "type": "number" + }, "moduleId": { + "description": "Unique ID of the Thermocycler.", "title": "Moduleid", - "description": "Unique ID of the Temperature Module.", "type": "string" }, + "profile": { + "description": "Array of profile steps with target temperature and temperature hold time.", + "items": { + "$ref": "#/$defs/RunProfileStepParams" + }, + "title": "Profile", + "type": "array" + } + }, + "required": ["moduleId", "profile"], + "title": "RunProfileParams", + "type": "object" + }, + "RunProfileStepParams": { + "description": "Input parameters for an individual Thermocycler profile step.", + "properties": { "celsius": { + "description": "Target temperature in \u00b0C.", "title": "Celsius", - "description": "Target temperature in \u00b0C. If not specified, will default to the module's target temperature. Specifying a celsius parameter other than the target temperature could lead to unpredictable behavior and hence is not recommended for use. This parameter can be removed in a future version without prior notice.", + "type": "number" + }, + "holdSeconds": { + "description": "Time to hold target temperature at in seconds.", + "title": "Holdseconds", "type": "number" } }, - "required": ["moduleId"] + "required": ["celsius", "holdSeconds"], + "title": "RunProfileStepParams", + "type": "object" }, - "opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureCreate": { - "title": "WaitForTemperatureCreate", - "description": "A request to create a Temperature Module's wait for temperature command.", - "type": "object", + "SavePositionCreate": { + "description": "Save position command creation request model.", "properties": { "commandType": { + "const": "savePosition", + "default": "savePosition", + "enum": ["savePosition"], "title": "Commandtype", - "default": "temperatureModule/waitForTemperature", - "enum": ["temperatureModule/waitForTemperature"], "type": "string" }, - "params": { - "$ref": "#/definitions/opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/SavePositionParams" } }, - "required": ["params"] + "required": ["params"], + "title": "SavePositionCreate", + "type": "object" }, - "DeactivateTemperatureParams": { - "title": "DeactivateTemperatureParams", - "description": "Input parameters to deactivate a Temperature Module.", - "type": "object", + "SavePositionParams": { + "description": "Payload needed to save a pipette's current position.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Temperature Module.", + "failOnNotHomed": { + "description": "Require all axes to be homed before saving position.", + "title": "Failonnothomed", + "type": "boolean" + }, + "pipetteId": { + "description": "Unique identifier of the pipette in question.", + "title": "Pipetteid", + "type": "string" + }, + "positionId": { + "description": "An optional ID to assign to this command instance. Auto-assigned if not defined.", + "title": "Positionid", "type": "string" } }, - "required": ["moduleId"] + "required": ["pipetteId"], + "title": "SavePositionParams", + "type": "object" }, - "DeactivateTemperatureCreate": { - "title": "DeactivateTemperatureCreate", - "description": "A request to deactivate a Temperature Module.", - "type": "object", + "SetAndWaitForShakeSpeedCreate": { + "description": "A request to create a Heater-Shaker's set and wait for shake speed command.", "properties": { "commandType": { + "const": "heaterShaker/setAndWaitForShakeSpeed", + "default": "heaterShaker/setAndWaitForShakeSpeed", + "enum": ["heaterShaker/setAndWaitForShakeSpeed"], "title": "Commandtype", - "default": "temperatureModule/deactivate", - "enum": ["temperatureModule/deactivate"], "type": "string" }, - "params": { - "$ref": "#/definitions/DeactivateTemperatureParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/SetAndWaitForShakeSpeedParams" } }, - "required": ["params"] - }, - "SetTargetBlockTemperatureParams": { - "title": "SetTargetBlockTemperatureParams", - "description": "Input parameters to set a Thermocycler's target block temperature.", - "type": "object", - "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Thermocycler Module.", - "type": "string" - }, - "celsius": { - "title": "Celsius", - "description": "Target temperature in \u00b0C.", - "type": "number" - }, - "blockMaxVolumeUl": { - "title": "Blockmaxvolumeul", - "description": "Amount of liquid in uL of the most-full well in labware loaded onto the thermocycler.", - "type": "number" + "required": ["params"], + "title": "SetAndWaitForShakeSpeedCreate", + "type": "object" + }, + "SetAndWaitForShakeSpeedParams": { + "description": "Input parameters to set and wait for a shake speed for a Heater-Shaker Module.", + "properties": { + "moduleId": { + "description": "Unique ID of the Heater-Shaker Module.", + "title": "Moduleid", + "type": "string" }, - "holdTimeSeconds": { - "title": "Holdtimeseconds", - "description": "Amount of time, in seconds, to hold the temperature for. If specified, a waitForBlockTemperature command will block until the given hold time has elapsed.", + "rpm": { + "description": "Target speed in rotations per minute.", + "title": "Rpm", "type": "number" } }, - "required": ["moduleId", "celsius"] + "required": ["moduleId", "rpm"], + "title": "SetAndWaitForShakeSpeedParams", + "type": "object" }, - "SetTargetBlockTemperatureCreate": { - "title": "SetTargetBlockTemperatureCreate", - "description": "A request to create a Thermocycler's set block temperature command.", - "type": "object", + "SetRailLightsCreate": { + "description": "setRailLights command request model.", "properties": { "commandType": { + "const": "setRailLights", + "default": "setRailLights", + "enum": ["setRailLights"], "title": "Commandtype", - "default": "thermocycler/setTargetBlockTemperature", - "enum": ["thermocycler/setTargetBlockTemperature"], "type": "string" }, - "params": { - "$ref": "#/definitions/SetTargetBlockTemperatureParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/SetRailLightsParams" } }, - "required": ["params"] + "required": ["params"], + "title": "SetRailLightsCreate", + "type": "object" }, - "WaitForBlockTemperatureParams": { - "title": "WaitForBlockTemperatureParams", - "description": "Input parameters to wait for Thermocycler's target block temperature.", - "type": "object", + "SetRailLightsParams": { + "description": "Payload required to set the rail lights on or off.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Thermocycler Module.", - "type": "string" + "on": { + "description": "The field that determines if the light is turned off or on.", + "title": "On", + "type": "boolean" } }, - "required": ["moduleId"] + "required": ["on"], + "title": "SetRailLightsParams", + "type": "object" }, - "WaitForBlockTemperatureCreate": { - "title": "WaitForBlockTemperatureCreate", - "description": "A request to create Thermocycler's wait for block temperature command.", - "type": "object", + "SetStatusBarCreate": { + "description": "setStatusBar command request model.", "properties": { "commandType": { + "const": "setStatusBar", + "default": "setStatusBar", + "enum": ["setStatusBar"], "title": "Commandtype", - "default": "thermocycler/waitForBlockTemperature", - "enum": ["thermocycler/waitForBlockTemperature"], "type": "string" }, - "params": { - "$ref": "#/definitions/WaitForBlockTemperatureParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/SetStatusBarParams" } }, - "required": ["params"] + "required": ["params"], + "title": "SetStatusBarCreate", + "type": "object" }, - "SetTargetLidTemperatureParams": { - "title": "SetTargetLidTemperatureParams", - "description": "Input parameters to set a Thermocycler's target lid temperature.", - "type": "object", + "SetStatusBarParams": { + "description": "Payload required to set the status bar to run an animation.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Thermocycler Module.", - "type": "string" - }, - "celsius": { - "title": "Celsius", - "description": "Target temperature in \u00b0C.", - "type": "number" + "animation": { + "$ref": "#/$defs/StatusBarAnimation", + "description": "The animation that should be executed on the status bar." } }, - "required": ["moduleId", "celsius"] + "required": ["animation"], + "title": "SetStatusBarParams", + "type": "object" }, - "SetTargetLidTemperatureCreate": { - "title": "SetTargetLidTemperatureCreate", - "description": "A request to create a Thermocycler's set lid temperature command.", - "type": "object", + "SetTargetBlockTemperatureCreate": { + "description": "A request to create a Thermocycler's set block temperature command.", "properties": { "commandType": { + "const": "thermocycler/setTargetBlockTemperature", + "default": "thermocycler/setTargetBlockTemperature", + "enum": ["thermocycler/setTargetBlockTemperature"], "title": "Commandtype", - "default": "thermocycler/setTargetLidTemperature", - "enum": ["thermocycler/setTargetLidTemperature"], "type": "string" }, - "params": { - "$ref": "#/definitions/SetTargetLidTemperatureParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/SetTargetBlockTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "SetTargetBlockTemperatureCreate", + "type": "object" }, - "WaitForLidTemperatureParams": { - "title": "WaitForLidTemperatureParams", - "description": "Input parameters to wait for Thermocycler's lid temperature.", - "type": "object", + "SetTargetBlockTemperatureParams": { + "description": "Input parameters to set a Thermocycler's target block temperature.", "properties": { + "blockMaxVolumeUl": { + "description": "Amount of liquid in uL of the most-full well in labware loaded onto the thermocycler.", + "title": "Blockmaxvolumeul", + "type": "number" + }, + "celsius": { + "description": "Target temperature in \u00b0C.", + "title": "Celsius", + "type": "number" + }, + "holdTimeSeconds": { + "description": "Amount of time, in seconds, to hold the temperature for. If specified, a waitForBlockTemperature command will block until the given hold time has elapsed.", + "title": "Holdtimeseconds", + "type": "number" + }, "moduleId": { - "title": "Moduleid", "description": "Unique ID of the Thermocycler Module.", + "title": "Moduleid", "type": "string" } }, - "required": ["moduleId"] + "required": ["moduleId", "celsius"], + "title": "SetTargetBlockTemperatureParams", + "type": "object" }, - "WaitForLidTemperatureCreate": { - "title": "WaitForLidTemperatureCreate", - "description": "A request to create Thermocycler's wait for lid temperature command.", - "type": "object", + "SetTargetLidTemperatureCreate": { + "description": "A request to create a Thermocycler's set lid temperature command.", "properties": { "commandType": { + "const": "thermocycler/setTargetLidTemperature", + "default": "thermocycler/setTargetLidTemperature", + "enum": ["thermocycler/setTargetLidTemperature"], "title": "Commandtype", - "default": "thermocycler/waitForLidTemperature", - "enum": ["thermocycler/waitForLidTemperature"], "type": "string" }, - "params": { - "$ref": "#/definitions/WaitForLidTemperatureParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/SetTargetLidTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "SetTargetLidTemperatureCreate", + "type": "object" }, - "DeactivateBlockParams": { - "title": "DeactivateBlockParams", - "description": "Input parameters to unset a Thermocycler's target block temperature.", - "type": "object", + "SetTargetLidTemperatureParams": { + "description": "Input parameters to set a Thermocycler's target lid temperature.", "properties": { + "celsius": { + "description": "Target temperature in \u00b0C.", + "title": "Celsius", + "type": "number" + }, "moduleId": { + "description": "Unique ID of the Thermocycler Module.", "title": "Moduleid", - "description": "Unique ID of the Thermocycler.", "type": "string" } }, - "required": ["moduleId"] + "required": ["moduleId", "celsius"], + "title": "SetTargetLidTemperatureParams", + "type": "object" }, - "DeactivateBlockCreate": { - "title": "DeactivateBlockCreate", - "description": "A request to create a Thermocycler's deactivate block command.", - "type": "object", + "SingleDispenseProperties": { + "description": "Properties specific to the single-dispense function.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "thermocycler/deactivateBlock", - "enum": ["thermocycler/deactivateBlock"], - "type": "string" + "correctionByVolume": { + "description": "Settings for volume correction keyed by by target dispense volume, representing additional volume the plunger should move to accurately hit target volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Correctionbyvolume", + "type": "array" }, - "params": { - "$ref": "#/definitions/DeactivateBlockParams" + "delay": { + "$ref": "#/$defs/DelayProperties", + "description": "Delay after dispense, in seconds." }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "flowRateByVolume": { + "description": "Settings for flow rate keyed by target dispense volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Flowratebyvolume", + "type": "array" + }, + "mix": { + "$ref": "#/$defs/MixProperties", + "description": "Mixing settings for after a dispense" + }, + "offset": { + "$ref": "#/$defs/Coordinate", + "description": "Relative offset for single dispense." + }, + "positionReference": { + "$ref": "#/$defs/PositionReference", + "description": "Position reference for single dispense." + }, + "pushOutByVolume": { + "description": "Settings for pushout keyed by target dispense volume.", + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + }, + { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "minimum": 0.0, + "type": "number" + } + ] + } + ], + "type": "array" + }, + "title": "Pushoutbyvolume", + "type": "array" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" + "retract": { + "$ref": "#/$defs/RetractDispense", + "description": "Pipette retract settings after a single dispense." + }, + "submerge": { + "$ref": "#/$defs/Submerge", + "description": "Submerge settings for single dispense." } }, - "required": ["params"] + "required": [ + "submerge", + "retract", + "positionReference", + "offset", + "flowRateByVolume", + "correctionByVolume", + "mix", + "pushOutByVolume", + "delay" + ], + "title": "SingleDispenseProperties", + "type": "object" }, - "DeactivateLidParams": { - "title": "DeactivateLidParams", - "description": "Input parameters to unset a Thermocycler's target lid temperature.", - "type": "object", + "SingleNozzleLayoutConfiguration": { + "description": "Minimum information required for a new nozzle configuration.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Thermocycler.", + "primaryNozzle": { + "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", + "enum": ["A1", "H1", "A12", "H12"], + "title": "Primarynozzle", + "type": "string" + }, + "style": { + "const": "SINGLE", + "default": "SINGLE", + "enum": ["SINGLE"], + "title": "Style", "type": "string" } }, - "required": ["moduleId"] + "required": ["primaryNozzle"], + "title": "SingleNozzleLayoutConfiguration", + "type": "object" }, - "DeactivateLidCreate": { - "title": "DeactivateLidCreate", - "description": "A request to create a Thermocycler's deactivate lid command.", - "type": "object", + "StatusBarAnimation": { + "description": "Status Bar animation options.", + "enum": ["idle", "confirm", "updating", "disco", "off"], + "title": "StatusBarAnimation", + "type": "string" + }, + "Submerge": { + "description": "Shared properties for the submerge function before aspiration or dispense.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "thermocycler/deactivateLid", - "enum": ["thermocycler/deactivateLid"], - "type": "string" + "delay": { + "$ref": "#/$defs/DelayProperties", + "description": "Delay settings for submerge." }, - "params": { - "$ref": "#/definitions/DeactivateLidParams" + "offset": { + "$ref": "#/$defs/Coordinate", + "description": "Relative offset for submerge." }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ + "positionReference": { + "$ref": "#/$defs/PositionReference", + "description": "Position reference for submerge." + }, + "speed": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, { - "$ref": "#/definitions/CommandIntent" + "minimum": 0.0, + "type": "number" } - ] - }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", - "type": "string" + ], + "description": "Speed of submerging, in millimeters per second.", + "title": "Speed" } }, - "required": ["params"] + "required": ["positionReference", "offset", "speed", "delay"], + "title": "Submerge", + "type": "object" }, - "opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidParams": { - "title": "OpenLidParams", - "description": "Input parameters to open a Thermocycler's lid.", - "type": "object", - "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Thermocycler.", - "type": "string" - } - }, - "required": ["moduleId"] + "TipPresenceStatus": { + "description": "Tip presence status reported by a pipette.", + "enum": ["present", "absent", "unknown"], + "title": "TipPresenceStatus", + "type": "string" }, - "opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidCreate": { - "title": "OpenLidCreate", - "description": "A request to open a Thermocycler's lid.", - "type": "object", + "TouchTipCreate": { + "description": "Touch tip command creation request model.", "properties": { "commandType": { + "const": "touchTip", + "default": "touchTip", + "enum": ["touchTip"], "title": "Commandtype", - "default": "thermocycler/openLid", - "enum": ["thermocycler/openLid"], "type": "string" }, - "params": { - "$ref": "#/definitions/opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/TouchTipParams" } }, - "required": ["params"] - }, - "opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidParams": { - "title": "CloseLidParams", - "description": "Input parameters to close a Thermocycler's lid.", - "type": "object", - "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Thermocycler.", - "type": "string" - } - }, - "required": ["moduleId"] + "required": ["params"], + "title": "TouchTipCreate", + "type": "object" }, - "opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidCreate": { - "title": "CloseLidCreate", - "description": "A request to close a Thermocycler's lid.", - "type": "object", + "TouchTipParams": { + "description": "Payload needed to touch a pipette tip the sides of a specific well.", "properties": { - "commandType": { - "title": "Commandtype", - "default": "thermocycler/closeLid", - "enum": ["thermocycler/closeLid"], + "labwareId": { + "description": "Identifier of labware to use.", + "title": "Labwareid", "type": "string" }, - "params": { - "$ref": "#/definitions/opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidParams" - }, - "intent": { - "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "mmFromEdge": { + "description": "Offset away from the the well edge, in millimeters.Incompatible when a radius is included as a non 1.0 value.", + "title": "Mmfromedge", + "type": "number" }, - "key": { - "title": "Key", - "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" - } - }, - "required": ["params"] - }, - "RunProfileStepParams": { - "title": "RunProfileStepParams", - "description": "Input parameters for an individual Thermocycler profile step.", - "type": "object", - "properties": { - "celsius": { - "title": "Celsius", - "description": "Target temperature in \u00b0C.", + }, + "radius": { + "default": 1.0, + "description": "The proportion of the target well's radius the pipette tip will move towards.", + "title": "Radius", "type": "number" }, - "holdSeconds": { - "title": "Holdseconds", - "description": "Time to hold target temperature at in seconds.", + "speed": { + "description": "Override the travel speed in mm/s. This controls the straight linear speed of motion.", + "title": "Speed", "type": "number" + }, + "wellLocation": { + "$ref": "#/$defs/WellLocation", + "description": "Relative well location at which to perform the operation" + }, + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", + "type": "string" } }, - "required": ["celsius", "holdSeconds"] + "required": ["labwareId", "wellName", "pipetteId"], + "title": "TouchTipParams", + "type": "object" }, - "RunProfileParams": { - "title": "RunProfileParams", - "description": "Input parameters to run a Thermocycler profile.", - "type": "object", + "TouchTipProperties": { + "description": "Shared properties for the touch-tip function.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Thermocycler.", - "type": "string" - }, - "profile": { - "title": "Profile", - "description": "Array of profile steps with target temperature and temperature hold time.", - "type": "array", - "items": { - "$ref": "#/definitions/RunProfileStepParams" - } + "enable": { + "description": "Whether touch-tip is enabled.", + "title": "Enable", + "type": "boolean" }, - "blockMaxVolumeUl": { - "title": "Blockmaxvolumeul", - "description": "Amount of liquid in uL of the most-full well in labware loaded onto the thermocycler.", - "type": "number" + "params": { + "$ref": "#/$defs/LiquidClassTouchTipParams", + "description": "Parameters for the touch-tip function.", + "title": "Params" } }, - "required": ["moduleId", "profile"] + "required": ["enable"], + "title": "TouchTipProperties", + "type": "object" }, - "RunProfileCreate": { - "title": "RunProfileCreate", - "description": "A request to execute a Thermocycler profile run.", - "type": "object", + "TryLiquidProbeCreate": { + "description": "The request model for a `tryLiquidProbe` command.", "properties": { "commandType": { + "const": "tryLiquidProbe", + "default": "tryLiquidProbe", + "enum": ["tryLiquidProbe"], "title": "Commandtype", - "default": "thermocycler/runProfile", - "enum": ["thermocycler/runProfile"], "type": "string" }, - "params": { - "$ref": "#/definitions/RunProfileParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" - } - }, - "required": ["params"] - }, - "ProfileStep": { - "title": "ProfileStep", - "description": "An individual step in a Thermocycler extended profile.", - "type": "object", - "properties": { - "celsius": { - "title": "Celsius", - "description": "Target temperature in \u00b0C.", - "type": "number" - }, - "holdSeconds": { - "title": "Holdseconds", - "description": "Time to hold target temperature in seconds.", - "type": "number" - } - }, - "required": ["celsius", "holdSeconds"] - }, - "ProfileCycle": { - "title": "ProfileCycle", - "description": "An individual cycle in a Thermocycler extended profile.", - "type": "object", - "properties": { - "steps": { - "title": "Steps", - "description": "Steps to repeat.", - "type": "array", - "items": { - "$ref": "#/definitions/ProfileStep" - } }, - "repetitions": { - "title": "Repetitions", - "description": "Number of times to repeat the steps.", - "type": "integer" + "params": { + "$ref": "#/$defs/TryLiquidProbeParams" } }, - "required": ["steps", "repetitions"] + "required": ["params"], + "title": "TryLiquidProbeCreate", + "type": "object" }, - "RunExtendedProfileParams": { - "title": "RunExtendedProfileParams", - "description": "Input parameters for an individual Thermocycler profile step.", - "type": "object", + "TryLiquidProbeParams": { + "description": "Parameters required for a `tryLiquidProbe` command.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Thermocycler.", + "labwareId": { + "description": "Identifier of labware to use.", + "title": "Labwareid", "type": "string" }, - "profileElements": { - "title": "Profileelements", - "description": "Elements of the profile. Each can be either a step or a cycle.", - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/definitions/ProfileStep" - }, - { - "$ref": "#/definitions/ProfileCycle" - } - ] - } + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" }, - "blockMaxVolumeUl": { - "title": "Blockmaxvolumeul", - "description": "Amount of liquid in uL of the most-full well in labware loaded onto the thermocycler.", - "type": "number" + "wellLocation": { + "$ref": "#/$defs/WellLocation", + "description": "Relative well location at which to perform the operation" + }, + "wellName": { + "description": "Name of well to use in labware.", + "title": "Wellname", + "type": "string" } }, - "required": ["moduleId", "profileElements"] + "required": ["labwareId", "wellName", "pipetteId"], + "title": "TryLiquidProbeParams", + "type": "object" }, - "RunExtendedProfileCreate": { - "title": "RunExtendedProfileCreate", - "description": "A request to execute a Thermocycler profile run.", - "type": "object", + "UnsafeBlowOutInPlaceCreate": { + "description": "UnsafeBlowOutInPlace command request model.", "properties": { "commandType": { + "const": "unsafe/blowOutInPlace", + "default": "unsafe/blowOutInPlace", + "enum": ["unsafe/blowOutInPlace"], "title": "Commandtype", - "default": "thermocycler/runExtendedProfile", - "enum": ["thermocycler/runExtendedProfile"], "type": "string" }, - "params": { - "$ref": "#/definitions/RunExtendedProfileParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/UnsafeBlowOutInPlaceParams" } }, - "required": ["params"] + "required": ["params"], + "title": "UnsafeBlowOutInPlaceCreate", + "type": "object" }, - "opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidParams": { - "title": "CloseLidParams", - "description": "Input parameters to close the lid on an absorbance reading.", - "type": "object", + "UnsafeBlowOutInPlaceParams": { + "description": "Payload required to blow-out in place while position is unknown.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the absorbance reader.", + "flowRate": { + "description": "Speed in \u00b5L/s configured for the pipette", + "exclusiveMinimum": 0.0, + "title": "Flowrate", + "type": "number" + }, + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" } }, - "required": ["moduleId"] + "required": ["flowRate", "pipetteId"], + "title": "UnsafeBlowOutInPlaceParams", + "type": "object" }, - "opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidCreate": { - "title": "CloseLidCreate", - "description": "A request to execute an Absorbance Reader close lid command.", - "type": "object", + "UnsafeDropTipInPlaceCreate": { + "description": "Drop tip in place command creation request model.", "properties": { "commandType": { + "const": "unsafe/dropTipInPlace", + "default": "unsafe/dropTipInPlace", + "enum": ["unsafe/dropTipInPlace"], "title": "Commandtype", - "default": "absorbanceReader/closeLid", - "enum": ["absorbanceReader/closeLid"], "type": "string" }, - "params": { - "$ref": "#/definitions/opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/UnsafeDropTipInPlaceParams" } }, - "required": ["params"] + "required": ["params"], + "title": "UnsafeDropTipInPlaceCreate", + "type": "object" }, - "opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidParams": { - "title": "OpenLidParams", - "description": "Input parameters to open the lid on an absorbance reading.", - "type": "object", + "UnsafeDropTipInPlaceParams": { + "description": "Payload required to drop a tip in place even if the plunger position is not known.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the absorbance reader.", + "homeAfter": { + "description": "Whether to home this pipette's plunger after dropping the tip. You should normally leave this unspecified to let the robot choose a safe default depending on its hardware.", + "title": "Homeafter", + "type": "boolean" + }, + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", "type": "string" } }, - "required": ["moduleId"] + "required": ["pipetteId"], + "title": "UnsafeDropTipInPlaceParams", + "type": "object" }, - "opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidCreate": { - "title": "OpenLidCreate", - "description": "A request to execute an Absorbance Reader open lid command.", - "type": "object", + "UnsafeEngageAxesCreate": { + "description": "UnsafeEngageAxes command request model.", "properties": { "commandType": { + "const": "unsafe/engageAxes", + "default": "unsafe/engageAxes", + "enum": ["unsafe/engageAxes"], "title": "Commandtype", - "default": "absorbanceReader/openLid", - "enum": ["absorbanceReader/openLid"], "type": "string" }, - "params": { - "$ref": "#/definitions/opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/UnsafeEngageAxesParams" } }, - "required": ["params"] + "required": ["params"], + "title": "UnsafeEngageAxesCreate", + "type": "object" }, - "InitializeParams": { - "title": "InitializeParams", - "description": "Input parameters to initialize an absorbance reading.", - "type": "object", + "UnsafeEngageAxesParams": { + "description": "Payload required for an UnsafeEngageAxes command.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the absorbance reader.", - "type": "string" - }, - "measureMode": { - "title": "Measuremode", - "description": "Initialize single or multi measurement mode.", - "enum": ["single", "multi"], - "type": "string" - }, - "sampleWavelengths": { - "title": "Samplewavelengths", - "description": "Sample wavelengths in nm.", - "type": "array", + "axes": { + "description": "The axes for which to enable.", "items": { - "type": "integer" - } - }, - "referenceWavelength": { - "title": "Referencewavelength", - "description": "Optional reference wavelength in nm.", - "type": "integer" + "$ref": "#/$defs/MotorAxis" + }, + "title": "Axes", + "type": "array" } }, - "required": ["moduleId", "measureMode", "sampleWavelengths"] + "required": ["axes"], + "title": "UnsafeEngageAxesParams", + "type": "object" }, - "InitializeCreate": { - "title": "InitializeCreate", - "description": "A request to execute an Absorbance Reader measurement.", - "type": "object", + "UnsafePlaceLabwareCreate": { + "description": "UnsafePlaceLabware command request model.", "properties": { "commandType": { + "const": "unsafe/placeLabware", + "default": "unsafe/placeLabware", + "enum": ["unsafe/placeLabware"], "title": "Commandtype", - "default": "absorbanceReader/initialize", - "enum": ["absorbanceReader/initialize"], "type": "string" }, - "params": { - "$ref": "#/definitions/InitializeParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/UnsafePlaceLabwareParams" } }, - "required": ["params"] + "required": ["params"], + "title": "UnsafePlaceLabwareCreate", + "type": "object" }, - "ReadAbsorbanceParams": { - "title": "ReadAbsorbanceParams", - "description": "Input parameters for an absorbance reading.", - "type": "object", + "UnsafePlaceLabwareParams": { + "description": "Payload required for an UnsafePlaceLabware command.", "properties": { - "moduleId": { - "title": "Moduleid", - "description": "Unique ID of the Absorbance Reader.", + "labwareURI": { + "description": "Labware URI for labware.", + "title": "Labwareuri", "type": "string" }, - "fileName": { - "title": "Filename", - "description": "Optional file name to use when storing the results of a measurement.", - "type": "string" + "location": { + "anyOf": [ + { + "$ref": "#/$defs/DeckSlotLocation" + }, + { + "$ref": "#/$defs/ModuleLocation" + }, + { + "$ref": "#/$defs/OnLabwareLocation" + }, + { + "$ref": "#/$defs/AddressableAreaLocation" + } + ], + "description": "Where to place the labware.", + "title": "Location" } }, - "required": ["moduleId"] + "required": ["labwareURI", "location"], + "title": "UnsafePlaceLabwareParams", + "type": "object" }, - "ReadAbsorbanceCreate": { - "title": "ReadAbsorbanceCreate", - "description": "A request to execute an Absorbance Reader measurement.", - "type": "object", + "UnsafeUngripLabwareCreate": { + "description": "UnsafeEngageAxes command request model.", "properties": { "commandType": { + "const": "unsafe/ungripLabware", + "default": "unsafe/ungripLabware", + "enum": ["unsafe/ungripLabware"], "title": "Commandtype", - "default": "absorbanceReader/read", - "enum": ["absorbanceReader/read"], + "type": "string" + }, + "intent": { + "$ref": "#/$defs/CommandIntent", + "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", + "title": "Intent" + }, + "key": { + "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" }, "params": { - "$ref": "#/definitions/ReadAbsorbanceParams" + "$ref": "#/$defs/UnsafeUngripLabwareParams" + } + }, + "required": ["params"], + "title": "UnsafeUngripLabwareCreate", + "type": "object" + }, + "UnsafeUngripLabwareParams": { + "description": "Payload required for an UngripLabware command.", + "properties": {}, + "title": "UnsafeUngripLabwareParams", + "type": "object" + }, + "UpdatePositionEstimatorsCreate": { + "description": "UpdatePositionEstimators command request model.", + "properties": { + "commandType": { + "const": "unsafe/updatePositionEstimators", + "default": "unsafe/updatePositionEstimators", + "enum": ["unsafe/updatePositionEstimators"], + "title": "Commandtype", + "type": "string" }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/UpdatePositionEstimatorsParams" } }, - "required": ["params"] + "required": ["params"], + "title": "UpdatePositionEstimatorsCreate", + "type": "object" }, - "CalibrateGripperParamsJaw": { - "title": "CalibrateGripperParamsJaw", - "description": "An enumeration.", - "enum": ["front", "rear"] + "UpdatePositionEstimatorsParams": { + "description": "Payload required for an UpdatePositionEstimators command.", + "properties": { + "axes": { + "description": "The axes for which to update the position estimators. Any axes that are not physically present will be ignored.", + "items": { + "$ref": "#/$defs/MotorAxis" + }, + "title": "Axes", + "type": "array" + } + }, + "required": ["axes"], + "title": "UpdatePositionEstimatorsParams", + "type": "object" }, "Vec3f": { - "title": "Vec3f", "description": "A 3D vector of floats.", - "type": "object", "properties": { "x": { "title": "X", "type": "number" }, "y": { - "title": "Y", - "type": "number" - }, - "z": { - "title": "Z", - "type": "number" - } - }, - "required": ["x", "y", "z"] - }, - "CalibrateGripperParams": { - "title": "CalibrateGripperParams", - "description": "Parameters for a `calibrateGripper` command.", - "type": "object", - "properties": { - "jaw": { - "description": "Which of the gripper's jaws to use to measure its offset. The robot will assume that a human operator has already attached the capacitive probe to the jaw and none is attached to the other jaw.", - "allOf": [ - { - "$ref": "#/definitions/CalibrateGripperParamsJaw" - } - ] + "title": "Y", + "type": "number" }, - "otherJawOffset": { - "title": "Otherjawoffset", - "description": "If an offset for the other probe is already found, then specifying it here will enable the CalibrateGripper command to complete the calibration process by calculating the total offset and saving it to disk. If this param is not specified then the command will only find and return the offset for the specified probe.", - "allOf": [ - { - "$ref": "#/definitions/Vec3f" - } - ] + "z": { + "title": "Z", + "type": "number" } }, - "required": ["jaw"] + "required": ["x", "y", "z"], + "title": "Vec3f", + "type": "object" }, - "CalibrateGripperCreate": { - "title": "CalibrateGripperCreate", - "description": "A request to create a `calibrateGripper` command.", - "type": "object", + "VerifyTipPresenceCreate": { + "description": "VerifyTipPresence command creation request model.", "properties": { "commandType": { + "const": "verifyTipPresence", + "default": "verifyTipPresence", + "enum": ["verifyTipPresence"], "title": "Commandtype", - "default": "calibration/calibrateGripper", - "enum": ["calibration/calibrateGripper"], "type": "string" }, - "params": { - "$ref": "#/definitions/CalibrateGripperParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/VerifyTipPresenceParams" } }, - "required": ["params"] + "required": ["params"], + "title": "VerifyTipPresenceCreate", + "type": "object" }, - "CalibratePipetteParams": { - "title": "CalibratePipetteParams", - "description": "Payload required to calibrate-pipette.", - "type": "object", + "VerifyTipPresenceParams": { + "description": "Payload required for a VerifyTipPresence command.", "properties": { - "mount": { - "description": "Instrument mount to calibrate.", - "allOf": [ - { - "$ref": "#/definitions/MountType" - } - ] + "expectedState": { + "$ref": "#/$defs/TipPresenceStatus", + "description": "The expected tip presence status on the pipette." + }, + "followSingularSensor": { + "$ref": "#/$defs/InstrumentSensorId", + "description": "The sensor id to follow if the other can be ignored.", + "title": "Followsingularsensor" + }, + "pipetteId": { + "description": "Identifier of pipette to use for liquid handling.", + "title": "Pipetteid", + "type": "string" } }, - "required": ["mount"] + "required": ["pipetteId", "expectedState"], + "title": "VerifyTipPresenceParams", + "type": "object" }, - "CalibratePipetteCreate": { - "title": "CalibratePipetteCreate", - "description": "Create calibrate-pipette command request model.", - "type": "object", + "WaitForBlockTemperatureCreate": { + "description": "A request to create Thermocycler's wait for block temperature command.", "properties": { "commandType": { + "const": "thermocycler/waitForBlockTemperature", + "default": "thermocycler/waitForBlockTemperature", + "enum": ["thermocycler/waitForBlockTemperature"], "title": "Commandtype", - "default": "calibration/calibratePipette", - "enum": ["calibration/calibratePipette"], "type": "string" }, - "params": { - "$ref": "#/definitions/CalibratePipetteParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/WaitForBlockTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "WaitForBlockTemperatureCreate", + "type": "object" }, - "CalibrateModuleParams": { - "title": "CalibrateModuleParams", - "description": "Payload required to calibrate-module.", - "type": "object", + "WaitForBlockTemperatureParams": { + "description": "Input parameters to wait for Thermocycler's target block temperature.", "properties": { "moduleId": { + "description": "Unique ID of the Thermocycler Module.", "title": "Moduleid", - "description": "The unique id of module to calibrate.", - "type": "string" - }, - "labwareId": { - "title": "Labwareid", - "description": "The unique id of module calibration adapter labware.", "type": "string" - }, - "mount": { - "description": "The instrument mount used to calibrate the module.", - "allOf": [ - { - "$ref": "#/definitions/MountType" - } - ] } }, - "required": ["moduleId", "labwareId", "mount"] + "required": ["moduleId"], + "title": "WaitForBlockTemperatureParams", + "type": "object" }, - "CalibrateModuleCreate": { - "title": "CalibrateModuleCreate", - "description": "Create calibrate-module command request model.", - "type": "object", + "WaitForDurationCreate": { + "description": "Wait for duration command request model.", "properties": { "commandType": { + "const": "waitForDuration", + "default": "waitForDuration", + "enum": ["waitForDuration"], "title": "Commandtype", - "default": "calibration/calibrateModule", - "enum": ["calibration/calibrateModule"], "type": "string" }, - "params": { - "$ref": "#/definitions/CalibrateModuleParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/WaitForDurationParams" } }, - "required": ["params"] - }, - "MaintenancePosition": { - "title": "MaintenancePosition", - "description": "Maintenance position options.", - "enum": ["attachPlate", "attachInstrument"] + "required": ["params"], + "title": "WaitForDurationCreate", + "type": "object" }, - "MoveToMaintenancePositionParams": { - "title": "MoveToMaintenancePositionParams", - "description": "Calibration set up position command parameters.", - "type": "object", + "WaitForDurationParams": { + "description": "Payload required to pause the protocol.", "properties": { - "mount": { - "description": "Gantry mount to move maintenance position.", - "allOf": [ - { - "$ref": "#/definitions/MountType" - } - ] + "message": { + "description": "A user-facing message associated with the pause", + "title": "Message", + "type": "string" }, - "maintenancePosition": { - "description": "The position the gantry mount needs to move to.", - "default": "attachInstrument", - "allOf": [ - { - "$ref": "#/definitions/MaintenancePosition" - } - ] + "seconds": { + "description": "Duration, in seconds, to wait for.", + "title": "Seconds", + "type": "number" } }, - "required": ["mount"] + "required": ["seconds"], + "title": "WaitForDurationParams", + "type": "object" }, - "MoveToMaintenancePositionCreate": { - "title": "MoveToMaintenancePositionCreate", - "description": "Calibration set up position command creation request model.", - "type": "object", + "WaitForLidTemperatureCreate": { + "description": "A request to create Thermocycler's wait for lid temperature command.", "properties": { "commandType": { + "const": "thermocycler/waitForLidTemperature", + "default": "thermocycler/waitForLidTemperature", + "enum": ["thermocycler/waitForLidTemperature"], "title": "Commandtype", - "default": "calibration/moveToMaintenancePosition", - "enum": ["calibration/moveToMaintenancePosition"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveToMaintenancePositionParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/WaitForLidTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "WaitForLidTemperatureCreate", + "type": "object" }, - "UnsafeBlowOutInPlaceParams": { - "title": "UnsafeBlowOutInPlaceParams", - "description": "Payload required to blow-out in place while position is unknown.", - "type": "object", + "WaitForLidTemperatureParams": { + "description": "Input parameters to wait for Thermocycler's lid temperature.", "properties": { - "flowRate": { - "title": "Flowrate", - "description": "Speed in \u00b5L/s configured for the pipette", - "exclusiveMinimum": 0, - "type": "number" - }, - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", + "moduleId": { + "description": "Unique ID of the Thermocycler Module.", + "title": "Moduleid", "type": "string" } }, - "required": ["flowRate", "pipetteId"] + "required": ["moduleId"], + "title": "WaitForLidTemperatureParams", + "type": "object" }, - "UnsafeBlowOutInPlaceCreate": { - "title": "UnsafeBlowOutInPlaceCreate", - "description": "UnsafeBlowOutInPlace command request model.", - "type": "object", + "WaitForResumeCreate": { + "description": "Wait for resume command request model.", "properties": { "commandType": { + "default": "waitForResume", + "enum": ["waitForResume", "pause"], "title": "Commandtype", - "default": "unsafe/blowOutInPlace", - "enum": ["unsafe/blowOutInPlace"], "type": "string" }, - "params": { - "$ref": "#/definitions/UnsafeBlowOutInPlaceParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/WaitForResumeParams" } }, - "required": ["params"] + "required": ["params"], + "title": "WaitForResumeCreate", + "type": "object" }, - "UnsafeDropTipInPlaceParams": { - "title": "UnsafeDropTipInPlaceParams", - "description": "Payload required to drop a tip in place even if the plunger position is not known.", - "type": "object", + "WaitForResumeParams": { + "description": "Payload required to pause the protocol.", "properties": { - "pipetteId": { - "title": "Pipetteid", - "description": "Identifier of pipette to use for liquid handling.", + "message": { + "description": "A user-facing message associated with the pause", + "title": "Message", "type": "string" + } + }, + "title": "WaitForResumeParams", + "type": "object" + }, + "WellLocation": { + "description": "A relative location in reference to a well's location.", + "properties": { + "offset": { + "$ref": "#/$defs/WellOffset" }, - "homeAfter": { - "title": "Homeafter", - "description": "Whether to home this pipette's plunger after dropping the tip. You should normally leave this unspecified to let the robot choose a safe default depending on its hardware.", - "type": "boolean" + "origin": { + "$ref": "#/$defs/WellOrigin", + "default": "top" + }, + "volumeOffset": { + "default": 0.0, + "description": "A volume of liquid, in \u00b5L, to offset the z-axis offset.", + "title": "Volumeoffset", + "type": "number" } }, - "required": ["pipetteId"] + "title": "WellLocation", + "type": "object" }, - "UnsafeDropTipInPlaceCreate": { - "title": "UnsafeDropTipInPlaceCreate", - "description": "Drop tip in place command creation request model.", - "type": "object", + "WellOffset": { + "description": "An offset vector in (x, y, z).", + "properties": { + "x": { + "default": 0, + "title": "X", + "type": "number" + }, + "y": { + "default": 0, + "title": "Y", + "type": "number" + }, + "z": { + "default": 0, + "title": "Z", + "type": "number" + } + }, + "title": "WellOffset", + "type": "object" + }, + "WellOrigin": { + "description": "Origin of WellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well\n MENISCUS: the meniscus-center of the well", + "enum": ["top", "bottom", "center", "meniscus"], + "title": "WellOrigin", + "type": "string" + }, + "closeGripperJawCreate": { + "description": "closeGripperJaw command request model.", "properties": { "commandType": { + "const": "robot/closeGripperJaw", + "default": "robot/closeGripperJaw", + "enum": ["robot/closeGripperJaw"], "title": "Commandtype", - "default": "unsafe/dropTipInPlace", - "enum": ["unsafe/dropTipInPlace"], "type": "string" }, - "params": { - "$ref": "#/definitions/UnsafeDropTipInPlaceParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/closeGripperJawParams" } }, - "required": ["params"] - }, - "UpdatePositionEstimatorsParams": { - "title": "UpdatePositionEstimatorsParams", - "description": "Payload required for an UpdatePositionEstimators command.", - "type": "object", + "required": ["params"], + "title": "closeGripperJawCreate", + "type": "object" + }, + "closeGripperJawParams": { + "description": "Payload required to close a gripper.", "properties": { - "axes": { - "description": "The axes for which to update the position estimators. Any axes that are not physically present will be ignored.", - "type": "array", - "items": { - "$ref": "#/definitions/MotorAxis" - } + "force": { + "description": "The force the gripper should use to hold the jaws, falls to default if none is provided.", + "title": "Force", + "type": "number" } }, - "required": ["axes"] + "title": "closeGripperJawParams", + "type": "object" }, - "UpdatePositionEstimatorsCreate": { - "title": "UpdatePositionEstimatorsCreate", - "description": "UpdatePositionEstimators command request model.", - "type": "object", + "openGripperJawCreate": { + "description": "openGripperJaw command request model.", "properties": { "commandType": { + "const": "robot/openGripperJaw", + "default": "robot/openGripperJaw", + "enum": ["robot/openGripperJaw"], "title": "Commandtype", - "default": "unsafe/updatePositionEstimators", - "enum": ["unsafe/updatePositionEstimators"], "type": "string" }, - "params": { - "$ref": "#/definitions/UpdatePositionEstimatorsParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/openGripperJawParams" } }, - "required": ["params"] + "required": ["params"], + "title": "openGripperJawCreate", + "type": "object" }, - "UnsafeEngageAxesParams": { - "title": "UnsafeEngageAxesParams", - "description": "Payload required for an UnsafeEngageAxes command.", - "type": "object", - "properties": { - "axes": { - "description": "The axes for which to enable.", - "type": "array", - "items": { - "$ref": "#/definitions/MotorAxis" - } - } - }, - "required": ["axes"] + "openGripperJawParams": { + "description": "Payload required to release a gripper.", + "properties": {}, + "title": "openGripperJawParams", + "type": "object" }, - "UnsafeEngageAxesCreate": { - "title": "UnsafeEngageAxesCreate", - "description": "UnsafeEngageAxes command request model.", - "type": "object", + "opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidCreate": { + "description": "A request to execute an Absorbance Reader close lid command.", "properties": { "commandType": { + "const": "absorbanceReader/closeLid", + "default": "absorbanceReader/closeLid", + "enum": ["absorbanceReader/closeLid"], "title": "Commandtype", - "default": "unsafe/engageAxes", - "enum": ["unsafe/engageAxes"], "type": "string" }, - "params": { - "$ref": "#/definitions/UnsafeEngageAxesParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidParams" } }, - "required": ["params"] + "required": ["params"], + "title": "CloseLidCreate", + "type": "object" }, - "UnsafeUngripLabwareParams": { - "title": "UnsafeUngripLabwareParams", - "description": "Payload required for an UngripLabware command.", - "type": "object", - "properties": {} + "opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidParams": { + "description": "Input parameters to close the lid on an absorbance reading.", + "properties": { + "moduleId": { + "description": "Unique ID of the absorbance reader.", + "title": "Moduleid", + "type": "string" + } + }, + "required": ["moduleId"], + "title": "CloseLidParams", + "type": "object" }, - "UnsafeUngripLabwareCreate": { - "title": "UnsafeUngripLabwareCreate", - "description": "UnsafeEngageAxes command request model.", - "type": "object", + "opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidCreate": { + "description": "A request to execute an Absorbance Reader open lid command.", "properties": { "commandType": { + "const": "absorbanceReader/openLid", + "default": "absorbanceReader/openLid", + "enum": ["absorbanceReader/openLid"], "title": "Commandtype", - "default": "unsafe/ungripLabware", - "enum": ["unsafe/ungripLabware"], "type": "string" }, - "params": { - "$ref": "#/definitions/UnsafeUngripLabwareParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidParams" } }, - "required": ["params"] + "required": ["params"], + "title": "OpenLidCreate", + "type": "object" }, - "UnsafePlaceLabwareParams": { - "title": "UnsafePlaceLabwareParams", - "description": "Payload required for an UnsafePlaceLabware command.", - "type": "object", + "opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidParams": { + "description": "Input parameters to open the lid on an absorbance reading.", "properties": { - "labwareURI": { - "title": "Labwareuri", - "description": "Labware URI for labware.", + "moduleId": { + "description": "Unique ID of the absorbance reader.", + "title": "Moduleid", "type": "string" - }, - "location": { - "title": "Location", - "description": "Where to place the labware.", - "anyOf": [ - { - "$ref": "#/definitions/DeckSlotLocation" - }, - { - "$ref": "#/definitions/ModuleLocation" - }, - { - "$ref": "#/definitions/OnLabwareLocation" - }, - { - "$ref": "#/definitions/AddressableAreaLocation" - } - ] } }, - "required": ["labwareURI", "location"] + "required": ["moduleId"], + "title": "OpenLidParams", + "type": "object" }, - "UnsafePlaceLabwareCreate": { - "title": "UnsafePlaceLabwareCreate", - "description": "UnsafePlaceLabware command request model.", - "type": "object", + "opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureCreate": { + "description": "A request to create a Heater-Shaker's set temperature command.", "properties": { "commandType": { + "const": "heaterShaker/setTargetTemperature", + "default": "heaterShaker/setTargetTemperature", + "enum": ["heaterShaker/setTargetTemperature"], "title": "Commandtype", - "default": "unsafe/placeLabware", - "enum": ["unsafe/placeLabware"], "type": "string" }, - "params": { - "$ref": "#/definitions/UnsafePlaceLabwareParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "SetTargetTemperatureCreate", + "type": "object" }, - "MoveAxesRelativeParams": { - "title": "MoveAxesRelativeParams", - "description": "Payload required to move axes relative to position.", - "type": "object", + "opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureParams": { + "description": "Input parameters to set a Heater-Shaker's target temperature.", "properties": { - "axis_map": { - "title": "Axis Map", - "description": "A dictionary mapping axes to relative movements in mm.", - "type": "object", - "additionalProperties": { - "type": "number" - } - }, - "speed": { - "title": "Speed", - "description": "The max velocity to move the axes at. Will fall to hardware defaults if none provided.", + "celsius": { + "description": "Target temperature in \u00b0C.", + "title": "Celsius", "type": "number" + }, + "moduleId": { + "description": "Unique ID of the Heater-Shaker Module.", + "title": "Moduleid", + "type": "string" } }, - "required": ["axis_map"] + "required": ["moduleId", "celsius"], + "title": "SetTargetTemperatureParams", + "type": "object" }, - "MoveAxesRelativeCreate": { - "title": "MoveAxesRelativeCreate", - "description": "MoveAxesRelative command request model.", - "type": "object", + "opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureCreate": { + "description": "A request to create a Heater-Shaker's wait for temperature command.", "properties": { "commandType": { + "const": "heaterShaker/waitForTemperature", + "default": "heaterShaker/waitForTemperature", + "enum": ["heaterShaker/waitForTemperature"], "title": "Commandtype", - "default": "robot/moveAxesRelative", - "enum": ["robot/moveAxesRelative"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveAxesRelativeParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "WaitForTemperatureCreate", + "type": "object" }, - "MoveAxesToParams": { - "title": "MoveAxesToParams", - "description": "Payload required to move axes to absolute position.", - "type": "object", + "opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureParams": { + "description": "Input parameters to wait for a Heater-Shaker's target temperature.", "properties": { - "axis_map": { - "title": "Axis Map", - "description": "The specified axes to move to an absolute deck position with.", - "type": "object", - "additionalProperties": { - "type": "number" - } - }, - "critical_point": { - "title": "Critical Point", - "description": "The critical point to move the mount with.", - "type": "object", - "additionalProperties": { - "type": "number" - } - }, - "speed": { - "title": "Speed", - "description": "The max velocity to move the axes at. Will fall to hardware defaults if none provided.", + "celsius": { + "description": "Target temperature in \u00b0C. If not specified, will default to the module's target temperature. Specifying a celsius parameter other than the target temperature could lead to unpredictable behavior and hence is not recommended for use. This parameter can be removed in a future version without prior notice.", + "title": "Celsius", "type": "number" + }, + "moduleId": { + "description": "Unique ID of the Heater-Shaker Module.", + "title": "Moduleid", + "type": "string" } }, - "required": ["axis_map"] + "required": ["moduleId"], + "title": "WaitForTemperatureParams", + "type": "object" }, - "MoveAxesToCreate": { - "title": "MoveAxesToCreate", - "description": "MoveAxesTo command request model.", - "type": "object", + "opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureCreate": { + "description": "A request to create a Temperature Module's set temperature command.", "properties": { "commandType": { + "const": "temperatureModule/setTargetTemperature", + "default": "temperatureModule/setTargetTemperature", + "enum": ["temperatureModule/setTargetTemperature"], "title": "Commandtype", - "default": "robot/moveAxesTo", - "enum": ["robot/moveAxesTo"], "type": "string" }, - "params": { - "$ref": "#/definitions/MoveAxesToParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "SetTargetTemperatureCreate", + "type": "object" }, - "MoveToParams": { - "title": "MoveToParams", - "description": "Payload required to move to a destination position.", - "type": "object", + "opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureParams": { + "description": "Input parameters to set a Temperature Module's target temperature.", "properties": { - "mount": { - "description": "The mount to move to the destination point.", - "allOf": [ - { - "$ref": "#/definitions/MountType" - } - ] - }, - "destination": { - "title": "Destination", - "description": "X, Y and Z coordinates in mm from deck's origin location (left-front-bottom corner of work space)", - "allOf": [ - { - "$ref": "#/definitions/DeckPoint" - } - ] - }, - "speed": { - "title": "Speed", - "description": "The max velocity to move the axes at. Will fall to hardware defaults if none provided.", + "celsius": { + "description": "Target temperature in \u00b0C.", + "title": "Celsius", "type": "number" + }, + "moduleId": { + "description": "Unique ID of the Temperature Module.", + "title": "Moduleid", + "type": "string" } }, - "required": ["mount", "destination"] + "required": ["moduleId", "celsius"], + "title": "SetTargetTemperatureParams", + "type": "object" }, - "MoveToCreate": { - "title": "MoveToCreate", - "description": "MoveTo command request model.", - "type": "object", + "opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureCreate": { + "description": "A request to create a Temperature Module's wait for temperature command.", "properties": { "commandType": { - "title": "Commandtype", - "default": "robot/moveTo", - "enum": ["robot/moveTo"], + "const": "temperatureModule/waitForTemperature", + "default": "temperatureModule/waitForTemperature", + "enum": ["temperatureModule/waitForTemperature"], + "title": "Commandtype", "type": "string" }, - "params": { - "$ref": "#/definitions/MoveToParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureParams" } }, - "required": ["params"] + "required": ["params"], + "title": "WaitForTemperatureCreate", + "type": "object" }, - "openGripperJawParams": { - "title": "openGripperJawParams", - "description": "Payload required to release a gripper.", - "type": "object", - "properties": {} + "opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureParams": { + "description": "Input parameters to wait for a Temperature Module's target temperature.", + "properties": { + "celsius": { + "description": "Target temperature in \u00b0C. If not specified, will default to the module's target temperature. Specifying a celsius parameter other than the target temperature could lead to unpredictable behavior and hence is not recommended for use. This parameter can be removed in a future version without prior notice.", + "title": "Celsius", + "type": "number" + }, + "moduleId": { + "description": "Unique ID of the Temperature Module.", + "title": "Moduleid", + "type": "string" + } + }, + "required": ["moduleId"], + "title": "WaitForTemperatureParams", + "type": "object" }, - "openGripperJawCreate": { - "title": "openGripperJawCreate", - "description": "openGripperJaw command request model.", - "type": "object", + "opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidCreate": { + "description": "A request to close a Thermocycler's lid.", "properties": { "commandType": { + "const": "thermocycler/closeLid", + "default": "thermocycler/closeLid", + "enum": ["thermocycler/closeLid"], "title": "Commandtype", - "default": "robot/openGripperJaw", - "enum": ["robot/openGripperJaw"], "type": "string" }, - "params": { - "$ref": "#/definitions/openGripperJawParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", "type": "string" + }, + "params": { + "$ref": "#/$defs/opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidParams" } }, - "required": ["params"] + "required": ["params"], + "title": "CloseLidCreate", + "type": "object" }, - "closeGripperJawParams": { - "title": "closeGripperJawParams", - "description": "Payload required to close a gripper.", - "type": "object", + "opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidParams": { + "description": "Input parameters to close a Thermocycler's lid.", "properties": { - "force": { - "title": "Force", - "description": "The force the gripper should use to hold the jaws, falls to default if none is provided.", - "type": "number" + "moduleId": { + "description": "Unique ID of the Thermocycler.", + "title": "Moduleid", + "type": "string" } - } + }, + "required": ["moduleId"], + "title": "CloseLidParams", + "type": "object" }, - "closeGripperJawCreate": { - "title": "closeGripperJawCreate", - "description": "closeGripperJaw command request model.", - "type": "object", + "opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidCreate": { + "description": "A request to open a Thermocycler's lid.", "properties": { "commandType": { + "const": "thermocycler/openLid", + "default": "thermocycler/openLid", + "enum": ["thermocycler/openLid"], "title": "Commandtype", - "default": "robot/closeGripperJaw", - "enum": ["robot/closeGripperJaw"], "type": "string" }, - "params": { - "$ref": "#/definitions/closeGripperJawParams" - }, "intent": { + "$ref": "#/$defs/CommandIntent", "description": "The reason the command was added. If not specified or `protocol`, the command will be treated as part of the protocol run itself, and added to the end of the existing command queue.\n\nIf `setup`, the command will be treated as part of run setup. A setup command may only be enqueued if the run has not started.\n\nUse setup commands for activities like pre-run calibration checks and module setup, like pre-heating.", - "allOf": [ - { - "$ref": "#/definitions/CommandIntent" - } - ] + "title": "Intent" }, "key": { - "title": "Key", "description": "A key value, unique in this run, that can be used to track the same logical command across multiple runs of the same protocol. If a value is not provided, one will be generated.", + "title": "Key", + "type": "string" + }, + "params": { + "$ref": "#/$defs/opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidParams" + } + }, + "required": ["params"], + "title": "OpenLidCreate", + "type": "object" + }, + "opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidParams": { + "description": "Input parameters to open a Thermocycler's lid.", + "properties": { + "moduleId": { + "description": "Unique ID of the Thermocycler.", + "title": "Moduleid", "type": "string" } }, - "required": ["params"] + "required": ["moduleId"], + "title": "OpenLidParams", + "type": "object" } }, "$id": "opentronsCommandSchemaV11", - "$schema": "http://json-schema.org/draft-07/schema#" + "$schema": "http://json-schema.org/draft-07/schema#", + "discriminator": { + "mapping": { + "absorbanceReader/closeLid": "#/$defs/opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidCreate", + "absorbanceReader/initialize": "#/$defs/InitializeCreate", + "absorbanceReader/openLid": "#/$defs/opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidCreate", + "absorbanceReader/read": "#/$defs/ReadAbsorbanceCreate", + "airGapInPlace": "#/$defs/AirGapInPlaceCreate", + "aspirate": "#/$defs/AspirateCreate", + "aspirateInPlace": "#/$defs/AspirateInPlaceCreate", + "blowOutInPlace": "#/$defs/BlowOutInPlaceCreate", + "blowout": "#/$defs/BlowOutCreate", + "calibration/calibrateGripper": "#/$defs/CalibrateGripperCreate", + "calibration/calibrateModule": "#/$defs/CalibrateModuleCreate", + "calibration/calibratePipette": "#/$defs/CalibratePipetteCreate", + "calibration/moveToMaintenancePosition": "#/$defs/MoveToMaintenancePositionCreate", + "comment": "#/$defs/CommentCreate", + "configureForVolume": "#/$defs/ConfigureForVolumeCreate", + "configureNozzleLayout": "#/$defs/ConfigureNozzleLayoutCreate", + "custom": "#/$defs/CustomCreate", + "dispense": "#/$defs/DispenseCreate", + "dispenseInPlace": "#/$defs/DispenseInPlaceCreate", + "dropTip": "#/$defs/DropTipCreate", + "dropTipInPlace": "#/$defs/DropTipInPlaceCreate", + "getNextTip": "#/$defs/GetNextTipCreate", + "getTipPresence": "#/$defs/GetTipPresenceCreate", + "heaterShaker/closeLabwareLatch": "#/$defs/CloseLabwareLatchCreate", + "heaterShaker/deactivateHeater": "#/$defs/DeactivateHeaterCreate", + "heaterShaker/deactivateShaker": "#/$defs/DeactivateShakerCreate", + "heaterShaker/openLabwareLatch": "#/$defs/OpenLabwareLatchCreate", + "heaterShaker/setAndWaitForShakeSpeed": "#/$defs/SetAndWaitForShakeSpeedCreate", + "heaterShaker/setTargetTemperature": "#/$defs/opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureCreate", + "heaterShaker/waitForTemperature": "#/$defs/opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureCreate", + "home": "#/$defs/HomeCreate", + "liquidProbe": "#/$defs/LiquidProbeCreate", + "loadLabware": "#/$defs/LoadLabwareCreate", + "loadLiquid": "#/$defs/LoadLiquidCreate", + "loadLiquidClass": "#/$defs/LoadLiquidClassCreate", + "loadModule": "#/$defs/LoadModuleCreate", + "loadPipette": "#/$defs/LoadPipetteCreate", + "magneticModule/disengage": "#/$defs/DisengageCreate", + "magneticModule/engage": "#/$defs/EngageCreate", + "moveLabware": "#/$defs/MoveLabwareCreate", + "moveRelative": "#/$defs/MoveRelativeCreate", + "moveToAddressableArea": "#/$defs/MoveToAddressableAreaCreate", + "moveToAddressableAreaForDropTip": "#/$defs/MoveToAddressableAreaForDropTipCreate", + "moveToCoordinates": "#/$defs/MoveToCoordinatesCreate", + "moveToWell": "#/$defs/MoveToWellCreate", + "pause": "#/$defs/WaitForResumeCreate", + "pickUpTip": "#/$defs/PickUpTipCreate", + "prepareToAspirate": "#/$defs/PrepareToAspirateCreate", + "reloadLabware": "#/$defs/ReloadLabwareCreate", + "retractAxis": "#/$defs/RetractAxisCreate", + "robot/closeGripperJaw": "#/$defs/closeGripperJawCreate", + "robot/moveAxesRelative": "#/$defs/MoveAxesRelativeCreate", + "robot/moveAxesTo": "#/$defs/MoveAxesToCreate", + "robot/moveTo": "#/$defs/MoveToCreate", + "robot/openGripperJaw": "#/$defs/openGripperJawCreate", + "savePosition": "#/$defs/SavePositionCreate", + "setRailLights": "#/$defs/SetRailLightsCreate", + "setStatusBar": "#/$defs/SetStatusBarCreate", + "temperatureModule/deactivate": "#/$defs/DeactivateTemperatureCreate", + "temperatureModule/setTargetTemperature": "#/$defs/opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureCreate", + "temperatureModule/waitForTemperature": "#/$defs/opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureCreate", + "thermocycler/closeLid": "#/$defs/opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidCreate", + "thermocycler/deactivateBlock": "#/$defs/DeactivateBlockCreate", + "thermocycler/deactivateLid": "#/$defs/DeactivateLidCreate", + "thermocycler/openLid": "#/$defs/opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidCreate", + "thermocycler/runExtendedProfile": "#/$defs/RunExtendedProfileCreate", + "thermocycler/runProfile": "#/$defs/RunProfileCreate", + "thermocycler/setTargetBlockTemperature": "#/$defs/SetTargetBlockTemperatureCreate", + "thermocycler/setTargetLidTemperature": "#/$defs/SetTargetLidTemperatureCreate", + "thermocycler/waitForBlockTemperature": "#/$defs/WaitForBlockTemperatureCreate", + "thermocycler/waitForLidTemperature": "#/$defs/WaitForLidTemperatureCreate", + "touchTip": "#/$defs/TouchTipCreate", + "tryLiquidProbe": "#/$defs/TryLiquidProbeCreate", + "unsafe/blowOutInPlace": "#/$defs/UnsafeBlowOutInPlaceCreate", + "unsafe/dropTipInPlace": "#/$defs/UnsafeDropTipInPlaceCreate", + "unsafe/engageAxes": "#/$defs/UnsafeEngageAxesCreate", + "unsafe/placeLabware": "#/$defs/UnsafePlaceLabwareCreate", + "unsafe/ungripLabware": "#/$defs/UnsafeUngripLabwareCreate", + "unsafe/updatePositionEstimators": "#/$defs/UpdatePositionEstimatorsCreate", + "verifyTipPresence": "#/$defs/VerifyTipPresenceCreate", + "waitForDuration": "#/$defs/WaitForDurationCreate", + "waitForResume": "#/$defs/WaitForResumeCreate" + }, + "propertyName": "commandType" + }, + "oneOf": [ + { + "$ref": "#/$defs/AirGapInPlaceCreate" + }, + { + "$ref": "#/$defs/AspirateCreate" + }, + { + "$ref": "#/$defs/AspirateInPlaceCreate" + }, + { + "$ref": "#/$defs/CommentCreate" + }, + { + "$ref": "#/$defs/ConfigureForVolumeCreate" + }, + { + "$ref": "#/$defs/ConfigureNozzleLayoutCreate" + }, + { + "$ref": "#/$defs/CustomCreate" + }, + { + "$ref": "#/$defs/DispenseCreate" + }, + { + "$ref": "#/$defs/DispenseInPlaceCreate" + }, + { + "$ref": "#/$defs/BlowOutCreate" + }, + { + "$ref": "#/$defs/BlowOutInPlaceCreate" + }, + { + "$ref": "#/$defs/DropTipCreate" + }, + { + "$ref": "#/$defs/DropTipInPlaceCreate" + }, + { + "$ref": "#/$defs/HomeCreate" + }, + { + "$ref": "#/$defs/RetractAxisCreate" + }, + { + "$ref": "#/$defs/LoadLabwareCreate" + }, + { + "$ref": "#/$defs/ReloadLabwareCreate" + }, + { + "$ref": "#/$defs/LoadLiquidCreate" + }, + { + "$ref": "#/$defs/LoadLiquidClassCreate" + }, + { + "$ref": "#/$defs/LoadModuleCreate" + }, + { + "$ref": "#/$defs/LoadPipetteCreate" + }, + { + "$ref": "#/$defs/MoveLabwareCreate" + }, + { + "$ref": "#/$defs/MoveRelativeCreate" + }, + { + "$ref": "#/$defs/MoveToCoordinatesCreate" + }, + { + "$ref": "#/$defs/MoveToWellCreate" + }, + { + "$ref": "#/$defs/MoveToAddressableAreaCreate" + }, + { + "$ref": "#/$defs/MoveToAddressableAreaForDropTipCreate" + }, + { + "$ref": "#/$defs/PrepareToAspirateCreate" + }, + { + "$ref": "#/$defs/WaitForResumeCreate" + }, + { + "$ref": "#/$defs/WaitForDurationCreate" + }, + { + "$ref": "#/$defs/PickUpTipCreate" + }, + { + "$ref": "#/$defs/SavePositionCreate" + }, + { + "$ref": "#/$defs/SetRailLightsCreate" + }, + { + "$ref": "#/$defs/TouchTipCreate" + }, + { + "$ref": "#/$defs/SetStatusBarCreate" + }, + { + "$ref": "#/$defs/VerifyTipPresenceCreate" + }, + { + "$ref": "#/$defs/GetTipPresenceCreate" + }, + { + "$ref": "#/$defs/GetNextTipCreate" + }, + { + "$ref": "#/$defs/LiquidProbeCreate" + }, + { + "$ref": "#/$defs/TryLiquidProbeCreate" + }, + { + "$ref": "#/$defs/opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureCreate" + }, + { + "$ref": "#/$defs/opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureCreate" + }, + { + "$ref": "#/$defs/DeactivateHeaterCreate" + }, + { + "$ref": "#/$defs/SetAndWaitForShakeSpeedCreate" + }, + { + "$ref": "#/$defs/DeactivateShakerCreate" + }, + { + "$ref": "#/$defs/OpenLabwareLatchCreate" + }, + { + "$ref": "#/$defs/CloseLabwareLatchCreate" + }, + { + "$ref": "#/$defs/DisengageCreate" + }, + { + "$ref": "#/$defs/EngageCreate" + }, + { + "$ref": "#/$defs/opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureCreate" + }, + { + "$ref": "#/$defs/opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureCreate" + }, + { + "$ref": "#/$defs/DeactivateTemperatureCreate" + }, + { + "$ref": "#/$defs/SetTargetBlockTemperatureCreate" + }, + { + "$ref": "#/$defs/WaitForBlockTemperatureCreate" + }, + { + "$ref": "#/$defs/SetTargetLidTemperatureCreate" + }, + { + "$ref": "#/$defs/WaitForLidTemperatureCreate" + }, + { + "$ref": "#/$defs/DeactivateBlockCreate" + }, + { + "$ref": "#/$defs/DeactivateLidCreate" + }, + { + "$ref": "#/$defs/opentrons__protocol_engine__commands__thermocycler__open_lid__OpenLidCreate" + }, + { + "$ref": "#/$defs/opentrons__protocol_engine__commands__thermocycler__close_lid__CloseLidCreate" + }, + { + "$ref": "#/$defs/RunProfileCreate" + }, + { + "$ref": "#/$defs/RunExtendedProfileCreate" + }, + { + "$ref": "#/$defs/opentrons__protocol_engine__commands__absorbance_reader__close_lid__CloseLidCreate" + }, + { + "$ref": "#/$defs/opentrons__protocol_engine__commands__absorbance_reader__open_lid__OpenLidCreate" + }, + { + "$ref": "#/$defs/InitializeCreate" + }, + { + "$ref": "#/$defs/ReadAbsorbanceCreate" + }, + { + "$ref": "#/$defs/CalibrateGripperCreate" + }, + { + "$ref": "#/$defs/CalibratePipetteCreate" + }, + { + "$ref": "#/$defs/CalibrateModuleCreate" + }, + { + "$ref": "#/$defs/MoveToMaintenancePositionCreate" + }, + { + "$ref": "#/$defs/UnsafeBlowOutInPlaceCreate" + }, + { + "$ref": "#/$defs/UnsafeDropTipInPlaceCreate" + }, + { + "$ref": "#/$defs/UpdatePositionEstimatorsCreate" + }, + { + "$ref": "#/$defs/UnsafeEngageAxesCreate" + }, + { + "$ref": "#/$defs/UnsafeUngripLabwareCreate" + }, + { + "$ref": "#/$defs/UnsafePlaceLabwareCreate" + }, + { + "$ref": "#/$defs/MoveAxesRelativeCreate" + }, + { + "$ref": "#/$defs/MoveAxesToCreate" + }, + { + "$ref": "#/$defs/MoveToCreate" + }, + { + "$ref": "#/$defs/openGripperJawCreate" + }, + { + "$ref": "#/$defs/closeGripperJawCreate" + } + ] } diff --git a/shared-data/js/fixtures.ts b/shared-data/js/fixtures.ts index 4f13a8b0321..51f46fdf433 100644 --- a/shared-data/js/fixtures.ts +++ b/shared-data/js/fixtures.ts @@ -238,7 +238,7 @@ export function getAddressableAreaNamesFromLoadedModule( return [...acc, ...providedAddressableAreas] }, []) } - +// note: we've decided not to translate these strings export function getFixtureDisplayName( cutoutFixtureId: CutoutFixtureId | null, usbPortNumber?: number @@ -295,7 +295,7 @@ export function getFixtureDisplayName( } } -const STANDARD_OT2_SLOTS: AddressableAreaName[] = [ +export const STANDARD_OT2_SLOTS: AddressableAreaName[] = [ ADDRESSABLE_AREA_1, ADDRESSABLE_AREA_2, ADDRESSABLE_AREA_3, @@ -309,7 +309,7 @@ const STANDARD_OT2_SLOTS: AddressableAreaName[] = [ ADDRESSABLE_AREA_11, ] -const STANDARD_FLEX_SLOTS: AddressableAreaName[] = [ +export const STANDARD_FLEX_SLOTS: AddressableAreaName[] = [ A1_ADDRESSABLE_AREA, A2_ADDRESSABLE_AREA, A3_ADDRESSABLE_AREA, diff --git a/shared-data/js/index.ts b/shared-data/js/index.ts index 9a8d2e6c39f..50be81a940e 100644 --- a/shared-data/js/index.ts +++ b/shared-data/js/index.ts @@ -10,6 +10,7 @@ export * from './gripper' export * from './helpers' export * from './labware' export * from './labwareTools' +export * from './liquidClasses' export * from './modules' export * from './pipettes' export * from './protocols' diff --git a/shared-data/js/liquidClasses.ts b/shared-data/js/liquidClasses.ts new file mode 100644 index 00000000000..d97db8afa47 --- /dev/null +++ b/shared-data/js/liquidClasses.ts @@ -0,0 +1,8 @@ +import waterV1Uncasted from '../liquid-class/definitions/1/water.json' +import type { LiquidClass } from '.' + +const waterV1 = waterV1Uncasted as LiquidClass + +const defs = { waterV1 } + +export const getAllLiquidClassDefs = (): Record => defs diff --git a/shared-data/js/pipettes.ts b/shared-data/js/pipettes.ts index 2921696b820..134f4efbd82 100644 --- a/shared-data/js/pipettes.ts +++ b/shared-data/js/pipettes.ts @@ -186,9 +186,9 @@ const getHighestVersion = ( } const V2_DEFINITION_TYPES = ['general', 'geometry'] -/* takes in pipetteName such as 'p300_single' or 'p300_single_gen1' +/* takes in pipetteName such as 'p300_single' or 'p300_single_gen1' or PipetteModel such as 'p300_single_v1.3' and converts it to channels, -model, and version in order to return the correct pipette schema v2 json files. +model, and version in order to return the correct pipette schema v2 json files. **/ export const getPipetteSpecsV2 = ( name?: PipetteName | PipetteModel @@ -200,7 +200,15 @@ export const getPipetteSpecsV2 = ( const nameSplit = name.split('_') const pipetteModel = nameSplit[0] // ex: p300 const channels = getChannelsFromString(nameSplit[1] as PipChannelString) // ex: single -> single_channel - const pipetteGen = getVersionFromGen(nameSplit[2] as Gen) + let version_index: number + let oemString: string = '' + if (nameSplit.length === 4) { + version_index = 3 + oemString = `_${nameSplit[2]}` + } else { + version_index = 2 + } + const pipetteGen = getVersionFromGen(nameSplit[version_index] as Gen) let version: string = '' let majorVersion: number // the first 2 conditions are to accommodate version from the pipetteName @@ -215,7 +223,7 @@ export const getPipetteSpecsV2 = ( majorVersion = pipetteGen // ex: gen1 -> 1 // the 'else' is to accommodate the exact version if PipetteModel was added } else { - const versionNumber = nameSplit[2].split('v')[1] + const versionNumber = nameSplit[version_index].split('v')[1] if (versionNumber.includes('.')) { version = versionNumber.replace('.', '_') // ex: 1.0 -> 1_0 } else { @@ -236,7 +244,7 @@ export const getPipetteSpecsV2 = ( ) V2_DEFINITION_TYPES.forEach(type => { if ( - `../pipette/definitions/2/${type}/${channels}/${pipetteModel}/${ + `../pipette/definitions/2/${type}/${channels}${oemString}/${pipetteModel}/${ version === '' ? highestVersion : version }.json` === path ) { diff --git a/shared-data/js/types.ts b/shared-data/js/types.ts index 71c932a35d4..6abe511bb8f 100644 --- a/shared-data/js/types.ts +++ b/shared-data/js/types.ts @@ -693,6 +693,106 @@ export interface Liquid { displayColor?: string } +// TODO(ND, 12/17/2024): investigate why typescript doesn't allow Array<[number, number]> +type LiquidHandlingPropertyByVolume = number[][] +type PositionReference = + | 'well-bottom' + | 'well-top' + | 'well-center' + | 'liquid-meniscus' +type BlowoutLocation = 'source' | 'destination' | 'trash' +interface DelayParams { + duration: number +} +interface DelayProperties { + enable: boolean + params?: DelayParams +} +interface TouchTipParams { + zOffset: number + mmToEdge: number + speed: number +} +interface TouchTipProperties { + enable: boolean + params?: TouchTipParams +} + +interface MixParams { + repetitions: number + volume: number +} +interface MixProperties { + enable: boolean + params?: MixParams +} +interface BlowoutParams { + location: BlowoutLocation + flowRate: number +} +interface BlowoutProperties { + enable: boolean + params?: BlowoutParams +} +interface Submerge { + positionReference: PositionReference + offset: Coordinates + speed: number + delay: DelayProperties +} +interface BaseRetract { + positionReference: PositionReference + offset: Coordinates + speed: number + airGapByVolume: LiquidHandlingPropertyByVolume + touchTip: TouchTipProperties + delay: DelayProperties +} +type RetractAspirate = BaseRetract +interface RetractDispense extends BaseRetract { + blowout: BlowoutProperties +} +interface BaseLiquidHandlingProperties { + submerge: Submerge + retract: RetractType + positionReference: PositionReference + offset: Coordinates + flowRateByVolume: LiquidHandlingPropertyByVolume + correctionByVolume: LiquidHandlingPropertyByVolume + delay: DelayProperties +} +interface AspirateProperties + extends BaseLiquidHandlingProperties { + preWet: boolean + mix: MixProperties +} +interface SingleDispenseProperties + extends BaseLiquidHandlingProperties { + mix: MixProperties + pushOutByVolume: LiquidHandlingPropertyByVolume +} +interface MultiDispenseProperties { + conditioningByVolume: LiquidHandlingPropertyByVolume + disposalByVolume: LiquidHandlingPropertyByVolume +} +interface ByTipTypeSetting { + tiprack: string + aspirate: AspirateProperties + singleDispense: SingleDispenseProperties + multiDispense?: MultiDispenseProperties +} +interface ByPipetteSetting { + pipetteModel: string + byTipType: ByTipTypeSetting[] +} +export interface LiquidClass { + liquidClassName: string + displayName: string + schemaVersion: number + namespace: string + byPipette: ByPipetteSetting[] +} + export interface AnalysisError { id: string detail: string diff --git a/shared-data/liquid-class/definitions/1/ethanol_80.json b/shared-data/liquid-class/definitions/1/ethanol_80.json new file mode 100644 index 00000000000..392d1454f8f --- /dev/null +++ b/shared-data/liquid-class/definitions/1/ethanol_80.json @@ -0,0 +1,2602 @@ +{ + "liquidClassName": "ethanol_80", + "displayName": "Ethanol-80", + "schemaVersion": 1, + "namespace": "opentrons", + "byPipette": [ + { + "pipetteModel": "flex_1channel_50", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": true, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 30.0] + ], + "correctionByVolume": [ + [1.0, -1.6], + [10.0, -1.1], + [50.0, -3.0] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 30.0]], + "correctionByVolume": [ + [1.0, -1.6], + [10.0, -1.1], + [50.0, -3.0] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [[0.0, 1.0]], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 30.0]], + "correctionByVolume": [ + [1.0, -1.6], + [10.0, -1.1], + [50.0, -3.0] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + } + ] + }, + { + "pipetteModel": "flex_8channel_50", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": true, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 30.0] + ], + "correctionByVolume": [ + [1.0, -1.6], + [10.0, -1.1], + [50.0, -3.0] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 30.0]], + "correctionByVolume": [ + [1.0, -1.6], + [10.0, -1.1], + [50.0, -3.0] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [[0.0, 1.0]], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 30.0]], + "correctionByVolume": [ + [1.0, -1.6], + [10.0, -1.1], + [50.0, -3.0] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + } + ] + }, + { + "pipetteModel": "flex_1channel_1000", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": true, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 30.0] + ], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 10.0], + [10.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_200ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": true, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [5.0, 7.0], + [50.0, 50.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 8.0], + [50.0, 4.0], + [200.0, 4.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_1000ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [10.0, 10.0], + [100.0, 100.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [10.0, 6.0], + [100.0, 3.0], + [1000.0, 3.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + } + ] + }, + { + "pipetteModel": "flex_8channel_1000", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": true, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 30.0] + ], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 10.0], + [10.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_200ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": true, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [5.0, 7.0], + [50.0, 50.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 8.0], + [50.0, 4.0], + [200.0, 4.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_1000ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [10.0, 10.0], + [100.0, 100.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [10.0, 6.0], + [100.0, 3.0], + [1000.0, 3.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 100, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + } + ] + }, + { + "pipetteModel": "flex_96channel_1000", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": true, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 30.0] + ], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 10.0], + [10.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [1.0, 5.0], + [45.0, 5.0], + [50.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -2.1], + [10.0, -1.7], + [50.0, -3.3] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_200ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": true, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [5.0, 7.0], + [50.0, 50.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 8.0], + [50.0, 4.0], + [200.0, 4.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [5.0, 10.0], + [190.0, 10.0], + [200.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 125.0]], + "correctionByVolume": [ + [5.0, -1.5], + [50.0, -2.2], + [200.0, -7.4] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_1000ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [10.0, 10.0], + [100.0, 100.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 200.0]], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [10.0, 6.0], + [100.0, 3.0], + [1000.0, 3.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 35, + "airGapByVolume": [ + [5.0, 12.0], + [188.0, 12.0], + [1000.0, 0.0] + ], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 200.0]], + "correctionByVolume": [ + [10.0, -1.9], + [100.0, -3.6], + [1000.0, -32.2] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.2 + } + } + } + } + ] + } + ] +} diff --git a/shared-data/liquid-class/definitions/1/glycerol_50.json b/shared-data/liquid-class/definitions/1/glycerol_50.json new file mode 100644 index 00000000000..0cb8dad21b8 --- /dev/null +++ b/shared-data/liquid-class/definitions/1/glycerol_50.json @@ -0,0 +1,2462 @@ +{ + "liquidClassName": "glycerol_50", + "displayName": "Glycerol-50", + "schemaVersion": 1, + "namespace": "opentrons", + "byPipette": [ + { + "pipetteModel": "flex_1channel_50", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 50.0] + ], + "correctionByVolume": [ + [1.0, -0.5], + [10.0, -0.2], + [50.0, -0.2] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 25.0]], + "correctionByVolume": [ + [1.0, -0.5], + [10.0, -0.2], + [50.0, -0.2] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [1.0, 11.7], + [4.999, 11.7], + [5.0, 3.9], + [50.0, 3.9] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 25.0]], + "correctionByVolume": [ + [1.0, -0.5], + [10.0, -0.2], + [50.0, -0.2] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + } + } + ] + }, + { + "pipetteModel": "flex_8channel_50", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 50.0] + ], + "correctionByVolume": [ + [1.0, -0.5], + [10.0, -0.2], + [50.0, -0.2] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 25.0]], + "correctionByVolume": [ + [1.0, -0.5], + [10.0, -0.2], + [50.0, -0.2] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [1.0, 11.7], + [4.999, 11.7], + [5.0, 3.9], + [50.0, 3.9] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 25.0]], + "correctionByVolume": [ + [1.0, -0.5], + [10.0, -0.2], + [50.0, -0.2] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + } + } + ] + }, + { + "pipetteModel": "flex_1channel_1000", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 40.0] + ], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 2.0 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 30.0], + [10.0, 20.0], + [50.0, 20.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_200ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [5.0, 10.0], + [50.0, 50.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [[0.0, 20.0]], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_1000ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [10.0, 10.0], + [100.0, 100.0], + [1000.0, 800.0] + ], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.7 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [[0.0, 35.0]], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + } + } + ] + }, + { + "pipetteModel": "flex_8channel_1000", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 40.0] + ], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 2.0 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 30.0], + [10.0, 20.0], + [50.0, 20.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_200ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [5.0, 10.0], + [50.0, 50.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [[0.0, 20.0]], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_1000ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [10.0, 10.0], + [100.0, 100.0], + [1000.0, 800.0] + ], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.7 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [[0.0, 35.0]], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + } + } + ] + }, + { + "pipetteModel": "flex_96channel_1000", + "byTipType": [ + { + "tiprack": "opentrons_flex_96_tiprack_50ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 40.0] + ], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 2.0 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 40.0] + ], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [ + [5.0, 30.0], + [10.0, 20.0], + [50.0, 20.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [1.0, 7.0], + [10.0, 10.0], + [50.0, 40.0] + ], + "correctionByVolume": [ + [5.0, -0.3], + [10.0, -0.2], + [50.0, 0.0] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [40.0, 5.0], + [45.0, 0.0], + [50.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_200ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [5.0, 10.0], + [50.0, 50.0], + [200.0, 200.0] + ], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 1.0 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [[0.0, 20.0]], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 50.0]], + "correctionByVolume": [ + [5.0, -0.3], + [50.0, -0.3], + [200.0, -0.8] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [190.0, 5.0], + [195.0, 0.0], + [200.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + } + }, + { + "tiprack": "opentrons_flex_96_tiprack_1000ul", + "aspirate": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-bottom", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [ + [10.0, 10.0], + [100.0, 100.0], + [200.0, 200.0], + [1000.0, 200.0] + ], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "preWet": false, + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "delay": { + "enable": true, + "params": { + "duration": 0.7 + } + } + }, + "singleDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 250.0]], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "mix": { + "enable": false, + "params": { + "repetitions": 1, + "volume": 50 + } + }, + "pushOutByVolume": [[0.0, 35.0]], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + }, + "multiDispense": { + "submerge": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "delay": { + "enable": false, + "params": { + "duration": 0.0 + } + } + }, + "retract": { + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "speed": 4, + "airGapByVolume": [[0.0, 0.0]], + "blowout": { + "enable": false + }, + "touchTip": { + "enable": false, + "params": { + "zOffset": -1, + "mmToEdge": 0.5, + "speed": 30 + } + }, + "delay": { + "enable": false, + "params": { + "duration": 0 + } + } + }, + "positionReference": "well-top", + "offset": { + "x": 0, + "y": 0, + "z": 2 + }, + "flowRateByVolume": [[0.0, 200.0]], + "correctionByVolume": [ + [10.0, -0.2], + [100.0, -0.1], + [1000.0, -2.5] + ], + "conditioningByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "disposalByVolume": [ + [1.0, 5.0], + [990.0, 5.0], + [995.0, 0.0], + [1000.0, 0.0] + ], + "delay": { + "enable": true, + "params": { + "duration": 0.5 + } + } + } + } + ] + } + ] +} diff --git a/shared-data/module/definitions/3/absorbanceReaderV1.json b/shared-data/module/definitions/3/absorbanceReaderV1.json index 57b0bfc1b10..7ced1f05ded 100644 --- a/shared-data/module/definitions/3/absorbanceReaderV1.json +++ b/shared-data/module/definitions/3/absorbanceReaderV1.json @@ -11,8 +11,8 @@ "bareOverallHeight": 18.5, "overLabwareHeight": 0.0, "lidHeight": 60.0, - "xDimension": 95.5, - "yDimension": 155.3, + "xDimension": 155.3, + "yDimension": 95.5, "labwareInterfaceXDimension": 127.8, "labwareInterfaceYDimension": 85.5 }, diff --git a/shared-data/pipette/definitions/1/pipetteModelSpecs.json b/shared-data/pipette/definitions/1/pipetteModelSpecs.json index a66312eb522..6a1eea16bd4 100644 --- a/shared-data/pipette/definitions/1/pipetteModelSpecs.json +++ b/shared-data/pipette/definitions/1/pipetteModelSpecs.json @@ -8788,6 +8788,201 @@ "returnTipHeight": 0.71, "idleCurrent": 0.3 }, + "p1000_multi_em_v3.0": { + "name": "p1000_multi_em_flex", + "backCompatNames": [], + "top": { + "value": 0.5, + "min": 0, + "max": 45, + "units": "mm", + "type": "float" + }, + "bottom": { + "value": 71.5, + "min": 55, + "max": 80, + "type": "float", + "units": "mm" + }, + "blowout": { + "value": 76.5, + "min": 60, + "max": 85, + "units": "mm", + "type": "float" + }, + "dropTip": { + "value": 92.5, + "min": 78, + "max": 110, + "units": "mm", + "type": "float" + }, + "pickUpCurrent": { + "value": 0.5, + "min": 0.05, + "max": 2.0, + "units": "amps", + "type": "float" + }, + "pickUpDistance": { + "value": 13, + "min": 1, + "max": 30, + "units": "mm", + "type": "float" + }, + "pickUpIncrement": { + "value": 0.0, + "min": 0.0, + "max": 10.0, + "units": "mm", + "type": "float" + }, + "pickUpPresses": { + "value": 1, + "min": 0, + "max": 10, + "units": "presses", + "type": "int" + }, + "pickUpSpeed": { + "value": 10, + "min": 1, + "max": 30, + "units": "mm/s", + "type": "float" + }, + "nozzleOffset": [-8.0, -16.0, -259.15], + "modelOffset": [0.0, 0.0, 25.14], + "ulPerMm": [ + { + "aspirate": [ + [0.7511, 3.9556, 6.455], + [1.3075, 2.1664, 5.8839], + [1.8737, 1.1513, 7.2111], + [3.177, 0.9374, 7.612], + [4.5368, 0.5531, 8.8328], + [7.31, 0.3035, 9.9651], + [10.0825, 0.1513, 11.0781], + [12.9776, 0.1293, 11.2991], + [15.9173, 0.0976, 11.7115], + [18.8243, 0.06244, 12.2706], + [21.8529, 0.07004, 12.1275], + [24.8068, 0.04182, 12.7442], + [27.7744, 0.0356, 12.8984], + [35.2873, 0.03031, 13.04544], + [42.799, 0.02015, 13.4038], + [50.4562, 0.01956, 13.4293], + [58.1081, 0.0145, 13.6843], + [65.7267, 0.01036, 13.9252], + [73.2857, 0.006776, 14.1606], + [81.00159, 0.009126, 13.9883], + [88.6617, 0.006448, 14.2052], + [103.9829, 0.005074, 14.3271], + [119.4408, 0.004878, 14.3476], + [134.889, 0.003727, 14.485], + [150.273, 0.00258, 14.6402], + [181.2798, 0.002559, 14.6427], + [212.4724, 0.002242, 14.7002], + [243.577, 0.00151, 14.856], + [274.7216, 0.001244, 14.9205], + [305.8132, 0.0009118, 15.0118], + [368.06968, 0.0007321, 15.06677], + [430.2513, 0.0004805, 15.1594], + [492.3487, 0.0003186, 15.2291], + [554.5713, 0.0003031, 15.237], + [616.6825, 0.0001981, 15.2948], + [694.4168, 0.0001855, 15.3027], + [772.0327, 0.0001181, 15.3494], + [849.617, 0.00008929, 15.3717], + [927.2556, 0.00008601, 15.3745], + [1004.87, 0.00006801, 15.3912], + [1051.4648, 0.00006824, 15.391] + ], + "dispense": [ + [0.7511, 3.9556, 6.455], + [1.3075, 2.1664, 5.8839], + [1.8737, 1.1513, 7.2111], + [3.177, 0.9374, 7.612], + [4.5368, 0.5531, 8.8328], + [7.31, 0.3035, 9.9651], + [10.0825, 0.1513, 11.0781], + [12.9776, 0.1293, 11.2991], + [15.9173, 0.0976, 11.7115], + [18.8243, 0.06244, 12.2706], + [21.8529, 0.07004, 12.1275], + [24.8068, 0.04182, 12.7442], + [27.7744, 0.0356, 12.8984], + [35.2873, 0.03031, 13.04544], + [42.799, 0.02015, 13.4038], + [50.4562, 0.01956, 13.4293], + [58.1081, 0.0145, 13.6843], + [65.7267, 0.01036, 13.9252], + [73.2857, 0.006776, 14.1606], + [81.00159, 0.009126, 13.9883], + [88.6617, 0.006448, 14.2052], + [103.9829, 0.005074, 14.3271], + [119.4408, 0.004878, 14.3476], + [134.889, 0.003727, 14.485], + [150.273, 0.00258, 14.6402], + [181.2798, 0.002559, 14.6427], + [212.4724, 0.002242, 14.7002], + [243.577, 0.00151, 14.856], + [274.7216, 0.001244, 14.9205], + [305.8132, 0.0009118, 15.0118], + [368.06968, 0.0007321, 15.06677], + [430.2513, 0.0004805, 15.1594], + [492.3487, 0.0003186, 15.2291], + [554.5713, 0.0003031, 15.237], + [616.6825, 0.0001981, 15.2948], + [694.4168, 0.0001855, 15.3027], + [772.0327, 0.0001181, 15.3494], + [849.617, 0.00008929, 15.3717], + [927.2556, 0.00008601, 15.3745], + [1004.87, 0.00006801, 15.3912], + [1051.4648, 0.00006824, 15.391] + ] + } + ], + "plungerCurrent": { + "value": 1, + "min": 0.1, + "max": 1.5, + "units": "amps", + "type": "float" + }, + "dropTipCurrent": { + "value": 1, + "min": 0.1, + "max": 1.25, + "units": "amps", + "type": "float" + }, + "dropTipSpeed": { + "value": 10, + "min": 0.001, + "max": 30, + "units": "mm/sec", + "type": "float" + }, + "tipOverlap": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5 + }, + "tipLength": { + "value": 78.3, + "units": "mm", + "type": "float", + "min": 0, + "max": 100 + }, + "quirks": ["highSpeed"], + "returnTipHeight": 0.71, + "idleCurrent": 0.3 + }, "p50_multi_v3.0": { "name": "p50_multi_flex", "backCompatNames": [], diff --git a/shared-data/pipette/definitions/1/pipetteNameSpecs.json b/shared-data/pipette/definitions/1/pipetteNameSpecs.json index 61cd56d32f6..7beeb92aabc 100644 --- a/shared-data/pipette/definitions/1/pipetteNameSpecs.json +++ b/shared-data/pipette/definitions/1/pipetteNameSpecs.json @@ -599,6 +599,54 @@ "opentrons/opentrons_flex_96_filtertiprack_50ul/1" ] }, + "p1000_multi_em_flex": { + "displayName": "Flex 8-Channel EM 1000 μL", + "displayCategory": "FLEX", + "defaultAspirateFlowRate": { + "value": 478, + "min": 3, + "max": 812, + "valuesByApiLevel": { + "2.0": 159.04, + "2.6": 159.04, + "2.14": 478 + } + }, + "defaultDispenseFlowRate": { + "value": 478, + "min": 3, + "max": 812, + "valuesByApiLevel": { + "2.0": 159.04, + "2.14": 478 + } + }, + "defaultBlowOutFlowRate": { + "value": 478, + "min": 3, + "max": 812, + "valuesByApiLevel": { + "2.0": 78.52, + "2.14": 478 + } + }, + "channels": 8, + "minVolume": 5, + "maxVolume": 1000, + "smoothieConfigs": { + "stepsPerMM": 2133.33, + "homePosition": 230.15, + "travelDistance": 80 + }, + "defaultTipracks": [ + "opentrons/opentrons_flex_96_tiprack_1000ul/1", + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1", + "opentrons/opentrons_flex_96_tiprack_200ul/1", + "opentrons/opentrons_flex_96_filtertiprack_200ul/1", + "opentrons/opentrons_flex_96_tiprack_50ul/1", + "opentrons/opentrons_flex_96_filtertiprack_50ul/1" + ] + }, "p50_multi_flex": { "displayName": "Flex 8-Channel 50 μL", "displayCategory": "FLEX", diff --git a/shared-data/pipette/definitions/2/general/eight_channel_em/p1000/1_0.json b/shared-data/pipette/definitions/2/general/eight_channel_em/p1000/1_0.json new file mode 100644 index 00000000000..c267504b404 --- /dev/null +++ b/shared-data/pipette/definitions/2/general/eight_channel_em/p1000/1_0.json @@ -0,0 +1,320 @@ +{ + "$otSharedSchema": "#/pipette/schemas/2/pipettePropertiesSchema.json", + "displayName": "FLEX 8-Channel EM 1000 μL", + "model": "p1000", + "displayCategory": "FLEX", + "validNozzleMaps": { + "maps": { + "SingleA1": ["A1"], + "SingleH1": ["H1"], + "H1toG1": ["G1", "H1"], + "H1toF1": ["F1", "G1", "H1"], + "H1toE1": ["E1", "F1", "G1", "H1"], + "H1toD1": ["D1", "E1", "F1", "G1", "H1"], + "H1toC1": ["C1", "D1", "E1", "F1", "G1", "H1"], + "H1toB1": ["B1", "C1", "D1", "E1", "F1", "G1", "H1"], + "Full": ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"] + } + }, + "pickUpTipConfigurations": { + "pressFit": { + "presses": 1, + "increment": 0.0, + "configurationsByNozzleMap": { + "SingleA1": { + "default": { + "speed": 10.0, + "distance": 11.0, + "current": 0.15, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 10.08, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.08, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.26, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.08, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.26 + } + } + } + }, + "SingleH1": { + "default": { + "speed": 10.0, + "distance": 11.0, + "current": 0.15, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 9.52, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.71, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.52, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.2, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.71, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.52, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.2 + } + } + } + }, + "H1toG1": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.2, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 9.24, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.52, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.24, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.73, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.52, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.24, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.73 + } + } + } + }, + "H1toF1": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.2, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 9.2, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.3, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.2, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.8, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.3, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.2, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.8 + } + } + } + }, + "H1toE1": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.35, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 9.2, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.3, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.2, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.8, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.3, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.2, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.8 + } + } + } + }, + "H1toD1": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 9.2, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.3, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.2, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.8, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.3, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.2, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.8 + } + } + } + }, + "H1toC1": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 9.2, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.3, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.2, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.8, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.3, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.2, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.8 + } + } + } + }, + "H1toB1": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.5, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 9.13, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.23, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.13, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.9, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.23, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.13, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.9 + } + } + } + }, + "Full": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 10.17, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.1, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.05, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 10.17 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.42, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.27, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.67, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.42, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.27, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.67 + }, + "v3": { + "default": 9.28, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.37, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.28, + "opentrons/opentrons_flex_96_tiprack_1000ul/1": 9.67, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.37, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.28, + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1": 9.67 + } + } + } + } + } + } + }, + "dropTipConfigurations": { + "plungerEject": { + "current": 1.0, + "speed": 10 + } + }, + "plungerMotorConfigurations": { + "idle": 0.3, + "run": 1.0 + }, + "plungerPositionsConfigurations": { + "default": { + "top": 0.0, + "bottom": 71.5, + "blowout": 76.5, + "drop": 91.5 + } + }, + "availableSensors": { + "sensors": ["capacitive", "environment"], + "capacitive": { + "count": 2 + }, + "environment": { + "count": 1 + } + }, + "partialTipConfigurations": { + "partialTipSupported": true, + "availableConfigurations": [1, 2, 3, 4, 5, 6, 7, 8] + }, + "backCompatNames": [], + "channels": 8, + "shaftDiameter": 4.5, + "shaftULperMM": 15.904, + "backlashDistance": 0.1, + "quirks": ["highSpeed"], + "plungerHomingConfigurations": { + "current": 1.0, + "speed": 30 + } +} diff --git a/shared-data/pipette/definitions/2/geometry/eight_channel_em/p1000/1_0.json b/shared-data/pipette/definitions/2/geometry/eight_channel_em/p1000/1_0.json new file mode 100644 index 00000000000..b92e7415fe3 --- /dev/null +++ b/shared-data/pipette/definitions/2/geometry/eight_channel_em/p1000/1_0.json @@ -0,0 +1,55 @@ +{ + "$otSharedSchema": "#/pipette/schemas/2/pipetteGeometrySchema.json", + "pathTo3D": "pipette/definitions/2/geometry/eight_channel_em/p1000/placeholder.gltf", + "nozzleOffset": [-8.0, -16.0, -259.15], + "pipetteBoundingBoxOffsets": { + "backLeftCorner": [-38.5, 0.0, -259.15], + "frontRightCorner": [11.5, -95.0, -259.15] + }, + "orderedRows": [ + { + "key": "A", + "orderedNozzles": ["A1"] + }, + { + "key": "B", + "orderedNozzles": ["B1"] + }, + { "key": "C", "orderedNozzles": ["C1"] }, + { "key": "D", "orderedNozzles": ["D1"] }, + { "key": "E", "orderedNozzles": ["E1"] }, + { "key": "F", "orderedNozzles": ["F1"] }, + { "key": "G", "orderedNozzles": ["G1"] }, + { "key": "H", "orderedNozzles": ["H1"] } + ], + "orderedColumns": [ + { + "key": "1", + "orderedNozzles": ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"] + } + ], + "nozzleMap": { + "A1": [-8.0, -16.0, -259.15], + "B1": [-8.0, -25.0, -259.15], + "C1": [-8.0, -34.0, -259.15], + "D1": [-8.0, -43.0, -259.15], + "E1": [-8.0, -52.0, -259.15], + "F1": [-8.0, -61.0, -259.15], + "G1": [-8.0, -70.0, -259.15], + "H1": [-8.0, -79.0, -259.15] + }, + "lldSettings": { + "t50": { + "minHeight": 1.0, + "minVolume": 0 + }, + "t200": { + "minHeight": 1.0, + "minVolume": 0 + }, + "t1000": { + "minHeight": 1.5, + "minVolume": 0 + } + } +} diff --git a/shared-data/pipette/definitions/2/liquid/eight_channel_em/p1000/default/1_0.json b/shared-data/pipette/definitions/2/liquid/eight_channel_em/p1000/default/1_0.json new file mode 100644 index 00000000000..52c7b58171d --- /dev/null +++ b/shared-data/pipette/definitions/2/liquid/eight_channel_em/p1000/default/1_0.json @@ -0,0 +1,236 @@ +{ + "$otSharedSchema": "#/pipette/schemas/2/pipetteLiquidPropertiesSchema.json", + "supportedTips": { + "t50": { + "uiMaxFlowRate": 1431.0, + "defaultAspirateFlowRate": { + "default": 478, + "valuesByApiLevel": { "2.14": 478 } + }, + "defaultDispenseFlowRate": { + "default": 478, + "valuesByApiLevel": { "2.14": 478 } + }, + "defaultBlowOutFlowRate": { + "default": 478, + "valuesByApiLevel": { "2.14": 478 } + }, + "defaultFlowAcceleration": 24000.0, + "defaultTipLength": 57.9, + "defaultReturnTipHeight": 0.71, + "aspirate": { + "default": { + "1": [ + [0.12, -57.973785, 8.495981], + [0.11, 40.31047, -3.298129], + [0.09, 19.330223, -0.990302], + [0.375, 6.200306, 0.19139], + [1.17, 4.795927, 0.718032], + [1.92, 2.746428, 3.115947], + [2.145, 1.592373, 5.331732], + [2.4, 1.336497, 5.880586], + [2.66, 1.043996, 6.582588], + [2.84, 0.280189, 8.614315], + [2.985, -0.698973, 11.395134], + [3.085, -5.627462, 26.106674], + [3.625, 1.899561, 2.885808], + [4.43, 1.977851, 2.602006], + [5.155, 0.596916, 8.71955], + [6.71, 0.366092, 9.909446], + [8.62, 0.233878, 10.796602], + [11.015, 0.158281, 11.448248], + [13.97, 0.101002, 12.079177], + [17.545, 0.047056, 12.832813], + [22.075, 0.043416, 12.896662], + [27.955, 0.049456, 12.763333], + [34.695, 0.00096, 14.119053], + [43.535, 0.018347, 13.515795], + [54.08, 0.001949, 14.229706] + ] + } + }, + "dispense": { + "default": { + "1": [ + [0.12, -57.973785, 8.495981], + [0.11, 40.31047, -3.298129], + [0.09, 19.330223, -0.990302], + [0.375, 6.200306, 0.19139], + [1.17, 4.795927, 0.718032], + [1.92, 2.746428, 3.115947], + [2.145, 1.592373, 5.331732], + [2.4, 1.336497, 5.880586], + [2.66, 1.043996, 6.582588], + [2.84, 0.280189, 8.614315], + [2.985, -0.698973, 11.395134], + [3.085, -5.627462, 26.106674], + [3.625, 1.899561, 2.885808], + [4.43, 1.977851, 2.602006], + [5.155, 0.596916, 8.71955], + [6.71, 0.366092, 9.909446], + [8.62, 0.233878, 10.796602], + [11.015, 0.158281, 11.448248], + [13.97, 0.101002, 12.079177], + [17.545, 0.047056, 12.832813], + [22.075, 0.043416, 12.896662], + [27.955, 0.049456, 12.763333], + [34.695, 0.00096, 14.119053], + [43.535, 0.018347, 13.515795], + [54.08, 0.001949, 14.229706] + ] + } + }, + "defaultPushOutVolume": 7 + }, + "t200": { + "uiMaxFlowRate": 1431.0, + "defaultAspirateFlowRate": { + "default": 716, + "valuesByApiLevel": { "2.14": 716 } + }, + "defaultDispenseFlowRate": { + "default": 716, + "valuesByApiLevel": { "2.14": 716 } + }, + "defaultBlowOutFlowRate": { + "default": 716, + "valuesByApiLevel": { "2.14": 716 } + }, + "defaultFlowAcceleration": 24000.0, + "defaultTipLength": 58.35, + "defaultReturnTipHeight": 0.71, + "aspirate": { + "default": { + "1": [ + [0.28375, -141.180627, 42.499381], + [0.26125, 27.065799, -5.240543], + [0.715, 4.916546, 0.54595], + [1.685, 3.844391, 1.31254], + [2.6025, 2.148973, 4.169319], + [3.75875, 1.461751, 5.957816], + [4.9975, 0.733738, 8.694235], + [6.41375, 0.377599, 10.474036], + [8.1225, 0.214926, 11.517382], + [10.2425, 0.152451, 12.024835], + [12.80125, 0.081051, 12.75615], + [15.9875, 0.062849, 12.989161], + [19.9625, 0.051585, 13.169235], + [24.83625, 0.030593, 13.588301], + [30.89125, 0.024593, 13.737307], + [38.42625, 0.020128, 13.875257], + [47.71875, 0.014091, 14.107204], + [59.28375, 0.011625, 14.224918], + [73.41375, 0.00635, 14.537608], + [90.84375, 0.004458, 14.676515], + [112.32, 0.003084, 14.801312], + [138.7675, 0.002045, 14.917998], + [171.29875, 0.001319, 15.018758], + [211.27375, 0.000719, 15.121662] + ] + } + }, + "dispense": { + "default": { + "1": [ + [0.28375, -141.180627, 42.499381], + [0.26125, 27.065799, -5.240543], + [0.715, 4.916546, 0.54595], + [1.685, 3.844391, 1.31254], + [2.6025, 2.148973, 4.169319], + [3.75875, 1.461751, 5.957816], + [4.9975, 0.733738, 8.694235], + [6.41375, 0.377599, 10.474036], + [8.1225, 0.214926, 11.517382], + [10.2425, 0.152451, 12.024835], + [12.80125, 0.081051, 12.75615], + [15.9875, 0.062849, 12.989161], + [19.9625, 0.051585, 13.169235], + [24.83625, 0.030593, 13.588301], + [30.89125, 0.024593, 13.737307], + [38.42625, 0.020128, 13.875257], + [47.71875, 0.014091, 14.107204], + [59.28375, 0.011625, 14.224918], + [73.41375, 0.00635, 14.537608], + [90.84375, 0.004458, 14.676515], + [112.32, 0.003084, 14.801312], + [138.7675, 0.002045, 14.917998], + [171.29875, 0.001319, 15.018758], + [211.27375, 0.000719, 15.121662] + ] + } + }, + "defaultPushOutVolume": 5 + }, + "t1000": { + "uiMaxFlowRate": 1431.0, + "defaultAspirateFlowRate": { + "default": 716, + "valuesByApiLevel": { "2.14": 716 } + }, + "defaultDispenseFlowRate": { + "default": 716, + "valuesByApiLevel": { "2.14": 716 } + }, + "defaultBlowOutFlowRate": { + "default": 716, + "valuesByApiLevel": { "2.14": 716 } + }, + "defaultFlowAcceleration": 24000.0, + "defaultTipLength": 95.6, + "defaultReturnTipHeight": 0.82, + "aspirate": { + "default": { + "1": [ + [2.1443, 1.9858, 4.2677], + [3.0286, 1.2526, 5.84], + [4.9557, 0.6268, 7.7351], + [9.7943, 0.2745, 9.4811], + [12.1514, 0.1715, 10.4901], + [14.9414, 0.0897, 11.4833], + [51.46, 0.0424, 12.1913], + [92.68, 0.0095, 13.881], + [112.4886, 0.0049, 14.3053], + [243.5986, 0.0028, 14.5507], + [356.5686, 0.0009, 15.0019], + [430.99, 0.0005, 15.1492], + [628.7886, 0.0003, 15.2496], + [1001.15, 0.0001, 15.3472], + [1106.0857, 0.0001, 15.3551] + ] + } + }, + "dispense": { + "default": { + "1": [ + [2.1443, 1.9858, 4.2677], + [3.0286, 1.2526, 5.84], + [4.9557, 0.6268, 7.7351], + [9.7943, 0.2745, 9.4811], + [12.1514, 0.1715, 10.4901], + [14.9414, 0.0897, 11.4833], + [51.46, 0.0424, 12.1913], + [92.68, 0.0095, 13.881], + [112.4886, 0.0049, 14.3053], + [243.5986, 0.0028, 14.5507], + [356.5686, 0.0009, 15.0019], + [430.99, 0.0005, 15.1492], + [628.7886, 0.0003, 15.2496], + [1001.15, 0.0001, 15.3472], + [1106.0857, 0.0001, 15.3551] + ] + } + }, + "defaultPushOutVolume": 20 + } + }, + "maxVolume": 1000, + "minVolume": 5, + "defaultTipracks": [ + "opentrons/opentrons_flex_96_tiprack_1000ul/1", + "opentrons/opentrons_flex_96_tiprack_200ul/1", + "opentrons/opentrons_flex_96_tiprack_50ul/1", + "opentrons/opentrons_flex_96_filtertiprack_1000ul/1", + "opentrons/opentrons_flex_96_filtertiprack_200ul/1", + "opentrons/opentrons_flex_96_filtertiprack_50ul/1" + ] +} diff --git a/shared-data/python/Pipfile b/shared-data/python/Pipfile index dff2c2318bd..36fe4d3a4c2 100644 --- a/shared-data/python/Pipfile +++ b/shared-data/python/Pipfile @@ -4,7 +4,7 @@ verify_ssl = true name = "pypi" [dev-packages] -mypy = "==1.8.0" +mypy = "==1.11.0" flake8 = "~=7.0.0" flake8-annotations = "~=3.0.1" flake8-docstrings = "~=1.7.0" @@ -27,5 +27,5 @@ pytest-clarity = "~=1.0.0" [packages] opentrons-shared-data = { editable = true, path = "." } jsonschema = "==4.21.1" -pydantic = "==1.10.12" +pydantic = "==2.9.0" numpy = "==1.22.3" diff --git a/shared-data/python/Pipfile.lock b/shared-data/python/Pipfile.lock index 43ce052e327..be15adba052 100644 --- a/shared-data/python/Pipfile.lock +++ b/shared-data/python/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9b5174a247c5fe717a5db26f523afd532a6b0fc27943d86f6588839785ef51f4" + "sha256": "26e2a27ca64e01b1f0274cbbe632e28a6d15977ac1d4eecdc5077dd3acb6e364" }, "pipfile-spec": 6, "requires": {}, @@ -14,13 +14,21 @@ ] }, "default": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "jsonschema": { "hashes": [ @@ -33,11 +41,11 @@ }, "jsonschema-specifications": { "hashes": [ - "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", - "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", + "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf" ], - "markers": "python_version >= '3.8'", - "version": "==2023.12.1" + "markers": "python_version >= '3.9'", + "version": "==2024.10.1" }, "numpy": { "hashes": [ @@ -68,172 +76,245 @@ }, "opentrons-shared-data": { "editable": true, - "markers": "python_version >= '3.7'", + "markers": "python_version >= '3.10'", "path": "." }, "pydantic": { "hashes": [ - "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303", - "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe", - "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47", - "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494", - "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33", - "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86", - "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d", - "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c", - "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a", - "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565", - "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb", - "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62", - "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62", - "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0", - "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523", - "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d", - "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405", - "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f", - "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b", - "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718", - "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed", - "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb", - "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5", - "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc", - "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942", - "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe", - "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246", - "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350", - "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303", - "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09", - "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33", - "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8", - "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a", - "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1", - "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6", - "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d" + "sha256:c7a8a9fdf7d100afa49647eae340e2d23efa382466a8d177efcd1381e9be5598", + "sha256:f66a7073abd93214a20c5f7b32d56843137a7a2e70d02111f3be287035c45370" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.10.12" + "markers": "python_version >= '3.8'", + "version": "==2.9.0" + }, + "pydantic-core": { + "hashes": [ + "sha256:0102e49ac7d2df3379ef8d658d3bc59d3d769b0bdb17da189b75efa861fc07b4", + "sha256:0123655fedacf035ab10c23450163c2f65a4174f2bb034b188240a6cf06bb123", + "sha256:043ef8469f72609c4c3a5e06a07a1f713d53df4d53112c6d49207c0bd3c3bd9b", + "sha256:0448b81c3dfcde439551bb04a9f41d7627f676b12701865c8a2574bcea034437", + "sha256:05b366fb8fe3d8683b11ac35fa08947d7b92be78ec64e3277d03bd7f9b7cda79", + "sha256:07049ec9306ec64e955b2e7c40c8d77dd78ea89adb97a2013d0b6e055c5ee4c5", + "sha256:084414ffe9a85a52940b49631321d636dadf3576c30259607b75516d131fecd0", + "sha256:086c5db95157dc84c63ff9d96ebb8856f47ce113c86b61065a066f8efbe80acf", + "sha256:12625e69b1199e94b0ae1c9a95d000484ce9f0182f9965a26572f054b1537e44", + "sha256:16b25a4a120a2bb7dab51b81e3d9f3cde4f9a4456566c403ed29ac81bf49744f", + "sha256:19f1352fe4b248cae22a89268720fc74e83f008057a652894f08fa931e77dced", + "sha256:1a2ab4f410f4b886de53b6bddf5dd6f337915a29dd9f22f20f3099659536b2f6", + "sha256:1c7b81beaf7c7ebde978377dc53679c6cba0e946426fc7ade54251dfe24a7604", + "sha256:1cf842265a3a820ebc6388b963ead065f5ce8f2068ac4e1c713ef77a67b71f7c", + "sha256:1eb37f7d6a8001c0f86dc8ff2ee8d08291a536d76e49e78cda8587bb54d8b329", + "sha256:23af245b8f2f4ee9e2c99cb3f93d0e22fb5c16df3f2f643f5a8da5caff12a653", + "sha256:257d6a410a0d8aeb50b4283dea39bb79b14303e0fab0f2b9d617701331ed1515", + "sha256:276ae78153a94b664e700ac362587c73b84399bd1145e135287513442e7dfbc7", + "sha256:2b1a195efd347ede8bcf723e932300292eb13a9d2a3c1f84eb8f37cbbc905b7f", + "sha256:329a721253c7e4cbd7aad4a377745fbcc0607f9d72a3cc2102dd40519be75ed2", + "sha256:358331e21a897151e54d58e08d0219acf98ebb14c567267a87e971f3d2a3be59", + "sha256:3649bd3ae6a8ebea7dc381afb7f3c6db237fc7cebd05c8ac36ca8a4187b03b30", + "sha256:3713dc093d5048bfaedbba7a8dbc53e74c44a140d45ede020dc347dda18daf3f", + "sha256:3ef71ec876fcc4d3bbf2ae81961959e8d62f8d74a83d116668409c224012e3af", + "sha256:41ae8537ad371ec018e3c5da0eb3f3e40ee1011eb9be1da7f965357c4623c501", + "sha256:4a801c5e1e13272e0909c520708122496647d1279d252c9e6e07dac216accc41", + "sha256:4c83c64d05ffbbe12d4e8498ab72bdb05bcc1026340a4a597dc647a13c1605ec", + "sha256:4cebb9794f67266d65e7e4cbe5dcf063e29fc7b81c79dc9475bd476d9534150e", + "sha256:5668b3173bb0b2e65020b60d83f5910a7224027232c9f5dc05a71a1deac9f960", + "sha256:56e6a12ec8d7679f41b3750ffa426d22b44ef97be226a9bab00a03365f217b2b", + "sha256:582871902e1902b3c8e9b2c347f32a792a07094110c1bca6c2ea89b90150caac", + "sha256:5c8aa40f6ca803f95b1c1c5aeaee6237b9e879e4dfb46ad713229a63651a95fb", + "sha256:5d813fd871b3d5c3005157622ee102e8908ad6011ec915a18bd8fde673c4360e", + "sha256:5dd0ec5f514ed40e49bf961d49cf1bc2c72e9b50f29a163b2cc9030c6742aa73", + "sha256:5f3cf3721eaf8741cffaf092487f1ca80831202ce91672776b02b875580e174a", + "sha256:6294907eaaccf71c076abdd1c7954e272efa39bb043161b4b8aa1cd76a16ce43", + "sha256:64d094ea1aa97c6ded4748d40886076a931a8bf6f61b6e43e4a1041769c39dd2", + "sha256:6650a7bbe17a2717167e3e23c186849bae5cef35d38949549f1c116031b2b3aa", + "sha256:67b6655311b00581914aba481729971b88bb8bc7996206590700a3ac85e457b8", + "sha256:6b06c5d4e8701ac2ba99a2ef835e4e1b187d41095a9c619c5b185c9068ed2a49", + "sha256:6ce883906810b4c3bd90e0ada1f9e808d9ecf1c5f0b60c6b8831d6100bcc7dd6", + "sha256:6db09153d8438425e98cdc9a289c5fade04a5d2128faff8f227c459da21b9703", + "sha256:6f80fba4af0cb1d2344869d56430e304a51396b70d46b91a55ed4959993c0589", + "sha256:743e5811b0c377eb830150d675b0847a74a44d4ad5ab8845923d5b3a756d8100", + "sha256:753294d42fb072aa1775bfe1a2ba1012427376718fa4c72de52005a3d2a22178", + "sha256:7568f682c06f10f30ef643a1e8eec4afeecdafde5c4af1b574c6df079e96f96c", + "sha256:7706e15cdbf42f8fab1e6425247dfa98f4a6f8c63746c995d6a2017f78e619ae", + "sha256:785e7f517ebb9890813d31cb5d328fa5eda825bb205065cde760b3150e4de1f7", + "sha256:7a05c0240f6c711eb381ac392de987ee974fa9336071fb697768dfdb151345ce", + "sha256:7ce7eaf9a98680b4312b7cebcdd9352531c43db00fca586115845df388f3c465", + "sha256:7ce8e26b86a91e305858e018afc7a6e932f17428b1eaa60154bd1f7ee888b5f8", + "sha256:7d0324a35ab436c9d768753cbc3c47a865a2cbc0757066cb864747baa61f6ece", + "sha256:7e9b24cca4037a561422bf5dc52b38d390fb61f7bfff64053ce1b72f6938e6b2", + "sha256:810ca06cca91de9107718dc83d9ac4d2e86efd6c02cba49a190abcaf33fb0472", + "sha256:820f6ee5c06bc868335e3b6e42d7ef41f50dfb3ea32fbd523ab679d10d8741c0", + "sha256:82764c0bd697159fe9947ad59b6db6d7329e88505c8f98990eb07e84cc0a5d81", + "sha256:8ae65fdfb8a841556b52935dfd4c3f79132dc5253b12c0061b96415208f4d622", + "sha256:8d5b0ff3218858859910295df6953d7bafac3a48d5cd18f4e3ed9999efd2245f", + "sha256:95d6bf449a1ac81de562d65d180af5d8c19672793c81877a2eda8fde5d08f2fd", + "sha256:964c7aa318da542cdcc60d4a648377ffe1a2ef0eb1e996026c7f74507b720a78", + "sha256:96ef39add33ff58cd4c112cbac076726b96b98bb8f1e7f7595288dcfb2f10b57", + "sha256:a6612c2a844043e4d10a8324c54cdff0042c558eef30bd705770793d70b224aa", + "sha256:a8031074a397a5925d06b590121f8339d34a5a74cfe6970f8a1124eb8b83f4ac", + "sha256:aab9e522efff3993a9e98ab14263d4e20211e62da088298089a03056980a3e69", + "sha256:ae579143826c6f05a361d9546446c432a165ecf1c0b720bbfd81152645cb897d", + "sha256:ae90b9e50fe1bd115b24785e962b51130340408156d34d67b5f8f3fa6540938e", + "sha256:b18cf68255a476b927910c6873d9ed00da692bb293c5b10b282bd48a0afe3ae2", + "sha256:b7efb12e5071ad8d5b547487bdad489fbd4a5a35a0fc36a1941517a6ad7f23e0", + "sha256:c4d9f15ffe68bcd3898b0ad7233af01b15c57d91cd1667f8d868e0eacbfe3f87", + "sha256:c53100c8ee5a1e102766abde2158077d8c374bee0639201f11d3032e3555dfbc", + "sha256:c57e493a0faea1e4c38f860d6862ba6832723396c884fbf938ff5e9b224200e2", + "sha256:c8319e0bd6a7b45ad76166cc3d5d6a36c97d0c82a196f478c3ee5346566eebfd", + "sha256:caffda619099cfd4f63d48462f6aadbecee3ad9603b4b88b60cb821c1b258576", + "sha256:cc0c316fba3ce72ac3ab7902a888b9dc4979162d320823679da270c2d9ad0cad", + "sha256:cdd02a08205dc90238669f082747612cb3c82bd2c717adc60f9b9ecadb540f80", + "sha256:d50ac34835c6a4a0d456b5db559b82047403c4317b3bc73b3455fefdbdc54b0a", + "sha256:d6b9dd6aa03c812017411734e496c44fef29b43dba1e3dd1fa7361bbacfc1354", + "sha256:da3131ef2b940b99106f29dfbc30d9505643f766704e14c5d5e504e6a480c35e", + "sha256:da43cbe593e3c87d07108d0ebd73771dc414488f1f91ed2e204b0370b94b37ac", + "sha256:dd59638025160056687d598b054b64a79183f8065eae0d3f5ca523cde9943940", + "sha256:e1895e949f8849bc2757c0dbac28422a04be031204df46a56ab34bcf98507342", + "sha256:e1a79ad49f346aa1a2921f31e8dbbab4d64484823e813a002679eaa46cba39e1", + "sha256:e460475719721d59cd54a350c1f71c797c763212c836bf48585478c5514d2854", + "sha256:e64ffaf8f6e17ca15eb48344d86a7a741454526f3a3fa56bc493ad9d7ec63936", + "sha256:e6e3ccebdbd6e53474b0bb7ab8b88e83c0cfe91484b25e058e581348ee5a01a5", + "sha256:e758d271ed0286d146cf7c04c539a5169a888dd0b57026be621547e756af55bc", + "sha256:f087879f1ffde024dd2788a30d55acd67959dcf6c431e9d3682d1c491a0eb474", + "sha256:f477d26183e94eaafc60b983ab25af2a809a1b48ce4debb57b343f671b7a90b6", + "sha256:fc535cb898ef88333cf317777ecdfe0faac1c2a3187ef7eb061b6f7ecf7e6bae" + ], + "markers": "python_version >= '3.8'", + "version": "==2.23.2" }, "referencing": { "hashes": [ - "sha256:3c57da0513e9563eb7e203ebe9bb3a1b509b042016433bd1e45a2853466c3dd3", - "sha256:7e4dc12271d8e15612bfe35792f5ea1c40970dadf8624602e33db2758f7ee554" + "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", + "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de" ], "markers": "python_version >= '3.8'", - "version": "==0.32.1" + "version": "==0.35.1" }, "rpds-py": { "hashes": [ - "sha256:01f58a7306b64e0a4fe042047dd2b7d411ee82e54240284bab63e325762c1147", - "sha256:0210b2668f24c078307260bf88bdac9d6f1093635df5123789bfee4d8d7fc8e7", - "sha256:02866e060219514940342a1f84303a1ef7a1dad0ac311792fbbe19b521b489d2", - "sha256:0387ce69ba06e43df54e43968090f3626e231e4bc9150e4c3246947567695f68", - "sha256:060f412230d5f19fc8c8b75f315931b408d8ebf56aec33ef4168d1b9e54200b1", - "sha256:071bc28c589b86bc6351a339114fb7a029f5cddbaca34103aa573eba7b482382", - "sha256:0bfb09bf41fe7c51413f563373e5f537eaa653d7adc4830399d4e9bdc199959d", - "sha256:10162fe3f5f47c37ebf6d8ff5a2368508fe22007e3077bf25b9c7d803454d921", - "sha256:149c5cd24f729e3567b56e1795f74577aa3126c14c11e457bec1b1c90d212e38", - "sha256:1701fc54460ae2e5efc1dd6350eafd7a760f516df8dbe51d4a1c79d69472fbd4", - "sha256:1957a2ab607f9added64478a6982742eb29f109d89d065fa44e01691a20fc20a", - "sha256:1a746a6d49665058a5896000e8d9d2f1a6acba8a03b389c1e4c06e11e0b7f40d", - "sha256:1bfcad3109c1e5ba3cbe2f421614e70439f72897515a96c462ea657261b96518", - "sha256:1d36b2b59e8cc6e576f8f7b671e32f2ff43153f0ad6d0201250a7c07f25d570e", - "sha256:1db228102ab9d1ff4c64148c96320d0be7044fa28bd865a9ce628ce98da5973d", - "sha256:1dc29db3900cb1bb40353772417800f29c3d078dbc8024fd64655a04ee3c4bdf", - "sha256:1e626b365293a2142a62b9a614e1f8e331b28f3ca57b9f05ebbf4cf2a0f0bdc5", - "sha256:1f3c3461ebb4c4f1bbc70b15d20b565759f97a5aaf13af811fcefc892e9197ba", - "sha256:20de7b7179e2031a04042e85dc463a93a82bc177eeba5ddd13ff746325558aa6", - "sha256:24e4900a6643f87058a27320f81336d527ccfe503984528edde4bb660c8c8d59", - "sha256:2528ff96d09f12e638695f3a2e0c609c7b84c6df7c5ae9bfeb9252b6fa686253", - "sha256:25f071737dae674ca8937a73d0f43f5a52e92c2d178330b4c0bb6ab05586ffa6", - "sha256:270987bc22e7e5a962b1094953ae901395e8c1e1e83ad016c5cfcfff75a15a3f", - "sha256:292f7344a3301802e7c25c53792fae7d1593cb0e50964e7bcdcc5cf533d634e3", - "sha256:2953937f83820376b5979318840f3ee47477d94c17b940fe31d9458d79ae7eea", - "sha256:2a792b2e1d3038daa83fa474d559acfd6dc1e3650ee93b2662ddc17dbff20ad1", - "sha256:2a7b2f2f56a16a6d62e55354dd329d929560442bd92e87397b7a9586a32e3e76", - "sha256:2f4eb548daf4836e3b2c662033bfbfc551db58d30fd8fe660314f86bf8510b93", - "sha256:3664d126d3388a887db44c2e293f87d500c4184ec43d5d14d2d2babdb4c64cad", - "sha256:3677fcca7fb728c86a78660c7fb1b07b69b281964673f486ae72860e13f512ad", - "sha256:380e0df2e9d5d5d339803cfc6d183a5442ad7ab3c63c2a0982e8c824566c5ccc", - "sha256:3ac732390d529d8469b831949c78085b034bff67f584559340008d0f6041a049", - "sha256:4128980a14ed805e1b91a7ed551250282a8ddf8201a4e9f8f5b7e6225f54170d", - "sha256:4341bd7579611cf50e7b20bb8c2e23512a3dc79de987a1f411cb458ab670eb90", - "sha256:436474f17733c7dca0fbf096d36ae65277e8645039df12a0fa52445ca494729d", - "sha256:4dc889a9d8a34758d0fcc9ac86adb97bab3fb7f0c4d29794357eb147536483fd", - "sha256:4e21b76075c01d65d0f0f34302b5a7457d95721d5e0667aea65e5bb3ab415c25", - "sha256:516fb8c77805159e97a689e2f1c80655c7658f5af601c34ffdb916605598cda2", - "sha256:5576ee2f3a309d2bb403ec292d5958ce03953b0e57a11d224c1f134feaf8c40f", - "sha256:5a024fa96d541fd7edaa0e9d904601c6445e95a729a2900c5aec6555fe921ed6", - "sha256:5d0e8a6434a3fbf77d11448c9c25b2f25244226cfbec1a5159947cac5b8c5fa4", - "sha256:5e7d63ec01fe7c76c2dbb7e972fece45acbb8836e72682bde138e7e039906e2c", - "sha256:60e820ee1004327609b28db8307acc27f5f2e9a0b185b2064c5f23e815f248f8", - "sha256:637b802f3f069a64436d432117a7e58fab414b4e27a7e81049817ae94de45d8d", - "sha256:65dcf105c1943cba45d19207ef51b8bc46d232a381e94dd38719d52d3980015b", - "sha256:698ea95a60c8b16b58be9d854c9f993c639f5c214cf9ba782eca53a8789d6b19", - "sha256:70fcc6c2906cfa5c6a552ba7ae2ce64b6c32f437d8f3f8eea49925b278a61453", - "sha256:720215373a280f78a1814becb1312d4e4d1077b1202a56d2b0815e95ccb99ce9", - "sha256:7450dbd659fed6dd41d1a7d47ed767e893ba402af8ae664c157c255ec6067fde", - "sha256:7b7d9ca34542099b4e185b3c2a2b2eda2e318a7dbde0b0d83357a6d4421b5296", - "sha256:7fbd70cb8b54fe745301921b0816c08b6d917593429dfc437fd024b5ba713c58", - "sha256:81038ff87a4e04c22e1d81f947c6ac46f122e0c80460b9006e6517c4d842a6ec", - "sha256:810685321f4a304b2b55577c915bece4c4a06dfe38f6e62d9cc1d6ca8ee86b99", - "sha256:82ada4a8ed9e82e443fcef87e22a3eed3654dd3adf6e3b3a0deb70f03e86142a", - "sha256:841320e1841bb53fada91c9725e766bb25009cfd4144e92298db296fb6c894fb", - "sha256:8587fd64c2a91c33cdc39d0cebdaf30e79491cc029a37fcd458ba863f8815383", - "sha256:8ffe53e1d8ef2520ebcf0c9fec15bb721da59e8ef283b6ff3079613b1e30513d", - "sha256:9051e3d2af8f55b42061603e29e744724cb5f65b128a491446cc029b3e2ea896", - "sha256:91e5a8200e65aaac342a791272c564dffcf1281abd635d304d6c4e6b495f29dc", - "sha256:93432e747fb07fa567ad9cc7aaadd6e29710e515aabf939dfbed8046041346c6", - "sha256:938eab7323a736533f015e6069a7d53ef2dcc841e4e533b782c2bfb9fb12d84b", - "sha256:9584f8f52010295a4a417221861df9bea4c72d9632562b6e59b3c7b87a1522b7", - "sha256:9737bdaa0ad33d34c0efc718741abaafce62fadae72c8b251df9b0c823c63b22", - "sha256:99da0a4686ada4ed0f778120a0ea8d066de1a0a92ab0d13ae68492a437db78bf", - "sha256:99f567dae93e10be2daaa896e07513dd4bf9c2ecf0576e0533ac36ba3b1d5394", - "sha256:9bdf1303df671179eaf2cb41e8515a07fc78d9d00f111eadbe3e14262f59c3d0", - "sha256:9f0e4dc0f17dcea4ab9d13ac5c666b6b5337042b4d8f27e01b70fae41dd65c57", - "sha256:a000133a90eea274a6f28adc3084643263b1e7c1a5a66eb0a0a7a36aa757ed74", - "sha256:a3264e3e858de4fc601741498215835ff324ff2482fd4e4af61b46512dd7fc83", - "sha256:a71169d505af63bb4d20d23a8fbd4c6ce272e7bce6cc31f617152aa784436f29", - "sha256:a967dd6afda7715d911c25a6ba1517975acd8d1092b2f326718725461a3d33f9", - "sha256:aa5bfb13f1e89151ade0eb812f7b0d7a4d643406caaad65ce1cbabe0a66d695f", - "sha256:ae35e8e6801c5ab071b992cb2da958eee76340e6926ec693b5ff7d6381441745", - "sha256:b686f25377f9c006acbac63f61614416a6317133ab7fafe5de5f7dc8a06d42eb", - "sha256:b760a56e080a826c2e5af09002c1a037382ed21d03134eb6294812dda268c811", - "sha256:b86b21b348f7e5485fae740d845c65a880f5d1eda1e063bc59bef92d1f7d0c55", - "sha256:b9412abdf0ba70faa6e2ee6c0cc62a8defb772e78860cef419865917d86c7342", - "sha256:bd345a13ce06e94c753dab52f8e71e5252aec1e4f8022d24d56decd31e1b9b23", - "sha256:be22ae34d68544df293152b7e50895ba70d2a833ad9566932d750d3625918b82", - "sha256:bf046179d011e6114daf12a534d874958b039342b347348a78b7cdf0dd9d6041", - "sha256:c3d2010656999b63e628a3c694f23020322b4178c450dc478558a2b6ef3cb9bb", - "sha256:c64602e8be701c6cfe42064b71c84ce62ce66ddc6422c15463fd8127db3d8066", - "sha256:d65e6b4f1443048eb7e833c2accb4fa7ee67cc7d54f31b4f0555b474758bee55", - "sha256:d8bbd8e56f3ba25a7d0cf980fc42b34028848a53a0e36c9918550e0280b9d0b6", - "sha256:da1ead63368c04a9bded7904757dfcae01eba0e0f9bc41d3d7f57ebf1c04015a", - "sha256:dbbb95e6fc91ea3102505d111b327004d1c4ce98d56a4a02e82cd451f9f57140", - "sha256:dbc56680ecf585a384fbd93cd42bc82668b77cb525343170a2d86dafaed2a84b", - "sha256:df3b6f45ba4515632c5064e35ca7f31d51d13d1479673185ba8f9fefbbed58b9", - "sha256:dfe07308b311a8293a0d5ef4e61411c5c20f682db6b5e73de6c7c8824272c256", - "sha256:e796051f2070f47230c745d0a77a91088fbee2cc0502e9b796b9c6471983718c", - "sha256:efa767c220d94aa4ac3a6dd3aeb986e9f229eaf5bce92d8b1b3018d06bed3772", - "sha256:f0b8bf5b8db49d8fd40f54772a1dcf262e8be0ad2ab0206b5a2ec109c176c0a4", - "sha256:f175e95a197f6a4059b50757a3dca33b32b61691bdbd22c29e8a8d21d3914cae", - "sha256:f2f3b28b40fddcb6c1f1f6c88c6f3769cd933fa493ceb79da45968a21dccc920", - "sha256:f6c43b6f97209e370124baf2bf40bb1e8edc25311a158867eb1c3a5d449ebc7a", - "sha256:f7f4cb1f173385e8a39c29510dd11a78bf44e360fb75610594973f5ea141028b", - "sha256:fad059a4bd14c45776600d223ec194e77db6c20255578bb5bcdd7c18fd169361", - "sha256:ff1dcb8e8bc2261a088821b2595ef031c91d499a0c1b031c152d43fe0a6ecec8", - "sha256:ffee088ea9b593cc6160518ba9bd319b5475e5f3e578e4552d63818773c6f56a" + "sha256:02a0629ec053fc013808a85178524e3cb63a61dbc35b22499870194a63578fb9", + "sha256:07924c1b938798797d60c6308fa8ad3b3f0201802f82e4a2c41bb3fafb44cc28", + "sha256:07f59760ef99f31422c49038964b31c4dfcfeb5d2384ebfc71058a7c9adae2d2", + "sha256:0a3a1e9ee9728b2c1734f65d6a1d376c6f2f6fdcc13bb007a08cc4b1ff576dc5", + "sha256:0a90c373ea2975519b58dece25853dbcb9779b05cc46b4819cb1917e3b3215b6", + "sha256:0ad56edabcdb428c2e33bbf24f255fe2b43253b7d13a2cdbf05de955217313e6", + "sha256:0b581f47257a9fce535c4567782a8976002d6b8afa2c39ff616edf87cbeff712", + "sha256:0f8f741b6292c86059ed175d80eefa80997125b7c478fb8769fd9ac8943a16c0", + "sha256:0fc212779bf8411667234b3cdd34d53de6c2b8b8b958e1e12cb473a5f367c338", + "sha256:13c56de6518e14b9bf6edde23c4c39dac5b48dcf04160ea7bce8fca8397cdf86", + "sha256:142c0a5124d9bd0e2976089484af5c74f47bd3298f2ed651ef54ea728d2ea42c", + "sha256:14511a539afee6f9ab492b543060c7491c99924314977a55c98bfa2ee29ce78c", + "sha256:15a842bb369e00295392e7ce192de9dcbf136954614124a667f9f9f17d6a216f", + "sha256:16d4477bcb9fbbd7b5b0e4a5d9b493e42026c0bf1f06f723a9353f5153e75d30", + "sha256:1791ff70bc975b098fe6ecf04356a10e9e2bd7dc21fa7351c1742fdeb9b4966f", + "sha256:19b73643c802f4eaf13d97f7855d0fb527fbc92ab7013c4ad0e13a6ae0ed23bd", + "sha256:200a23239781f46149e6a415f1e870c5ef1e712939fe8fa63035cd053ac2638e", + "sha256:2249280b870e6a42c0d972339e9cc22ee98730a99cd7f2f727549af80dd5a963", + "sha256:2b431c777c9653e569986ecf69ff4a5dba281cded16043d348bf9ba505486f36", + "sha256:2cc3712a4b0b76a1d45a9302dd2f53ff339614b1c29603a911318f2357b04dd2", + "sha256:2fbb0ffc754490aff6dabbf28064be47f0f9ca0b9755976f945214965b3ace7e", + "sha256:32b922e13d4c0080d03e7b62991ad7f5007d9cd74e239c4b16bc85ae8b70252d", + "sha256:36785be22066966a27348444b40389f8444671630063edfb1a2eb04318721e17", + "sha256:37fe0f12aebb6a0e3e17bb4cd356b1286d2d18d2e93b2d39fe647138458b4bcb", + "sha256:3aea7eed3e55119635a74bbeb80b35e776bafccb70d97e8ff838816c124539f1", + "sha256:3c6afcf2338e7f374e8edc765c79fbcb4061d02b15dd5f8f314a4af2bdc7feb5", + "sha256:3ccb8ac2d3c71cda472b75af42818981bdacf48d2e21c36331b50b4f16930163", + "sha256:3d089d0b88996df627693639d123c8158cff41c0651f646cd8fd292c7da90eaf", + "sha256:3dd645e2b0dcb0fd05bf58e2e54c13875847687d0b71941ad2e757e5d89d4356", + "sha256:3e310838a5801795207c66c73ea903deda321e6146d6f282e85fa7e3e4854804", + "sha256:42cbde7789f5c0bcd6816cb29808e36c01b960fb5d29f11e052215aa85497c93", + "sha256:483b29f6f7ffa6af845107d4efe2e3fa8fb2693de8657bc1849f674296ff6a5a", + "sha256:4888e117dd41b9d34194d9e31631af70d3d526efc363085e3089ab1a62c32ed1", + "sha256:49fe9b04b6fa685bd39237d45fad89ba19e9163a1ccaa16611a812e682913496", + "sha256:4a5a844f68776a7715ecb30843b453f07ac89bad393431efbf7accca3ef599c1", + "sha256:4a916087371afd9648e1962e67403c53f9c49ca47b9680adbeef79da3a7811b0", + "sha256:4f676e21db2f8c72ff0936f895271e7a700aa1f8d31b40e4e43442ba94973899", + "sha256:518d2ca43c358929bf08f9079b617f1c2ca6e8848f83c1225c88caeac46e6cbc", + "sha256:5265505b3d61a0f56618c9b941dc54dc334dc6e660f1592d112cd103d914a6db", + "sha256:55cd1fa4ecfa6d9f14fbd97ac24803e6f73e897c738f771a9fe038f2f11ff07c", + "sha256:58b1d5dd591973d426cbb2da5e27ba0339209832b2f3315928c9790e13f159e8", + "sha256:59240685e7da61fb78f65a9f07f8108e36a83317c53f7b276b4175dc44151684", + "sha256:5b48e790e0355865197ad0aca8cde3d8ede347831e1959e158369eb3493d2191", + "sha256:5d4eea0761e37485c9b81400437adb11c40e13ef513375bbd6973e34100aeb06", + "sha256:648386ddd1e19b4a6abab69139b002bc49ebf065b596119f8f37c38e9ecee8ff", + "sha256:653647b8838cf83b2e7e6a0364f49af96deec64d2a6578324db58380cff82aca", + "sha256:6740a3e8d43a32629bb9b009017ea5b9e713b7210ba48ac8d4cb6d99d86c8ee8", + "sha256:6889469bfdc1eddf489729b471303739bf04555bb151fe8875931f8564309afc", + "sha256:68cb0a499f2c4a088fd2f521453e22ed3527154136a855c62e148b7883b99f9a", + "sha256:6aa97af1558a9bef4025f8f5d8c60d712e0a3b13a2fe875511defc6ee77a1ab7", + "sha256:6b73c67850ca7cae0f6c56f71e356d7e9fa25958d3e18a64927c2d930859b8e4", + "sha256:6c8e9340ce5a52f95fa7d3b552b35c7e8f3874d74a03a8a69279fd5fca5dc751", + "sha256:6ca91093a4a8da4afae7fe6a222c3b53ee4eef433ebfee4d54978a103435159e", + "sha256:754bbed1a4ca48479e9d4182a561d001bbf81543876cdded6f695ec3d465846b", + "sha256:762703bdd2b30983c1d9e62b4c88664df4a8a4d5ec0e9253b0231171f18f6d75", + "sha256:78f0b6877bfce7a3d1ff150391354a410c55d3cdce386f862926a4958ad5ab7e", + "sha256:7a07ced2b22f0cf0b55a6a510078174c31b6d8544f3bc00c2bcee52b3d613f74", + "sha256:7dca7081e9a0c3b6490a145593f6fe3173a94197f2cb9891183ef75e9d64c425", + "sha256:7e21b7031e17c6b0e445f42ccc77f79a97e2687023c5746bfb7a9e45e0921b84", + "sha256:7f5179583d7a6cdb981151dd349786cbc318bab54963a192692d945dd3f6435d", + "sha256:83cba698cfb3c2c5a7c3c6bac12fe6c6a51aae69513726be6411076185a8b24a", + "sha256:842c19a6ce894493563c3bd00d81d5100e8e57d70209e84d5491940fdb8b9e3a", + "sha256:84b8382a90539910b53a6307f7c35697bc7e6ffb25d9c1d4e998a13e842a5e83", + "sha256:8ba6f89cac95c0900d932c9efb7f0fb6ca47f6687feec41abcb1bd5e2bd45535", + "sha256:8bbe951244a838a51289ee53a6bae3a07f26d4e179b96fc7ddd3301caf0518eb", + "sha256:925d176a549f4832c6f69fa6026071294ab5910e82a0fe6c6228fce17b0706bd", + "sha256:92b68b79c0da2a980b1c4197e56ac3dd0c8a149b4603747c4378914a68706979", + "sha256:93da1d3db08a827eda74356f9f58884adb254e59b6664f64cc04cdff2cc19b0d", + "sha256:95f3b65d2392e1c5cec27cff08fdc0080270d5a1a4b2ea1d51d5f4a2620ff08d", + "sha256:9c4cb04a16b0f199a8c9bf807269b2f63b7b5b11425e4a6bd44bd6961d28282c", + "sha256:a624cc00ef2158e04188df5e3016385b9353638139a06fb77057b3498f794782", + "sha256:a649dfd735fff086e8a9d0503a9f0c7d01b7912a333c7ae77e1515c08c146dad", + "sha256:a94e52537a0e0a85429eda9e49f272ada715506d3b2431f64b8a3e34eb5f3e75", + "sha256:aa7ac11e294304e615b43f8c441fee5d40094275ed7311f3420d805fde9b07b4", + "sha256:b41b6321805c472f66990c2849e152aff7bc359eb92f781e3f606609eac877ad", + "sha256:b71b8666eeea69d6363248822078c075bac6ed135faa9216aa85f295ff009b1e", + "sha256:b9c2fe36d1f758b28121bef29ed1dee9b7a2453e997528e7d1ac99b94892527c", + "sha256:bb63804105143c7e24cee7db89e37cb3f3941f8e80c4379a0b355c52a52b6780", + "sha256:be5ef2f1fc586a7372bfc355986226484e06d1dc4f9402539872c8bb99e34b01", + "sha256:c142b88039b92e7e0cb2552e8967077e3179b22359e945574f5e2764c3953dcf", + "sha256:c14937af98c4cc362a1d4374806204dd51b1e12dded1ae30645c298e5a5c4cb1", + "sha256:ca449520e7484534a2a44faf629362cae62b660601432d04c482283c47eaebab", + "sha256:cd945871335a639275eee904caef90041568ce3b42f402c6959b460d25ae8732", + "sha256:d0b937b2a1988f184a3e9e577adaa8aede21ec0b38320d6009e02bd026db04fa", + "sha256:d126b52e4a473d40232ec2052a8b232270ed1f8c9571aaf33f73a14cc298c24f", + "sha256:d8761c3c891cc51e90bc9926d6d2f59b27beaf86c74622c8979380a29cc23ac3", + "sha256:d9ecb51120de61e4604650666d1f2b68444d46ae18fd492245a08f53ad2b7711", + "sha256:da584ff96ec95e97925174eb8237e32f626e7a1a97888cdd27ee2f1f24dd0ad8", + "sha256:dbcf360c9e3399b056a238523146ea77eeb2a596ce263b8814c900263e46031a", + "sha256:dbddc10776ca7ebf2a299c41a4dde8ea0d8e3547bfd731cb87af2e8f5bf8962d", + "sha256:dc73505153798c6f74854aba69cc75953888cf9866465196889c7cdd351e720c", + "sha256:e13de156137b7095442b288e72f33503a469aa1980ed856b43c353ac86390519", + "sha256:e1791c4aabd117653530dccd24108fa03cc6baf21f58b950d0a73c3b3b29a350", + "sha256:e75ba609dba23f2c95b776efb9dd3f0b78a76a151e96f96cc5b6b1b0004de66f", + "sha256:e79059d67bea28b53d255c1437b25391653263f0e69cd7dec170d778fdbca95e", + "sha256:ecd27a66740ffd621d20b9a2f2b5ee4129a56e27bfb9458a3bcc2e45794c96cb", + "sha256:f009c69bc8c53db5dfab72ac760895dc1f2bc1b62ab7408b253c8d1ec52459fc", + "sha256:f16bc1334853e91ddaaa1217045dd7be166170beec337576818461268a3de67f", + "sha256:f19169781dddae7478a32301b499b2858bc52fc45a112955e798ee307e294977", + "sha256:fa3060d885657abc549b2a0f8e1b79699290e5d83845141717c6c90c2df38311", + "sha256:fa41a64ac5b08b292906e248549ab48b69c5428f3987b09689ab2441f267d04d", + "sha256:fbf15aff64a163db29a91ed0868af181d6f68ec1a3a7d5afcfe4501252840bad", + "sha256:fe00a9057d100e69b4ae4a094203a708d65b0f345ed546fdef86498bf5390982" ], "markers": "python_version >= '3.8'", - "version": "==0.17.1" + "version": "==0.20.1" }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "markers": "python_version < '3.13'", + "version": "==4.12.2" + }, + "tzdata": { + "hashes": [ + "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", + "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.2" } }, "develop": { @@ -247,11 +328,19 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" + }, + "backports.tarfile": { + "hashes": [ + "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", + "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991" + ], + "markers": "python_version < '3.12'", + "version": "==1.2.0" }, "black": { "hashes": [ @@ -285,107 +374,122 @@ }, "certifi": { "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" ], "markers": "python_version >= '3.6'", - "version": "==2023.11.17" + "version": "==2024.8.30" }, "charset-normalizer": { "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", + "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", + "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", + "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", + "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", + "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", + "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", + "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", + "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", + "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", + "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", + "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", + "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", + "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", + "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", + "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", + "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", + "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", + "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", + "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", + "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", + "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", + "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", + "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", + "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", + "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", + "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", + "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", + "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", + "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", + "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", + "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", + "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", + "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", + "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", + "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", + "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", + "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", + "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", + "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", + "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", + "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", + "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", + "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", + "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", + "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", + "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", + "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", + "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", + "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", + "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", + "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", + "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", + "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", + "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", + "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", + "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", + "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", + "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", + "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", + "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", + "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", + "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", + "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", + "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", + "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", + "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", + "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", + "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", + "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", + "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", + "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", + "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", + "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", + "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", + "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", + "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", + "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", + "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", + "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", + "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", + "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", + "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", + "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", + "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", + "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", + "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", + "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", + "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", + "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", + "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", + "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", + "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", + "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", + "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", + "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", + "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", + "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", + "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", + "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", + "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", + "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", + "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", + "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", + "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "version": "==3.4.0" }, "click": { "hashes": [ @@ -408,85 +512,95 @@ "toml" ], "hashes": [ - "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca", - "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471", - "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a", - "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058", - "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85", - "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143", - "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446", - "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590", - "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a", - "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105", - "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9", - "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a", - "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac", - "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25", - "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2", - "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450", - "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932", - "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba", - "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137", - "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae", - "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614", - "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70", - "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e", - "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505", - "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870", - "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc", - "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451", - "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7", - "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e", - "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566", - "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5", - "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26", - "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2", - "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42", - "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555", - "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43", - "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed", - "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa", - "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516", - "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952", - "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd", - "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09", - "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c", - "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f", - "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6", - "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1", - "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0", - "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e", - "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9", - "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9", - "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e", - "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06" - ], - "markers": "python_version >= '3.8'", - "version": "==7.4.0" + "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376", + "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", + "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111", + "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", + "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", + "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", + "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", + "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", + "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", + "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c", + "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", + "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", + "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", + "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0", + "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", + "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", + "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", + "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", + "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", + "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", + "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", + "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", + "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", + "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", + "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", + "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", + "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", + "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", + "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", + "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901", + "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", + "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", + "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", + "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", + "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", + "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", + "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", + "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", + "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3", + "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", + "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076", + "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", + "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", + "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", + "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", + "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", + "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", + "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09", + "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", + "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", + "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f", + "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", + "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", + "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", + "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", + "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", + "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", + "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", + "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", + "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", + "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", + "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858" + ], + "markers": "python_version >= '3.9'", + "version": "==7.6.4" }, "docutils": { "hashes": [ - "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6", - "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b" + "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", + "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2" ], - "markers": "python_version >= '3.7'", - "version": "==0.20.1" + "markers": "python_version >= '3.9'", + "version": "==0.21.2" }, "exceptiongroup": { "hashes": [ - "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", - "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.0" + "version": "==1.2.2" }, "execnet": { "hashes": [ - "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", - "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" + "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", + "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.2" + "markers": "python_version >= '3.8'", + "version": "==2.1.1" }, "flake8": { "hashes": [ @@ -526,19 +640,19 @@ }, "idna": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], - "markers": "python_version >= '3.5'", - "version": "==3.6" + "markers": "python_version >= '3.6'", + "version": "==3.10" }, "importlib-metadata": { "hashes": [ - "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e", - "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc" + "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", + "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7" ], "markers": "python_version >= '3.8'", - "version": "==7.0.1" + "version": "==8.5.0" }, "iniconfig": { "hashes": [ @@ -550,19 +664,35 @@ }, "jaraco.classes": { "hashes": [ - "sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb", - "sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621" + "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", + "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" ], "markers": "python_version >= '3.8'", - "version": "==3.3.0" + "version": "==3.4.0" + }, + "jaraco.context": { + "hashes": [ + "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", + "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4" + ], + "markers": "python_version >= '3.8'", + "version": "==6.0.1" + }, + "jaraco.functools": { + "hashes": [ + "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", + "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649" + ], + "markers": "python_version >= '3.8'", + "version": "==4.1.0" }, "keyring": { "hashes": [ - "sha256:4446d35d636e6a10b8bce7caa66913dd9eca5fd222ca03a3d42c38608ac30836", - "sha256:e730ecffd309658a08ee82535a3b5ec4b4c8669a9be11efb66249d8e0aeb9a25" + "sha256:4c753b3ec91717fe713c4edd522d625889d8973a349b0e582622f49766de58e6", + "sha256:e67f8ac32b04be4714b42fe84ce7dad9c40985b9ca827c592cc303e7c26d9741" ], "markers": "python_version >= '3.8'", - "version": "==24.3.0" + "version": "==25.5.0" }, "markdown-it-py": { "hashes": [ @@ -590,45 +720,45 @@ }, "more-itertools": { "hashes": [ - "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684", - "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1" + "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", + "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6" ], "markers": "python_version >= '3.8'", - "version": "==10.2.0" + "version": "==10.5.0" }, "mypy": { "hashes": [ - "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", - "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", - "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", - "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", - "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", - "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", - "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", - "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", - "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", - "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", - "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", - "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", - "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", - "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", - "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", - "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", - "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", - "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", - "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", - "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", - "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", - "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", - "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", - "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", - "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", - "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", - "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.8.0" + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -640,32 +770,32 @@ }, "nh3": { "hashes": [ - "sha256:0d02d0ff79dfd8208ed25a39c12cbda092388fff7f1662466e27d97ad011b770", - "sha256:3277481293b868b2715907310c7be0f1b9d10491d5adf9fce11756a97e97eddf", - "sha256:3b803a5875e7234907f7d64777dfde2b93db992376f3d6d7af7f3bc347deb305", - "sha256:427fecbb1031db085eaac9931362adf4a796428ef0163070c484b5a768e71601", - "sha256:5f0d77272ce6d34db6c87b4f894f037d55183d9518f948bba236fe81e2bb4e28", - "sha256:60684857cfa8fdbb74daa867e5cad3f0c9789415aba660614fe16cd66cbb9ec7", - "sha256:6f42f99f0cf6312e470b6c09e04da31f9abaadcd3eb591d7d1a88ea931dca7f3", - "sha256:86e447a63ca0b16318deb62498db4f76fc60699ce0a1231262880b38b6cff911", - "sha256:8d595df02413aa38586c24811237e95937ef18304e108b7e92c890a06793e3bf", - "sha256:9c0d415f6b7f2338f93035bba5c0d8c1b464e538bfbb1d598acd47d7969284f0", - "sha256:a5167a6403d19c515217b6bcaaa9be420974a6ac30e0da9e84d4fc67a5d474c5", - "sha256:ac19c0d68cd42ecd7ead91a3a032fdfff23d29302dbb1311e641a130dfefba97", - "sha256:b1e97221cedaf15a54f5243f2c5894bb12ca951ae4ddfd02a9d4ea9df9e1a29d", - "sha256:bc2d086fb540d0fa52ce35afaded4ea526b8fc4d3339f783db55c95de40ef02e", - "sha256:d1e30ff2d8d58fb2a14961f7aac1bbb1c51f9bdd7da727be35c63826060b0bf3", - "sha256:f3b53ba93bb7725acab1e030bc2ecd012a817040fd7851b332f86e2f9bb98dc6" - ], - "version": "==0.2.15" + "sha256:0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164", + "sha256:14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86", + "sha256:19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b", + "sha256:34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad", + "sha256:36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204", + "sha256:3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a", + "sha256:42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200", + "sha256:5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189", + "sha256:6955369e4d9f48f41e3f238a9e60f9410645db7e07435e62c6a9ea6135a4907f", + "sha256:7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811", + "sha256:8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844", + "sha256:94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4", + "sha256:a7f1b5b2c15866f2db413a3649a8fe4fd7b428ae58be2c0f6bca5eefd53ca2be", + "sha256:c8b3a1cebcba9b3669ed1a84cc65bf005728d2f0bc1ed2a6594a992e817f3a50", + "sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307", + "sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe" + ], + "version": "==0.2.18" }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", + "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==24.1" }, "pathspec": { "hashes": [ @@ -677,27 +807,27 @@ }, "pkginfo": { "hashes": [ - "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546", - "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046" + "sha256:9ec518eefccd159de7ed45386a6bb4c6ca5fa2cb3bd9b71154fae44f6f1b36a3", + "sha256:c6bc916b8298d159e31f2c216e35ee5b86da7da18874f879798d0a1983537c86" ], - "markers": "python_version >= '3.6'", - "version": "==1.9.6" + "markers": "python_version >= '3.8'", + "version": "==1.11.2" }, "platformdirs": { "hashes": [ - "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380", - "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420" + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" ], "markers": "python_version >= '3.8'", - "version": "==4.1.0" + "version": "==4.3.6" }, "pluggy": { "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" ], "markers": "python_version >= '3.8'", - "version": "==1.3.0" + "version": "==1.5.0" }, "pprintpp": { "hashes": [ @@ -732,11 +862,11 @@ }, "pygments": { "hashes": [ - "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", - "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367" + "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", + "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" ], - "markers": "python_version >= '3.7'", - "version": "==2.17.2" + "markers": "python_version >= '3.8'", + "version": "==2.18.0" }, "pytest": { "hashes": [ @@ -775,19 +905,19 @@ }, "readme-renderer": { "hashes": [ - "sha256:13d039515c1f24de668e2c93f2e877b9dbe6c6c32328b90a40a49d8b2b85f36d", - "sha256:2d55489f83be4992fe4454939d1a051c33edbab778e82761d060c9fc6b308cd1" + "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", + "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1" ], - "markers": "python_version >= '3.8'", - "version": "==42.0" + "markers": "python_version >= '3.9'", + "version": "==44.0" }, "requests": { "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], - "markers": "python_version >= '3.7'", - "version": "==2.31.0" + "markers": "python_version >= '3.8'", + "version": "==2.32.3" }, "requests-toolbelt": { "hashes": [ @@ -807,11 +937,11 @@ }, "rich": { "hashes": [ - "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa", - "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235" + "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", + "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==13.7.0" + "markers": "python_full_version >= '3.8.0'", + "version": "==13.9.4" }, "snowballstemmer": { "hashes": [ @@ -822,11 +952,11 @@ }, "tomli": { "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", + "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed" ], "markers": "python_version < '3.11'", - "version": "==2.0.1" + "version": "==2.0.2" }, "twine": { "hashes": [ @@ -848,19 +978,19 @@ }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "markers": "python_version < '3.13'", + "version": "==4.12.2" }, "urllib3": { "hashes": [ - "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", - "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54" + "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" ], "markers": "python_version >= '3.8'", - "version": "==2.1.0" + "version": "==2.2.3" }, "wheel": { "hashes": [ @@ -873,11 +1003,11 @@ }, "zipp": { "hashes": [ - "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31", - "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0" + "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", + "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29" ], "markers": "python_version >= '3.8'", - "version": "==3.17.0" + "version": "==3.20.2" } } } diff --git a/shared-data/python/mypy.ini b/shared-data/python/mypy.ini index 17d9374de2a..a12fe382e75 100644 --- a/shared-data/python/mypy.ini +++ b/shared-data/python/mypy.ini @@ -16,26 +16,7 @@ warn_untyped_fields = True warn_return_any = False [mypy-opentrons_shared_data.pipette.*] -no_implicit_optional = False warn_return_any = False [mypy-opentrons_shared_data.protocol.*] warn_return_any = False - -[mypy-tests.deck.*] -disallow_untyped_defs = False - -[mypy-tests.labware.*] -disallow_untyped_defs = False -disallow_untyped_calls = False - -[mypy-tests.module.*] -disallow_untyped_defs = False -disallow_untyped_calls = False - -[mypy-tests.pipette.*] -disallow_untyped_defs = False -disallow_untyped_calls = False - -[mypy-tests.protocol.*] -disallow_untyped_defs = False diff --git a/shared-data/python/opentrons_shared_data/gripper/__init__.py b/shared-data/python/opentrons_shared_data/gripper/__init__.py index c9ee59543b6..ab5ab38af72 100644 --- a/shared-data/python/opentrons_shared_data/gripper/__init__.py +++ b/shared-data/python/opentrons_shared_data/gripper/__init__.py @@ -45,7 +45,7 @@ def load_definition( """Load gripper definition based on schema version and gripper model.""" try: path = Path("gripper") / "definitions" / f"{version}" / f"{model.value}.json" - return GripperDefinition.parse_obj(json.loads(load_shared_data(path))) + return GripperDefinition.model_validate(json.loads(load_shared_data(path))) except FileNotFoundError: raise InvalidGripperDefinition( f"Gripper model {model} definition in schema version {version} does not exist." diff --git a/shared-data/python/opentrons_shared_data/gripper/gripper_definition.py b/shared-data/python/opentrons_shared_data/gripper/gripper_definition.py index 707d960a9ba..99e362c25d0 100644 --- a/shared-data/python/opentrons_shared_data/gripper/gripper_definition.py +++ b/shared-data/python/opentrons_shared_data/gripper/gripper_definition.py @@ -1,8 +1,8 @@ """Gripper configurations.""" -from typing_extensions import Literal -from typing import TYPE_CHECKING, List, Dict, Tuple, Any, NewType -from pydantic import BaseModel, Field, conint, confloat +from typing_extensions import Annotated, Literal +from typing import List, Dict, Tuple, Any, NewType +from pydantic import ConfigDict, BaseModel, Field from enum import Enum @@ -42,22 +42,20 @@ def __str__(self) -> str: GripperSchema = Dict[str, Any] -if TYPE_CHECKING: - _StrictNonNegativeInt = int - _StrictNonNegativeFloat = float -else: - _StrictNonNegativeInt = conint(strict=True, ge=0) - _StrictNonNegativeFloat = confloat(strict=True, ge=0.0) +_StrictNonNegativeInt = Annotated[int, Field(strict=True, ge=0)] +_StrictNonNegativeFloat = Annotated[float, Field(strict=True, ge=0.0)] + + +PolynomialTerm = Tuple[_StrictNonNegativeInt, float] +_Polynomial = Annotated[List[PolynomialTerm], Field(min_length=1)] class GripperBaseModel(BaseModel): """Gripper base model.""" - class Config: - """Config.""" - - alias_generator = _snake_to_camel_case - allow_population_by_field_name = True + model_config = ConfigDict( + alias_generator=_snake_to_camel_case, populate_by_name=True + ) Offset = Tuple[float, float, float] @@ -74,16 +72,12 @@ class Geometry(GripperBaseModel): max_allowed_grip_error: _StrictNonNegativeFloat -PolynomialTerm = Tuple[_StrictNonNegativeInt, float] - - class GripForceProfile(GripperBaseModel): """Gripper force profile.""" - polynomial: List[PolynomialTerm] = Field( + polynomial: _Polynomial = Field( ..., description="Polynomial function to convert a grip force in Newton to the jaw motor duty cycle value, which will be read by the gripper firmware.", - min_items=1, ) default_grip_force: _StrictNonNegativeFloat default_idle_force: _StrictNonNegativeFloat diff --git a/shared-data/python/opentrons_shared_data/labware/labware_definition.py b/shared-data/python/opentrons_shared_data/labware/labware_definition.py index 3363c874c55..994d4e743eb 100644 --- a/shared-data/python/opentrons_shared_data/labware/labware_definition.py +++ b/shared-data/python/opentrons_shared_data/labware/labware_definition.py @@ -6,21 +6,19 @@ from __future__ import annotations from enum import Enum -from typing import TYPE_CHECKING, Dict, List, Optional, Union +from typing import Dict, List, Optional, Union from math import sqrt, asin from numpy import pi, trapz from functools import cached_property from pydantic import ( + ConfigDict, BaseModel, - Extra, Field, - conint, - confloat, StrictInt, StrictFloat, ) -from typing_extensions import Literal +from typing_extensions import Annotated, Literal from .constants import ( Conical, @@ -36,12 +34,8 @@ SAFE_STRING_REGEX = "^[a-z0-9._]+$" -if TYPE_CHECKING: - _StrictNonNegativeInt = int - _StrictNonNegativeFloat = float -else: - _StrictNonNegativeInt = conint(strict=True, ge=0) - _StrictNonNegativeFloat = confloat(strict=True, ge=0.0) +_StrictNonNegativeInt = Annotated[int, Field(strict=True, ge=0)] +_StrictNonNegativeFloat = Annotated[float, Field(strict=True, ge=0.0)] _Number = Union[StrictInt, StrictFloat] @@ -176,7 +170,7 @@ class Parameters(BaseModel): loadName: str = Field( ..., description="Name used to reference a labware definition", - regex=SAFE_STRING_REGEX, + pattern=SAFE_STRING_REGEX, ) isMagneticModuleCompatible: bool = Field( ..., @@ -199,8 +193,7 @@ class Dimensions(BaseModel): class WellDefinition(BaseModel): - class Config: - extra = Extra.allow + model_config = ConfigDict(extra="allow") depth: _NonNegativeNumber = Field(...) x: _NonNegativeNumber = Field( @@ -269,8 +262,7 @@ class SphericalSegment(BaseModel): def count(self) -> int: return self.xCount * self.yCount - class Config: - keep_untouched = (cached_property,) + model_config = ConfigDict(ignored_types=(cached_property,)) class ConicalFrustum(BaseModel): @@ -304,8 +296,7 @@ class ConicalFrustum(BaseModel): def count(self) -> int: return self.xCount * self.yCount - class Config: - keep_untouched = (cached_property,) + model_config = ConfigDict(ignored_types=(cached_property,)) class CuboidalFrustum(BaseModel): @@ -348,8 +339,7 @@ class CuboidalFrustum(BaseModel): def count(self) -> int: return self.xCount * self.yCount - class Config: - keep_untouched = (cached_property,) + model_config = ConfigDict(ignored_types=(cached_property,)) # A squared cone is the intersection of a cube and a cone that both @@ -490,8 +480,7 @@ def volume_to_height_table(self) -> Dict[float, float]: def count(self) -> int: return self.xCount * self.yCount - class Config: - keep_untouched = (cached_property,) + model_config = ConfigDict(ignored_types=(cached_property,)) """ @@ -616,8 +605,7 @@ class RoundedCuboidSegment(BaseModel): def count(self) -> int: return self.xCount * self.yCount - class Config: - keep_untouched = (cached_property,) + model_config = ConfigDict(ignored_types=(cached_property,)) class Metadata1(BaseModel): @@ -672,9 +660,9 @@ class LabwareDefinition(BaseModel): ..., description="Version of the labware definition itself " "(eg myPlate v1/v2/v3). An incrementing integer", - ge=1.0, + ge=1, ) - namespace: str = Field(..., regex=SAFE_STRING_REGEX) + namespace: str = Field(..., pattern=SAFE_STRING_REGEX) metadata: Metadata = Field( ..., description="Properties used for search and display" ) @@ -731,11 +719,11 @@ class LabwareDefinition(BaseModel): "during labware movement.", ) gripHeightFromLabwareBottom: Optional[float] = Field( - default_factory=None, + default=None, description="The Z-height, from labware bottom, where the gripper should grip the labware.", ) gripForce: Optional[float] = Field( - default_factory=None, + default=None, description="Force, in Newtons, with which the gripper should grip the labware.", ) innerLabwareGeometry: Optional[Dict[str, InnerWellGeometry]] = Field( diff --git a/shared-data/python/opentrons_shared_data/liquid_classes/__init__.py b/shared-data/python/opentrons_shared_data/liquid_classes/__init__.py index b5da3d7ba52..1b8458adf25 100644 --- a/shared-data/python/opentrons_shared_data/liquid_classes/__init__.py +++ b/shared-data/python/opentrons_shared_data/liquid_classes/__init__.py @@ -1,5 +1,4 @@ """Types and functions for accessing liquid class definitions.""" -import json from .. import load_shared_data from .liquid_class_definition import LiquidClassSchemaV1 @@ -18,10 +17,8 @@ def load_definition(name: str, version: int = DEFAULT_VERSION) -> LiquidClassSch Note: this is an expensive operation and should be called sparingly. """ try: - return LiquidClassSchemaV1.parse_obj( - json.loads( - load_shared_data(f"liquid-class/definitions/{version}/{name}.json") - ) + return LiquidClassSchemaV1.model_validate_json( + load_shared_data(f"liquid-class/definitions/{version}/{name}.json") ) except FileNotFoundError: raise LiquidClassDefinitionDoesNotExist( diff --git a/shared-data/python/opentrons_shared_data/liquid_classes/liquid_class_definition.py b/shared-data/python/opentrons_shared_data/liquid_classes/liquid_class_definition.py index 2c2de84e07e..aca371d43e6 100644 --- a/shared-data/python/opentrons_shared_data/liquid_classes/liquid_class_definition.py +++ b/shared-data/python/opentrons_shared_data/liquid_classes/liquid_class_definition.py @@ -1,25 +1,22 @@ """Python shared data models for liquid class definitions.""" from enum import Enum -from typing import TYPE_CHECKING, Literal, Union, Optional, Dict, Any, Sequence, Tuple +from typing import Literal, Union, Optional, Sequence, Tuple, Any from pydantic import ( BaseModel, - validator, + field_validator, + ValidationInfo, Field, - conint, - confloat, StrictInt, StrictFloat, ) +from pydantic.json_schema import SkipJsonSchema +from typing_extensions import Annotated -if TYPE_CHECKING: - _StrictNonNegativeInt = int - _StrictNonNegativeFloat = float -else: - _StrictNonNegativeInt = conint(strict=True, ge=0) - _StrictNonNegativeFloat = confloat(strict=True, ge=0.0) +_StrictNonNegativeInt = Annotated[int, Field(strict=True, ge=0)] +_StrictNonNegativeFloat = Annotated[float, Field(strict=True, ge=0.0)] _Number = Union[StrictInt, StrictFloat] @@ -35,6 +32,10 @@ """Settings for correctionByVolume, which unlike other `byVolume` properties allows negative values with volume.""" +def _remove_default(s: dict[str, Any]) -> None: + s.pop("default") + + class PositionReference(Enum): """Positional reference for liquid handling operations.""" @@ -72,15 +73,18 @@ class DelayProperties(BaseModel): """Shared properties for delay..""" enable: bool = Field(..., description="Whether delay is enabled.") - params: Optional[DelayParams] = Field( - None, description="Parameters for the delay function." + params: DelayParams | SkipJsonSchema[None] = Field( + None, + description="Parameters for the delay function.", + json_schema_extra=_remove_default, ) - @validator("params") + @field_validator("params") + @classmethod def _validate_params( - cls, v: Optional[DelayParams], values: Dict[str, Any] + cls, v: Optional[DelayParams], info: ValidationInfo ) -> Optional[DelayParams]: - if v is None and values["enable"]: + if v is None and info.data.get("enable", False): raise ValueError("If enable is true parameters for delay must be defined.") return v @@ -108,15 +112,18 @@ class TouchTipProperties(BaseModel): """Shared properties for the touch-tip function.""" enable: bool = Field(..., description="Whether touch-tip is enabled.") - params: Optional[LiquidClassTouchTipParams] = Field( - None, description="Parameters for the touch-tip function." + params: LiquidClassTouchTipParams | SkipJsonSchema[None] = Field( + None, + description="Parameters for the touch-tip function.", + json_schema_extra=_remove_default, ) - @validator("params") + @field_validator("params") + @classmethod def _validate_params( - cls, v: Optional[LiquidClassTouchTipParams], values: Dict[str, Any] + cls, v: Optional[LiquidClassTouchTipParams], info: ValidationInfo ) -> Optional[LiquidClassTouchTipParams]: - if v is None and values["enable"]: + if v is None and info.data.get("enable", False): raise ValueError( "If enable is true parameters for touch tip must be defined." ) @@ -136,15 +143,18 @@ class MixProperties(BaseModel): """Mixing properties.""" enable: bool = Field(..., description="Whether mix is enabled.") - params: Optional[MixParams] = Field( - None, description="Parameters for the mix function." + params: MixParams | SkipJsonSchema[None] = Field( + None, + description="Parameters for the mix function.", + json_schema_extra=_remove_default, ) - @validator("params") + @field_validator("params") + @classmethod def _validate_params( - cls, v: Optional[MixParams], values: Dict[str, Any] + cls, v: Optional[MixParams], info: ValidationInfo ) -> Optional[MixParams]: - if v is None and values["enable"]: + if v is None and info.data.get("enable", False): raise ValueError("If enable is true parameters for mix must be defined.") return v @@ -164,15 +174,18 @@ class BlowoutProperties(BaseModel): """Blowout properties.""" enable: bool = Field(..., description="Whether blow-out is enabled.") - params: Optional[BlowoutParams] = Field( - None, description="Parameters for the blowout function." + params: BlowoutParams | SkipJsonSchema[None] = Field( + None, + description="Parameters for the blowout function.", + json_schema_extra=_remove_default, ) - @validator("params") + @field_validator("params") + @classmethod def _validate_params( - cls, v: Optional[BlowoutParams], values: Dict[str, Any] + cls, v: Optional[BlowoutParams], info: ValidationInfo ) -> Optional[BlowoutParams]: - if v is None and values["enable"]: + if v is None and info.data.get("enable", False): raise ValueError( "If enable is true parameters for blowout must be defined." ) @@ -344,8 +357,10 @@ class ByTipTypeSetting(BaseModel): singleDispense: SingleDispenseProperties = Field( ..., description="Single dispense parameters for this tip type." ) - multiDispense: Optional[MultiDispenseProperties] = Field( - None, description="Optional multi-dispense parameters for this tip type." + multiDispense: MultiDispenseProperties | SkipJsonSchema[None] = Field( + None, + description="Optional multi-dispense parameters for this tip type.", + json_schema_extra=_remove_default, ) diff --git a/shared-data/python/opentrons_shared_data/pipette/__init__.py b/shared-data/python/opentrons_shared_data/pipette/__init__.py index 1f3fce2b6d5..91473529a90 100644 --- a/shared-data/python/opentrons_shared_data/pipette/__init__.py +++ b/shared-data/python/opentrons_shared_data/pipette/__init__.py @@ -4,7 +4,7 @@ opentrons_shared_data.pipette: functions and types for pipette config """ import copy -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING, Dict, Optional import json from functools import lru_cache @@ -63,7 +63,7 @@ def name_for_model(pipette_model: PipetteModel) -> PipetteName: def fuse_specs( - pipette_model: PipetteModel, pipette_name: PipetteName = None + pipette_model: PipetteModel, pipette_name: Optional[PipetteName] = None ) -> PipetteFusedSpec: """Combine the model and name spec for a given model. @@ -75,7 +75,7 @@ def fuse_specs( @lru_cache(maxsize=None) def _fuse_specs_cached( - pipette_model: PipetteModel, pipette_name: PipetteName = None + pipette_model: PipetteModel, pipette_name: Optional[PipetteName] = None ) -> PipetteFusedSpec: """ Do the work of fusing the specs inside an lru cache. This can't be the diff --git a/shared-data/python/opentrons_shared_data/pipette/load_data.py b/shared-data/python/opentrons_shared_data/pipette/load_data.py index 40027d54394..f495713b3d8 100644 --- a/shared-data/python/opentrons_shared_data/pipette/load_data.py +++ b/shared-data/python/opentrons_shared_data/pipette/load_data.py @@ -22,6 +22,7 @@ PipetteModelMajorVersion, PipetteModelMinorVersion, LiquidClasses, + PipetteOEMType, ) @@ -35,8 +36,10 @@ def _get_configuration_dictionary( channels: PipetteChannelType, model: PipetteModelType, version: PipetteVersionType, + oem: PipetteOEMType, liquid_class: Optional[LiquidClasses] = None, ) -> LoadedConfiguration: + oem_extension = f"_{oem.value}" if oem != PipetteOEMType.OT else "" if liquid_class: config_path = ( get_shared_data_root() @@ -44,7 +47,7 @@ def _get_configuration_dictionary( / "definitions" / "2" / config_type - / channels.name.lower() + / f"{channels.name.lower()}{oem_extension}" / model.value / liquid_class.name / f"{version.major}_{version.minor}.json" @@ -56,7 +59,7 @@ def _get_configuration_dictionary( / "definitions" / "2" / config_type - / channels.name.lower() + / f"{channels.name.lower()}{oem_extension}" / model.value / f"{version.major}_{version.minor}.json" ) @@ -68,8 +71,9 @@ def _geometry( channels: PipetteChannelType, model: PipetteModelType, version: PipetteVersionType, + oem: PipetteOEMType, ) -> LoadedConfiguration: - return _get_configuration_dictionary("geometry", channels, model, version) + return _get_configuration_dictionary("geometry", channels, model, version, oem) @lru_cache(maxsize=None) @@ -77,12 +81,13 @@ def _liquid( channels: PipetteChannelType, model: PipetteModelType, version: PipetteVersionType, + oem: PipetteOEMType, ) -> Dict[str, LoadedConfiguration]: liquid_dict = {} for liquid_class in LiquidClasses: try: liquid_dict[liquid_class.name] = _get_configuration_dictionary( - "liquid", channels, model, version, liquid_class + "liquid", channels, model, version, oem, liquid_class ) except FileNotFoundError: continue @@ -95,8 +100,9 @@ def _physical( channels: PipetteChannelType, model: PipetteModelType, version: PipetteVersionType, + oem: PipetteOEMType, ) -> LoadedConfiguration: - return _get_configuration_dictionary("general", channels, model, version) + return _get_configuration_dictionary("general", channels, model, version, oem) def _dirs_in(path: Path) -> Iterator[Path]: @@ -152,10 +158,11 @@ def load_liquid_model( model: PipetteModelType, channels: PipetteChannelType, version: PipetteVersionType, + oem: PipetteOEMType, ) -> Dict[str, PipetteLiquidPropertiesDefinition]: - liquid_dict = _liquid(channels, model, version) + liquid_dict = _liquid(channels, model, version, oem) return { - k: PipetteLiquidPropertiesDefinition.parse_obj(v) + k: PipetteLiquidPropertiesDefinition.model_validate(v) for k, v in liquid_dict.items() } @@ -213,7 +220,7 @@ def update_pipette_configuration( Given an input of v1 mutable configs, look up the equivalent keyed value of that configuration.""" quirks_list = [] - dict_of_base_model = base_configurations.dict(by_alias=True) + dict_of_base_model = base_configurations.model_dump(by_alias=True) for c, v in v1_configuration_changes.items(): lookup_key = _change_to_camel_case(c) @@ -245,13 +252,14 @@ def update_pipette_configuration( k.name: v for k, v in dict_of_base_model["plungerPositionsConfigurations"].items() } - return PipetteConfigurations.parse_obj(dict_of_base_model) + return PipetteConfigurations.model_validate(dict_of_base_model) def load_definition( model: PipetteModelType, channels: PipetteChannelType, version: PipetteVersionType, + oem: PipetteOEMType, ) -> PipetteConfigurations: if ( version.major not in PipetteModelMajorVersion @@ -259,14 +267,14 @@ def load_definition( ): raise KeyError("Pipette version not found.") - geometry_dict = _geometry(channels, model, version) - physical_dict = _physical(channels, model, version) - liquid_dict = _liquid(channels, model, version) + geometry_dict = _geometry(channels, model, version, oem) + physical_dict = _physical(channels, model, version, oem) + liquid_dict = _liquid(channels, model, version, oem) generation = PipetteGenerationType(physical_dict["displayCategory"]) mount_configs = MOUNT_CONFIG_LOOKUP_TABLE[generation][channels] - return PipetteConfigurations.parse_obj( + return PipetteConfigurations.model_validate( { **geometry_dict, **physical_dict, @@ -281,6 +289,7 @@ def load_valid_nozzle_maps( model: PipetteModelType, channels: PipetteChannelType, version: PipetteVersionType, + oem: PipetteOEMType, ) -> ValidNozzleMaps: if ( version.major not in PipetteModelMajorVersion @@ -288,5 +297,5 @@ def load_valid_nozzle_maps( ): raise KeyError("Pipette version not found.") - physical_dict = _physical(channels, model, version) - return ValidNozzleMaps.parse_obj(physical_dict["validNozzleMaps"]) + physical_dict = _physical(channels, model, version, oem) + return ValidNozzleMaps.model_validate(physical_dict["validNozzleMaps"]) diff --git a/shared-data/python/opentrons_shared_data/pipette/model_constants.py b/shared-data/python/opentrons_shared_data/pipette/model_constants.py index daf9b233e52..7d34e0e5f6a 100644 --- a/shared-data/python/opentrons_shared_data/pipette/model_constants.py +++ b/shared-data/python/opentrons_shared_data/pipette/model_constants.py @@ -20,7 +20,6 @@ PipetteGenerationType.FLEX: { PipetteChannelType.SINGLE_CHANNEL: RobotMountConfigs(2133.33, 230.15, 80), PipetteChannelType.EIGHT_CHANNEL: RobotMountConfigs(2133.33, 230.15, 80), - PipetteChannelType.EIGHT_CHANNEL_EM: RobotMountConfigs(2133.33, 230.15, 80), PipetteChannelType.NINETY_SIX_CHANNEL: RobotMountConfigs(2133.33, 230.15, 80), }, } diff --git a/shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py b/shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py index 7e1beb5dd35..7214adf2483 100644 --- a/shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py +++ b/shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py @@ -80,7 +80,7 @@ def _migrate_to_v2_configurations( Given an input of v1 mutable configs, look up the equivalent keyed value of that configuration.""" quirks_list = [] - dict_of_base_model = base_configurations.dict(by_alias=True) + dict_of_base_model = base_configurations.model_dump(by_alias=True) for c, v in v1_mutable_configs.items(): if isinstance(v, str): # ignore the saved model @@ -112,7 +112,7 @@ def _migrate_to_v2_configurations( k.name: v for k, v in dict_of_base_model["plungerPositionsConfigurations"].items() } - return PipetteConfigurations.parse_obj(dict_of_base_model) + return PipetteConfigurations.model_validate(dict_of_base_model) def _load_available_overrides( @@ -238,8 +238,9 @@ def _load_full_mutable_configs( pipette_model.pipette_type, pipette_model.pipette_channels, pipette_model.pipette_version, + pipette_model.oem_type, ) - base_configs_dict = base_configs.dict(by_alias=True) + base_configs_dict = base_configs.model_dump(by_alias=True) full_mutable_configs = _list_all_mutable_configs(overrides, base_configs_dict) if not full_mutable_configs.get("name"): @@ -334,6 +335,7 @@ def load_with_mutable_configurations( pipette_model.pipette_type, pipette_model.pipette_channels, pipette_model.pipette_version, + pipette_model.oem_type, ) # Load overrides if we have a pipette id if pipette_serial_number: @@ -431,8 +433,9 @@ def save_overrides( pipette_model.pipette_type, pipette_model.pipette_channels, pipette_model.pipette_version, + pipette_model.oem_type, ) - base_configs_dict = base_configs.dict(by_alias=True) + base_configs_dict = base_configs.model_dump(by_alias=True) try: existing_overrides = _load_available_overrides( pipette_serial_number, pipette_override_path diff --git a/shared-data/python/opentrons_shared_data/pipette/pipette_definition.py b/shared-data/python/opentrons_shared_data/pipette/pipette_definition.py index 84566c4ea92..9f52eaf85f2 100644 --- a/shared-data/python/opentrons_shared_data/pipette/pipette_definition.py +++ b/shared-data/python/opentrons_shared_data/pipette/pipette_definition.py @@ -1,7 +1,12 @@ import re -from typing import List, Dict, Tuple, Optional -from pydantic import BaseModel, Field, validator -from typing_extensions import Literal +from typing import List, Dict, Tuple, Optional, Annotated, Literal, TypeVar +from pydantic import ( + field_validator, + BaseModel, + Field, + BeforeValidator, + PlainSerializer, +) from dataclasses import dataclass from . import types as pip_types, types @@ -16,6 +21,17 @@ NOZZLE_MAP_NAMES = re.compile(r"(?P[A-Z]+)(?P[0-9]+)") COLUMN_NAMES = re.compile(r"[0-9]+") ROW_NAMES = re.compile(r"[A-Z]+") +OT_TIPRACK_NAMES = re.compile(r"opentrons/[a-z0-9._]+/[0-9]") + + +def validate_opentrons_tiprack(v: str) -> str: + if not OT_TIPRACK_NAMES.match(v): + raise ValueError("{v} is not a valid tiprack name.") + return v + + +EnumType = TypeVar("EnumType") +EnumSerializer = Annotated[EnumType, PlainSerializer(lambda v: v.value)] # TODO (lc 12-5-2022) Ideally we can deprecate this @@ -25,9 +41,15 @@ class PipetteNameType: pipette_type: pip_types.PipetteModelType pipette_channels: pip_types.PipetteChannelType pipette_generation: pip_types.PipetteGenerationType + oem_type: pip_types.PipetteOEMType def __repr__(self) -> str: - base_name = f"{self.pipette_type.name}_{str(self.pipette_channels)}" + oem_name = ( + f"_{self.oem_type.value}" + if self.oem_type != pip_types.PipetteOEMType.OT + else "" + ) + base_name = f"{self.pipette_type.name}_{str(self.pipette_channels)}{oem_name}" if self.pipette_generation == pip_types.PipetteGenerationType.GEN1: return base_name elif self.pipette_channels == pip_types.PipetteChannelType.NINETY_SIX_CHANNEL: @@ -49,9 +71,15 @@ class PipetteModelVersionType: pipette_type: pip_types.PipetteModelType pipette_channels: pip_types.PipetteChannelType pipette_version: pip_types.PipetteVersionType + oem_type: pip_types.PipetteOEMType def __repr__(self) -> str: - base_name = f"{self.pipette_type.name}_{str(self.pipette_channels)}" + oem_name = ( + f"_{self.oem_type.value}" + if self.oem_type != pip_types.PipetteOEMType.OT + else "" + ) + base_name = f"{self.pipette_type.name}_{str(self.pipette_channels)}{oem_name}" return f"{base_name}_v{self.pipette_version}" @@ -257,10 +285,12 @@ class CamActionDropTipConfiguration(BaseModel): class DropTipConfigurations(BaseModel): plunger_eject: Optional[PlungerEjectDropTipConfiguration] = Field( - description="Configuration for tip drop via plunger eject", alias="plungerEject" + None, + description="Configuration for tip drop via plunger eject", + alias="plungerEject", ) cam_action: Optional[CamActionDropTipConfiguration] = Field( - description="Configuration for tip drop via cam action", alias="camAction" + None, description="Configuration for tip drop via cam action", alias="camAction" ) @@ -307,12 +337,12 @@ class PipettePhysicalPropertiesDefinition(BaseModel): description="A list of pipette names that are compatible with this pipette.", alias="backCompatNames", ) - pipette_type: pip_types.PipetteModelType = Field( + pipette_type: EnumSerializer[pip_types.PipetteModelType] = Field( ..., description="The pipette model type (related to number of channels).", alias="model", ) - display_category: pip_types.PipetteGenerationType = Field( + display_category: EnumSerializer[pip_types.PipetteGenerationType] = Field( ..., description="The product model of the pipette.", alias="displayCategory" ) pick_up_tip_configurations: PickUpTipConfigurations = Field( @@ -334,7 +364,7 @@ class PipettePhysicalPropertiesDefinition(BaseModel): partial_tip_configurations: PartialTipDefinition = Field( ..., alias="partialTipConfigurations" ) - channels: pip_types.PipetteChannelType = Field( + channels: EnumSerializer[pip_types.PipetteChannelType] = Field( ..., description="The maximum number of channels on the pipette." ) shaft_diameter: float = Field( @@ -350,7 +380,7 @@ class PipettePhysicalPropertiesDefinition(BaseModel): description="The distance of backlash on the plunger motor.", alias="backlashDistance", ) - quirks: List[pip_types.Quirks] = Field( + quirks: List[EnumSerializer[pip_types.Quirks]] = Field( ..., description="The list of quirks available for the loaded configuration" ) tip_presence_check_distance_mm: float = Field( @@ -364,44 +394,42 @@ class PipettePhysicalPropertiesDefinition(BaseModel): alias="endTipActionRetractDistanceMM", ) - @validator("pipette_type", pre=True) + @field_validator("pipette_type", mode="before") + @classmethod def convert_pipette_model_string(cls, v: str) -> pip_types.PipetteModelType: return pip_types.PipetteModelType(v) - @validator("channels", pre=True) + @field_validator("channels", mode="before") + @classmethod def convert_channels(cls, v: int) -> pip_types.PipetteChannelType: return pip_types.PipetteChannelType(v) - @validator("display_category", pre=True) + @field_validator("display_category", mode="before") + @classmethod def convert_display_category(cls, v: str) -> pip_types.PipetteGenerationType: if not v: return pip_types.PipetteGenerationType.GEN1 return pip_types.PipetteGenerationType(v) - @validator("quirks", pre=True) + @field_validator("quirks", mode="before") + @classmethod def convert_quirks(cls, v: List[str]) -> List[pip_types.Quirks]: return [pip_types.Quirks(q) for q in v] - @validator("plunger_positions_configurations", pre=True) + @field_validator("plunger_positions_configurations", mode="before") + @classmethod def convert_plunger_positions( cls, v: Dict[str, PlungerPositions] ) -> Dict[pip_types.LiquidClasses, PlungerPositions]: return {pip_types.LiquidClasses[key]: value for key, value in v.items()} - class Config: - json_encoders = { - pip_types.PipetteChannelType: lambda v: v.value, - pip_types.PipetteModelType: lambda v: v.value, - pip_types.PipetteGenerationType: lambda v: v.value, - pip_types.Quirks: lambda v: v.value, - } - class PipetteRowDefinition(BaseModel): key: str ordered_nozzles: List[str] = Field(..., alias="orderedNozzles") - @validator("key") + @field_validator("key") + @classmethod def check_key_is_row(cls, v: str) -> str: if not ROW_NAMES.search(v): raise ValueError(f"{v} is not a valid row name") @@ -412,7 +440,8 @@ class PipetteColumnDefinition(BaseModel): key: str ordered_nozzles: List[str] = Field(..., alias="orderedNozzles") - @validator("key") + @field_validator("key") + @classmethod def check_key_is_column(cls, v: str) -> str: if not COLUMN_NAMES.search(v): raise ValueError(f"{v} is not a valid column name") @@ -441,7 +470,8 @@ class PipetteGeometryDefinition(BaseModel): ordered_rows: List[PipetteRowDefinition] = Field(..., alias="orderedRows") lld_settings: Dict[str, Dict[str, float]] = Field(..., alias="lldSettings") - @validator("nozzle_map", pre=True) + @field_validator("nozzle_map", mode="before") + @classmethod def check_nonempty_strings( cls, v: Dict[str, List[float]] ) -> Dict[str, List[float]]: @@ -469,14 +499,16 @@ class PipetteLiquidPropertiesDefinition(BaseModel): description="The minimum supported volume of the pipette.", alias="minVolume", ) - default_tipracks: List[str] = Field( + default_tipracks: List[ + Annotated[str, BeforeValidator(validate_opentrons_tiprack)] + ] = Field( ..., description="A list of default tiprack paths.", - regex="opentrons/[a-z0-9._]+/[0-9]", alias="defaultTipracks", ) - @validator("supported_tips", pre=True) + @field_validator("supported_tips", mode="before") + @classmethod def convert_aspirate_key_to_channel_type( cls, v: Dict[str, SupportedTipsDefinition] ) -> Dict[pip_types.PipetteTipType, SupportedTipsDefinition]: @@ -501,7 +533,8 @@ class PipetteConfigurations( ..., description="A dictionary of liquid properties keyed by liquid classes." ) - @validator("liquid_properties", pre=True) + @field_validator("liquid_properties", mode="before") + @classmethod def convert_liquid_properties_key( cls, v: Dict[str, PipetteLiquidPropertiesDefinition] ) -> Dict[pip_types.LiquidClasses, PipetteLiquidPropertiesDefinition]: diff --git a/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py b/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py index 865862bfec2..2c7dd0dc57c 100644 --- a/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py +++ b/shared-data/python/opentrons_shared_data/pipette/pipette_load_name_conversions.py @@ -11,6 +11,7 @@ PipetteGenerationType, PipetteModelMajorVersionType, PipetteModelMinorVersionType, + PipetteOEMType, ) from .pipette_definition import ( PipetteNameType, @@ -21,6 +22,7 @@ DEFAULT_MODEL = PipetteModelType.p1000 DEFAULT_CHANNELS = PipetteChannelType.SINGLE_CHANNEL DEFAULT_MODEL_VERSION = PipetteVersionType(major=1, minor=0) +DEFAULT_OEM = PipetteOEMType.OT PIPETTE_AVAILABLE_TYPES = [m.name for m in PipetteModelType] PIPETTE_CHANNELS_INTS = [c.value for c in PipetteChannelType] @@ -81,8 +83,6 @@ def channels_from_string(channels: str) -> PipetteChannelType: if channels == "96": return PipetteChannelType.NINETY_SIX_CHANNEL elif "multi" in channels: - if "em" in channels: - return PipetteChannelType.EIGHT_CHANNEL_EM return PipetteChannelType.EIGHT_CHANNEL elif channels == "single": return PipetteChannelType.SINGLE_CHANNEL @@ -229,8 +229,8 @@ def convert_to_pipette_name_type( channels = channels_from_string(split_pipette_model_or_name[1]) generation = generation_from_string(split_pipette_model_or_name) pipette_type = PipetteModelType[split_pipette_model_or_name[0]] - - return PipetteNameType(pipette_type, channels, generation) + oem = PipetteOEMType.get_oem_from_model_str(model_or_name) + return PipetteNameType(pipette_type, channels, generation, oem) def convert_pipette_name( @@ -266,8 +266,8 @@ def convert_pipette_name( version = version_from_generation(pipette_name_tuple) pipette_type = PipetteModelType[split_pipette_name[0]] - - return PipetteModelVersionType(pipette_type, channels, version) + oem = PipetteOEMType.get_oem_from_model_str(name) + return PipetteModelVersionType(pipette_type, channels, version, oem) def convert_pipette_model( @@ -304,12 +304,17 @@ def convert_pipette_model( pipette_type, parsed_channels, parsed_oem, parsed_version = exploded channels = channels_from_string(f"{parsed_channels}_{parsed_oem}") version = version_from_string(parsed_version) + oem = PipetteOEMType.get_oem_from_model_str(str(model)) elif model and provided_version: pipette_type, parsed_channels = model.split("_") channels = channels_from_string(parsed_channels) version = version_from_string(provided_version) + oem = PipetteOEMType.get_oem_from_model_str(str(model)) else: pipette_type = DEFAULT_MODEL.value channels = DEFAULT_CHANNELS version = DEFAULT_MODEL_VERSION - return PipetteModelVersionType(PipetteModelType[pipette_type], channels, version) + oem = DEFAULT_OEM + return PipetteModelVersionType( + PipetteModelType[pipette_type], channels, version, oem + ) diff --git a/shared-data/python/opentrons_shared_data/pipette/scripts/build_json_script.py b/shared-data/python/opentrons_shared_data/pipette/scripts/build_json_script.py index 510d0ae5251..e3631d04669 100644 --- a/shared-data/python/opentrons_shared_data/pipette/scripts/build_json_script.py +++ b/shared-data/python/opentrons_shared_data/pipette/scripts/build_json_script.py @@ -138,7 +138,7 @@ def _build_partial_tip_configurations(channels: int) -> PartialTipDefinition: def build_geometry_model_v2( input_dictionary: Dict[str, Any] ) -> PipetteGeometryDefinition: - return PipetteGeometryDefinition.parse_obj(input_dictionary) + return PipetteGeometryDefinition.model_validate(input_dictionary) def build_liquid_model_v2( @@ -147,11 +147,11 @@ def build_liquid_model_v2( ) -> PipetteLiquidPropertiesDefinition: if input_dictionary: if input_dictionary.get("partialTipConfigurations"): - return PipetteLiquidPropertiesDefinition.parse_obj( + return PipetteLiquidPropertiesDefinition.model_validate( {**input_dictionary, "supportedTips": supported_tip_configurations} ) else: - return PipetteLiquidPropertiesDefinition.parse_obj( + return PipetteLiquidPropertiesDefinition.model_validate( { **input_dictionary, "supportedTips": supported_tip_configurations, @@ -163,7 +163,7 @@ def build_liquid_model_v2( "please input the load names of default tipracks separated by commas\n" ) list_default_tipracks = default_tipracks.split(",") - return PipetteLiquidPropertiesDefinition.parse_obj( + return PipetteLiquidPropertiesDefinition.model_validate( { "supportedTips": supported_tip_configurations, "maxVolume": max_volume, @@ -181,7 +181,7 @@ def build_physical_model_v2( sensors=input_dictionary.pop("availableSensors", []) ) back_compat_names = input_dictionary.pop("backCompatNames", []) - return PipettePhysicalPropertiesDefinition.parse_obj( + return PipettePhysicalPropertiesDefinition.model_validate( { **input_dictionary, "availableSensors": available_sensors, @@ -213,7 +213,7 @@ def build_physical_model_v2( back_compat_names = [i.strip() for i in back_compat_names_str.split(",")] else: back_compat_names = [] - return PipettePhysicalPropertiesDefinition.parse_obj( + return PipettePhysicalPropertiesDefinition.model_validate( { "displayName": display_name, "model": pipette_type, @@ -235,7 +235,7 @@ def build_physical_model_v2( def build_supported_tips(input_dictionary: Dict[str, Any]) -> SupportedTipsDefinition: - return SupportedTipsDefinition.parse_obj(input_dictionary) + return SupportedTipsDefinition.model_validate(input_dictionary) def save_to_file( @@ -332,7 +332,7 @@ def build_new_pipette_model_v2( top_level_pipette_model["liquid"], pipette_functions_dict, ) - liquid_model_dict = liquid_model.dict(by_alias=True) + liquid_model_dict = liquid_model.model_dump(by_alias=True) liquid_model_dict["supportedTips"] = { k.name: v for k, v in liquid_model_dict["supportedTips"].items() } diff --git a/shared-data/python/opentrons_shared_data/pipette/scripts/update_configuration_files.py b/shared-data/python/opentrons_shared_data/pipette/scripts/update_configuration_files.py index d72a09e666b..2c8a13a3e17 100644 --- a/shared-data/python/opentrons_shared_data/pipette/scripts/update_configuration_files.py +++ b/shared-data/python/opentrons_shared_data/pipette/scripts/update_configuration_files.py @@ -68,9 +68,9 @@ def _change_to_camel_case(c: str) -> str: def list_configuration_keys() -> Tuple[List[str], Dict[int, str]]: """List out the model keys available to modify at the top level.""" - lookup = {i: v for (i, v) in enumerate(PipetteConfigurations.__fields__)} + lookup = {i: v for (i, v) in enumerate(PipetteConfigurations.model_fields)} return [ - f"{i}: {v}" for (i, v) in enumerate(PipetteConfigurations.__fields__) + f"{i}: {v}" for (i, v) in enumerate(PipetteConfigurations.model_fields) ], lookup @@ -84,7 +84,7 @@ def handle_subclass_model( ) -> List[str]: """Handle sub-classed basemodels and update the top level model as necessary.""" if is_basemodel: - if base_model.__fields__ == SupportedTipsDefinition.__fields__: + if base_model.model_fields == SupportedTipsDefinition.model_fields: # pydantic does something weird with the types in ModelFields so # we cannot use isinstance checks to confirm if the base model # is a supported tips definition @@ -96,8 +96,8 @@ def handle_subclass_model( ] top_level_configuration.append(tip_type.name) - lookup = {i: v for (i, v) in enumerate(base_model.__fields__)} - config_list = [f"{i}: {v}" for (i, v) in enumerate(base_model.__fields__)] + lookup = {i: v for (i, v) in enumerate(base_model.model_fields)} + config_list = [f"{i}: {v}" for (i, v) in enumerate(base_model.model_fields)] print(f"you selected the basemodel {base_model.__name__}:") # type: ignore[attr-defined] for row in config_list: print(f"\t{row}") @@ -105,7 +105,9 @@ def handle_subclass_model( configuration_to_update = lookup[ int(input("select a specific configuration from above\n")) ] - field_type = base_model.__fields__[configuration_to_update].type_ + field_type = base_model.model_fields[ + configuration_to_update + ].rebuild_annotation() is_basemodel = isinstance(field_type, ModelMetaclass) top_level_configuration.append(configuration_to_update) @@ -196,7 +198,7 @@ def load_and_update_file_from_config( """ camel_list_to_update = iter([_change_to_camel_case(i) for i in config_to_update]) - if config_to_update[0] in PipetteGeometryDefinition.__fields__: + if config_to_update[0] in PipetteGeometryDefinition.model_fields: geometry = _geometry( model_to_update.pipette_channels, model_to_update.pipette_type, @@ -216,7 +218,7 @@ def load_and_update_file_from_config( geometry["nozzleOffset"] = value_to_update else: geometry = update(geometry, camel_list_to_update, value_to_update) - PipetteGeometryDefinition.parse_obj(geometry) + PipetteGeometryDefinition.model_validate(geometry) filepath = ( ROOT @@ -229,7 +231,7 @@ def load_and_update_file_from_config( f"{model_to_update.pipette_version.major}_{model_to_update.pipette_version.minor}", geometry, ) - elif config_to_update[0] in PipettePhysicalPropertiesDefinition.__fields__: + elif config_to_update[0] in PipettePhysicalPropertiesDefinition.model_fields: physical = _physical( model_to_update.pipette_channels, model_to_update.pipette_type, @@ -238,7 +240,7 @@ def load_and_update_file_from_config( physical = update(physical, camel_list_to_update, value_to_update) - PipettePhysicalPropertiesDefinition.parse_obj(physical) + PipettePhysicalPropertiesDefinition.model_validate(physical) filepath = ( ROOT / "general" @@ -272,7 +274,7 @@ def load_and_update_file_from_config( liquid[c.name.lower()], camel_list_to_update, value_to_update ) - PipetteLiquidPropertiesDefinition.parse_obj(liquid) + PipetteLiquidPropertiesDefinition.model_validate(liquid) filepath = ( ROOT / "liquid" @@ -290,7 +292,7 @@ def load_and_update_file_from_config( liquid = update( liquid[lc.name.lower()], camel_list_to_update, value_to_update ) - PipetteLiquidPropertiesDefinition.parse_obj(liquid) + PipetteLiquidPropertiesDefinition.model_validate(liquid) filepath = ( ROOT @@ -401,9 +403,9 @@ def determine_models_to_update(update_all_models: bool) -> None: f"NOTE: updating the {configuration_to_update[0]} will automatically update the {NOZZLE_LOCATION_CONFIGS[1]}\n" ) - field_type = PipetteConfigurations.__fields__[ + field_type = PipetteConfigurations.model_fields[ configuration_to_update[0] - ].type_ + ].rebuild_annotation() is_basemodel = isinstance(field_type, ModelMetaclass) configuration_to_update = handle_subclass_model( diff --git a/shared-data/python/opentrons_shared_data/pipette/types.py b/shared-data/python/opentrons_shared_data/pipette/types.py index 685dae89957..93bfe2c0593 100644 --- a/shared-data/python/opentrons_shared_data/pipette/types.py +++ b/shared-data/python/opentrons_shared_data/pipette/types.py @@ -49,7 +49,6 @@ def check_and_return_type( class PipetteChannelType(int, enum.Enum): SINGLE_CHANNEL = 1 EIGHT_CHANNEL = 8 - EIGHT_CHANNEL_EM = 82 NINETY_SIX_CHANNEL = 96 def __str__(self) -> str: @@ -57,8 +56,6 @@ def __str__(self) -> str: return "96" elif self.value == 8: return "multi" - elif self.value == 82: - return "multi_em" else: return "single" @@ -115,6 +112,21 @@ class Quirks(enum.Enum): highSpeed = "highSpeed" +class PipetteOEMType(enum.Enum): + OT = "ot" # opentrons type + EM = "em" # Emulsifying Pipette + + @classmethod + def get_oem_from_quirks(cls, quirks: List[Quirks]) -> "PipetteOEMType": + """Return an oem type if true based on the quirks.""" + return cls.EM if Quirks.highSpeed in quirks else cls.OT + + @classmethod + def get_oem_from_model_str(cls, model_str: str) -> "PipetteOEMType": + """Return an oem type if true based on the model string.""" + return cls.EM if "multi_em" in model_str else cls.OT + + class AvailableUnits(enum.Enum): mm = "mm" amps = "amps" @@ -220,7 +232,7 @@ def dict_for_encode(self) -> bool: "p1000_single_gen2", "p1000_single_flex", "p1000_multi_flex", - "p1000_multi_em", + "p1000_multi_em_flex", "p1000_96", "p200_96", ] @@ -247,7 +259,7 @@ class PipetteNameType(str, enum.Enum): P1000_SINGLE_GEN2 = "p1000_single_gen2" P1000_SINGLE_FLEX = "p1000_single_flex" P1000_MULTI_FLEX = "p1000_multi_flex" - P1000_MULTI_EM = "p1000_multi_em" + P1000_MULTI_EM = "p1000_multi_em_flex" P1000_96 = "p1000_96" P200_96 = "p200_96" diff --git a/shared-data/python/opentrons_shared_data/protocol/models/__init__.py b/shared-data/python/opentrons_shared_data/protocol/models/__init__.py index 76f8449d93d..ac9a0df7cba 100644 --- a/shared-data/python/opentrons_shared_data/protocol/models/__init__.py +++ b/shared-data/python/opentrons_shared_data/protocol/models/__init__.py @@ -2,7 +2,7 @@ from . import protocol_schema_v6, protocol_schema_v7, protocol_schema_v8 from .protocol_schema_v6 import ProtocolSchemaV6 from .protocol_schema_v7 import ProtocolSchemaV7 -from .protocol_schema_v8 import ProtocolSchemaV8 +from .protocol_schema_v8 import ProtocolSchemaV8, CommandSchemaId from .shared_models import ( Liquid, Labware, @@ -45,4 +45,5 @@ "Pipette", "Robot", "DesignerApplication", + "CommandSchemaId", ] diff --git a/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v6.py b/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v6.py index 24aeb1826bd..f8278d019f8 100644 --- a/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v6.py +++ b/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v6.py @@ -1,4 +1,9 @@ -from pydantic import BaseModel, Field, validator +from pydantic import ( + ConfigDict, + BaseModel, + Field, + model_validator, +) from typing import Any, List, Optional, Dict, Union from typing_extensions import Literal from opentrons_shared_data.labware.labware_definition import LabwareDefinition @@ -21,44 +26,44 @@ # TODO (tamar 3/15/22): split apart all the command payloads when we tackle #9583 class Params(BaseModel): - slotName: Optional[str] - axes: Optional[List[str]] - pipetteId: Optional[str] - mount: Optional[str] - moduleId: Optional[str] - location: Optional[Union[Location, Literal["offDeck"]]] - labwareId: Optional[str] - displayName: Optional[str] - liquidId: Optional[str] - volumeByWell: Optional[Dict[str, Any]] - wellName: Optional[str] - volume: Optional[float] - flowRate: Optional[float] - wellLocation: Optional[Union[WellLocation]] - waitForResume: Optional[Literal[True]] - seconds: Optional[float] - minimumZHeight: Optional[float] - forceDirect: Optional[bool] - speed: Optional[float] - message: Optional[str] - coordinates: Optional[OffsetVector] - axis: Optional[str] - distance: Optional[float] - positionId: Optional[str] - temperature: Optional[float] - celsius: Optional[float] - blockMaxVolumeUl: Optional[float] - rpm: Optional[float] - height: Optional[float] - offset: Optional[OffsetVector] - profile: Optional[List[ProfileStep]] - radius: Optional[float] + slotName: Optional[str] = None + axes: Optional[List[str]] = None + pipetteId: Optional[str] = None + mount: Optional[str] = None + moduleId: Optional[str] = None + location: Optional[Union[Location, Literal["offDeck"]]] = None + labwareId: Optional[str] = None + displayName: Optional[str] = None + liquidId: Optional[str] = None + volumeByWell: Optional[Dict[str, Any]] = None + wellName: Optional[str] = None + volume: Optional[float] = None + flowRate: Optional[float] = None + wellLocation: Optional[Union[WellLocation]] = None + waitForResume: Optional[Literal[True]] = None + seconds: Optional[float] = None + minimumZHeight: Optional[float] = None + forceDirect: Optional[bool] = None + speed: Optional[float] = None + message: Optional[str] = None + coordinates: Optional[OffsetVector] = None + axis: Optional[str] = None + distance: Optional[float] = None + positionId: Optional[str] = None + temperature: Optional[float] = None + celsius: Optional[float] = None + blockMaxVolumeUl: Optional[float] = None + rpm: Optional[float] = None + height: Optional[float] = None + offset: Optional[OffsetVector] = None + profile: Optional[List[ProfileStep]] = None + radius: Optional[float] = None class Command(BaseModel): commandType: str params: Params - key: Optional[str] + key: Optional[str] = None class ProtocolSchemaV6(BaseModel): @@ -73,38 +78,23 @@ class ProtocolSchemaV6(BaseModel): robot: Robot pipettes: Dict[str, Pipette] labware: Dict[str, Labware] - modules: Optional[Dict[str, Module]] - liquids: Optional[Dict[str, Liquid]] + modules: Optional[Dict[str, Module]] = None + liquids: Optional[Dict[str, Liquid]] = None labwareDefinitions: Dict[str, LabwareDefinition] # commands must be after pipettes, labware, etc. for its @validator to work. commands: List[Command] - commandAnnotations: Optional[List[CommandAnnotation]] - designerApplication: Optional[DesignerApplication] - - class Config: - # added for constructing the class with field name instead of alias - allow_population_by_field_name = True + commandAnnotations: Optional[List[CommandAnnotation]] = None + designerApplication: Optional[DesignerApplication] = None + model_config = ConfigDict(populate_by_name=True) - @validator("commands") - def _validate_commands( - cls, - value: List[Command], - values: Dict[str, Any], - ) -> List[Command]: - pipette_ids = set(values["pipettes"].keys()) if "pipettes" in values else set() - labware_ids = set(values["labware"].keys()) if "labware" in values else set() - module_ids = ( - set(values["modules"].keys()) - if "modules" in values and values["modules"] - else set() - ) - liquid_ids = ( - set(values["liquids"].keys()) - if "liquids" in values and values["liquids"] - else set() - ) + @model_validator(mode="after") + def _validate_commands(self) -> "ProtocolSchemaV6": + pipette_ids = set(self.pipettes.keys() if self.pipettes else []) + labware_ids = set(self.labware.keys() if self.labware else []) + module_ids = set(self.modules.keys() if self.modules else []) + liquid_ids = set(self.liquids.keys() if self.liquids else []) - for index, command in enumerate(value): + for index, command in enumerate(self.commands): if ( command.params.pipetteId is not None and command.params.pipetteId not in pipette_ids @@ -142,4 +132,4 @@ def _validate_commands( f" which doesn't exist." ) - return value + return self diff --git a/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v7.py b/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v7.py index 46eb242b990..97986ed385a 100644 --- a/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v7.py +++ b/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v7.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field from typing import Any, List, Optional, Dict, Union from typing_extensions import Literal @@ -19,59 +19,59 @@ # TODO (tamar 3/15/22): split apart all the command payloads when we tackle #9583 class Params(BaseModel): - slotName: Optional[str] - axes: Optional[List[str]] - pipetteId: Optional[str] - mount: Optional[str] - moduleId: Optional[str] - location: Optional[Union[Location, Literal["offDeck"]]] - labwareId: Optional[str] - displayName: Optional[str] - liquidId: Optional[str] - volumeByWell: Optional[Dict[str, Any]] - wellName: Optional[str] - volume: Optional[float] - flowRate: Optional[float] - wellLocation: Optional[WellLocation] - waitForResume: Optional[Literal[True]] - seconds: Optional[float] - minimumZHeight: Optional[float] - forceDirect: Optional[bool] - speed: Optional[float] - message: Optional[str] - coordinates: Optional[OffsetVector] - axis: Optional[str] - distance: Optional[float] - positionId: Optional[str] - temperature: Optional[float] - celsius: Optional[float] - blockMaxVolumeUl: Optional[float] - rpm: Optional[float] - height: Optional[float] - offset: Optional[OffsetVector] - profile: Optional[List[ProfileStep]] - radius: Optional[float] + slotName: Optional[str] = None + axes: Optional[List[str]] = None + pipetteId: Optional[str] = None + mount: Optional[str] = None + moduleId: Optional[str] = None + location: Optional[Union[Location, Literal["offDeck"]]] = None + labwareId: Optional[str] = None + displayName: Optional[str] = None + liquidId: Optional[str] = None + volumeByWell: Optional[Dict[str, Any]] = None + wellName: Optional[str] = None + volume: Optional[float] = None + flowRate: Optional[float] = None + wellLocation: Optional[WellLocation] = None + waitForResume: Optional[Literal[True]] = None + seconds: Optional[float] = None + minimumZHeight: Optional[float] = None + forceDirect: Optional[bool] = None + speed: Optional[float] = None + message: Optional[str] = None + coordinates: Optional[OffsetVector] = None + axis: Optional[str] = None + distance: Optional[float] = None + positionId: Optional[str] = None + temperature: Optional[float] = None + celsius: Optional[float] = None + blockMaxVolumeUl: Optional[float] = None + rpm: Optional[float] = None + height: Optional[float] = None + offset: Optional[OffsetVector] = None + profile: Optional[List[ProfileStep]] = None + radius: Optional[float] = None # schema v7 add-ons - newLocation: Optional[Union[Location, Literal["offDeck"]]] - strategy: Optional[str] - pickUpOffset: Optional[OffsetVector] - dropOffset: Optional[OffsetVector] - homeAfter: Optional[bool] - alternateDropLocation: Optional[bool] - holdTimeSeconds: Optional[float] - maintenancePosition: Optional[str] - pipetteName: Optional[str] - model: Optional[str] - loadName: Optional[str] - namespace: Optional[str] - version: Optional[int] - pushOut: Optional[float] + newLocation: Optional[Union[Location, Literal["offDeck"]]] = None + strategy: Optional[str] = None + pickUpOffset: Optional[OffsetVector] = None + dropOffset: Optional[OffsetVector] = None + homeAfter: Optional[bool] = None + alternateDropLocation: Optional[bool] = None + holdTimeSeconds: Optional[float] = None + maintenancePosition: Optional[str] = None + pipetteName: Optional[str] = None + model: Optional[str] = None + loadName: Optional[str] = None + namespace: Optional[str] = None + version: Optional[int] = None + pushOut: Optional[float] = None class Command(BaseModel): commandType: str params: Params - key: Optional[str] + key: Optional[str] = None class ProtocolSchemaV7(BaseModel): @@ -84,12 +84,9 @@ class ProtocolSchemaV7(BaseModel): schemaVersion: Literal[7] metadata: Metadata robot: Robot - liquids: Optional[Dict[str, Liquid]] + liquids: Optional[Dict[str, Liquid]] = None labwareDefinitions: Dict[str, LabwareDefinition] commands: List[Command] - commandAnnotations: Optional[List[CommandAnnotation]] - designerApplication: Optional[DesignerApplication] - - class Config: - # added for constructing the class with field name instead of alias - allow_population_by_field_name = True + commandAnnotations: Optional[List[CommandAnnotation]] = None + designerApplication: Optional[DesignerApplication] = None + model_config = ConfigDict(populate_by_name=True) diff --git a/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py b/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py index 11beddb96c0..08b6457c206 100644 --- a/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py +++ b/shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v8.py @@ -2,7 +2,7 @@ from typing import Any, List, Optional, Dict from typing_extensions import Literal -from pydantic import BaseModel, Field, Extra +from pydantic import BaseModel, Field, ConfigDict from opentrons_shared_data.labware.labware_definition import LabwareDefinition from opentrons_shared_data.command import known_schema_ids @@ -18,19 +18,19 @@ class Command(BaseModel): commandType: str params: Dict[str, Any] - key: Optional[str] + key: Optional[str] = None class CommandAnnotation(BaseModel): commandKeys: List[str] annotationType: str - - class Config: - extra = Extra.allow + model_config = ConfigDict(extra="allow") CommandSchemaId = Enum( # type: ignore[misc] - "CommandSchemaId", ((schema_id, schema_id) for schema_id in known_schema_ids()) + "CommandSchemaId", + ((schema_id, schema_id) for schema_id in known_schema_ids()), + type=str, ) @@ -52,8 +52,5 @@ class ProtocolSchemaV8(BaseModel): commands: List[Command] commandAnnotationSchemaId: Literal["opentronsCommandAnnotationSchemaV1"] commandAnnotations: List[CommandAnnotation] - designerApplication: Optional[DesignerApplication] - - class Config: - # added for constructing the class with field name instead of alias - allow_population_by_field_name = True + designerApplication: Optional[DesignerApplication] = None + model_config = ConfigDict(populate_by_name=True) diff --git a/shared-data/python/opentrons_shared_data/protocol/models/shared_models.py b/shared-data/python/opentrons_shared_data/protocol/models/shared_models.py index 8cf3276f71f..148ca44cc7e 100644 --- a/shared-data/python/opentrons_shared_data/protocol/models/shared_models.py +++ b/shared-data/python/opentrons_shared_data/protocol/models/shared_models.py @@ -1,5 +1,4 @@ -from typing import Optional, List, Dict, Any -from typing_extensions import Literal +from typing import Optional, List, Dict, Any, Literal from enum import Enum from pydantic import BaseModel @@ -31,20 +30,20 @@ class WellDefinition(BaseModel): x: float y: float z: float - diameter: Optional[float] - yDimension: Optional[float] - xDimension: Optional[float] + diameter: Optional[float] = None + yDimension: Optional[float] = None + xDimension: Optional[float] = None class Metadata(BaseModel): - protocolName: Optional[str] - author: Optional[str] - description: Optional[str] - created: Optional[int] - lastModified: Optional[int] - category: Optional[str] - subcategory: Optional[str] - tags: Optional[List[str]] + protocolName: Optional[str] = None + author: Optional[str] = None + description: Optional[str] = None + created: Optional[int] = None + lastModified: Optional[int] = None + category: Optional[str] = None + subcategory: Optional[str] = None + tags: Optional[List[str]] = None class Module(BaseModel): @@ -61,9 +60,9 @@ class Robot(BaseModel): class DesignerApplication(BaseModel): - name: Optional[str] - version: Optional[str] - data: Optional[Dict[str, Any]] + name: Optional[str] = None + version: Optional[str] = None + data: Optional[Dict[str, Any]] = None class CommandAnnotation(BaseModel): @@ -72,16 +71,16 @@ class CommandAnnotation(BaseModel): class OffsetVector(BaseModel): - x: Optional[float] - y: Optional[float] - z: Optional[float] + x: Optional[float] = None + y: Optional[float] = None + z: Optional[float] = None class Location(BaseModel): - slotName: Optional[str] - moduleId: Optional[str] - labwareId: Optional[str] - addressableAreaName: Optional[str] + slotName: Optional[str] = None + moduleId: Optional[str] = None + labwareId: Optional[str] = None + addressableAreaName: Optional[str] = None class ProfileStep(BaseModel): @@ -90,22 +89,22 @@ class ProfileStep(BaseModel): class WellLocation(BaseModel): - origin: Optional[str] - offset: Optional[OffsetVector] + origin: Optional[str] = None + offset: Optional[OffsetVector] = None class Liquid(BaseModel): displayName: str description: str - displayColor: Optional[str] + displayColor: Optional[str] = None class Labware(BaseModel): - displayName: Optional[str] + displayName: Optional[str] = None definitionId: str class NozzleConfigurationParams(BaseModel): style: str - primaryNozzle: Optional[str] - frontRightNozzle: Optional[str] + primaryNozzle: Optional[str] = None + frontRightNozzle: Optional[str] = None diff --git a/shared-data/python/pytest.ini b/shared-data/python/pytest.ini index e552559af25..588979766ed 100644 --- a/shared-data/python/pytest.ini +++ b/shared-data/python/pytest.ini @@ -1,2 +1,8 @@ [pytest] addopts = --color=yes --strict-markers + +filterwarnings = + # Pydantic's shims for its legacy v1 methods (e.g. `BaseModel.construct()`) + # are not type-checked properly. Forbid them, so we're forced to use their newer + # v2 replacements which are type-checked (e.g. ``BaseModel.model_construct()`) + error::pydantic.PydanticDeprecatedSince20 diff --git a/shared-data/python/setup.py b/shared-data/python/setup.py index 4e1720cb610..d58ed77c94d 100644 --- a/shared-data/python/setup.py +++ b/shared-data/python/setup.py @@ -141,9 +141,9 @@ def get_version(): ) PACKAGES = find_packages(where=".", exclude=["tests", "tests.*"]) INSTALL_REQUIRES = [ - "jsonschema>=3.0.1,<4.18.0", + "jsonschema>=4.0.0,<5", "typing-extensions>=4.0.0,<5", - "pydantic>=1.10.9,<2.0.0", + "pydantic>=2.0.0,<3.0.0", ] diff --git a/shared-data/python/tests/deck/test_typechecks.py b/shared-data/python/tests/deck/test_typechecks.py index a5fa3747e99..9477f9c6292 100644 --- a/shared-data/python/tests/deck/test_typechecks.py +++ b/shared-data/python/tests/deck/test_typechecks.py @@ -12,12 +12,12 @@ @pytest.mark.parametrize("defname", list_deck_definition_names(version=3)) -def test_v3_defs(defname): +def test_v3_defs(defname: str) -> None: defn = load_deck_definition(name=defname, version=3) typeguard.check_type(defn, DeckDefinitionV3) @pytest.mark.parametrize("defname", list_deck_definition_names(version=5)) -def test_v5_defs(defname): +def test_v5_defs(defname: str) -> None: defn = load_deck_definition(name=defname, version=5) typeguard.check_type(defn, DeckDefinitionV5) diff --git a/shared-data/python/tests/gripper/test_definition.py b/shared-data/python/tests/gripper/test_definition.py index 6cbfbc77fd5..3128531b95f 100644 --- a/shared-data/python/tests/gripper/test_definition.py +++ b/shared-data/python/tests/gripper/test_definition.py @@ -17,14 +17,14 @@ def test_gripper_definition() -> None: def test_gripper_definition_type() -> None: - assert GripperDefinition.parse_obj(GRIPPER_DEF) + assert GripperDefinition.model_validate(GRIPPER_DEF) # missing key del GRIPPER_DEF["gripForceProfile"] with pytest.raises(ValidationError): - assert GripperDefinition.parse_obj(GRIPPER_DEF) + assert GripperDefinition.model_validate(GRIPPER_DEF) # no missing key but with incorrect value GRIPPER_DEF["geometry"]["gripForceProfile"] = {"min": 1.0, "max": "0.0"} with pytest.raises(ValidationError): - assert GripperDefinition.parse_obj(GRIPPER_DEF) + assert GripperDefinition.model_validate(GRIPPER_DEF) diff --git a/shared-data/python/tests/labware/test_typechecks.py b/shared-data/python/tests/labware/test_typechecks.py index b62dd27cc70..0e9a8b4b724 100644 --- a/shared-data/python/tests/labware/test_typechecks.py +++ b/shared-data/python/tests/labware/test_typechecks.py @@ -8,6 +8,6 @@ @pytest.mark.parametrize("loadname,version", get_ot_defs()) -def test_opentrons_definition_types(loadname, version): +def test_opentrons_definition_types(loadname: str, version: int) -> None: defdict = load_definition(loadname, version) typeguard.check_type(defdict, LabwareDefinition) diff --git a/shared-data/python/tests/labware/test_validations.py b/shared-data/python/tests/labware/test_validations.py index 39052e5d150..b4d06a40e1d 100644 --- a/shared-data/python/tests/labware/test_validations.py +++ b/shared-data/python/tests/labware/test_validations.py @@ -11,11 +11,11 @@ def test_loadname_regex_applied() -> None: defdict = load_definition(*get_ot_defs()[0]) defdict["parameters"]["loadName"] = "ALSJHDAKJLA" with pytest.raises(ValidationError): - LabwareDefinition.parse_obj(defdict) + LabwareDefinition.model_validate(defdict) def test_namespace_regex_applied() -> None: defdict = load_definition(*get_ot_defs()[0]) defdict["namespace"] = "ALSJHDAKJLA" with pytest.raises(ValidationError): - LabwareDefinition.parse_obj(defdict) + LabwareDefinition.model_validate(defdict) diff --git a/shared-data/python/tests/liquid_classes/test_load.py b/shared-data/python/tests/liquid_classes/test_load.py index c8bf7b25244..d0d96fd00fe 100644 --- a/shared-data/python/tests/liquid_classes/test_load.py +++ b/shared-data/python/tests/liquid_classes/test_load.py @@ -14,9 +14,9 @@ def test_load_liquid_class_schema_v1() -> None: fixture_data = load_shared_data("liquid-class/definitions/1/water.json") - liquid_class_model = LiquidClassSchemaV1.parse_raw(fixture_data) + liquid_class_model = LiquidClassSchemaV1.model_validate_json(fixture_data) liquid_class_def_from_model = json.loads( - liquid_class_model.json(exclude_unset=True) + liquid_class_model.model_dump_json(exclude_unset=True) ) expected_liquid_class_def = json.loads(fixture_data) assert liquid_class_def_from_model == expected_liquid_class_def diff --git a/shared-data/python/tests/pipette/test_load_data.py b/shared-data/python/tests/pipette/test_load_data.py index 012aed7baca..78b7ac74c5b 100644 --- a/shared-data/python/tests/pipette/test_load_data.py +++ b/shared-data/python/tests/pipette/test_load_data.py @@ -9,6 +9,7 @@ PipetteChannelType, PipetteModelType, PipetteVersionType, + PipetteOEMType, PipetteTipType, Quirks, LiquidClasses, @@ -20,6 +21,7 @@ def test_load_pipette_definition() -> None: PipetteModelType.p50, PipetteChannelType.SINGLE_CHANNEL, PipetteVersionType(major=3, minor=3), + PipetteOEMType.OT, ) assert pipette_config_one.channels == 1 @@ -38,6 +40,7 @@ def test_load_pipette_definition() -> None: PipetteModelType.p50, PipetteChannelType.SINGLE_CHANNEL, PipetteVersionType(major=1, minor=0), + PipetteOEMType.OT, ) assert pipette_config_two.channels == 1 @@ -83,14 +86,17 @@ def test_update_pipette_configuration( cast(types.PipetteModel, pipette_model) ) base_configurations = load_data.load_definition( - model_name.pipette_type, model_name.pipette_channels, model_name.pipette_version + model_name.pipette_type, + model_name.pipette_channels, + model_name.pipette_version, + model_name.oem_type, ) updated_configurations = load_data.update_pipette_configuration( base_configurations, v1_configuration_changes, liquid_class ) - updated_configurations_dict = updated_configurations.dict() + updated_configurations_dict = updated_configurations.model_dump() for k, v in v1_configuration_changes.items(): if k == "tip_length": for i in updated_configurations_dict["liquid_properties"][liquid_class][ diff --git a/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py b/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py index aae0c1a4e1b..e0797ddec08 100644 --- a/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py +++ b/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py @@ -75,6 +75,7 @@ def test_max_flow_rates_per_volume(pipette: PipetteModel, action: str) -> None: pipette_model_version.pipette_type, pipette_model_version.pipette_channels, pipette_model_version.pipette_version, + pipette_model_version.oem_type, ) pipette_model_version_str = f"{pipette_model_version}" diff --git a/shared-data/python/tests/pipette/test_mutable_configurations.py b/shared-data/python/tests/pipette/test_mutable_configurations.py index 38920c473e8..322682360c8 100644 --- a/shared-data/python/tests/pipette/test_mutable_configurations.py +++ b/shared-data/python/tests/pipette/test_mutable_configurations.py @@ -76,6 +76,7 @@ def test_load_old_overrides_regression( pipette_type=types.PipetteModelType.p20, pipette_channels=types.PipetteChannelType.SINGLE_CHANNEL, pipette_version=types.PipetteVersionType(2, 2), + oem_type=types.PipetteOEMType.OT, ), override_configuration_path, "P20SV222021040709", @@ -269,10 +270,11 @@ def test_load_with_overrides( pipette_model.pipette_type, pipette_model.pipette_channels, pipette_model.pipette_version, + pipette_model.oem_type, ) if serial_number == TEST_SERIAL_NUMBER: - dict_loaded_configs = loaded_base_configurations.dict(by_alias=True) + dict_loaded_configs = loaded_base_configurations.model_dump(by_alias=True) for map_key in dict_loaded_configs["pickUpTipConfigurations"]["pressFit"][ "configurationsByNozzleMap" ]: @@ -283,7 +285,7 @@ def test_load_with_overrides( "configurationsByNozzleMap" ][map_key][tip_key]["speed"] = 5.0 - updated_configurations_dict = updated_configurations.dict(by_alias=True) + updated_configurations_dict = updated_configurations.model_dump(by_alias=True) assert set(dict_loaded_configs.pop("quirks")) == set( updated_configurations_dict.pop("quirks") ) @@ -454,7 +456,9 @@ def test_loading_does_not_log_warnings( load_with_mutable_configurations() suppresses and logs internal exceptions to protect its caller, but those are still bugs, and we still want tests to catch them. """ - model = pipette_definition.PipetteModelVersionType(type, channels, version) + model = pipette_definition.PipetteModelVersionType( + type, channels, version, types.PipetteOEMType.OT + ) (override_configuration_path / filename).write_text(file_contents) with caplog.at_level(logging.WARNING): mutable_configurations.load_with_mutable_configurations( diff --git a/shared-data/python/tests/pipette/test_pipette_definition.py b/shared-data/python/tests/pipette/test_pipette_definition.py index 9fd134ec059..2d5e2aec87a 100644 --- a/shared-data/python/tests/pipette/test_pipette_definition.py +++ b/shared-data/python/tests/pipette/test_pipette_definition.py @@ -19,10 +19,10 @@ def get_liquid_definition_for( liquid_class: LiquidClasses, ) -> PipetteLiquidPropertiesDefinition: if liquid_class == LiquidClasses.lowVolumeDefault: - return PipetteLiquidPropertiesDefinition.parse_obj( + return PipetteLiquidPropertiesDefinition.model_validate( { "supportedTips": { - "t50": SupportedTipsDefinition.parse_obj( + "t50": SupportedTipsDefinition.model_validate( { "defaultAspirateFlowRate": { "default": 5, @@ -52,10 +52,10 @@ def get_liquid_definition_for( } ) else: - return PipetteLiquidPropertiesDefinition.parse_obj( + return PipetteLiquidPropertiesDefinition.model_validate( { "supportedTips": { - "t50": SupportedTipsDefinition.parse_obj( + "t50": SupportedTipsDefinition.model_validate( { "defaultAspirateFlowRate": { "default": 5, diff --git a/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py b/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py index 6e792560e9c..fdd9ec434cd 100644 --- a/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py +++ b/shared-data/python/tests/pipette/test_pipette_load_name_conversions.py @@ -6,6 +6,7 @@ PipetteModelType, PipetteVersionType, PipetteGenerationType, + PipetteOEMType, ) from opentrons_shared_data.pipette.types import PipetteModel, PipetteName from opentrons_shared_data.pipette import ( @@ -23,6 +24,7 @@ PipetteModelType.p50, PipetteChannelType.SINGLE_CHANNEL, PipetteVersionType(2, 0), + PipetteOEMType.OT, ), ], [ @@ -31,6 +33,7 @@ PipetteModelType.p1000, PipetteChannelType.EIGHT_CHANNEL, PipetteVersionType(1, 0), + PipetteOEMType.OT, ), ], [ @@ -39,6 +42,7 @@ PipetteModelType.p1000, PipetteChannelType.NINETY_SIX_CHANNEL, PipetteVersionType(1, 0), + PipetteOEMType.OT, ), ], ], @@ -59,6 +63,7 @@ def test_convert_pipette_model( PipetteModelType.p50, PipetteChannelType.SINGLE_CHANNEL, PipetteVersionType(2, 0), + PipetteOEMType.OT, ), ], [ @@ -68,6 +73,7 @@ def test_convert_pipette_model( PipetteModelType.p1000, PipetteChannelType.EIGHT_CHANNEL, PipetteVersionType(3, 3), + PipetteOEMType.OT, ), ], [ @@ -77,6 +83,7 @@ def test_convert_pipette_model( PipetteModelType.p1000, PipetteChannelType.NINETY_SIX_CHANNEL, PipetteVersionType(1, 1), + PipetteOEMType.OT, ), ], ], @@ -96,6 +103,7 @@ def test_convert_pipette_model_provided_version( PipetteModelType.p50, PipetteChannelType.SINGLE_CHANNEL, PipetteVersionType(2, 0), + PipetteOEMType.OT, ), ], [ @@ -104,6 +112,7 @@ def test_convert_pipette_model_provided_version( PipetteModelType.p1000, PipetteChannelType.EIGHT_CHANNEL, PipetteVersionType(3, 5), + PipetteOEMType.OT, ), ], [ @@ -112,6 +121,7 @@ def test_convert_pipette_model_provided_version( PipetteModelType.p1000, PipetteChannelType.NINETY_SIX_CHANNEL, PipetteVersionType(3, 6), + PipetteOEMType.OT, ), ], ], @@ -123,19 +133,21 @@ def test_convert_pipette_name( @pytest.mark.parametrize( - argnames=["model_type", "channels", "generation", "output"], + argnames=["model_type", "channels", "generation", "output", "oem"], argvalues=[ [ PipetteModelType.p50, PipetteChannelType.SINGLE_CHANNEL, PipetteGenerationType.GEN2, "p50_single_gen2", + PipetteOEMType.OT, ], [ PipetteModelType.p1000, PipetteChannelType.EIGHT_CHANNEL, PipetteGenerationType.GEN2, "p1000_multi_gen2", + PipetteOEMType.OT, ], [ # 96 channel has a unique "name" right now @@ -143,6 +155,7 @@ def test_convert_pipette_name( PipetteChannelType.NINETY_SIX_CHANNEL, PipetteGenerationType.FLEX, "p1000_96", + PipetteOEMType.OT, ], ], ) @@ -151,35 +164,40 @@ def test_model_version_type_string_version( channels: PipetteChannelType, generation: PipetteGenerationType, output: PipetteName, + oem: PipetteOEMType, ) -> None: data = pc.PipetteNameType( pipette_type=model_type, pipette_channels=channels, pipette_generation=generation, + oem_type=oem, ) assert output == str(data) @pytest.mark.parametrize( - argnames=["model_type", "channels", "version", "output"], + argnames=["model_type", "channels", "version", "output", "oem"], argvalues=[ [ PipetteModelType.p50, PipetteChannelType.SINGLE_CHANNEL, PipetteVersionType(1, 0), "p50_single_v1", + PipetteOEMType.OT, ], [ PipetteModelType.p1000, PipetteChannelType.EIGHT_CHANNEL, PipetteVersionType(2, 1), "p1000_multi_v2.1", + PipetteOEMType.OT, ], [ PipetteModelType.p1000, PipetteChannelType.NINETY_SIX_CHANNEL, PipetteVersionType(3, 3), "p1000_96_v3.3", + PipetteOEMType.OT, ], ], ) @@ -188,9 +206,13 @@ def test_name_type_string_generation( channels: PipetteChannelType, version: PipetteVersionType, output: PipetteModel, + oem: PipetteOEMType, ) -> None: data = pc.PipetteModelVersionType( - pipette_type=model_type, pipette_channels=channels, pipette_version=version + pipette_type=model_type, + pipette_channels=channels, + pipette_version=version, + oem_type=oem, ) assert output == str(data) diff --git a/shared-data/python/tests/pipette/test_typechecks.py b/shared-data/python/tests/pipette/test_typechecks.py index a44b673d3ed..e953443846f 100644 --- a/shared-data/python/tests/pipette/test_typechecks.py +++ b/shared-data/python/tests/pipette/test_typechecks.py @@ -1,6 +1,8 @@ import pytest import typeguard +from typing import Generator, Tuple + from opentrons_shared_data.pipette import ( model_config, name_config, @@ -11,20 +13,22 @@ PipetteModelSpecs, PipetteNameSpecs, PipetteFusedSpec, + PipetteName, + PipetteModel, ) -def test_model_config_check(): +def test_model_config_check() -> None: defdict = model_config() typeguard.check_type(defdict, PipetteModelSpecs) -def test_name_config_check(): +def test_name_config_check() -> None: defdict = name_config() typeguard.check_type(defdict, PipetteNameSpecs) -def build_model_name_pairs(): +def build_model_name_pairs() -> Generator[Tuple[PipetteModel, PipetteName], None, None]: for model, conf in model_config()["config"].items(): yield model, conf["name"] for bcn in conf.get("backCompatNames", []): @@ -32,12 +36,12 @@ def build_model_name_pairs(): @pytest.mark.parametrize("model,name", list(build_model_name_pairs())) -def test_fuse(model, name): +def test_fuse(model: PipetteModel, name: PipetteName) -> None: defdict = fuse_specs(model, name) typeguard.check_type(defdict, PipetteFusedSpec) @pytest.mark.parametrize("name", list(name_config().keys())) -def test_model_for_name(name): +def test_model_for_name(name: PipetteName) -> None: model = dummy_model_for_name(name) assert model in model_config()["config"] diff --git a/shared-data/python/tests/pipette/test_validate_schema.py b/shared-data/python/tests/pipette/test_validate_schema.py index 57f19dfe3ad..cf1d35288cb 100644 --- a/shared-data/python/tests/pipette/test_validate_schema.py +++ b/shared-data/python/tests/pipette/test_validate_schema.py @@ -13,10 +13,10 @@ from opentrons_shared_data.pipette.pipette_load_name_conversions import ( convert_pipette_model, ) -from opentrons_shared_data.pipette.types import PipetteModel +from opentrons_shared_data.pipette import types -def iterate_models() -> Iterator[PipetteModel]: +def iterate_models() -> Iterator[types.PipetteModel]: """Get an iterator of all pipette models.""" _channel_model_str = { "single_channel": "single", @@ -31,7 +31,7 @@ def iterate_models() -> Iterator[PipetteModel]: for lc_dir in model_dir.iterdir(): for version_file in lc_dir.iterdir(): version_list = version_file.stem.split("_") - yield PipetteModel( + yield types.PipetteModel( f"{model_dir.stem}_{_channel_model_str[channel_dir.stem]}_v{version_list[0]}.{version_list[1]}" ) @@ -45,6 +45,7 @@ def test_check_all_models_are_valid() -> None: model_version.pipette_type, model_version.pipette_channels, model_version.pipette_version, + model_version.oem_type, ) except json.JSONDecodeError: print( @@ -72,7 +73,7 @@ def test_pick_up_configs_configuration_by_nozzle_map_keys() -> None: for version_file in os.listdir(paths_to_validate / channel_dir / model_dir): print(version_file) version_list = version_file.split(".json")[0].split("_") - built_model: PipetteModel = PipetteModel( + built_model: types.PipetteModel = types.PipetteModel( f"{model_dir}_{_channel_model_str[channel_dir]}_v{version_list[0]}.{version_list[1]}" ) @@ -81,11 +82,13 @@ def test_pick_up_configs_configuration_by_nozzle_map_keys() -> None: model_version.pipette_type, model_version.pipette_channels, model_version.pipette_version, + model_version.oem_type, ) valid_nozzle_maps = load_valid_nozzle_maps( model_version.pipette_type, model_version.pipette_channels, model_version.pipette_version, + model_version.oem_type, ) pipette_maps = list( @@ -114,7 +117,7 @@ def test_pick_up_configs_configuration_ordered_from_smallest_to_largest() -> Non for model_dir in os.listdir(paths_to_validate / channel_dir): for version_file in os.listdir(paths_to_validate / channel_dir / model_dir): version_list = version_file.split(".json")[0].split("_") - built_model: PipetteModel = PipetteModel( + built_model: types.PipetteModel = types.PipetteModel( f"{model_dir}_{_channel_model_str[channel_dir]}_v{version_list[0]}.{version_list[1]}" ) @@ -123,11 +126,13 @@ def test_pick_up_configs_configuration_ordered_from_smallest_to_largest() -> Non model_version.pipette_type, model_version.pipette_channels, model_version.pipette_version, + model_version.oem_type, ) valid_nozzle_maps = load_valid_nozzle_maps( model_version.pipette_type, model_version.pipette_channels, model_version.pipette_version, + model_version.oem_type, ) map_keys = list(valid_nozzle_maps.maps.keys()) @@ -145,3 +150,49 @@ def test_pick_up_configs_configuration_ordered_from_smallest_to_largest() -> Non assert len(valid_nozzle_maps.maps[pipette_maps[i]]) <= len( valid_nozzle_maps.maps[key] ) + + +def test_serializer() -> None: + """Verify that the serializer works as expected.""" + + loaded_model = load_definition( + types.PipetteModelType.p1000, + types.PipetteChannelType.NINETY_SIX_CHANNEL, + types.PipetteVersionType(3, 3), + types.PipetteOEMType.OT, + ) + quirk_0 = types.Quirks.pickupTipShake + quirk_1 = types.Quirks.dropTipShake + loaded_model.quirks = [quirk_0, quirk_1] + + assert loaded_model.pipette_type == types.PipetteModelType.p1000 + assert loaded_model.display_category == types.PipetteGenerationType.FLEX + assert loaded_model.channels == types.PipetteChannelType.NINETY_SIX_CHANNEL + + model_dict = loaded_model.model_dump() + # each field should be the value of the enum class + assert ( + isinstance(model_dict["pipette_type"], str) + and model_dict["pipette_type"] == loaded_model.pipette_type.value + ) + assert ( + isinstance(model_dict["display_category"], str) + and model_dict["display_category"] == loaded_model.display_category.value + ) + assert ( + isinstance(model_dict["channels"], int) + and model_dict["channels"] == loaded_model.channels.value + ) + + assert len(model_dict["quirks"]) == 2 + dict_quirk_0 = model_dict["quirks"][0] + dict_quirk_1 = model_dict["quirks"][1] + assert isinstance(dict_quirk_0, str) and dict_quirk_0 == quirk_0.value + assert isinstance(dict_quirk_1, str) and dict_quirk_1 == quirk_1.value + + +# TODO: (AA, 4/9/2024) we should add a test to validate the dumped json to make +# sure we can re-load it as the BaseModel class. But we haven't added serializer +# for other enums yet, such as LiquidClass, and since we haven't been +# creating the definition files using model_dump/model_dump_json, it is okay to +# skip this for now. diff --git a/shared-data/python/tests/protocol/test_protocol_schema_v6.py b/shared-data/python/tests/protocol/test_protocol_schema_v6.py index cf8a073eafc..67ccfdc6a9f 100644 --- a/shared-data/python/tests/protocol/test_protocol_schema_v6.py +++ b/shared-data/python/tests/protocol/test_protocol_schema_v6.py @@ -1,6 +1,7 @@ import json import pytest from typing import Any, Dict +from pathlib import Path from opentrons_shared_data import load_shared_data from opentrons_shared_data.protocol.models import ( protocol_schema_v6, @@ -16,10 +17,10 @@ @pytest.mark.parametrize("defpath", list_fixtures(6)) -def test_v6_types(defpath): +def test_v6_types(defpath: Path) -> None: def_data = load_shared_data(defpath) - def_model = protocol_schema_v6.ProtocolSchemaV6.parse_raw(def_data) - def_dict_from_model = def_model.dict( + def_model = protocol_schema_v6.ProtocolSchemaV6.model_validate_json(def_data) + def_dict_from_model = def_model.model_dump( exclude_unset=True, # 'schemaVersion' in python is '$schemaVersion' in JSON by_alias=True, diff --git a/shared-data/python/tests/protocol/test_protocol_schema_v7.py b/shared-data/python/tests/protocol/test_protocol_schema_v7.py index 8ccb9bd725b..0e5609148c3 100644 --- a/shared-data/python/tests/protocol/test_protocol_schema_v7.py +++ b/shared-data/python/tests/protocol/test_protocol_schema_v7.py @@ -1,6 +1,7 @@ import json import pytest from typing import Any, Dict +from pathlib import Path from opentrons_shared_data import load_shared_data from opentrons_shared_data.protocol.models import protocol_schema_v7 @@ -9,10 +10,10 @@ @pytest.mark.parametrize("defpath", list_fixtures(7)) -def test_v7_types(defpath): +def test_v7_types(defpath: Path) -> None: def_data = load_shared_data(defpath) - def_model = protocol_schema_v7.ProtocolSchemaV7.parse_raw(def_data) - def_dict_from_model = def_model.dict( + def_model = protocol_schema_v7.ProtocolSchemaV7.model_validate_json(def_data) + def_dict_from_model = def_model.model_dump( exclude_unset=True, # 'schemaVersion' in python is '$schemaVersion' in JSON by_alias=True, diff --git a/shared-data/python/tests/protocol/test_protocol_schema_v8.py b/shared-data/python/tests/protocol/test_protocol_schema_v8.py index 63d00db571d..d2488904235 100644 --- a/shared-data/python/tests/protocol/test_protocol_schema_v8.py +++ b/shared-data/python/tests/protocol/test_protocol_schema_v8.py @@ -1,5 +1,6 @@ import json import pytest +from pathlib import Path from opentrons_shared_data import load_shared_data from opentrons_shared_data.protocol.models import protocol_schema_v8 @@ -8,15 +9,9 @@ @pytest.mark.parametrize("defpath", list_fixtures(8)) -def test_v8_types(defpath): +def test_v8_types(defpath: Path) -> None: def_data = load_shared_data(defpath) - def_model = protocol_schema_v8.ProtocolSchemaV8.parse_raw(def_data) - def_dict_from_model = json.loads( - def_model.json( - exclude_unset=True, - # 'schemaVersion' in python is '$schemaVersion' in JSON - by_alias=True, - ) - ) + def_model = protocol_schema_v8.ProtocolSchemaV8.model_validate_json(def_data) + def_dict_from_model = def_model.model_dump(by_alias=True, exclude_unset=True) expected_def_dict = json.loads(def_data) assert def_dict_from_model == expected_def_dict diff --git a/shared-data/python/tests/protocol/test_typechecks.py b/shared-data/python/tests/protocol/test_typechecks.py index 0b52f15c24e..0b4f20511a8 100644 --- a/shared-data/python/tests/protocol/test_typechecks.py +++ b/shared-data/python/tests/protocol/test_typechecks.py @@ -1,5 +1,6 @@ import json import pytest +from pathlib import Path import typeguard from opentrons_shared_data import load_shared_data @@ -12,18 +13,18 @@ @pytest.mark.parametrize("defpath", list_fixtures(3)) -def test_v3_types(defpath): +def test_v3_types(defpath: Path) -> None: defn = json.loads(load_shared_data(defpath)) typeguard.check_type(defn, JsonProtocolV3) @pytest.mark.parametrize("defpath", list_fixtures(4)) -def test_v4_types(defpath): +def test_v4_types(defpath: Path) -> None: defn = json.loads(load_shared_data(defpath)) typeguard.check_type(defn, JsonProtocolV4) @pytest.mark.parametrize("defpath", list_fixtures(5)) -def test_v5_types(defpath): +def test_v5_types(defpath: Path) -> None: defn = json.loads(load_shared_data(defpath)) typeguard.check_type(defn, JsonProtocolV5) diff --git a/step-generation/src/constants.ts b/step-generation/src/constants.ts index 30c44782e8e..cc215de8c99 100644 --- a/step-generation/src/constants.ts +++ b/step-generation/src/constants.ts @@ -58,6 +58,7 @@ export const HEATERSHAKER_MODULE_INITIAL_STATE: HeaterShakerModuleState = { const ABSORBANCE_READER_INITIAL_STATE: AbsorbanceReaderState = { type: 'absorbanceReaderType', + lidOpen: null, } const MAGNETIC_BLOCK_INITIAL_STATE: MagneticBlockState = { type: 'magneticBlockType', diff --git a/step-generation/src/types.ts b/step-generation/src/types.ts index 021b6cfa515..c1716fa62c3 100644 --- a/step-generation/src/types.ts +++ b/step-generation/src/types.ts @@ -77,6 +77,7 @@ export interface MagneticBlockState { export interface AbsorbanceReaderState { type: typeof ABSORBANCE_READER_TYPE + lidOpen: boolean | null } export type ModuleState = diff --git a/system-server/Pipfile b/system-server/Pipfile index d1ce7f43f6a..7f71cb61d77 100644 --- a/system-server/Pipfile +++ b/system-server/Pipfile @@ -4,13 +4,14 @@ verify_ssl = true name = "pypi" [packages] -fastapi = "==0.99.1" +fastapi = "==0.100.0" uvicorn = "==0.27.0.post1" anyio = "==3.7.1" typing-extensions = ">=4.0.0,<5" python-dotenv = "==1.0.1" python-multipart = "==0.0.6" -pydantic = "==1.10.12" +pydantic = "==2.9.0" +pydantic-settings = "==2.4.0" importlib-metadata = ">=4.13.0,<5" sqlalchemy = "==1.4.51" pyjwt = "==2.6.0" @@ -37,7 +38,7 @@ coverage = "==7.4.0" atomicwrites = { version = "==1.4.0", markers="sys_platform=='win32'" } colorama = { version = "==0.4.4", markers="sys_platform=='win32'" } sqlalchemy2-stubs = "==0.0.2a21" -mypy = "==1.8.0" +mypy = "==1.11.0" black = "==22.3.0" decoy = "==2.1.1" mock = "~=5.1.0" diff --git a/system-server/Pipfile.lock b/system-server/Pipfile.lock index d7d315362f2..f36f806b824 100644 --- a/system-server/Pipfile.lock +++ b/system-server/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "a0353342ca006092f2014adfa5aab4abaa720a1ddc89c0dfe34d77b72ed525b1" + "sha256": "b9d3786493ef92719c2aa2738e20b27d06ac68d25a0c9ae998088b8f36d98316" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,14 @@ ] }, "default": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "anyio": { "hashes": [ "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", @@ -35,20 +43,20 @@ }, "exceptiongroup": { "hashes": [ - "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", - "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.0" + "version": "==1.2.2" }, "fastapi": { "hashes": [ - "sha256:976df7bab51ac7beda9f68c4513b8c4490b5c1135c72aafd0a5ee4023ec5282e", - "sha256:ac78f717cd80d657bd183f94d33b9bda84aa376a46a9dab513586b8eef1dc6fc" + "sha256:271662daf986da8fa98dc2b7c7f61c4abdfdccfb4786d79ed8b2878f172c6d5f", + "sha256:acb5f941ea8215663283c10018323ba7ea737c571b67fc7e88e9469c7eb1d12e" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.99.1" + "version": "==0.100.0" }, "filetype": { "hashes": [ @@ -68,11 +76,11 @@ }, "idna": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], - "markers": "python_version >= '3.5'", - "version": "==3.6" + "markers": "python_version >= '3.6'", + "version": "==3.8" }, "importlib-metadata": { "hashes": [ @@ -85,46 +93,116 @@ }, "pydantic": { "hashes": [ - "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303", - "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe", - "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47", - "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494", - "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33", - "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86", - "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d", - "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c", - "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a", - "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565", - "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb", - "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62", - "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62", - "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0", - "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523", - "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d", - "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405", - "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f", - "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b", - "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718", - "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed", - "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb", - "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5", - "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc", - "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942", - "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe", - "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246", - "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350", - "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303", - "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09", - "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33", - "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8", - "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a", - "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1", - "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6", - "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d" + "sha256:c7a8a9fdf7d100afa49647eae340e2d23efa382466a8d177efcd1381e9be5598", + "sha256:f66a7073abd93214a20c5f7b32d56843137a7a2e70d02111f3be287035c45370" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.10.12" + "markers": "python_version >= '3.8'", + "version": "==2.9.0" + }, + "pydantic-core": { + "hashes": [ + "sha256:0102e49ac7d2df3379ef8d658d3bc59d3d769b0bdb17da189b75efa861fc07b4", + "sha256:0123655fedacf035ab10c23450163c2f65a4174f2bb034b188240a6cf06bb123", + "sha256:043ef8469f72609c4c3a5e06a07a1f713d53df4d53112c6d49207c0bd3c3bd9b", + "sha256:0448b81c3dfcde439551bb04a9f41d7627f676b12701865c8a2574bcea034437", + "sha256:05b366fb8fe3d8683b11ac35fa08947d7b92be78ec64e3277d03bd7f9b7cda79", + "sha256:07049ec9306ec64e955b2e7c40c8d77dd78ea89adb97a2013d0b6e055c5ee4c5", + "sha256:084414ffe9a85a52940b49631321d636dadf3576c30259607b75516d131fecd0", + "sha256:086c5db95157dc84c63ff9d96ebb8856f47ce113c86b61065a066f8efbe80acf", + "sha256:12625e69b1199e94b0ae1c9a95d000484ce9f0182f9965a26572f054b1537e44", + "sha256:16b25a4a120a2bb7dab51b81e3d9f3cde4f9a4456566c403ed29ac81bf49744f", + "sha256:19f1352fe4b248cae22a89268720fc74e83f008057a652894f08fa931e77dced", + "sha256:1a2ab4f410f4b886de53b6bddf5dd6f337915a29dd9f22f20f3099659536b2f6", + "sha256:1c7b81beaf7c7ebde978377dc53679c6cba0e946426fc7ade54251dfe24a7604", + "sha256:1cf842265a3a820ebc6388b963ead065f5ce8f2068ac4e1c713ef77a67b71f7c", + "sha256:1eb37f7d6a8001c0f86dc8ff2ee8d08291a536d76e49e78cda8587bb54d8b329", + "sha256:23af245b8f2f4ee9e2c99cb3f93d0e22fb5c16df3f2f643f5a8da5caff12a653", + "sha256:257d6a410a0d8aeb50b4283dea39bb79b14303e0fab0f2b9d617701331ed1515", + "sha256:276ae78153a94b664e700ac362587c73b84399bd1145e135287513442e7dfbc7", + "sha256:2b1a195efd347ede8bcf723e932300292eb13a9d2a3c1f84eb8f37cbbc905b7f", + "sha256:329a721253c7e4cbd7aad4a377745fbcc0607f9d72a3cc2102dd40519be75ed2", + "sha256:358331e21a897151e54d58e08d0219acf98ebb14c567267a87e971f3d2a3be59", + "sha256:3649bd3ae6a8ebea7dc381afb7f3c6db237fc7cebd05c8ac36ca8a4187b03b30", + "sha256:3713dc093d5048bfaedbba7a8dbc53e74c44a140d45ede020dc347dda18daf3f", + "sha256:3ef71ec876fcc4d3bbf2ae81961959e8d62f8d74a83d116668409c224012e3af", + "sha256:41ae8537ad371ec018e3c5da0eb3f3e40ee1011eb9be1da7f965357c4623c501", + "sha256:4a801c5e1e13272e0909c520708122496647d1279d252c9e6e07dac216accc41", + "sha256:4c83c64d05ffbbe12d4e8498ab72bdb05bcc1026340a4a597dc647a13c1605ec", + "sha256:4cebb9794f67266d65e7e4cbe5dcf063e29fc7b81c79dc9475bd476d9534150e", + "sha256:5668b3173bb0b2e65020b60d83f5910a7224027232c9f5dc05a71a1deac9f960", + "sha256:56e6a12ec8d7679f41b3750ffa426d22b44ef97be226a9bab00a03365f217b2b", + "sha256:582871902e1902b3c8e9b2c347f32a792a07094110c1bca6c2ea89b90150caac", + "sha256:5c8aa40f6ca803f95b1c1c5aeaee6237b9e879e4dfb46ad713229a63651a95fb", + "sha256:5d813fd871b3d5c3005157622ee102e8908ad6011ec915a18bd8fde673c4360e", + "sha256:5dd0ec5f514ed40e49bf961d49cf1bc2c72e9b50f29a163b2cc9030c6742aa73", + "sha256:5f3cf3721eaf8741cffaf092487f1ca80831202ce91672776b02b875580e174a", + "sha256:6294907eaaccf71c076abdd1c7954e272efa39bb043161b4b8aa1cd76a16ce43", + "sha256:64d094ea1aa97c6ded4748d40886076a931a8bf6f61b6e43e4a1041769c39dd2", + "sha256:6650a7bbe17a2717167e3e23c186849bae5cef35d38949549f1c116031b2b3aa", + "sha256:67b6655311b00581914aba481729971b88bb8bc7996206590700a3ac85e457b8", + "sha256:6b06c5d4e8701ac2ba99a2ef835e4e1b187d41095a9c619c5b185c9068ed2a49", + "sha256:6ce883906810b4c3bd90e0ada1f9e808d9ecf1c5f0b60c6b8831d6100bcc7dd6", + "sha256:6db09153d8438425e98cdc9a289c5fade04a5d2128faff8f227c459da21b9703", + "sha256:6f80fba4af0cb1d2344869d56430e304a51396b70d46b91a55ed4959993c0589", + "sha256:743e5811b0c377eb830150d675b0847a74a44d4ad5ab8845923d5b3a756d8100", + "sha256:753294d42fb072aa1775bfe1a2ba1012427376718fa4c72de52005a3d2a22178", + "sha256:7568f682c06f10f30ef643a1e8eec4afeecdafde5c4af1b574c6df079e96f96c", + "sha256:7706e15cdbf42f8fab1e6425247dfa98f4a6f8c63746c995d6a2017f78e619ae", + "sha256:785e7f517ebb9890813d31cb5d328fa5eda825bb205065cde760b3150e4de1f7", + "sha256:7a05c0240f6c711eb381ac392de987ee974fa9336071fb697768dfdb151345ce", + "sha256:7ce7eaf9a98680b4312b7cebcdd9352531c43db00fca586115845df388f3c465", + "sha256:7ce8e26b86a91e305858e018afc7a6e932f17428b1eaa60154bd1f7ee888b5f8", + "sha256:7d0324a35ab436c9d768753cbc3c47a865a2cbc0757066cb864747baa61f6ece", + "sha256:7e9b24cca4037a561422bf5dc52b38d390fb61f7bfff64053ce1b72f6938e6b2", + "sha256:810ca06cca91de9107718dc83d9ac4d2e86efd6c02cba49a190abcaf33fb0472", + "sha256:820f6ee5c06bc868335e3b6e42d7ef41f50dfb3ea32fbd523ab679d10d8741c0", + "sha256:82764c0bd697159fe9947ad59b6db6d7329e88505c8f98990eb07e84cc0a5d81", + "sha256:8ae65fdfb8a841556b52935dfd4c3f79132dc5253b12c0061b96415208f4d622", + "sha256:8d5b0ff3218858859910295df6953d7bafac3a48d5cd18f4e3ed9999efd2245f", + "sha256:95d6bf449a1ac81de562d65d180af5d8c19672793c81877a2eda8fde5d08f2fd", + "sha256:964c7aa318da542cdcc60d4a648377ffe1a2ef0eb1e996026c7f74507b720a78", + "sha256:96ef39add33ff58cd4c112cbac076726b96b98bb8f1e7f7595288dcfb2f10b57", + "sha256:a6612c2a844043e4d10a8324c54cdff0042c558eef30bd705770793d70b224aa", + "sha256:a8031074a397a5925d06b590121f8339d34a5a74cfe6970f8a1124eb8b83f4ac", + "sha256:aab9e522efff3993a9e98ab14263d4e20211e62da088298089a03056980a3e69", + "sha256:ae579143826c6f05a361d9546446c432a165ecf1c0b720bbfd81152645cb897d", + "sha256:ae90b9e50fe1bd115b24785e962b51130340408156d34d67b5f8f3fa6540938e", + "sha256:b18cf68255a476b927910c6873d9ed00da692bb293c5b10b282bd48a0afe3ae2", + "sha256:b7efb12e5071ad8d5b547487bdad489fbd4a5a35a0fc36a1941517a6ad7f23e0", + "sha256:c4d9f15ffe68bcd3898b0ad7233af01b15c57d91cd1667f8d868e0eacbfe3f87", + "sha256:c53100c8ee5a1e102766abde2158077d8c374bee0639201f11d3032e3555dfbc", + "sha256:c57e493a0faea1e4c38f860d6862ba6832723396c884fbf938ff5e9b224200e2", + "sha256:c8319e0bd6a7b45ad76166cc3d5d6a36c97d0c82a196f478c3ee5346566eebfd", + "sha256:caffda619099cfd4f63d48462f6aadbecee3ad9603b4b88b60cb821c1b258576", + "sha256:cc0c316fba3ce72ac3ab7902a888b9dc4979162d320823679da270c2d9ad0cad", + "sha256:cdd02a08205dc90238669f082747612cb3c82bd2c717adc60f9b9ecadb540f80", + "sha256:d50ac34835c6a4a0d456b5db559b82047403c4317b3bc73b3455fefdbdc54b0a", + "sha256:d6b9dd6aa03c812017411734e496c44fef29b43dba1e3dd1fa7361bbacfc1354", + "sha256:da3131ef2b940b99106f29dfbc30d9505643f766704e14c5d5e504e6a480c35e", + "sha256:da43cbe593e3c87d07108d0ebd73771dc414488f1f91ed2e204b0370b94b37ac", + "sha256:dd59638025160056687d598b054b64a79183f8065eae0d3f5ca523cde9943940", + "sha256:e1895e949f8849bc2757c0dbac28422a04be031204df46a56ab34bcf98507342", + "sha256:e1a79ad49f346aa1a2921f31e8dbbab4d64484823e813a002679eaa46cba39e1", + "sha256:e460475719721d59cd54a350c1f71c797c763212c836bf48585478c5514d2854", + "sha256:e64ffaf8f6e17ca15eb48344d86a7a741454526f3a3fa56bc493ad9d7ec63936", + "sha256:e6e3ccebdbd6e53474b0bb7ab8b88e83c0cfe91484b25e058e581348ee5a01a5", + "sha256:e758d271ed0286d146cf7c04c539a5169a888dd0b57026be621547e756af55bc", + "sha256:f087879f1ffde024dd2788a30d55acd67959dcf6c431e9d3682d1c491a0eb474", + "sha256:f477d26183e94eaafc60b983ab25af2a809a1b48ce4debb57b343f671b7a90b6", + "sha256:fc535cb898ef88333cf317777ecdfe0faac1c2a3187ef7eb061b6f7ecf7e6bae" + ], + "markers": "python_version >= '3.8'", + "version": "==2.23.2" + }, + "pydantic-settings": { + "hashes": [ + "sha256:bb6849dc067f1687574c12a639e231f3a6feeed0a12d710c1382045c5db1c315", + "sha256:ed81c3a0f46392b4d7c0a565c05884e6e54b3456e6f0fe4d8814981172dc9a88" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.4.0" }, "pyjwt": { "hashes": [ @@ -159,11 +237,11 @@ }, "sniffio": { "hashes": [ - "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", - "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384" + "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" ], "markers": "python_version >= '3.7'", - "version": "==1.3.0" + "version": "==1.3.1" }, "sqlalchemy": { "hashes": [ @@ -239,12 +317,20 @@ }, "typing-extensions": { "hashes": [ - "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", - "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.11.0" + "version": "==4.12.2" + }, + "tzdata": { + "hashes": [ + "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", + "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252" + ], + "markers": "python_version >= '3.9'", + "version": "==2024.1" }, "uvicorn": { "hashes": [ @@ -265,11 +351,11 @@ }, "zipp": { "hashes": [ - "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31", - "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0" + "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064", + "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b" ], "markers": "python_version >= '3.8'", - "version": "==3.17.0" + "version": "==3.20.1" } }, "develop": { @@ -283,11 +369,11 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "black": { "hashes": [ @@ -321,11 +407,11 @@ }, "certifi": { "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" ], "markers": "python_version >= '3.6'", - "version": "==2024.2.2" + "version": "==2024.8.30" }, "charset-normalizer": { "hashes": [ @@ -430,19 +516,19 @@ }, "exceptiongroup": { "hashes": [ - "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", - "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.0" + "version": "==1.2.2" }, "execnet": { "hashes": [ - "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", - "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" + "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", + "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.2" + "markers": "python_version >= '3.8'", + "version": "==2.1.1" }, "flake8": { "hashes": [ @@ -482,11 +568,11 @@ }, "idna": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], - "markers": "python_version >= '3.5'", - "version": "==3.6" + "markers": "python_version >= '3.6'", + "version": "==3.8" }, "iniconfig": { "hashes": [ @@ -506,11 +592,11 @@ }, "jsonschema": { "hashes": [ - "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f", - "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5" + "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", + "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566" ], "markers": "python_version >= '3.8'", - "version": "==4.21.1" + "version": "==4.23.0" }, "jsonschema-specifications": { "hashes": [ @@ -539,37 +625,37 @@ }, "mypy": { "hashes": [ - "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", - "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", - "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", - "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", - "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", - "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", - "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", - "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", - "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", - "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", - "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", - "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", - "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", - "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", - "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", - "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", - "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", - "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", - "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", - "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", - "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", - "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", - "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", - "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", - "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", - "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", - "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.8.0" + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -581,11 +667,11 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", + "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==24.1" }, "paho-mqtt": { "hashes": [ @@ -603,27 +689,27 @@ }, "pbr": { "hashes": [ - "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda", - "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9" + "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24", + "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a" ], "markers": "python_version >= '2.6'", - "version": "==6.0.0" + "version": "==6.1.0" }, "platformdirs": { "hashes": [ - "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", - "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" + "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", + "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" ], "markers": "python_version >= '3.8'", - "version": "==4.2.0" + "version": "==4.2.2" }, "pluggy": { "hashes": [ - "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", - "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" ], "markers": "python_version >= '3.8'", - "version": "==1.4.0" + "version": "==1.5.0" }, "py": { "hashes": [ @@ -750,76 +836,78 @@ }, "python-dateutil": { "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" + "version": "==2.9.0.post0" }, "pyyaml": { "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" ], - "markers": "python_version >= '3.6'", - "version": "==6.0.1" + "markers": "python_version >= '3.8'", + "version": "==6.0.2" }, "referencing": { "hashes": [ - "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5", - "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7" + "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", + "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de" ], "markers": "python_version >= '3.8'", - "version": "==0.33.0" + "version": "==0.35.1" }, "requests": { "hashes": [ @@ -832,116 +920,120 @@ }, "rpds-py": { "hashes": [ - "sha256:01f58a7306b64e0a4fe042047dd2b7d411ee82e54240284bab63e325762c1147", - "sha256:0210b2668f24c078307260bf88bdac9d6f1093635df5123789bfee4d8d7fc8e7", - "sha256:02866e060219514940342a1f84303a1ef7a1dad0ac311792fbbe19b521b489d2", - "sha256:0387ce69ba06e43df54e43968090f3626e231e4bc9150e4c3246947567695f68", - "sha256:060f412230d5f19fc8c8b75f315931b408d8ebf56aec33ef4168d1b9e54200b1", - "sha256:071bc28c589b86bc6351a339114fb7a029f5cddbaca34103aa573eba7b482382", - "sha256:0bfb09bf41fe7c51413f563373e5f537eaa653d7adc4830399d4e9bdc199959d", - "sha256:10162fe3f5f47c37ebf6d8ff5a2368508fe22007e3077bf25b9c7d803454d921", - "sha256:149c5cd24f729e3567b56e1795f74577aa3126c14c11e457bec1b1c90d212e38", - "sha256:1701fc54460ae2e5efc1dd6350eafd7a760f516df8dbe51d4a1c79d69472fbd4", - "sha256:1957a2ab607f9added64478a6982742eb29f109d89d065fa44e01691a20fc20a", - "sha256:1a746a6d49665058a5896000e8d9d2f1a6acba8a03b389c1e4c06e11e0b7f40d", - "sha256:1bfcad3109c1e5ba3cbe2f421614e70439f72897515a96c462ea657261b96518", - "sha256:1d36b2b59e8cc6e576f8f7b671e32f2ff43153f0ad6d0201250a7c07f25d570e", - "sha256:1db228102ab9d1ff4c64148c96320d0be7044fa28bd865a9ce628ce98da5973d", - "sha256:1dc29db3900cb1bb40353772417800f29c3d078dbc8024fd64655a04ee3c4bdf", - "sha256:1e626b365293a2142a62b9a614e1f8e331b28f3ca57b9f05ebbf4cf2a0f0bdc5", - "sha256:1f3c3461ebb4c4f1bbc70b15d20b565759f97a5aaf13af811fcefc892e9197ba", - "sha256:20de7b7179e2031a04042e85dc463a93a82bc177eeba5ddd13ff746325558aa6", - "sha256:24e4900a6643f87058a27320f81336d527ccfe503984528edde4bb660c8c8d59", - "sha256:2528ff96d09f12e638695f3a2e0c609c7b84c6df7c5ae9bfeb9252b6fa686253", - "sha256:25f071737dae674ca8937a73d0f43f5a52e92c2d178330b4c0bb6ab05586ffa6", - "sha256:270987bc22e7e5a962b1094953ae901395e8c1e1e83ad016c5cfcfff75a15a3f", - "sha256:292f7344a3301802e7c25c53792fae7d1593cb0e50964e7bcdcc5cf533d634e3", - "sha256:2953937f83820376b5979318840f3ee47477d94c17b940fe31d9458d79ae7eea", - "sha256:2a792b2e1d3038daa83fa474d559acfd6dc1e3650ee93b2662ddc17dbff20ad1", - "sha256:2a7b2f2f56a16a6d62e55354dd329d929560442bd92e87397b7a9586a32e3e76", - "sha256:2f4eb548daf4836e3b2c662033bfbfc551db58d30fd8fe660314f86bf8510b93", - "sha256:3664d126d3388a887db44c2e293f87d500c4184ec43d5d14d2d2babdb4c64cad", - "sha256:3677fcca7fb728c86a78660c7fb1b07b69b281964673f486ae72860e13f512ad", - "sha256:380e0df2e9d5d5d339803cfc6d183a5442ad7ab3c63c2a0982e8c824566c5ccc", - "sha256:3ac732390d529d8469b831949c78085b034bff67f584559340008d0f6041a049", - "sha256:4128980a14ed805e1b91a7ed551250282a8ddf8201a4e9f8f5b7e6225f54170d", - "sha256:4341bd7579611cf50e7b20bb8c2e23512a3dc79de987a1f411cb458ab670eb90", - "sha256:436474f17733c7dca0fbf096d36ae65277e8645039df12a0fa52445ca494729d", - "sha256:4dc889a9d8a34758d0fcc9ac86adb97bab3fb7f0c4d29794357eb147536483fd", - "sha256:4e21b76075c01d65d0f0f34302b5a7457d95721d5e0667aea65e5bb3ab415c25", - "sha256:516fb8c77805159e97a689e2f1c80655c7658f5af601c34ffdb916605598cda2", - "sha256:5576ee2f3a309d2bb403ec292d5958ce03953b0e57a11d224c1f134feaf8c40f", - "sha256:5a024fa96d541fd7edaa0e9d904601c6445e95a729a2900c5aec6555fe921ed6", - "sha256:5d0e8a6434a3fbf77d11448c9c25b2f25244226cfbec1a5159947cac5b8c5fa4", - "sha256:5e7d63ec01fe7c76c2dbb7e972fece45acbb8836e72682bde138e7e039906e2c", - "sha256:60e820ee1004327609b28db8307acc27f5f2e9a0b185b2064c5f23e815f248f8", - "sha256:637b802f3f069a64436d432117a7e58fab414b4e27a7e81049817ae94de45d8d", - "sha256:65dcf105c1943cba45d19207ef51b8bc46d232a381e94dd38719d52d3980015b", - "sha256:698ea95a60c8b16b58be9d854c9f993c639f5c214cf9ba782eca53a8789d6b19", - "sha256:70fcc6c2906cfa5c6a552ba7ae2ce64b6c32f437d8f3f8eea49925b278a61453", - "sha256:720215373a280f78a1814becb1312d4e4d1077b1202a56d2b0815e95ccb99ce9", - "sha256:7450dbd659fed6dd41d1a7d47ed767e893ba402af8ae664c157c255ec6067fde", - "sha256:7b7d9ca34542099b4e185b3c2a2b2eda2e318a7dbde0b0d83357a6d4421b5296", - "sha256:7fbd70cb8b54fe745301921b0816c08b6d917593429dfc437fd024b5ba713c58", - "sha256:81038ff87a4e04c22e1d81f947c6ac46f122e0c80460b9006e6517c4d842a6ec", - "sha256:810685321f4a304b2b55577c915bece4c4a06dfe38f6e62d9cc1d6ca8ee86b99", - "sha256:82ada4a8ed9e82e443fcef87e22a3eed3654dd3adf6e3b3a0deb70f03e86142a", - "sha256:841320e1841bb53fada91c9725e766bb25009cfd4144e92298db296fb6c894fb", - "sha256:8587fd64c2a91c33cdc39d0cebdaf30e79491cc029a37fcd458ba863f8815383", - "sha256:8ffe53e1d8ef2520ebcf0c9fec15bb721da59e8ef283b6ff3079613b1e30513d", - "sha256:9051e3d2af8f55b42061603e29e744724cb5f65b128a491446cc029b3e2ea896", - "sha256:91e5a8200e65aaac342a791272c564dffcf1281abd635d304d6c4e6b495f29dc", - "sha256:93432e747fb07fa567ad9cc7aaadd6e29710e515aabf939dfbed8046041346c6", - "sha256:938eab7323a736533f015e6069a7d53ef2dcc841e4e533b782c2bfb9fb12d84b", - "sha256:9584f8f52010295a4a417221861df9bea4c72d9632562b6e59b3c7b87a1522b7", - "sha256:9737bdaa0ad33d34c0efc718741abaafce62fadae72c8b251df9b0c823c63b22", - "sha256:99da0a4686ada4ed0f778120a0ea8d066de1a0a92ab0d13ae68492a437db78bf", - "sha256:99f567dae93e10be2daaa896e07513dd4bf9c2ecf0576e0533ac36ba3b1d5394", - "sha256:9bdf1303df671179eaf2cb41e8515a07fc78d9d00f111eadbe3e14262f59c3d0", - "sha256:9f0e4dc0f17dcea4ab9d13ac5c666b6b5337042b4d8f27e01b70fae41dd65c57", - "sha256:a000133a90eea274a6f28adc3084643263b1e7c1a5a66eb0a0a7a36aa757ed74", - "sha256:a3264e3e858de4fc601741498215835ff324ff2482fd4e4af61b46512dd7fc83", - "sha256:a71169d505af63bb4d20d23a8fbd4c6ce272e7bce6cc31f617152aa784436f29", - "sha256:a967dd6afda7715d911c25a6ba1517975acd8d1092b2f326718725461a3d33f9", - "sha256:aa5bfb13f1e89151ade0eb812f7b0d7a4d643406caaad65ce1cbabe0a66d695f", - "sha256:ae35e8e6801c5ab071b992cb2da958eee76340e6926ec693b5ff7d6381441745", - "sha256:b686f25377f9c006acbac63f61614416a6317133ab7fafe5de5f7dc8a06d42eb", - "sha256:b760a56e080a826c2e5af09002c1a037382ed21d03134eb6294812dda268c811", - "sha256:b86b21b348f7e5485fae740d845c65a880f5d1eda1e063bc59bef92d1f7d0c55", - "sha256:b9412abdf0ba70faa6e2ee6c0cc62a8defb772e78860cef419865917d86c7342", - "sha256:bd345a13ce06e94c753dab52f8e71e5252aec1e4f8022d24d56decd31e1b9b23", - "sha256:be22ae34d68544df293152b7e50895ba70d2a833ad9566932d750d3625918b82", - "sha256:bf046179d011e6114daf12a534d874958b039342b347348a78b7cdf0dd9d6041", - "sha256:c3d2010656999b63e628a3c694f23020322b4178c450dc478558a2b6ef3cb9bb", - "sha256:c64602e8be701c6cfe42064b71c84ce62ce66ddc6422c15463fd8127db3d8066", - "sha256:d65e6b4f1443048eb7e833c2accb4fa7ee67cc7d54f31b4f0555b474758bee55", - "sha256:d8bbd8e56f3ba25a7d0cf980fc42b34028848a53a0e36c9918550e0280b9d0b6", - "sha256:da1ead63368c04a9bded7904757dfcae01eba0e0f9bc41d3d7f57ebf1c04015a", - "sha256:dbbb95e6fc91ea3102505d111b327004d1c4ce98d56a4a02e82cd451f9f57140", - "sha256:dbc56680ecf585a384fbd93cd42bc82668b77cb525343170a2d86dafaed2a84b", - "sha256:df3b6f45ba4515632c5064e35ca7f31d51d13d1479673185ba8f9fefbbed58b9", - "sha256:dfe07308b311a8293a0d5ef4e61411c5c20f682db6b5e73de6c7c8824272c256", - "sha256:e796051f2070f47230c745d0a77a91088fbee2cc0502e9b796b9c6471983718c", - "sha256:efa767c220d94aa4ac3a6dd3aeb986e9f229eaf5bce92d8b1b3018d06bed3772", - "sha256:f0b8bf5b8db49d8fd40f54772a1dcf262e8be0ad2ab0206b5a2ec109c176c0a4", - "sha256:f175e95a197f6a4059b50757a3dca33b32b61691bdbd22c29e8a8d21d3914cae", - "sha256:f2f3b28b40fddcb6c1f1f6c88c6f3769cd933fa493ceb79da45968a21dccc920", - "sha256:f6c43b6f97209e370124baf2bf40bb1e8edc25311a158867eb1c3a5d449ebc7a", - "sha256:f7f4cb1f173385e8a39c29510dd11a78bf44e360fb75610594973f5ea141028b", - "sha256:fad059a4bd14c45776600d223ec194e77db6c20255578bb5bcdd7c18fd169361", - "sha256:ff1dcb8e8bc2261a088821b2595ef031c91d499a0c1b031c152d43fe0a6ecec8", - "sha256:ffee088ea9b593cc6160518ba9bd319b5475e5f3e578e4552d63818773c6f56a" + "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c", + "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585", + "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5", + "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6", + "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef", + "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2", + "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29", + "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318", + "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b", + "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399", + "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739", + "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee", + "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174", + "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a", + "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344", + "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2", + "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03", + "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5", + "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22", + "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e", + "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96", + "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91", + "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752", + "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075", + "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253", + "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee", + "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad", + "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5", + "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce", + "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7", + "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b", + "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8", + "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57", + "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3", + "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec", + "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209", + "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921", + "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045", + "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074", + "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580", + "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7", + "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5", + "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3", + "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0", + "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24", + "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139", + "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db", + "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc", + "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789", + "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f", + "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2", + "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c", + "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232", + "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6", + "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c", + "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29", + "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489", + "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94", + "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751", + "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2", + "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda", + "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9", + "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51", + "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c", + "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8", + "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989", + "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511", + "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1", + "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2", + "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150", + "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c", + "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965", + "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f", + "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58", + "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b", + "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f", + "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d", + "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821", + "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de", + "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121", + "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855", + "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272", + "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60", + "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02", + "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1", + "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140", + "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879", + "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940", + "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364", + "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4", + "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e", + "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420", + "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5", + "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24", + "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c", + "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf", + "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f", + "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e", + "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab", + "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08", + "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92", + "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a", + "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8" ], "markers": "python_version >= '3.8'", - "version": "==0.17.1" + "version": "==0.20.0" }, "ruamel.yaml": { "hashes": [ - "sha256:61917e3a35a569c1133a8f772e1226961bf5a1198bea7e23f06a0841dea1ab0e", - "sha256:a013ac02f99a69cdd6277d9664689eb1acba07069f912823177c5eced21a6ada" + "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636", + "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b" ], "markers": "python_version >= '3.7'", - "version": "==0.18.5" + "version": "==0.18.6" }, "ruamel.yaml.clib": { "hashes": [ @@ -1033,12 +1125,12 @@ }, "tavern": { "hashes": [ - "sha256:056c4c45e27c97552ae9a3eb6a249701820a09465b4131cc4e71489166d8442d", - "sha256:21ce0c29f9e15e4b613f5f43df6da96ed0e115e5d52b4b8c1501e898708e9d35" + "sha256:5a7c4234ab9e3aaaafb9b0e72c589d735d9b04cdee1fb082f6682e80f9a806f8", + "sha256:a3ad7e843452c84170e2865414d8629a57263100fa5875ed2414853f7720943a" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.9.1" + "version": "==2.9.3" }, "tomli": { "hashes": [ @@ -1066,20 +1158,20 @@ }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.12.2" }, "urllib3": { "hashes": [ - "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", - "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0" + "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", + "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.18" + "version": "==1.26.20" } } } diff --git a/system-server/pytest.ini b/system-server/pytest.ini index 3c283534412..61e37dad0cb 100644 --- a/system-server/pytest.ini +++ b/system-server/pytest.ini @@ -2,3 +2,9 @@ addopts = --cov=system_server --cov-report term-missing:skip-covered --cov-report xml:coverage.xml --color=yes --strict-markers asyncio_mode = auto tavern-global-cfg = tests/integration/common.yaml + +filterwarnings = + # Pydantic's shims for its legacy v1 methods (e.g. `BaseModel.construct()`) + # are not type-checked properly. Forbid them, so we're forced to use their newer + # v2 replacements which are type-checked (e.g. ``BaseModel.model_construct()`) + error::pydantic.PydanticDeprecatedSince20 diff --git a/system-server/setup.py b/system-server/setup.py index 4aeed436d55..232786cca4f 100644 --- a/system-server/setup.py +++ b/system-server/setup.py @@ -50,6 +50,7 @@ def get_version(): "pyjwt==2.6.0", "systemd-python==234; sys_platform=='linux'", "sqlalchemy==1.4.51", + "pydantic-settings==2.4.0", ] diff --git a/system-server/system_server/settings/settings.py b/system-server/system_server/settings/settings.py index 32e34079ebd..5256a1e09e1 100644 --- a/system-server/system_server/settings/settings.py +++ b/system-server/system_server/settings/settings.py @@ -2,8 +2,9 @@ import typing from functools import lru_cache -from pydantic import BaseSettings, Field +from pydantic import Field from dotenv import load_dotenv, set_key +from pydantic_settings import BaseSettings, SettingsConfigDict @lru_cache(maxsize=1) @@ -23,11 +24,7 @@ class Environment(BaseSettings): default=None, description="Path to a .env file to define system server settings.", ) - - class Config: - """Prefix configuration for environment variables.""" - - env_prefix = "OT_SYSTEM_SERVER_" + model_config = SettingsConfigDict(env_prefix="OT_SYSTEM_SERVER_") # If you update this, also update the generated settings_schema.json. @@ -69,21 +66,18 @@ class SystemServerSettings(BaseSettings): " the splash screen changes when the flag is enabled/disabled." ), ) - - class Config: - """Prefix configuration for environment variables.""" - - env_file = Environment().dot_env_path - env_prefix = "OT_SYSTEM_SERVER_" + model_config = SettingsConfigDict( + env_file=Environment().dot_env_path, env_prefix="OT_SYSTEM_SERVER_" + ) def save_settings(settings: SystemServerSettings) -> bool: """Save the settings to the dotenv file.""" env_path = Environment().dot_env_path env_path = env_path or f"{settings.persistence_directory}/system.env" - prefix = settings.Config.env_prefix + prefix = settings.model_config.get("env_prefix") try: - for key, val in settings.dict().items(): + for key, val in settings.model_dump().items(): name = f"{prefix}{key}" value = str(val) if val is not None else "" set_key(env_path, name, value) diff --git a/update-server/Pipfile b/update-server/Pipfile index 975e075afb9..c8d57b66a99 100644 --- a/update-server/Pipfile +++ b/update-server/Pipfile @@ -22,7 +22,7 @@ coverage = "==7.4.1" # https://github.com/pypa/pipenv/issues/4408#issuecomment-668324177 atomicwrites = {version="==1.4.0", markers="sys_platform=='win32'"} colorama = {version="==0.4.4", markers="sys_platform=='win32'"} -mypy = "==1.8.0" +mypy = "==1.11.0" black = "==22.3.0" decoy = "~=2.1.1" diff --git a/update-server/Pipfile.lock b/update-server/Pipfile.lock index 9f26d21c134..b45abf00681 100644 --- a/update-server/Pipfile.lock +++ b/update-server/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "115cd9d2eae2f695378fed0db8301eb63a0d84ac581e0ee0b606f0d77293206e" + "sha256": "125917af563c336d6ac51990931ecbc50c3da52f83b1e274290fc436da72f97c" }, "pipfile-spec": 6, "requires": { @@ -116,11 +116,11 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "frozenlist": { "hashes": [ @@ -207,11 +207,11 @@ }, "idna": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], - "markers": "python_version >= '3.5'", - "version": "==3.6" + "markers": "python_version >= '3.6'", + "version": "==3.8" }, "multidict": { "hashes": [ @@ -315,111 +315,121 @@ }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.12.2" }, "yarl": { "hashes": [ - "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", - "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", - "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", - "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", - "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", - "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", - "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", - "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", - "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", - "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", - "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", - "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", - "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", - "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", - "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", - "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", - "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", - "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", - "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", - "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", - "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", - "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", - "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", - "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", - "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", - "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", - "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", - "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", - "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", - "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", - "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", - "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", - "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", - "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", - "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", - "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", - "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", - "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", - "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", - "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", - "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", - "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", - "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", - "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", - "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", - "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", - "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", - "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", - "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", - "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", - "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", - "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", - "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", - "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", - "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", - "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", - "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", - "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", - "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", - "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", - "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", - "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", - "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", - "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", - "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", - "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", - "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", - "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", - "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", - "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", - "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", - "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", - "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", - "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", - "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", - "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", - "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", - "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", - "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", - "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", - "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", - "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", - "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", - "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", - "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", - "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", - "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", - "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", - "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", - "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" + "sha256:0324506afab4f2e176a93cb08b8abcb8b009e1f324e6cbced999a8f5dd9ddb76", + "sha256:0a205ec6349879f5e75dddfb63e069a24f726df5330b92ce76c4752a436aac01", + "sha256:0b0c70c451d2a86f8408abced5b7498423e2487543acf6fcf618b03f6e669b0a", + "sha256:0b2a8e5eb18181060197e3d5db7e78f818432725c0759bc1e5a9d603d9246389", + "sha256:0cbcc2c54084b2bda4109415631db017cf2960f74f9e8fd1698e1400e4f8aae2", + "sha256:17107b4b8c43e66befdcbe543fff2f9c93f7a3a9f8e3a9c9ac42bffeba0e8828", + "sha256:1c82126817492bb2ebc946e74af1ffa10aacaca81bee360858477f96124be39a", + "sha256:1cdb8f5bb0534986776a43df84031da7ff04ac0cf87cb22ae8a6368231949c40", + "sha256:21e56c30e39a1833e4e3fd0112dde98c2abcbc4c39b077e6105c76bb63d2aa04", + "sha256:224f8186c220ff00079e64bf193909829144d4e5174bb58665ef0da8bf6955c4", + "sha256:2d1c81c3b92bef0c1c180048e43a5a85754a61b4f69d6f84df8e4bd615bef25d", + "sha256:30f201bc65941a4aa59c1236783efe89049ec5549dafc8cd2b63cc179d3767b0", + "sha256:3a26a24bbd19241283d601173cea1e5b93dec361a223394e18a1e8e5b0ef20bd", + "sha256:3fcd056cb7dff3aea5b1ee1b425b0fbaa2fbf6a1c6003e88caf524f01de5f395", + "sha256:441049d3a449fb8756b0535be72c6a1a532938a33e1cf03523076700a5f87a01", + "sha256:4567cc08f479ad80fb07ed0c9e1bcb363a4f6e3483a490a39d57d1419bf1c4c7", + "sha256:475e09a67f8b09720192a170ad9021b7abf7827ffd4f3a83826317a705be06b7", + "sha256:47c0a3dc8076a8dd159de10628dea04215bc7ddaa46c5775bf96066a0a18f82b", + "sha256:4915818ac850c3b0413e953af34398775b7a337babe1e4d15f68c8f5c4872553", + "sha256:498439af143b43a2b2314451ffd0295410aa0dcbdac5ee18fc8633da4670b605", + "sha256:4ae079573efeaa54e5978ce86b77f4175cd32f42afcaf9bfb8a0677e91f84e4e", + "sha256:4d368e3b9ecd50fa22017a20c49e356471af6ae91c4d788c6e9297e25ddf5a62", + "sha256:4e4f820fde9437bb47297194f43d29086433e6467fa28fe9876366ad357bd7bb", + "sha256:504d19320c92532cabc3495fb7ed6bb599f3c2bfb45fed432049bf4693dbd6d0", + "sha256:51a6f770ac86477cd5c553f88a77a06fe1f6f3b643b053fcc7902ab55d6cbe14", + "sha256:545f2fbfa0c723b446e9298b5beba0999ff82ce2c126110759e8dac29b5deaf4", + "sha256:54cc24be98d7f4ff355ca2e725a577e19909788c0db6beead67a0dda70bd3f82", + "sha256:55a67dd29367ce7c08a0541bb602ec0a2c10d46c86b94830a1a665f7fd093dfa", + "sha256:569309a3efb8369ff5d32edb2a0520ebaf810c3059f11d34477418c90aa878fd", + "sha256:58081cea14b8feda57c7ce447520e9d0a96c4d010cce54373d789c13242d7083", + "sha256:5b593acd45cdd4cf6664d342ceacedf25cd95263b83b964fddd6c78930ea5211", + "sha256:5c23f6dc3d7126b4c64b80aa186ac2bb65ab104a8372c4454e462fb074197bc6", + "sha256:614fa50fd0db41b79f426939a413d216cdc7bab8d8c8a25844798d286a999c5a", + "sha256:61ec0e80970b21a8f3c4b97fa6c6d181c6c6a135dbc7b4a601a78add3feeb209", + "sha256:63a5dc2866791236779d99d7a422611d22bb3a3d50935bafa4e017ea13e51469", + "sha256:675004040f847c0284827f44a1fa92d8baf425632cc93e7e0aa38408774b07c1", + "sha256:67abcb7df27952864440c9c85f1c549a4ad94afe44e2655f77d74b0d25895454", + "sha256:6de3fa29e76fd1518a80e6af4902c44f3b1b4d7fed28eb06913bba4727443de3", + "sha256:6ff184002ee72e4b247240e35d5dce4c2d9a0e81fdbef715dde79ab4718aa541", + "sha256:70194da6e99713250aa3f335a7fa246b36adf53672a2bcd0ddaa375d04e53dc0", + "sha256:7230007ab67d43cf19200ec15bc6b654e6b85c402f545a6fc565d254d34ff754", + "sha256:735b285ea46ca7e86ad261a462a071d0968aade44e1a3ea2b7d4f3d63b5aab12", + "sha256:752c0d33b4aacdb147871d0754b88f53922c6dc2aff033096516b3d5f0c02a0f", + "sha256:752f4b5cf93268dc73c2ae994cc6d684b0dad5118bc87fbd965fd5d6dca20f45", + "sha256:755ae9cff06c429632d750aa8206f08df2e3d422ca67be79567aadbe74ae64cc", + "sha256:79e08c691deae6fcac2fdde2e0515ac561dd3630d7c8adf7b1e786e22f1e193b", + "sha256:7d2dee7d6485807c0f64dd5eab9262b7c0b34f760e502243dd83ec09d647d5e1", + "sha256:8503989860d7ac10c85cb5b607fec003a45049cf7a5b4b72451e87893c6bb990", + "sha256:85333d38a4fa5997fa2ff6fd169be66626d814b34fa35ec669e8c914ca50a097", + "sha256:8c2cf0c7ad745e1c6530fe6521dfb19ca43338239dfcc7da165d0ef2332c0882", + "sha256:8d6e1c1562b53bd26efd38e886fc13863b8d904d559426777990171020c478a9", + "sha256:8d7b717f77846a9631046899c6cc730ea469c0e2fb252ccff1cc119950dbc296", + "sha256:8e8ed183c7a8f75e40068333fc185566472a8f6c77a750cf7541e11810576ea5", + "sha256:9137975a4ccc163ad5d7a75aad966e6e4e95dedee08d7995eab896a639a0bce2", + "sha256:91c478741d7563a12162f7a2db96c0d23d93b0521563f1f1f0ece46ea1702d33", + "sha256:922ba3b74f0958a0b5b9c14ff1ef12714a381760c08018f2b9827632783a590c", + "sha256:94f71d54c5faf715e92c8434b4a0b968c4d1043469954d228fc031d51086f143", + "sha256:95adc179a02949c4560ef40f8f650a008380766eb253d74232eb9c024747c111", + "sha256:9636e4519f6c7558fdccf8f91e6e3b98df2340dc505c4cc3286986d33f2096c2", + "sha256:9e290de5db4fd4859b4ed57cddfe793fcb218504e65781854a8ac283ab8d5518", + "sha256:9fae7ec5c9a4fe22abb995804e6ce87067dfaf7e940272b79328ce37c8f22097", + "sha256:a5706821e1cf3c70dfea223e4e0958ea354f4e2af9420a1bd45c6b547297fb97", + "sha256:a744bdeda6c86cf3025c94eb0e01ccabe949cf385cd75b6576a3ac9669404b68", + "sha256:aaeffcb84faceb2923a94a8a9aaa972745d3c728ab54dd011530cc30a3d5d0c1", + "sha256:aeba4aaa59cb709edb824fa88a27cbbff4e0095aaf77212b652989276c493c00", + "sha256:afcac5bda602b74ff701e1f683feccd8cce0d5a21dbc68db81bf9bd8fd93ba56", + "sha256:b30703a7ade2b53f02e09a30685b70cd54f65ed314a8d9af08670c9a5391af1b", + "sha256:b3dfe17b4aed832c627319da22a33f27f282bd32633d6b145c726d519c89fbaf", + "sha256:b4a0e724a28d7447e4d549c8f40779f90e20147e94bf949d490402eee09845c6", + "sha256:b8f847cc092c2b85d22e527f91ea83a6cf51533e727e2461557a47a859f96734", + "sha256:c189bf01af155ac9882e128d9f3b3ad68a1f2c2f51404afad7201305df4e12b1", + "sha256:c1db9a4384694b5d20bdd9cb53f033b0831ac816416ab176c8d0997835015d22", + "sha256:c305c1bdf10869b5e51facf50bd5b15892884aeae81962ae4ba061fc11217103", + "sha256:c335342d482e66254ae94b1231b1532790afb754f89e2e0c646f7f19d09740aa", + "sha256:c59b23886234abeba62087fd97d10fb6b905d9e36e2f3465d1886ce5c0ca30df", + "sha256:c5b7b307140231ea4f7aad5b69355aba2a67f2d7bc34271cffa3c9c324d35b27", + "sha256:c6f6c87665a9e18a635f0545ea541d9640617832af2317d4f5ad389686b4ed3d", + "sha256:c7548a90cb72b67652e2cd6ae80e2683ee08fde663104528ac7df12d8ef271d2", + "sha256:ca35996e0a4bed28fa0640d9512d37952f6b50dea583bcc167d4f0b1e112ac7f", + "sha256:cc295969f8c2172b5d013c0871dccfec7a0e1186cf961e7ea575d47b4d5cbd32", + "sha256:ce2bd986b1e44528677c237b74d59f215c8bfcdf2d69442aa10f62fd6ab2951c", + "sha256:d65ad67f981e93ea11f87815f67d086c4f33da4800cf2106d650dd8a0b79dda4", + "sha256:d93c612b2024ac25a3dc01341fd98fdd19c8c5e2011f3dcd084b3743cba8d756", + "sha256:ddad5cfcda729e22422bb1c85520bdf2770ce6d975600573ac9017fe882f4b7e", + "sha256:dfa9b9d5c9c0dbe69670f5695264452f5e40947590ec3a38cfddc9640ae8ff89", + "sha256:e4a8c3dedd081cca134a21179aebe58b6e426e8d1e0202da9d1cafa56e01af3c", + "sha256:e5f50a2e26cc2b89186f04c97e0ec0ba107ae41f1262ad16832d46849864f914", + "sha256:e700eb26635ce665c018c8cfea058baff9b843ed0cc77aa61849d807bb82a64c", + "sha256:ef9610b2f5a73707d4d8bac040f0115ca848e510e3b1f45ca53e97f609b54130", + "sha256:f568d70b7187f4002b6b500c0996c37674a25ce44b20716faebe5fdb8bd356e7", + "sha256:fee45b3bd4d8d5786472e056aa1359cc4dc9da68aded95a10cd7929a0ec661fe", + "sha256:ff64f575d71eacb5a4d6f0696bfe991993d979423ea2241f23ab19ff63f0f9d1" ], - "markers": "python_version >= '3.7'", - "version": "==1.9.4" + "markers": "python_version >= '3.8'", + "version": "==1.9.11" } }, "develop": { + "aiohappyeyeballs": { + "hashes": [ + "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2", + "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd" + ], + "markers": "python_version >= '3.8'", + "version": "==2.4.0" + }, "aiohttp": { "hashes": [ "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168", @@ -528,11 +538,11 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "black": { "hashes": [ @@ -659,11 +669,11 @@ }, "exceptiongroup": { "hashes": [ - "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", - "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.0" + "version": "==1.2.2" }, "flake8": { "hashes": [ @@ -786,11 +796,11 @@ }, "idna": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], - "markers": "python_version >= '3.5'", - "version": "==3.6" + "markers": "python_version >= '3.6'", + "version": "==3.8" }, "iniconfig": { "hashes": [ @@ -906,37 +916,37 @@ }, "mypy": { "hashes": [ - "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", - "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", - "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", - "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", - "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", - "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", - "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", - "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", - "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", - "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", - "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", - "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", - "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", - "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", - "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", - "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", - "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", - "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", - "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", - "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", - "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", - "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", - "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", - "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", - "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", - "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", - "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.8.0" + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -948,11 +958,11 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", + "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==24.1" }, "pathspec": { "hashes": [ @@ -964,19 +974,19 @@ }, "platformdirs": { "hashes": [ - "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", - "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" + "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", + "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" ], "markers": "python_version >= '3.8'", - "version": "==4.2.0" + "version": "==4.2.2" }, "pluggy": { "hashes": [ - "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", - "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" ], "markers": "python_version >= '3.8'", - "version": "==1.4.0" + "version": "==1.5.0" }, "pycodestyle": { "hashes": [ @@ -1022,11 +1032,11 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:2143d9d9375bf372a73260e4114541485e84fca350b0b6b92674ca56ff5f7ea2", - "sha256:b0079dfac14b60cd1ce4691fbfb1748fe939db7d0234b5aba97197d10fbe0fef" + "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", + "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3" ], "markers": "python_version >= '3.8'", - "version": "==0.23.4" + "version": "==0.23.8" }, "pytest-cov": { "hashes": [ @@ -1061,141 +1071,146 @@ }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.12.2" }, "watchdog": { "hashes": [ - "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a", - "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100", - "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8", - "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc", - "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae", - "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41", - "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0", - "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f", - "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c", - "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9", - "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3", - "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709", - "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83", - "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759", - "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9", - "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3", - "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7", - "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f", - "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346", - "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674", - "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397", - "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96", - "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d", - "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a", - "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64", - "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44", - "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33" + "sha256:14dd4ed023d79d1f670aa659f449bcd2733c33a35c8ffd88689d9d243885198b", + "sha256:29e4a2607bd407d9552c502d38b45a05ec26a8e40cc7e94db9bb48f861fa5abc", + "sha256:3960136b2b619510569b90f0cd96408591d6c251a75c97690f4553ca88889769", + "sha256:3e8d5ff39f0a9968952cce548e8e08f849141a4fcc1290b1c17c032ba697b9d7", + "sha256:53ed1bf71fcb8475dd0ef4912ab139c294c87b903724b6f4a8bd98e026862e6d", + "sha256:5597c051587f8757798216f2485e85eac583c3b343e9aa09127a3a6f82c65ee8", + "sha256:638bcca3d5b1885c6ec47be67bf712b00a9ab3d4b22ec0881f4889ad870bc7e8", + "sha256:6bec703ad90b35a848e05e1b40bf0050da7ca28ead7ac4be724ae5ac2653a1a0", + "sha256:726eef8f8c634ac6584f86c9c53353a010d9f311f6c15a034f3800a7a891d941", + "sha256:72990192cb63872c47d5e5fefe230a401b87fd59d257ee577d61c9e5564c62e5", + "sha256:7d1aa7e4bb0f0c65a1a91ba37c10e19dabf7eaaa282c5787e51371f090748f4b", + "sha256:8c47150aa12f775e22efff1eee9f0f6beee542a7aa1a985c271b1997d340184f", + "sha256:901ee48c23f70193d1a7bc2d9ee297df66081dd5f46f0ca011be4f70dec80dab", + "sha256:963f7c4c91e3f51c998eeff1b3fb24a52a8a34da4f956e470f4b068bb47b78ee", + "sha256:9814adb768c23727a27792c77812cf4e2fd9853cd280eafa2bcfa62a99e8bd6e", + "sha256:aa9cd6e24126d4afb3752a3e70fce39f92d0e1a58a236ddf6ee823ff7dba28ee", + "sha256:b6dc8f1d770a8280997e4beae7b9a75a33b268c59e033e72c8a10990097e5fde", + "sha256:b84bff0391ad4abe25c2740c7aec0e3de316fdf7764007f41e248422a7760a7f", + "sha256:ba32efcccfe2c58f4d01115440d1672b4eb26cdd6fc5b5818f1fb41f7c3e1889", + "sha256:bda40c57115684d0216556671875e008279dea2dc00fcd3dde126ac8e0d7a2fb", + "sha256:c4a440f725f3b99133de610bfec93d570b13826f89616377715b9cd60424db6e", + "sha256:d010be060c996db725fbce7e3ef14687cdcc76f4ca0e4339a68cc4532c382a73", + "sha256:d2ab34adc9bf1489452965cdb16a924e97d4452fcf88a50b21859068b50b5c3b", + "sha256:d7594a6d32cda2b49df3fd9abf9b37c8d2f3eab5df45c24056b4a671ac661619", + "sha256:d961f4123bb3c447d9fcdcb67e1530c366f10ab3a0c7d1c0c9943050936d4877", + "sha256:dae7a1879918f6544201d33666909b040a46421054a50e0f773e0d870ed7438d", + "sha256:dcebf7e475001d2cdeb020be630dc5b687e9acdd60d16fea6bb4508e7b94cf76", + "sha256:f627c5bf5759fdd90195b0c0431f99cff4867d212a67b384442c51136a098ed7", + "sha256:f8b2918c19e0d48f5f20df458c84692e2a054f02d9df25e6c3c930063eca64c1", + "sha256:fb223456db6e5f7bd9bbd5cd969f05aae82ae21acc00643b60d81c770abd402b" ], - "markers": "python_version >= '3.7'", - "version": "==3.0.0" + "markers": "python_version >= '3.9'", + "version": "==5.0.2" }, "yarl": { "hashes": [ - "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", - "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", - "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", - "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", - "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", - "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", - "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", - "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", - "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", - "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", - "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", - "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", - "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", - "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", - "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", - "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", - "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", - "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", - "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", - "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", - "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", - "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", - "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", - "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", - "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", - "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", - "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", - "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", - "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", - "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", - "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", - "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", - "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", - "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", - "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", - "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", - "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", - "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", - "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", - "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", - "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", - "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", - "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", - "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", - "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", - "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", - "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", - "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", - "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", - "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", - "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", - "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", - "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", - "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", - "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", - "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", - "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", - "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", - "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", - "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", - "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", - "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", - "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", - "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", - "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", - "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", - "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", - "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", - "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", - "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", - "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", - "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", - "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", - "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", - "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", - "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", - "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", - "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", - "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", - "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", - "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", - "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", - "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", - "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", - "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", - "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", - "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", - "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", - "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", - "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" + "sha256:0324506afab4f2e176a93cb08b8abcb8b009e1f324e6cbced999a8f5dd9ddb76", + "sha256:0a205ec6349879f5e75dddfb63e069a24f726df5330b92ce76c4752a436aac01", + "sha256:0b0c70c451d2a86f8408abced5b7498423e2487543acf6fcf618b03f6e669b0a", + "sha256:0b2a8e5eb18181060197e3d5db7e78f818432725c0759bc1e5a9d603d9246389", + "sha256:0cbcc2c54084b2bda4109415631db017cf2960f74f9e8fd1698e1400e4f8aae2", + "sha256:17107b4b8c43e66befdcbe543fff2f9c93f7a3a9f8e3a9c9ac42bffeba0e8828", + "sha256:1c82126817492bb2ebc946e74af1ffa10aacaca81bee360858477f96124be39a", + "sha256:1cdb8f5bb0534986776a43df84031da7ff04ac0cf87cb22ae8a6368231949c40", + "sha256:21e56c30e39a1833e4e3fd0112dde98c2abcbc4c39b077e6105c76bb63d2aa04", + "sha256:224f8186c220ff00079e64bf193909829144d4e5174bb58665ef0da8bf6955c4", + "sha256:2d1c81c3b92bef0c1c180048e43a5a85754a61b4f69d6f84df8e4bd615bef25d", + "sha256:30f201bc65941a4aa59c1236783efe89049ec5549dafc8cd2b63cc179d3767b0", + "sha256:3a26a24bbd19241283d601173cea1e5b93dec361a223394e18a1e8e5b0ef20bd", + "sha256:3fcd056cb7dff3aea5b1ee1b425b0fbaa2fbf6a1c6003e88caf524f01de5f395", + "sha256:441049d3a449fb8756b0535be72c6a1a532938a33e1cf03523076700a5f87a01", + "sha256:4567cc08f479ad80fb07ed0c9e1bcb363a4f6e3483a490a39d57d1419bf1c4c7", + "sha256:475e09a67f8b09720192a170ad9021b7abf7827ffd4f3a83826317a705be06b7", + "sha256:47c0a3dc8076a8dd159de10628dea04215bc7ddaa46c5775bf96066a0a18f82b", + "sha256:4915818ac850c3b0413e953af34398775b7a337babe1e4d15f68c8f5c4872553", + "sha256:498439af143b43a2b2314451ffd0295410aa0dcbdac5ee18fc8633da4670b605", + "sha256:4ae079573efeaa54e5978ce86b77f4175cd32f42afcaf9bfb8a0677e91f84e4e", + "sha256:4d368e3b9ecd50fa22017a20c49e356471af6ae91c4d788c6e9297e25ddf5a62", + "sha256:4e4f820fde9437bb47297194f43d29086433e6467fa28fe9876366ad357bd7bb", + "sha256:504d19320c92532cabc3495fb7ed6bb599f3c2bfb45fed432049bf4693dbd6d0", + "sha256:51a6f770ac86477cd5c553f88a77a06fe1f6f3b643b053fcc7902ab55d6cbe14", + "sha256:545f2fbfa0c723b446e9298b5beba0999ff82ce2c126110759e8dac29b5deaf4", + "sha256:54cc24be98d7f4ff355ca2e725a577e19909788c0db6beead67a0dda70bd3f82", + "sha256:55a67dd29367ce7c08a0541bb602ec0a2c10d46c86b94830a1a665f7fd093dfa", + "sha256:569309a3efb8369ff5d32edb2a0520ebaf810c3059f11d34477418c90aa878fd", + "sha256:58081cea14b8feda57c7ce447520e9d0a96c4d010cce54373d789c13242d7083", + "sha256:5b593acd45cdd4cf6664d342ceacedf25cd95263b83b964fddd6c78930ea5211", + "sha256:5c23f6dc3d7126b4c64b80aa186ac2bb65ab104a8372c4454e462fb074197bc6", + "sha256:614fa50fd0db41b79f426939a413d216cdc7bab8d8c8a25844798d286a999c5a", + "sha256:61ec0e80970b21a8f3c4b97fa6c6d181c6c6a135dbc7b4a601a78add3feeb209", + "sha256:63a5dc2866791236779d99d7a422611d22bb3a3d50935bafa4e017ea13e51469", + "sha256:675004040f847c0284827f44a1fa92d8baf425632cc93e7e0aa38408774b07c1", + "sha256:67abcb7df27952864440c9c85f1c549a4ad94afe44e2655f77d74b0d25895454", + "sha256:6de3fa29e76fd1518a80e6af4902c44f3b1b4d7fed28eb06913bba4727443de3", + "sha256:6ff184002ee72e4b247240e35d5dce4c2d9a0e81fdbef715dde79ab4718aa541", + "sha256:70194da6e99713250aa3f335a7fa246b36adf53672a2bcd0ddaa375d04e53dc0", + "sha256:7230007ab67d43cf19200ec15bc6b654e6b85c402f545a6fc565d254d34ff754", + "sha256:735b285ea46ca7e86ad261a462a071d0968aade44e1a3ea2b7d4f3d63b5aab12", + "sha256:752c0d33b4aacdb147871d0754b88f53922c6dc2aff033096516b3d5f0c02a0f", + "sha256:752f4b5cf93268dc73c2ae994cc6d684b0dad5118bc87fbd965fd5d6dca20f45", + "sha256:755ae9cff06c429632d750aa8206f08df2e3d422ca67be79567aadbe74ae64cc", + "sha256:79e08c691deae6fcac2fdde2e0515ac561dd3630d7c8adf7b1e786e22f1e193b", + "sha256:7d2dee7d6485807c0f64dd5eab9262b7c0b34f760e502243dd83ec09d647d5e1", + "sha256:8503989860d7ac10c85cb5b607fec003a45049cf7a5b4b72451e87893c6bb990", + "sha256:85333d38a4fa5997fa2ff6fd169be66626d814b34fa35ec669e8c914ca50a097", + "sha256:8c2cf0c7ad745e1c6530fe6521dfb19ca43338239dfcc7da165d0ef2332c0882", + "sha256:8d6e1c1562b53bd26efd38e886fc13863b8d904d559426777990171020c478a9", + "sha256:8d7b717f77846a9631046899c6cc730ea469c0e2fb252ccff1cc119950dbc296", + "sha256:8e8ed183c7a8f75e40068333fc185566472a8f6c77a750cf7541e11810576ea5", + "sha256:9137975a4ccc163ad5d7a75aad966e6e4e95dedee08d7995eab896a639a0bce2", + "sha256:91c478741d7563a12162f7a2db96c0d23d93b0521563f1f1f0ece46ea1702d33", + "sha256:922ba3b74f0958a0b5b9c14ff1ef12714a381760c08018f2b9827632783a590c", + "sha256:94f71d54c5faf715e92c8434b4a0b968c4d1043469954d228fc031d51086f143", + "sha256:95adc179a02949c4560ef40f8f650a008380766eb253d74232eb9c024747c111", + "sha256:9636e4519f6c7558fdccf8f91e6e3b98df2340dc505c4cc3286986d33f2096c2", + "sha256:9e290de5db4fd4859b4ed57cddfe793fcb218504e65781854a8ac283ab8d5518", + "sha256:9fae7ec5c9a4fe22abb995804e6ce87067dfaf7e940272b79328ce37c8f22097", + "sha256:a5706821e1cf3c70dfea223e4e0958ea354f4e2af9420a1bd45c6b547297fb97", + "sha256:a744bdeda6c86cf3025c94eb0e01ccabe949cf385cd75b6576a3ac9669404b68", + "sha256:aaeffcb84faceb2923a94a8a9aaa972745d3c728ab54dd011530cc30a3d5d0c1", + "sha256:aeba4aaa59cb709edb824fa88a27cbbff4e0095aaf77212b652989276c493c00", + "sha256:afcac5bda602b74ff701e1f683feccd8cce0d5a21dbc68db81bf9bd8fd93ba56", + "sha256:b30703a7ade2b53f02e09a30685b70cd54f65ed314a8d9af08670c9a5391af1b", + "sha256:b3dfe17b4aed832c627319da22a33f27f282bd32633d6b145c726d519c89fbaf", + "sha256:b4a0e724a28d7447e4d549c8f40779f90e20147e94bf949d490402eee09845c6", + "sha256:b8f847cc092c2b85d22e527f91ea83a6cf51533e727e2461557a47a859f96734", + "sha256:c189bf01af155ac9882e128d9f3b3ad68a1f2c2f51404afad7201305df4e12b1", + "sha256:c1db9a4384694b5d20bdd9cb53f033b0831ac816416ab176c8d0997835015d22", + "sha256:c305c1bdf10869b5e51facf50bd5b15892884aeae81962ae4ba061fc11217103", + "sha256:c335342d482e66254ae94b1231b1532790afb754f89e2e0c646f7f19d09740aa", + "sha256:c59b23886234abeba62087fd97d10fb6b905d9e36e2f3465d1886ce5c0ca30df", + "sha256:c5b7b307140231ea4f7aad5b69355aba2a67f2d7bc34271cffa3c9c324d35b27", + "sha256:c6f6c87665a9e18a635f0545ea541d9640617832af2317d4f5ad389686b4ed3d", + "sha256:c7548a90cb72b67652e2cd6ae80e2683ee08fde663104528ac7df12d8ef271d2", + "sha256:ca35996e0a4bed28fa0640d9512d37952f6b50dea583bcc167d4f0b1e112ac7f", + "sha256:cc295969f8c2172b5d013c0871dccfec7a0e1186cf961e7ea575d47b4d5cbd32", + "sha256:ce2bd986b1e44528677c237b74d59f215c8bfcdf2d69442aa10f62fd6ab2951c", + "sha256:d65ad67f981e93ea11f87815f67d086c4f33da4800cf2106d650dd8a0b79dda4", + "sha256:d93c612b2024ac25a3dc01341fd98fdd19c8c5e2011f3dcd084b3743cba8d756", + "sha256:ddad5cfcda729e22422bb1c85520bdf2770ce6d975600573ac9017fe882f4b7e", + "sha256:dfa9b9d5c9c0dbe69670f5695264452f5e40947590ec3a38cfddc9640ae8ff89", + "sha256:e4a8c3dedd081cca134a21179aebe58b6e426e8d1e0202da9d1cafa56e01af3c", + "sha256:e5f50a2e26cc2b89186f04c97e0ec0ba107ae41f1262ad16832d46849864f914", + "sha256:e700eb26635ce665c018c8cfea058baff9b843ed0cc77aa61849d807bb82a64c", + "sha256:ef9610b2f5a73707d4d8bac040f0115ca848e510e3b1f45ca53e97f609b54130", + "sha256:f568d70b7187f4002b6b500c0996c37674a25ce44b20716faebe5fdb8bd356e7", + "sha256:fee45b3bd4d8d5786472e056aa1359cc4dc9da68aded95a10cd7929a0ec661fe", + "sha256:ff64f575d71eacb5a4d6f0696bfe991993d979423ea2241f23ab19ff63f0f9d1" ], - "markers": "python_version >= '3.7'", - "version": "==1.9.4" + "markers": "python_version >= '3.8'", + "version": "==1.9.11" } } } diff --git a/update-server/otupdate/common/update_actions.py b/update-server/otupdate/common/update_actions.py index 56bd94f0abe..493029c066d 100644 --- a/update-server/otupdate/common/update_actions.py +++ b/update-server/otupdate/common/update_actions.py @@ -69,8 +69,8 @@ def write_update( self, rootfs_filepath: str, progress_callback: Callable[[float], None], - chunk_size: int, - file_size: Optional[int], + chunk_size: int = -1, + file_size: Optional[int] = None, ) -> Partition: """ Write the object to a specific rootfs path diff --git a/usb-bridge/Pipfile b/usb-bridge/Pipfile index 2743d3ab675..f5eb483758c 100644 --- a/usb-bridge/Pipfile +++ b/usb-bridge/Pipfile @@ -27,7 +27,7 @@ coverage = "==7.4.1" # https://github.com/pypa/pipenv/issues/4408#issuecomment-668324177 atomicwrites = {version="==1.4.0", markers="sys_platform=='win32'"} colorama = {version="==0.4.4", markers="sys_platform=='win32'"} -mypy = "==1.8.0" +mypy = "==1.11.0" black = "==22.3.0" decoy = "==2.1.1" mock = "~=5.1.0" diff --git a/usb-bridge/Pipfile.lock b/usb-bridge/Pipfile.lock index 6b7e9e62891..d67941d6143 100644 --- a/usb-bridge/Pipfile.lock +++ b/usb-bridge/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "0b53af0693972f92ea86d9477ff4af66c57874db5089a27727368fb15b48e534" + "sha256": "5c727aa6b7962e7cb9abe6cc03e205b5e4fcad066e4a2fc728a99c093b7060ac" }, "pipfile-spec": 6, "requires": { @@ -46,96 +46,119 @@ }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.12.2" } }, "develop": { + "aiohappyeyeballs": { + "hashes": [ + "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2", + "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd" + ], + "markers": "python_version >= '3.8'", + "version": "==2.4.0" + }, "aiohttp": { "hashes": [ - "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168", - "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb", - "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5", - "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f", - "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc", - "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c", - "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29", - "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4", - "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc", - "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc", - "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63", - "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e", - "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d", - "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a", - "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60", - "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38", - "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b", - "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2", - "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53", - "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5", - "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4", - "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96", - "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58", - "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa", - "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321", - "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae", - "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce", - "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8", - "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194", - "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c", - "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf", - "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d", - "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869", - "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b", - "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52", - "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528", - "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5", - "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1", - "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4", - "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8", - "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d", - "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7", - "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5", - "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54", - "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3", - "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5", - "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c", - "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29", - "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3", - "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747", - "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672", - "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5", - "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11", - "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca", - "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768", - "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6", - "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2", - "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533", - "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6", - "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266", - "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d", - "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec", - "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5", - "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1", - "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b", - "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679", - "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283", - "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb", - "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b", - "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3", - "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051", - "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511", - "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e", - "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d", - "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542", - "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f" + "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277", + "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1", + "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe", + "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb", + "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca", + "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91", + "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972", + "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a", + "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3", + "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa", + "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77", + "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b", + "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8", + "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599", + "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc", + "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf", + "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511", + "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699", + "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487", + "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987", + "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff", + "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db", + "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022", + "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce", + "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a", + "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5", + "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7", + "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820", + "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf", + "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e", + "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf", + "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5", + "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6", + "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6", + "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91", + "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3", + "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a", + "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d", + "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088", + "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc", + "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f", + "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75", + "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471", + "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e", + "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697", + "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092", + "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69", + "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3", + "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32", + "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589", + "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178", + "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92", + "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2", + "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e", + "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058", + "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857", + "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1", + "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6", + "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22", + "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0", + "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b", + "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57", + "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f", + "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e", + "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16", + "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1", + "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f", + "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6", + "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04", + "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae", + "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d", + "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b", + "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f", + "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862", + "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689", + "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c", + "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683", + "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef", + "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f", + "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12", + "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73", + "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061", + "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072", + "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11", + "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691", + "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77", + "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385", + "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172", + "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569", + "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f", + "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5" ], "markers": "python_version >= '3.8'", - "version": "==3.9.3" + "version": "==3.10.5" }, "aiosignal": { "hashes": [ @@ -163,11 +186,11 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" ], "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "version": "==24.2.0" }, "black": { "hashes": [ @@ -294,19 +317,19 @@ }, "exceptiongroup": { "hashes": [ - "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", - "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "markers": "python_version < '3.11'", - "version": "==1.2.0" + "version": "==1.2.2" }, "execnet": { "hashes": [ - "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", - "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" + "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", + "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.2" + "markers": "python_version >= '3.8'", + "version": "==2.1.1" }, "flake8": { "hashes": [ @@ -429,11 +452,11 @@ }, "idna": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], - "markers": "python_version >= '3.5'", - "version": "==3.6" + "markers": "python_version >= '3.6'", + "version": "==3.8" }, "iniconfig": { "hashes": [ @@ -558,37 +581,37 @@ }, "mypy": { "hashes": [ - "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", - "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", - "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", - "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", - "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", - "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", - "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", - "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", - "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", - "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", - "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", - "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", - "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", - "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", - "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", - "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", - "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", - "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", - "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", - "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", - "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", - "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", - "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", - "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", - "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", - "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", - "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3", + "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095", + "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac", + "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6", + "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20", + "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1", + "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00", + "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace", + "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7", + "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13", + "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be", + "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538", + "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850", + "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287", + "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb", + "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229", + "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd", + "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c", + "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac", + "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d", + "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba", + "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d", + "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9", + "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a", + "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf", + "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe", + "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.8.0" + "version": "==1.11.0" }, "mypy-extensions": { "hashes": [ @@ -600,11 +623,11 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", + "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==24.1" }, "pathspec": { "hashes": [ @@ -616,19 +639,19 @@ }, "platformdirs": { "hashes": [ - "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", - "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" + "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", + "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" ], "markers": "python_version >= '3.8'", - "version": "==4.2.0" + "version": "==4.2.2" }, "pluggy": { "hashes": [ - "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", - "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" ], "markers": "python_version >= '3.8'", - "version": "==1.4.0" + "version": "==1.5.0" }, "py": { "hashes": [ @@ -682,12 +705,12 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:2143d9d9375bf372a73260e4114541485e84fca350b0b6b92674ca56ff5f7ea2", - "sha256:b0079dfac14b60cd1ce4691fbfb1748fe939db7d0234b5aba97197d10fbe0fef" + "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", + "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.23.4" + "version": "==0.23.8" }, "pytest-cov": { "hashes": [ @@ -747,150 +770,155 @@ }, "types-mock": { "hashes": [ - "sha256:13ca379d5710ccb3f18f69ade5b08881874cb83383d8fb49b1d4dac9d5c5d090", - "sha256:3d116955495935b0bcba14954b38d97e507cd43eca3e3700fc1b8e4f5c6bf2c7" + "sha256:5281a645d72e827d70043e3cc144fe33b1c003db084f789dc203aa90e812a5a4", + "sha256:d586a01d39ad919d3ddcd73de6cde73ca7f3c69707219f722d1b8d7733641ad7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==5.1.0.20240106" + "version": "==5.1.0.20240425" }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "version": "==4.12.2" }, "watchdog": { "hashes": [ - "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a", - "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100", - "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8", - "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc", - "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae", - "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41", - "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0", - "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f", - "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c", - "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9", - "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3", - "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709", - "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83", - "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759", - "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9", - "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3", - "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7", - "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f", - "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346", - "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674", - "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397", - "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96", - "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d", - "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a", - "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64", - "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44", - "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33" - ], - "markers": "python_version >= '3.7'", - "version": "==3.0.0" + "sha256:14dd4ed023d79d1f670aa659f449bcd2733c33a35c8ffd88689d9d243885198b", + "sha256:29e4a2607bd407d9552c502d38b45a05ec26a8e40cc7e94db9bb48f861fa5abc", + "sha256:3960136b2b619510569b90f0cd96408591d6c251a75c97690f4553ca88889769", + "sha256:3e8d5ff39f0a9968952cce548e8e08f849141a4fcc1290b1c17c032ba697b9d7", + "sha256:53ed1bf71fcb8475dd0ef4912ab139c294c87b903724b6f4a8bd98e026862e6d", + "sha256:5597c051587f8757798216f2485e85eac583c3b343e9aa09127a3a6f82c65ee8", + "sha256:638bcca3d5b1885c6ec47be67bf712b00a9ab3d4b22ec0881f4889ad870bc7e8", + "sha256:6bec703ad90b35a848e05e1b40bf0050da7ca28ead7ac4be724ae5ac2653a1a0", + "sha256:726eef8f8c634ac6584f86c9c53353a010d9f311f6c15a034f3800a7a891d941", + "sha256:72990192cb63872c47d5e5fefe230a401b87fd59d257ee577d61c9e5564c62e5", + "sha256:7d1aa7e4bb0f0c65a1a91ba37c10e19dabf7eaaa282c5787e51371f090748f4b", + "sha256:8c47150aa12f775e22efff1eee9f0f6beee542a7aa1a985c271b1997d340184f", + "sha256:901ee48c23f70193d1a7bc2d9ee297df66081dd5f46f0ca011be4f70dec80dab", + "sha256:963f7c4c91e3f51c998eeff1b3fb24a52a8a34da4f956e470f4b068bb47b78ee", + "sha256:9814adb768c23727a27792c77812cf4e2fd9853cd280eafa2bcfa62a99e8bd6e", + "sha256:aa9cd6e24126d4afb3752a3e70fce39f92d0e1a58a236ddf6ee823ff7dba28ee", + "sha256:b6dc8f1d770a8280997e4beae7b9a75a33b268c59e033e72c8a10990097e5fde", + "sha256:b84bff0391ad4abe25c2740c7aec0e3de316fdf7764007f41e248422a7760a7f", + "sha256:ba32efcccfe2c58f4d01115440d1672b4eb26cdd6fc5b5818f1fb41f7c3e1889", + "sha256:bda40c57115684d0216556671875e008279dea2dc00fcd3dde126ac8e0d7a2fb", + "sha256:c4a440f725f3b99133de610bfec93d570b13826f89616377715b9cd60424db6e", + "sha256:d010be060c996db725fbce7e3ef14687cdcc76f4ca0e4339a68cc4532c382a73", + "sha256:d2ab34adc9bf1489452965cdb16a924e97d4452fcf88a50b21859068b50b5c3b", + "sha256:d7594a6d32cda2b49df3fd9abf9b37c8d2f3eab5df45c24056b4a671ac661619", + "sha256:d961f4123bb3c447d9fcdcb67e1530c366f10ab3a0c7d1c0c9943050936d4877", + "sha256:dae7a1879918f6544201d33666909b040a46421054a50e0f773e0d870ed7438d", + "sha256:dcebf7e475001d2cdeb020be630dc5b687e9acdd60d16fea6bb4508e7b94cf76", + "sha256:f627c5bf5759fdd90195b0c0431f99cff4867d212a67b384442c51136a098ed7", + "sha256:f8b2918c19e0d48f5f20df458c84692e2a054f02d9df25e6c3c930063eca64c1", + "sha256:fb223456db6e5f7bd9bbd5cd969f05aae82ae21acc00643b60d81c770abd402b" + ], + "markers": "python_version >= '3.9'", + "version": "==5.0.2" }, "yarl": { "hashes": [ - "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", - "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", - "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", - "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", - "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", - "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", - "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", - "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", - "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", - "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", - "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", - "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", - "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", - "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", - "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", - "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", - "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", - "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", - "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", - "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", - "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", - "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", - "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", - "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", - "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", - "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", - "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", - "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", - "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", - "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", - "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", - "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", - "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", - "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", - "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", - "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", - "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", - "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", - "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", - "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", - "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", - "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", - "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", - "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", - "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", - "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", - "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", - "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", - "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", - "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", - "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", - "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", - "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", - "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", - "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", - "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", - "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", - "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", - "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", - "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", - "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", - "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", - "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", - "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", - "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", - "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", - "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", - "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", - "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", - "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", - "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", - "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", - "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", - "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", - "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", - "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", - "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", - "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", - "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", - "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", - "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", - "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", - "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", - "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", - "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", - "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", - "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", - "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", - "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", - "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" + "sha256:0324506afab4f2e176a93cb08b8abcb8b009e1f324e6cbced999a8f5dd9ddb76", + "sha256:0a205ec6349879f5e75dddfb63e069a24f726df5330b92ce76c4752a436aac01", + "sha256:0b0c70c451d2a86f8408abced5b7498423e2487543acf6fcf618b03f6e669b0a", + "sha256:0b2a8e5eb18181060197e3d5db7e78f818432725c0759bc1e5a9d603d9246389", + "sha256:0cbcc2c54084b2bda4109415631db017cf2960f74f9e8fd1698e1400e4f8aae2", + "sha256:17107b4b8c43e66befdcbe543fff2f9c93f7a3a9f8e3a9c9ac42bffeba0e8828", + "sha256:1c82126817492bb2ebc946e74af1ffa10aacaca81bee360858477f96124be39a", + "sha256:1cdb8f5bb0534986776a43df84031da7ff04ac0cf87cb22ae8a6368231949c40", + "sha256:21e56c30e39a1833e4e3fd0112dde98c2abcbc4c39b077e6105c76bb63d2aa04", + "sha256:224f8186c220ff00079e64bf193909829144d4e5174bb58665ef0da8bf6955c4", + "sha256:2d1c81c3b92bef0c1c180048e43a5a85754a61b4f69d6f84df8e4bd615bef25d", + "sha256:30f201bc65941a4aa59c1236783efe89049ec5549dafc8cd2b63cc179d3767b0", + "sha256:3a26a24bbd19241283d601173cea1e5b93dec361a223394e18a1e8e5b0ef20bd", + "sha256:3fcd056cb7dff3aea5b1ee1b425b0fbaa2fbf6a1c6003e88caf524f01de5f395", + "sha256:441049d3a449fb8756b0535be72c6a1a532938a33e1cf03523076700a5f87a01", + "sha256:4567cc08f479ad80fb07ed0c9e1bcb363a4f6e3483a490a39d57d1419bf1c4c7", + "sha256:475e09a67f8b09720192a170ad9021b7abf7827ffd4f3a83826317a705be06b7", + "sha256:47c0a3dc8076a8dd159de10628dea04215bc7ddaa46c5775bf96066a0a18f82b", + "sha256:4915818ac850c3b0413e953af34398775b7a337babe1e4d15f68c8f5c4872553", + "sha256:498439af143b43a2b2314451ffd0295410aa0dcbdac5ee18fc8633da4670b605", + "sha256:4ae079573efeaa54e5978ce86b77f4175cd32f42afcaf9bfb8a0677e91f84e4e", + "sha256:4d368e3b9ecd50fa22017a20c49e356471af6ae91c4d788c6e9297e25ddf5a62", + "sha256:4e4f820fde9437bb47297194f43d29086433e6467fa28fe9876366ad357bd7bb", + "sha256:504d19320c92532cabc3495fb7ed6bb599f3c2bfb45fed432049bf4693dbd6d0", + "sha256:51a6f770ac86477cd5c553f88a77a06fe1f6f3b643b053fcc7902ab55d6cbe14", + "sha256:545f2fbfa0c723b446e9298b5beba0999ff82ce2c126110759e8dac29b5deaf4", + "sha256:54cc24be98d7f4ff355ca2e725a577e19909788c0db6beead67a0dda70bd3f82", + "sha256:55a67dd29367ce7c08a0541bb602ec0a2c10d46c86b94830a1a665f7fd093dfa", + "sha256:569309a3efb8369ff5d32edb2a0520ebaf810c3059f11d34477418c90aa878fd", + "sha256:58081cea14b8feda57c7ce447520e9d0a96c4d010cce54373d789c13242d7083", + "sha256:5b593acd45cdd4cf6664d342ceacedf25cd95263b83b964fddd6c78930ea5211", + "sha256:5c23f6dc3d7126b4c64b80aa186ac2bb65ab104a8372c4454e462fb074197bc6", + "sha256:614fa50fd0db41b79f426939a413d216cdc7bab8d8c8a25844798d286a999c5a", + "sha256:61ec0e80970b21a8f3c4b97fa6c6d181c6c6a135dbc7b4a601a78add3feeb209", + "sha256:63a5dc2866791236779d99d7a422611d22bb3a3d50935bafa4e017ea13e51469", + "sha256:675004040f847c0284827f44a1fa92d8baf425632cc93e7e0aa38408774b07c1", + "sha256:67abcb7df27952864440c9c85f1c549a4ad94afe44e2655f77d74b0d25895454", + "sha256:6de3fa29e76fd1518a80e6af4902c44f3b1b4d7fed28eb06913bba4727443de3", + "sha256:6ff184002ee72e4b247240e35d5dce4c2d9a0e81fdbef715dde79ab4718aa541", + "sha256:70194da6e99713250aa3f335a7fa246b36adf53672a2bcd0ddaa375d04e53dc0", + "sha256:7230007ab67d43cf19200ec15bc6b654e6b85c402f545a6fc565d254d34ff754", + "sha256:735b285ea46ca7e86ad261a462a071d0968aade44e1a3ea2b7d4f3d63b5aab12", + "sha256:752c0d33b4aacdb147871d0754b88f53922c6dc2aff033096516b3d5f0c02a0f", + "sha256:752f4b5cf93268dc73c2ae994cc6d684b0dad5118bc87fbd965fd5d6dca20f45", + "sha256:755ae9cff06c429632d750aa8206f08df2e3d422ca67be79567aadbe74ae64cc", + "sha256:79e08c691deae6fcac2fdde2e0515ac561dd3630d7c8adf7b1e786e22f1e193b", + "sha256:7d2dee7d6485807c0f64dd5eab9262b7c0b34f760e502243dd83ec09d647d5e1", + "sha256:8503989860d7ac10c85cb5b607fec003a45049cf7a5b4b72451e87893c6bb990", + "sha256:85333d38a4fa5997fa2ff6fd169be66626d814b34fa35ec669e8c914ca50a097", + "sha256:8c2cf0c7ad745e1c6530fe6521dfb19ca43338239dfcc7da165d0ef2332c0882", + "sha256:8d6e1c1562b53bd26efd38e886fc13863b8d904d559426777990171020c478a9", + "sha256:8d7b717f77846a9631046899c6cc730ea469c0e2fb252ccff1cc119950dbc296", + "sha256:8e8ed183c7a8f75e40068333fc185566472a8f6c77a750cf7541e11810576ea5", + "sha256:9137975a4ccc163ad5d7a75aad966e6e4e95dedee08d7995eab896a639a0bce2", + "sha256:91c478741d7563a12162f7a2db96c0d23d93b0521563f1f1f0ece46ea1702d33", + "sha256:922ba3b74f0958a0b5b9c14ff1ef12714a381760c08018f2b9827632783a590c", + "sha256:94f71d54c5faf715e92c8434b4a0b968c4d1043469954d228fc031d51086f143", + "sha256:95adc179a02949c4560ef40f8f650a008380766eb253d74232eb9c024747c111", + "sha256:9636e4519f6c7558fdccf8f91e6e3b98df2340dc505c4cc3286986d33f2096c2", + "sha256:9e290de5db4fd4859b4ed57cddfe793fcb218504e65781854a8ac283ab8d5518", + "sha256:9fae7ec5c9a4fe22abb995804e6ce87067dfaf7e940272b79328ce37c8f22097", + "sha256:a5706821e1cf3c70dfea223e4e0958ea354f4e2af9420a1bd45c6b547297fb97", + "sha256:a744bdeda6c86cf3025c94eb0e01ccabe949cf385cd75b6576a3ac9669404b68", + "sha256:aaeffcb84faceb2923a94a8a9aaa972745d3c728ab54dd011530cc30a3d5d0c1", + "sha256:aeba4aaa59cb709edb824fa88a27cbbff4e0095aaf77212b652989276c493c00", + "sha256:afcac5bda602b74ff701e1f683feccd8cce0d5a21dbc68db81bf9bd8fd93ba56", + "sha256:b30703a7ade2b53f02e09a30685b70cd54f65ed314a8d9af08670c9a5391af1b", + "sha256:b3dfe17b4aed832c627319da22a33f27f282bd32633d6b145c726d519c89fbaf", + "sha256:b4a0e724a28d7447e4d549c8f40779f90e20147e94bf949d490402eee09845c6", + "sha256:b8f847cc092c2b85d22e527f91ea83a6cf51533e727e2461557a47a859f96734", + "sha256:c189bf01af155ac9882e128d9f3b3ad68a1f2c2f51404afad7201305df4e12b1", + "sha256:c1db9a4384694b5d20bdd9cb53f033b0831ac816416ab176c8d0997835015d22", + "sha256:c305c1bdf10869b5e51facf50bd5b15892884aeae81962ae4ba061fc11217103", + "sha256:c335342d482e66254ae94b1231b1532790afb754f89e2e0c646f7f19d09740aa", + "sha256:c59b23886234abeba62087fd97d10fb6b905d9e36e2f3465d1886ce5c0ca30df", + "sha256:c5b7b307140231ea4f7aad5b69355aba2a67f2d7bc34271cffa3c9c324d35b27", + "sha256:c6f6c87665a9e18a635f0545ea541d9640617832af2317d4f5ad389686b4ed3d", + "sha256:c7548a90cb72b67652e2cd6ae80e2683ee08fde663104528ac7df12d8ef271d2", + "sha256:ca35996e0a4bed28fa0640d9512d37952f6b50dea583bcc167d4f0b1e112ac7f", + "sha256:cc295969f8c2172b5d013c0871dccfec7a0e1186cf961e7ea575d47b4d5cbd32", + "sha256:ce2bd986b1e44528677c237b74d59f215c8bfcdf2d69442aa10f62fd6ab2951c", + "sha256:d65ad67f981e93ea11f87815f67d086c4f33da4800cf2106d650dd8a0b79dda4", + "sha256:d93c612b2024ac25a3dc01341fd98fdd19c8c5e2011f3dcd084b3743cba8d756", + "sha256:ddad5cfcda729e22422bb1c85520bdf2770ce6d975600573ac9017fe882f4b7e", + "sha256:dfa9b9d5c9c0dbe69670f5695264452f5e40947590ec3a38cfddc9640ae8ff89", + "sha256:e4a8c3dedd081cca134a21179aebe58b6e426e8d1e0202da9d1cafa56e01af3c", + "sha256:e5f50a2e26cc2b89186f04c97e0ec0ba107ae41f1262ad16832d46849864f914", + "sha256:e700eb26635ce665c018c8cfea058baff9b843ed0cc77aa61849d807bb82a64c", + "sha256:ef9610b2f5a73707d4d8bac040f0115ca848e510e3b1f45ca53e97f609b54130", + "sha256:f568d70b7187f4002b6b500c0996c37674a25ce44b20716faebe5fdb8bd356e7", + "sha256:fee45b3bd4d8d5786472e056aa1359cc4dc9da68aded95a10cd7929a0ec661fe", + "sha256:ff64f575d71eacb5a4d6f0696bfe991993d979423ea2241f23ab19ff63f0f9d1" ], - "markers": "python_version >= '3.7'", - "version": "==1.9.4" + "markers": "python_version >= '3.8'", + "version": "==1.9.11" } } } diff --git a/vitest.config.ts b/vitest.config.mts similarity index 90% rename from vitest.config.ts rename to vitest.config.mts index a485e7536bd..1412fdcee4f 100644 --- a/vitest.config.ts +++ b/vitest.config.mts @@ -13,7 +13,12 @@ export default mergeConfig( environment: 'jsdom', allowOnly: true, exclude: [...configDefaults.exclude, '**/node_modules/**', '**/dist/**'], - setupFiles: ['./setup-vitest.ts'], + setupFiles: ['./setup-vitest.mts'], + coverage: { + exclude: ['**/node_modules/**', '**/dist/**', '**/__tests__/**'], + provider: 'v8', + reporter: ['text', 'json', 'html'], + }, }, resolve: { alias: { diff --git a/yarn.lock b/yarn.lock index d788db962fd..d58c1ef61ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,7 +22,7 @@ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== -"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.2.1": +"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== @@ -804,7 +804,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02" integrity sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g== -"@babel/core@>=7.2.2", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.18.9", "@babel/core@^7.20.12", "@babel/core@^7.23.0", "@babel/core@^7.23.2", "@babel/core@^7.23.3", "@babel/core@^7.23.5": +"@babel/core@>=7.2.2", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.18.9", "@babel/core@^7.20.12", "@babel/core@^7.23.0", "@babel/core@^7.23.2", "@babel/core@^7.23.3": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.4.tgz#1f758428e88e0d8c563874741bc4ffc4f71a4717" integrity sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg== @@ -1122,7 +1122,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== -"@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3": +"@babel/parser@^7.25.4", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3": version "7.26.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== @@ -1936,7 +1936,7 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3": +"@babel/types@^7.25.4", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3": version "7.26.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== @@ -2556,11 +2556,6 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== -"@esbuild/aix-ppc64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f" - integrity sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== - "@esbuild/aix-ppc64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" @@ -2581,11 +2576,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== -"@esbuild/android-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz#7ad65a36cfdb7e0d429c353e00f680d737c2aed4" - integrity sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== - "@esbuild/android-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" @@ -2606,11 +2596,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== -"@esbuild/android-arm@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz#b0c26536f37776162ca8bde25e42040c203f2824" - integrity sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== - "@esbuild/android-arm@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" @@ -2631,11 +2616,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== -"@esbuild/android-x64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz#cb13e2211282012194d89bf3bfe7721273473b3d" - integrity sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== - "@esbuild/android-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" @@ -2656,11 +2636,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== -"@esbuild/darwin-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz#cbee41e988020d4b516e9d9e44dd29200996275e" - integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== - "@esbuild/darwin-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" @@ -2681,11 +2656,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== -"@esbuild/darwin-x64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz#e37d9633246d52aecf491ee916ece709f9d5f4cd" - integrity sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== - "@esbuild/darwin-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" @@ -2706,11 +2676,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== -"@esbuild/freebsd-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz#1ee4d8b682ed363b08af74d1ea2b2b4dbba76487" - integrity sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== - "@esbuild/freebsd-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" @@ -2731,11 +2696,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== -"@esbuild/freebsd-x64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz#37a693553d42ff77cd7126764b535fb6cc28a11c" - integrity sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== - "@esbuild/freebsd-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" @@ -2756,11 +2716,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== -"@esbuild/linux-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz#be9b145985ec6c57470e0e051d887b09dddb2d4b" - integrity sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== - "@esbuild/linux-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" @@ -2781,11 +2736,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== -"@esbuild/linux-arm@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz#207ecd982a8db95f7b5279207d0ff2331acf5eef" - integrity sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== - "@esbuild/linux-arm@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" @@ -2806,11 +2756,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== -"@esbuild/linux-ia32@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz#d0d86b5ca1562523dc284a6723293a52d5860601" - integrity sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== - "@esbuild/linux-ia32@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" @@ -2831,11 +2776,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== -"@esbuild/linux-loong64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz#9a37f87fec4b8408e682b528391fa22afd952299" - integrity sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== - "@esbuild/linux-loong64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" @@ -2856,11 +2796,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== -"@esbuild/linux-mips64el@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz#4ddebd4e6eeba20b509d8e74c8e30d8ace0b89ec" - integrity sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== - "@esbuild/linux-mips64el@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" @@ -2881,11 +2816,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== -"@esbuild/linux-ppc64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz#adb67dadb73656849f63cd522f5ecb351dd8dee8" - integrity sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== - "@esbuild/linux-ppc64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" @@ -2906,11 +2836,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== -"@esbuild/linux-riscv64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz#11bc0698bf0a2abf8727f1c7ace2112612c15adf" - integrity sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== - "@esbuild/linux-riscv64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" @@ -2931,11 +2856,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== -"@esbuild/linux-s390x@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz#e86fb8ffba7c5c92ba91fc3b27ed5a70196c3cc8" - integrity sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== - "@esbuild/linux-s390x@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" @@ -2956,11 +2876,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== -"@esbuild/linux-x64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz#5f37cfdc705aea687dfe5dfbec086a05acfe9c78" - integrity sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== - "@esbuild/linux-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" @@ -2981,11 +2896,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== -"@esbuild/netbsd-x64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz#29da566a75324e0d0dd7e47519ba2f7ef168657b" - integrity sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== - "@esbuild/netbsd-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" @@ -3011,11 +2921,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== -"@esbuild/openbsd-x64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz#306c0acbdb5a99c95be98bdd1d47c916e7dc3ff0" - integrity sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== - "@esbuild/openbsd-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" @@ -3036,11 +2941,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== -"@esbuild/sunos-x64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz#0933eaab9af8b9b2c930236f62aae3fc593faf30" - integrity sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== - "@esbuild/sunos-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" @@ -3061,11 +2961,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== -"@esbuild/win32-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz#773bdbaa1971b36db2f6560088639ccd1e6773ae" - integrity sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== - "@esbuild/win32-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" @@ -3086,11 +2981,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== -"@esbuild/win32-ia32@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz#000516cad06354cc84a73f0943a4aa690ef6fd67" - integrity sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== - "@esbuild/win32-ia32@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" @@ -3111,11 +3001,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== -"@esbuild/win32-x64@0.19.12": - version "0.19.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae" - integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== - "@esbuild/win32-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" @@ -3408,7 +3293,12 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -5581,7 +5471,7 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.18.0", "@types/babel__core@^7.20.4", "@types/babel__core@^7.20.5": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.18.0", "@types/babel__core@^7.20.4": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -5805,7 +5695,7 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== @@ -6400,79 +6290,82 @@ magic-string "^0.27.0" react-refresh "^0.14.0" -"@vitejs/plugin-react@^4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz#744d8e4fcb120fc3dbaa471dadd3483f5a304bb9" - integrity sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ== - dependencies: - "@babel/core" "^7.23.5" - "@babel/plugin-transform-react-jsx-self" "^7.23.3" - "@babel/plugin-transform-react-jsx-source" "^7.23.3" - "@types/babel__core" "^7.20.5" - react-refresh "^0.14.0" - -"@vitest/coverage-v8@1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.3.0.tgz#31f98b1bad1d5e9db733a4c1ae8d46dec549cd3c" - integrity sha512-e5Y5uK5NNoQMQaNitGQQjo9FoA5ZNcu7Bn6pH+dxUf48u6po1cX38kFBYUHZ9GNVkF4JLbncE0WeWwTw+nLrxg== +"@vitest/coverage-v8@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz#738527e6e79cef5004248452527e272e0df12284" + integrity sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw== dependencies: - "@ampproject/remapping" "^2.2.1" + "@ampproject/remapping" "^2.3.0" "@bcoe/v8-coverage" "^0.2.3" - debug "^4.3.4" + debug "^4.3.7" istanbul-lib-coverage "^3.2.2" istanbul-lib-report "^3.0.1" - istanbul-lib-source-maps "^4.0.1" - istanbul-reports "^3.1.6" - magic-string "^0.30.5" - magicast "^0.3.3" - picocolors "^1.0.0" - std-env "^3.5.0" - test-exclude "^6.0.0" - v8-to-istanbul "^9.2.0" + istanbul-lib-source-maps "^5.0.6" + istanbul-reports "^3.1.7" + magic-string "^0.30.12" + magicast "^0.3.5" + std-env "^3.8.0" + test-exclude "^7.0.1" + tinyrainbow "^1.2.0" + +"@vitest/expect@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.8.tgz#13fad0e8d5a0bf0feb675dcf1d1f1a36a1773bc1" + integrity sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw== + dependencies: + "@vitest/spy" "2.1.8" + "@vitest/utils" "2.1.8" + chai "^5.1.2" + tinyrainbow "^1.2.0" -"@vitest/expect@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.2.2.tgz#39ea22e849bbf404b7e5272786551aa99e2663d0" - integrity sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg== +"@vitest/mocker@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.8.tgz#51dec42ac244e949d20009249e033e274e323f73" + integrity sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA== dependencies: - "@vitest/spy" "1.2.2" - "@vitest/utils" "1.2.2" - chai "^4.3.10" + "@vitest/spy" "2.1.8" + estree-walker "^3.0.3" + magic-string "^0.30.12" -"@vitest/runner@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.2.2.tgz#8b060a56ecf8b3d607b044d79f5f50d3cd9fee2f" - integrity sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg== +"@vitest/pretty-format@2.1.8", "@vitest/pretty-format@^2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.8.tgz#88f47726e5d0cf4ba873d50c135b02e4395e2bca" + integrity sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ== dependencies: - "@vitest/utils" "1.2.2" - p-limit "^5.0.0" - pathe "^1.1.1" + tinyrainbow "^1.2.0" -"@vitest/snapshot@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.2.2.tgz#f56fd575569774968f3eeba9382a166c26201042" - integrity sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA== +"@vitest/runner@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.8.tgz#b0e2dd29ca49c25e9323ea2a45a5125d8729759f" + integrity sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg== dependencies: - magic-string "^0.30.5" - pathe "^1.1.1" - pretty-format "^29.7.0" + "@vitest/utils" "2.1.8" + pathe "^1.1.2" -"@vitest/spy@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.2.2.tgz#8fc2aeccb96cecbbdd192c643729bd5f97a01c86" - integrity sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g== +"@vitest/snapshot@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.8.tgz#d5dc204f4b95dc8b5e468b455dfc99000047d2de" + integrity sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg== dependencies: - tinyspy "^2.2.0" + "@vitest/pretty-format" "2.1.8" + magic-string "^0.30.12" + pathe "^1.1.2" -"@vitest/utils@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.2.2.tgz#94b5a1bd8745ac28cf220a99a8719efea1bcfc83" - integrity sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g== +"@vitest/spy@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.8.tgz#bc41af3e1e6a41ae3b67e51f09724136b88fa447" + integrity sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg== dependencies: - diff-sequences "^29.6.3" - estree-walker "^3.0.3" - loupe "^2.3.7" - pretty-format "^29.7.0" + tinyspy "^3.0.2" + +"@vitest/utils@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.8.tgz#f8ef85525f3362ebd37fd25d268745108d6ae388" + integrity sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA== + dependencies: + "@vitest/pretty-format" "2.1.8" + loupe "^3.1.2" + tinyrainbow "^1.2.0" "@vituum/vite-plugin-postcss@1.1.0": version "1.1.0" @@ -6721,11 +6614,6 @@ acorn-walk@^7.1.1, acorn-walk@^7.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" - integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== - acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" @@ -6736,7 +6624,7 @@ acorn@^7.1.1, acorn@^7.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.10.0, acorn@^8.11.3, acorn@^8.2.4, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.11.3, acorn@^8.2.4, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -7264,10 +7152,10 @@ assert@^2.1.0: object.assign "^4.1.4" util "^0.12.5" -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== assign-symbols@^1.0.0: version "1.0.0" @@ -8326,18 +8214,16 @@ ccount@^2.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== -chai@^4.3.10: - version "4.4.1" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" - integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== +chai@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d" + integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw== dependencies: - assertion-error "^1.1.0" - check-error "^1.0.3" - deep-eql "^4.1.3" - get-func-name "^2.0.2" - loupe "^2.3.6" - pathval "^1.1.1" - type-detect "^4.0.8" + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" chalk@^1.1.3: version "1.1.3" @@ -8415,12 +8301,10 @@ character-reference-invalid@^2.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== -check-error@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" - integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== - dependencies: - get-func-name "^2.0.2" +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== check-more-types@^2.24.0: version "2.24.0" @@ -8891,11 +8775,6 @@ conf@^6.2.1: semver "^6.2.0" write-file-atomic "^3.0.0" -confbox@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579" - integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA== - config-file-ts@0.2.8-rc1: version "0.2.8-rc1" resolved "https://registry.yarnpkg.com/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz#fb7fc6ccb2e313f69dbeb78f1db0b00038049de0" @@ -9752,6 +9631,13 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.7: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -9868,12 +9754,10 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-eql@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" - integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== - dependencies: - type-detect "^4.0.0" +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== deep-equal@^1.0.1: version "1.1.2" @@ -10225,11 +10109,6 @@ dicer@0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -10952,6 +10831,11 @@ es-module-lexer@^0.9.3: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-module-lexer@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + es-object-atoms@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" @@ -11041,35 +10925,6 @@ esbuild@^0.18.0: "@esbuild/win32-ia32" "0.18.20" "@esbuild/win32-x64" "0.18.20" -esbuild@^0.19.3: - version "0.19.12" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.12.tgz#dc82ee5dc79e82f5a5c3b4323a2a641827db3e04" - integrity sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== - optionalDependencies: - "@esbuild/aix-ppc64" "0.19.12" - "@esbuild/android-arm" "0.19.12" - "@esbuild/android-arm64" "0.19.12" - "@esbuild/android-x64" "0.19.12" - "@esbuild/darwin-arm64" "0.19.12" - "@esbuild/darwin-x64" "0.19.12" - "@esbuild/freebsd-arm64" "0.19.12" - "@esbuild/freebsd-x64" "0.19.12" - "@esbuild/linux-arm" "0.19.12" - "@esbuild/linux-arm64" "0.19.12" - "@esbuild/linux-ia32" "0.19.12" - "@esbuild/linux-loong64" "0.19.12" - "@esbuild/linux-mips64el" "0.19.12" - "@esbuild/linux-ppc64" "0.19.12" - "@esbuild/linux-riscv64" "0.19.12" - "@esbuild/linux-s390x" "0.19.12" - "@esbuild/linux-x64" "0.19.12" - "@esbuild/netbsd-x64" "0.19.12" - "@esbuild/openbsd-x64" "0.19.12" - "@esbuild/sunos-x64" "0.19.12" - "@esbuild/win32-arm64" "0.19.12" - "@esbuild/win32-ia32" "0.19.12" - "@esbuild/win32-x64" "0.19.12" - esbuild@^0.20.1: version "0.20.2" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" @@ -11679,6 +11534,11 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" +expect-type@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" + integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA== + exponential-backoff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" @@ -12534,11 +12394,6 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.1, get-func-name@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" - integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== - get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" @@ -12784,7 +12639,7 @@ glob@^10.0.0: minipass "^7.0.4" path-scurry "^1.10.2" -glob@^10.3.12: +glob@^10.3.12, glob@^10.4.1: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -14618,16 +14473,16 @@ istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: make-dir "^4.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== +istanbul-lib-source-maps@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== dependencies: + "@jridgewell/trace-mapping" "^0.3.23" debug "^4.1.1" istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" -istanbul-reports@^3.1.6: +istanbul-reports@^3.1.7: version "3.1.7" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== @@ -15205,14 +15060,6 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -local-pkg@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.0.tgz#093d25a346bae59a99f80e75f6e9d36d7e8c925c" - integrity sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg== - dependencies: - mlly "^1.4.2" - pkg-types "^1.0.3" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -15395,12 +15242,10 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -loupe@^2.3.6, loupe@^2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" - integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== - dependencies: - get-func-name "^2.0.1" +loupe@^3.1.0, loupe@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" + integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== lower-case@^1.1.1: version "1.1.4" @@ -15493,20 +15338,27 @@ magic-string@^0.27.0: dependencies: "@jridgewell/sourcemap-codec" "^1.4.13" -magic-string@^0.30.0, magic-string@^0.30.5: +magic-string@^0.30.0: version "0.30.10" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== dependencies: "@jridgewell/sourcemap-codec" "^1.4.15" -magicast@^0.3.3: - version "0.3.4" - resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.4.tgz#bbda1791d03190a24b00ff3dd18151e7fd381d19" - integrity sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q== +magic-string@^0.30.12: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== dependencies: - "@babel/parser" "^7.24.4" - "@babel/types" "^7.24.0" + "@jridgewell/sourcemap-codec" "^1.5.0" + +magicast@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739" + integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + dependencies: + "@babel/parser" "^7.25.4" + "@babel/types" "^7.25.4" source-map-js "^1.2.0" make-dir@^1.0.0: @@ -16418,16 +16270,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mlly@^1.4.2, mlly@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.6.1.tgz#0983067dc3366d6314fc5e12712884e6978d028f" - integrity sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA== - dependencies: - acorn "^8.11.3" - pathe "^1.1.2" - pkg-types "^1.0.3" - ufo "^1.3.2" - mnemonist@0.38.3: version "0.38.3" resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d" @@ -16524,7 +16366,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -17253,13 +17095,6 @@ p-limit@^3.0.2, "p-limit@^3.1.0 ": dependencies: yocto-queue "^0.1.0" -p-limit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" - integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ== - dependencies: - yocto-queue "^1.0.0" - p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -17573,15 +17408,15 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pathe@^1.1.1, pathe@^1.1.2: +pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== pbkdf2@^3.0.3, pbkdf2@^3.1.2: version "3.1.2" @@ -17696,15 +17531,6 @@ pkg-dir@^5.0.0: dependencies: find-up "^5.0.0" -pkg-types@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.1.0.tgz#3ec1bf33379030fd0a34c227b6c650e8ea7ca271" - integrity sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA== - dependencies: - confbox "^0.1.7" - mlly "^1.6.1" - pathe "^1.1.2" - pkg-up@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -18490,7 +18316,7 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21 picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.1.7, postcss@^8.4, postcss@^8.4.32, postcss@^8.4.38: +postcss@^8.1.7, postcss@^8.4, postcss@^8.4.38: version "8.4.38" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== @@ -20038,7 +19864,7 @@ rollup@^2.44.0: optionalDependencies: fsevents "~2.3.2" -rollup@^4.13.0, rollup@^4.2.0: +rollup@^4.13.0: version "4.16.2" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.16.2.tgz#43bcbd225d0a6bc68df97a6e41c45003188a3845" integrity sha512-sxDP0+pya/Yi5ZtptF4p3avI+uWCIf/OdrfdH2Gbv1kWddLKk0U7WE3PmQokhi5JrektxsK3sK8s4hzAmjqahw== @@ -20945,10 +20771,10 @@ statuses@~1.4.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== -std-env@^3.5.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" - integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== +std-env@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" + integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== stdopt@^2.0.0: version "2.2.0" @@ -21261,13 +21087,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strip-literal@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.3.0.tgz#db3942c2ec1699e6836ad230090b84bb458e3a07" - integrity sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg== - dependencies: - acorn "^8.10.0" - strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" @@ -21672,6 +21491,15 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +test-exclude@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2" + integrity sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^10.4.1" + minimatch "^9.0.4" + text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -21754,25 +21582,35 @@ tiny-warning@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== -tinybench@^2.5.1: - version "2.8.0" - resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.8.0.tgz#30e19ae3a27508ee18273ffed9ac7018949acd7b" - integrity sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw== +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== tinycolor2@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e" integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw== -tinypool@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8" - integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ== +tinyexec@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98" + integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ== -tinyspy@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.1.tgz#117b2342f1f38a0dbdcc73a50a454883adf861d1" - integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A== +tinypool@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== + +tinyrainbow@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" + integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== + +tinyspy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== tmp-promise@^3.0.2: version "3.0.3" @@ -22033,11 +21871,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - type-fest@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" @@ -22177,7 +22010,7 @@ ua-parser-js@^0.7.23, ua-parser-js@^0.7.30: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.37.tgz#e464e66dac2d33a7a1251d7d7a99d6157ec27832" integrity sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA== -ufo@^1.3.2, ufo@^1.4.0: +ufo@^1.4.0: version "1.5.3" resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.3.tgz#3325bd3c977b6c6cd3160bf4ff52989adc9d3344" integrity sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw== @@ -22768,15 +22601,6 @@ v8-compile-cache@^2.1.0, v8-compile-cache@^2.1.1: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz#cdada8bec61e15865f05d097c5f4fd30e94dc128" integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw== -v8-to-istanbul@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" - integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^2.0.0" - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -22875,28 +22699,17 @@ vfile@^6.0.0: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" -vite-node@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.2.2.tgz#f6d329b06f9032130ae6eac1dc773f3663903c25" - integrity sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg== +vite-node@2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5" + integrity sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg== dependencies: cac "^6.7.14" - debug "^4.3.4" - pathe "^1.1.1" - picocolors "^1.0.0" + debug "^4.3.7" + es-module-lexer "^1.5.4" + pathe "^1.1.2" vite "^5.0.0" -vite@5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.5.tgz#3eebe3698e3b32cea36350f58879258fec858a3c" - integrity sha512-OekeWqR9Ls56f3zd4CaxzbbS11gqYkEiBtnWFFgYR2WV8oPJRRKq0mpskYy/XaoCL3L7VINDhqqOMNDiYdGvGg== - dependencies: - esbuild "^0.19.3" - postcss "^8.4.32" - rollup "^4.2.0" - optionalDependencies: - fsevents "~2.3.3" - vite@5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.2.tgz#2f0a8531c71060467ed3e0a205a203f269b6d9c8" @@ -22919,37 +22732,38 @@ vite@^5.0, vite@^5.0.0: optionalDependencies: fsevents "~2.3.3" -vitest-when@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/vitest-when/-/vitest-when-0.3.1.tgz#72db1c0a8e76fae81f8fc21c6da3c769f8e7f8bb" - integrity sha512-qZt4VmuvGtkLEqUpq5AJHQtdfhU8wJH+eXHk+WBo8kFT5zdfVV06+vFgYzvuSOq73srlCEsJ4VJqX7uBtOwWLg== +vitest-when@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/vitest-when/-/vitest-when-0.5.0.tgz#1cef4713a71c30af741964df780e50485177eaf0" + integrity sha512-BYDfzSawgKsV5GX3bU9ZbURuljjBCqi5KPtE2hBn/DsCRThU0z4qH0PAhJGemyKNnR01ADObXkmm1UPDHGzVUw== + dependencies: + pretty-format "^29.7.0" -vitest@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.2.2.tgz#9e29ad2a74a5df553c30c5798c57a062d58ce299" - integrity sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw== - dependencies: - "@vitest/expect" "1.2.2" - "@vitest/runner" "1.2.2" - "@vitest/snapshot" "1.2.2" - "@vitest/spy" "1.2.2" - "@vitest/utils" "1.2.2" - acorn-walk "^8.3.2" - cac "^6.7.14" - chai "^4.3.10" - debug "^4.3.4" - execa "^8.0.1" - local-pkg "^0.5.0" - magic-string "^0.30.5" - pathe "^1.1.1" - picocolors "^1.0.0" - std-env "^3.5.0" - strip-literal "^1.3.0" - tinybench "^2.5.1" - tinypool "^0.8.2" +vitest@2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.8.tgz#2e6a00bc24833574d535c96d6602fb64163092fa" + integrity sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ== + dependencies: + "@vitest/expect" "2.1.8" + "@vitest/mocker" "2.1.8" + "@vitest/pretty-format" "^2.1.8" + "@vitest/runner" "2.1.8" + "@vitest/snapshot" "2.1.8" + "@vitest/spy" "2.1.8" + "@vitest/utils" "2.1.8" + chai "^5.1.2" + debug "^4.3.7" + expect-type "^1.1.0" + magic-string "^0.30.12" + pathe "^1.1.2" + std-env "^3.8.0" + tinybench "^2.9.0" + tinyexec "^0.3.1" + tinypool "^1.0.1" + tinyrainbow "^1.2.0" vite "^5.0.0" - vite-node "1.2.2" - why-is-node-running "^2.2.2" + vite-node "2.1.8" + why-is-node-running "^2.3.0" vituum@^1.1: version "1.1.0" @@ -23383,10 +23197,10 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -why-is-node-running@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" - integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== dependencies: siginfo "^2.0.0" stackback "0.0.2" @@ -23725,11 +23539,6 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -yocto-queue@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" - integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== - yup@0.32.9: version "0.32.9" resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.9.tgz#9367bec6b1b0e39211ecbca598702e106019d872"