Merge branch 'dev' into beta-release
Gitea Scan/plate-tool/pipeline/head This commit looks good
Details
Gitea Scan/plate-tool/pipeline/head This commit looks good
Details
This commit is contained in:
commit
e71a72bff9
|
@ -3,10 +3,25 @@
|
|||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.1"
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -25,13 +40,13 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.68"
|
||||
version = "0.1.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
||||
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -46,6 +61,21 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
|
@ -63,9 +93,24 @@ checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.2"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -85,9 +130,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.2.2"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
|
||||
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
|
@ -97,18 +142,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
version = "0.20.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||
checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
|
@ -116,27 +161,27 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.4"
|
||||
version = "0.20.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||
checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.4"
|
||||
version = "0.20.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -147,18 +192,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.1.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.28"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
@ -170,9 +215,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.28"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
|
@ -180,44 +225,44 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.28"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.28"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.28"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.28"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.28"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
@ -233,9 +278,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.9"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -245,10 +290,16 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo"
|
||||
version = "0.8.0"
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a4bef6b277b3ab073253d4bca60761240cf8d6998f4bd142211957b69a61b20"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "gloo"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d"
|
||||
dependencies = [
|
||||
"gloo-console",
|
||||
"gloo-dialogs",
|
||||
|
@ -310,9 +361,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gloo-history"
|
||||
version = "0.1.3"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd451019e0b7a2b8a7a7b23e74916601abf1135c54664e57ff71dcc26dfcdeb7"
|
||||
checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f"
|
||||
dependencies = [
|
||||
"gloo-events",
|
||||
"gloo-utils",
|
||||
|
@ -326,14 +377,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gloo-net"
|
||||
version = "0.2.6"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10"
|
||||
checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"gloo-utils",
|
||||
"http",
|
||||
"js-sys",
|
||||
"pin-project",
|
||||
"serde",
|
||||
|
@ -381,9 +433,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gloo-utils"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5"
|
||||
checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
|
@ -417,11 +469,19 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -432,9 +492,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
|||
|
||||
[[package]]
|
||||
name = "implicit-clone"
|
||||
version = "0.3.5"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7"
|
||||
checksum = "cfd6201e7c30ccb24773cac7efa6fec1e06189d414b7439ce756a481c8bfbf53"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
@ -451,15 +511,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.63"
|
||||
version = "0.3.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
|
||||
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -472,72 +532,87 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.144"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.0"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead"
|
||||
checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.0"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
|
||||
checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
@ -557,7 +632,22 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "plate-tool"
|
||||
name = "plate-tool-lib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"csv",
|
||||
"getrandom",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rand",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plate-tool-web"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"csv",
|
||||
|
@ -565,6 +655,7 @@ dependencies = [
|
|||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"plate-tool-lib",
|
||||
"rand",
|
||||
"regex",
|
||||
"serde",
|
||||
|
@ -620,9 +711,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.58"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -646,9 +737,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.27"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -685,9 +776,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.1"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -696,21 +799,27 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.12"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
|
@ -720,18 +829,18 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.163"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.4.5"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
|
||||
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
|
@ -740,20 +849,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.163"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.96"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -774,9 +883,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
@ -800,9 +909,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.16"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -811,33 +920,32 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
version = "1.0.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
version = "1.0.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.28.1"
|
||||
version = "1.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105"
|
||||
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"backtrace",
|
||||
"pin-project-lite",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -853,11 +961,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.37"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
|
@ -865,35 +972,35 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.24"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.31"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
|
||||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"getrandom",
|
||||
|
@ -905,13 +1012,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "uuid-macro-internal"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49e7f3f3db8040a100710a11932239fd30697115e2ba4107080d8252939845e"
|
||||
checksum = "7abb14ae1a50dad63eaa768a458ef43d298cd1bd44951677bd10b732a9ba2a2d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -928,9 +1035,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.86"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
|
||||
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
@ -938,24 +1045,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.86"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
|
||||
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.36"
|
||||
version = "0.4.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e"
|
||||
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -965,9 +1072,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.86"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
|
||||
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -975,28 +1082,28 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.86"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
|
||||
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
"syn 2.0.48",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.86"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.36"
|
||||
version = "0.3.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e636f3a428ff62b3742ebc3c70e254dfe12b8c2b469d688ea59cdd4abcf502"
|
||||
checksum = "143ddeb4f833e2ed0d252e618986e18bfc7b0e52f2d28d77d05b2f045dd8eb61"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
|
@ -1008,12 +1115,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.36"
|
||||
version = "0.3.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f18c1fad2f7c4958e7bcce014fa212f59a65d5e3721d0f77e6c0b27ede936ba3"
|
||||
checksum = "a5211b7550606857312bba1d978a8ec75692eae187becc5e680444fffc5e6f89"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1029,80 +1137,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.63"
|
||||
version = "0.3.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
|
||||
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
||||
[[package]]
|
||||
name = "yew"
|
||||
version = "0.20.0"
|
||||
|
@ -1145,9 +1187,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yewdux"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653ba356bc60d1804c28ec6cc8ddac2741c686bde2a65074d07faba735914464"
|
||||
checksum = "3c528544a814a0beb30059c8340eb78c88160141db788112b51a5a08098cef2d"
|
||||
dependencies = [
|
||||
"anymap",
|
||||
"async-trait",
|
||||
|
@ -1164,13 +1206,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yewdux-macros"
|
||||
version = "0.9.2"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25bcd923aceaa85cb4affad8657cc36e3d6b6932740e711574182f7817492739"
|
||||
checksum = "d22b1832d3e3eaa61a5c2ecd40affa876507457180d1e599143368c1c3317c2d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
|
32
Cargo.toml
32
Cargo.toml
|
@ -1,29 +1,3 @@
|
|||
[package]
|
||||
name = "plate-tool"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
yew = { version = "0.20.0", features = ["csr"] }
|
||||
yewdux = "0.9"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["FormData", "HtmlFormElement",
|
||||
"HtmlDialogElement", "Blob", "Url", "Window",
|
||||
"HtmlAnchorElement", "ReadableStream", "HtmlSelectElement", "HtmlOptionElement", "HtmlButtonElement",
|
||||
"FileReader"] }
|
||||
js-sys = "0.3"
|
||||
log = "0.4"
|
||||
wasm-logger = "0.2"
|
||||
regex = "1"
|
||||
lazy_static = "1.4"
|
||||
uuid = { version = "1.6", features = ["v7", "fast-rng", "macro-diagnostics", "js", "serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
csv = "1.2"
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
[workspace]
|
||||
members = ["plate-tool-web", "plate-tool-lib"]
|
||||
resolver = "2"
|
||||
|
|
23
README.md
23
README.md
|
@ -78,13 +78,6 @@ To add a new plate, click the "New Plate" button:
|
|||
Keep in mind that this will overwrite any work you currently have open,
|
||||
so you may wish to export first (see above).
|
||||
|
||||
#### Import Transfer from CSV (Using a picklist as a transfer)
|
||||
If you have a CSV generated by another tool (or plate-tool),
|
||||
you can import it as a single transfer.
|
||||
To do so, mouse over the "File" tab, then "Import", and finally "Import Transfer from CSV".
|
||||
When creating transfers via this method, the transfer cannot be edited.
|
||||
This is useful if you have a pre-existing picklist that you would like to visualize in plate-tool.
|
||||
|
||||
_Note 1_: JSON files are plaintext!
|
||||
By default there is little whitespace (this makes comprehending them a challenge)
|
||||
but if we pass it through a "JSON Beautifier" (enter this into your search engine of choice)
|
||||
|
@ -99,6 +92,18 @@ To add a new plate, click the "New Plate" button:
|
|||
that this application does not "phone home".
|
||||
Your data is stored locally (unless you choose to export it and distribute it yourself).
|
||||
|
||||
#### Import Transfer from CSV (Using a picklist as a transfer)
|
||||
If you have a CSV generated by another tool (or plate-tool),
|
||||
you can import it as a single transfer.
|
||||
To do so, mouse over the "File" tab, then "Import", and finally "Import Transfer from CSV".
|
||||
When creating transfers via this method, the transfer cannot be edited.
|
||||
This is useful if you have a pre-existing picklist that you would like to visualize in plate-tool.
|
||||
|
||||
_Note_: If you try to use this feature and no plates are available to select,
|
||||
there was likely an issue parsing your picklist.
|
||||
Your browser's console may have guidance as to why parsing failed;
|
||||
plate-tool was probably expecting a different name for a column than was in your file.
|
||||
|
||||
### Other Neat Features
|
||||
|
||||
#### Taking Pictures of Plates
|
||||
|
@ -130,7 +135,7 @@ To add a new plate, click the "New Plate" button:
|
|||
Plate tool is hosted [here](https://ilia.moe/cool-stuff/plate-tool/) for your convenience.
|
||||
However, you're absolutely welcome to host your own instance (even locally).
|
||||
Here's how:
|
||||
(_Note:_ If you run Windows you're probably best off doing the following in WSL2)
|
||||
(_Note:_ ~~If you run Windows you're probably best off doing the following in WSL2~~ You're absolutely fine to install rustup in Powershell, and the subsequent steps should be very similar but likely with different filepaths.)
|
||||
|
||||
1. Make sure you have a working Rust toolchain
|
||||
1. Installing `rustup` is the easiest way to do this. See [their website](https://rustup.rs/),
|
||||
|
@ -140,7 +145,7 @@ Here's how:
|
|||
2. Install [trunk](https://trunkrs.dev/)
|
||||
- Run `cargo install --locked trunk`
|
||||
3. Clone this repository using git
|
||||
4. Enter the project directory and run `trunk serve`
|
||||
4. Enter the plate-tool-web directory and run `trunk serve`
|
||||
- You may need to check where `cargo` is installing binaries by default. For me, they're at `~/.cargo/bin`.
|
||||
If trunk is not automatically placed in your path, you would then run `/your/path/to/.cargo/bin/trunk serve`.
|
||||
- You can instead run `trunk build --release` for a more performant binary.
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
@use "sass:color";
|
||||
@use "../variables" as *;
|
||||
|
||||
div.plate_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
|
||||
border: 2px solid $color-dark;
|
||||
grid-column: right / right;
|
||||
grid-row: upper / 3;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 1%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/dist
|
|
@ -0,0 +1,339 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "plate-tool-lib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"csv",
|
||||
"getrandom",
|
||||
"log",
|
||||
"rand",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"getrandom",
|
||||
"rand",
|
||||
"serde",
|
||||
"uuid-macro-internal",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid-macro-internal"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abb14ae1a50dad63eaa768a458ef43d298cd1bd44951677bd10b732a9ba2a2d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "plate-tool-lib"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
regex = "1"
|
||||
uuid = { version = "1.6", features = ["v7", "fast-rng", "macro-diagnostics", "js", "serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
csv = "1.2"
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
lazy_static = "1.4"
|
|
@ -0,0 +1,173 @@
|
|||
use super::{string_well_to_pt, TransferRecord, read_csv};
|
||||
use crate::plate::{PlateFormat, PlateType};
|
||||
use crate::plate_instances::PlateInstance;
|
||||
use crate::transfer::Transfer;
|
||||
use crate::transfer_region::{CustomRegion, Region, TransferRegion};
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub struct AutoOutput {
|
||||
pub sources: Vec<PlateInstance>,
|
||||
pub destinations: Vec<PlateInstance>,
|
||||
pub transfers: Vec<Transfer>,
|
||||
}
|
||||
|
||||
struct UniquePlates {
|
||||
sources: Vec<PlateInstance>,
|
||||
destinations: Vec<PlateInstance>,
|
||||
}
|
||||
|
||||
const W96_COL_MAX: u8 = 12;
|
||||
const W96_ROW_MAX: u8 = 8;
|
||||
const W384_COL_MAX: u8 = 24;
|
||||
const W384_ROW_MAX: u8 = 16;
|
||||
const W1536_COL_MAX: u8 = 48;
|
||||
const W1536_ROW_MAX: u8 = 32;
|
||||
|
||||
pub fn auto(records: &[TransferRecord]) -> AutoOutput {
|
||||
let unique_plates = find_unique_plates(records);
|
||||
let transfers = get_transfer_for_all_pairs(records, &unique_plates);
|
||||
|
||||
AutoOutput { sources: unique_plates.sources, destinations: unique_plates.destinations, transfers }
|
||||
}
|
||||
|
||||
pub fn read_csv_auto(data: &str) -> AutoOutput {
|
||||
let transfer_records = read_csv(data);
|
||||
auto(&transfer_records)
|
||||
}
|
||||
|
||||
fn find_unique_plates(records: &[TransferRecord]) -> UniquePlates {
|
||||
let mut source_names: HashSet<&str> = HashSet::new();
|
||||
let mut destination_names: HashSet<&str> = HashSet::new();
|
||||
|
||||
for record in records {
|
||||
source_names.insert(&record.source_plate);
|
||||
destination_names.insert(&record.destination_plate);
|
||||
}
|
||||
|
||||
let mut sources: Vec<PlateInstance> = Vec::with_capacity(source_names.len());
|
||||
for source_name in source_names {
|
||||
let filtered_records: Vec<&TransferRecord> = records
|
||||
.iter()
|
||||
.filter(|x| x.source_plate == source_name)
|
||||
.collect();
|
||||
let format_guess = guess_plate_size(&filtered_records, PlateType::Source);
|
||||
sources.push(PlateInstance::new(
|
||||
PlateType::Source,
|
||||
format_guess,
|
||||
source_name.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
let mut destinations: Vec<PlateInstance> = Vec::with_capacity(destination_names.len());
|
||||
for destination_name in destination_names {
|
||||
let filtered_records: Vec<&TransferRecord> = records
|
||||
.iter()
|
||||
.filter(|x| x.destination_plate == destination_name)
|
||||
.collect();
|
||||
let format_guess = guess_plate_size(&filtered_records, PlateType::Destination);
|
||||
destinations.push(PlateInstance::new(
|
||||
PlateType::Destination,
|
||||
format_guess,
|
||||
destination_name.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
UniquePlates {
|
||||
sources,
|
||||
destinations,
|
||||
}
|
||||
}
|
||||
|
||||
fn guess_plate_size(plate_filtered_records: &[&TransferRecord], which: PlateType) -> PlateFormat {
|
||||
let mut guess = PlateFormat::W96; // Never guess smaller than 96
|
||||
for record in plate_filtered_records {
|
||||
if let Some((row, col)) = string_well_to_pt(match which {
|
||||
PlateType::Source => &record.source_well,
|
||||
PlateType::Destination => &record.destination_well,
|
||||
}) {
|
||||
if row > W1536_ROW_MAX || col > W1536_COL_MAX {
|
||||
return PlateFormat::W3456;
|
||||
} else if row > W384_ROW_MAX || col > W384_COL_MAX {
|
||||
guess = PlateFormat::W1536;
|
||||
} else if row > W96_ROW_MAX || col > W96_COL_MAX {
|
||||
guess = PlateFormat::W384;
|
||||
}
|
||||
}
|
||||
}
|
||||
guess
|
||||
}
|
||||
|
||||
fn get_transfer_for_all_pairs(
|
||||
records: &[TransferRecord],
|
||||
unique_plates: &UniquePlates,
|
||||
) -> Vec<Transfer> {
|
||||
let mut transfers: Vec<Transfer> = Vec::new();
|
||||
for source_instance in &unique_plates.sources {
|
||||
for destination_instance in &unique_plates.destinations {
|
||||
if let Some(transfer) =
|
||||
get_transfer_for_pair(records, source_instance, destination_instance)
|
||||
{
|
||||
transfers.push(transfer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transfers
|
||||
}
|
||||
|
||||
fn get_transfer_for_pair(
|
||||
records: &[TransferRecord],
|
||||
source: &PlateInstance,
|
||||
destination: &PlateInstance,
|
||||
) -> Option<Transfer> {
|
||||
let source_name: &str = &source.name;
|
||||
let destination_name: &str = &destination.name;
|
||||
|
||||
let mut filtered_records = records
|
||||
.iter()
|
||||
.filter(|x| x.source_plate == source_name && x.destination_plate == destination_name)
|
||||
.peekable();
|
||||
|
||||
if filtered_records.peek().is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut source_wells: HashSet<(u8, u8)> = HashSet::new();
|
||||
let mut destination_wells: HashSet<(u8, u8)> = HashSet::new();
|
||||
for record in filtered_records {
|
||||
let source_point_opt = string_well_to_pt(&record.source_well);
|
||||
let destination_point_opt = string_well_to_pt(&record.destination_well);
|
||||
|
||||
if source_point_opt.and(destination_point_opt).is_some() {
|
||||
let source_point = source_point_opt.unwrap();
|
||||
let destination_point = destination_point_opt.unwrap();
|
||||
|
||||
source_wells.insert(source_point);
|
||||
destination_wells.insert(destination_point);
|
||||
}
|
||||
}
|
||||
let source_wells_vec: Vec<(u8, u8)> = source_wells.into_iter().collect();
|
||||
let destination_wells_vec: Vec<(u8, u8)> = destination_wells.into_iter().collect();
|
||||
|
||||
let custom_region: Region =
|
||||
Region::Custom(CustomRegion::new(source_wells_vec, destination_wells_vec));
|
||||
|
||||
let transfer_region = TransferRegion {
|
||||
source_plate: source.plate,
|
||||
dest_plate: destination.plate,
|
||||
interleave_source: (1, 1),
|
||||
interleave_dest: (1, 1),
|
||||
source_region: custom_region.clone(),
|
||||
dest_region: custom_region,
|
||||
};
|
||||
|
||||
let transfer_name = format!("{} to {}", source.name, destination.name);
|
||||
|
||||
Some(Transfer::new(
|
||||
source.clone(),
|
||||
destination.clone(),
|
||||
transfer_region,
|
||||
transfer_name,
|
||||
))
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
use crate::transfer::Transfer;
|
||||
use crate::util::*;
|
||||
|
||||
use super::TransferRecord;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::error::Error;
|
||||
|
||||
pub fn transfer_to_records(
|
||||
tr: &Transfer,
|
||||
src_barcode: &str,
|
||||
dest_barcode: &str,
|
||||
) -> Vec<TransferRecord> {
|
||||
let source_wells = tr.transfer_region.get_source_wells();
|
||||
let map = tr.transfer_region.calculate_map();
|
||||
|
||||
let mut records: Vec<TransferRecord> = vec![];
|
||||
|
||||
for s_well in source_wells {
|
||||
let dest_wells = map(s_well);
|
||||
if let Some(dest_wells) = dest_wells {
|
||||
for d_well in dest_wells {
|
||||
records.push(TransferRecord {
|
||||
source_plate: src_barcode.to_string(),
|
||||
source_well: format!("{}{}", num_to_letters(s_well.0).unwrap(), s_well.1),
|
||||
destination_plate: dest_barcode.to_string(),
|
||||
destination_well: format!("{}{}", num_to_letters(d_well.0).unwrap(), d_well.1),
|
||||
volume: tr.volume,
|
||||
concentration: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
records
|
||||
}
|
||||
|
||||
pub fn records_to_csv(trs: Vec<TransferRecord>) -> Result<String, Box<dyn Error>> {
|
||||
let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
|
||||
for record in trs {
|
||||
wtr.serialize(record)?
|
||||
}
|
||||
let data = String::from_utf8(wtr.into_inner()?)?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn string_well_to_pt(input: &str) -> Option<(u8, u8)> {
|
||||
lazy_static! {
|
||||
static ref REGEX: Regex = Regex::new(r"([A-Z,a-z]+)(\d+)").unwrap();
|
||||
}
|
||||
if let Some(c1) = REGEX.captures(input) {
|
||||
if let (Some(row), Some(col)) = (letters_to_num(&c1[1]), c1[2].parse::<u8>().ok()) {
|
||||
Some((row, col))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_csv(data: &str) -> Vec<TransferRecord> {
|
||||
let (header, data) = data.split_at(data.find('\n').unwrap());
|
||||
let modified: String = header.to_lowercase() + data;
|
||||
|
||||
let mut rdr = csv::Reader::from_reader(modified.as_bytes());
|
||||
let mut records: Vec<TransferRecord> = Vec::new();
|
||||
for record in rdr.deserialize::<TransferRecord>() {
|
||||
match record {
|
||||
Ok(r) => {
|
||||
//log::debug!("{:?}", r);
|
||||
records.push(r);
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
records
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
mod transfer_record;
|
||||
mod conversion;
|
||||
mod auto;
|
||||
|
||||
pub use transfer_record::volume_default;
|
||||
pub use transfer_record::TransferRecord;
|
||||
pub use conversion::*;
|
||||
|
||||
pub use auto::{auto, read_csv_auto};
|
|
@ -0,0 +1,33 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::transfer::Transfer;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TransferRecord {
|
||||
#[serde(rename = "Source Plate", alias = "source plate", alias = "src plate")]
|
||||
pub source_plate: String,
|
||||
#[serde(rename = "Source Well", alias = "source well", alias = "src well")]
|
||||
pub source_well: String,
|
||||
#[serde(
|
||||
rename = "Dest Plate",
|
||||
alias = "dest plate",
|
||||
alias = "destination plate"
|
||||
)]
|
||||
pub destination_plate: String,
|
||||
#[serde(
|
||||
rename = "Destination Well",
|
||||
alias = "destination well",
|
||||
alias = "dest well"
|
||||
)]
|
||||
pub destination_well: String,
|
||||
#[serde(rename = "Transfer Volume", alias = "transfer volume")]
|
||||
#[serde(default = "volume_default")]
|
||||
pub volume: f32,
|
||||
#[serde(rename = "Concentration", alias = "concentration")]
|
||||
pub concentration: Option<f32>,
|
||||
}
|
||||
|
||||
pub fn volume_default() -> f32 {
|
||||
Transfer::default().volume
|
||||
}
|
||||
|
|
@ -3,3 +3,4 @@ pub mod plate;
|
|||
pub mod plate_instances;
|
||||
pub mod transfer;
|
||||
pub mod transfer_region;
|
||||
pub mod util;
|
|
@ -60,6 +60,23 @@ impl std::fmt::Display for PlateFormat {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl TryFrom<&str> for PlateFormat {
|
||||
type Error = ();
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let lower = value.to_lowercase();
|
||||
match lower.trim() {
|
||||
"w6" | "6" => Ok(PlateFormat::W6),
|
||||
"w12" | "12" => Ok(PlateFormat::W12),
|
||||
"w24" | "24" => Ok(PlateFormat::W24),
|
||||
"w48" | "48" => Ok(PlateFormat::W48),
|
||||
"w96" | "96" => Ok(PlateFormat::W96),
|
||||
"w384" | "384" => Ok(PlateFormat::W384),
|
||||
"w1536" | "1536" => Ok(PlateFormat::W1536),
|
||||
"w3456" | "3456" => Ok(PlateFormat::W3456),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlateFormat {
|
||||
pub fn size(&self) -> (u8, u8) {
|
|
@ -2,7 +2,7 @@ use super::plate::*;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct PlateInstance {
|
||||
pub plate: Plate,
|
||||
#[serde(rename = "id_v7")]
|
||||
|
@ -30,6 +30,10 @@ impl PlateInstance {
|
|||
pub fn change_name(&mut self, new_name: String) {
|
||||
self.name = new_name;
|
||||
}
|
||||
|
||||
pub fn change_format(&mut self, new_format: &PlateFormat) {
|
||||
self.plate.plate_format = *new_format;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Plate> for PlateInstance {
|
||||
|
@ -41,3 +45,10 @@ impl From<Plate> for PlateInstance {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlateInstance> for String {
|
||||
fn from(value: &PlateInstance) -> Self {
|
||||
// Could have other formatting here
|
||||
format!("{}, {}", value.name, value.plate.plate_format)
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ impl Default for Transfer {
|
|||
name: "New Transfer".to_string(),
|
||||
id: Default::default(),
|
||||
transfer_region: Default::default(),
|
||||
volume: 2.5f32,
|
||||
volume: default_volume(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::components::transfer_menu::RegionDisplay;
|
||||
|
||||
use super::plate::Plate;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
|
@ -10,6 +8,12 @@ pub struct CustomRegion {
|
|||
dest: Vec<(u8, u8)>,
|
||||
}
|
||||
|
||||
impl CustomRegion {
|
||||
pub fn new(src: Vec<(u8, u8)>, dest: Vec<(u8, u8)>) -> Self {
|
||||
CustomRegion { src, dest }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
pub enum Region {
|
||||
Rect((u8, u8), (u8, u8)),
|
||||
|
@ -32,8 +36,11 @@ impl TryFrom<Region> for ((u8, u8), (u8, u8)) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Corner = (u8, u8);
|
||||
type Rectangle = (Corner, Corner);
|
||||
impl Region {
|
||||
pub fn new_custom(transfers: &Vec<((u8, u8), (u8, u8))>) -> Self {
|
||||
pub fn new_custom(transfers: &Vec<Rectangle>) -> Self {
|
||||
let mut src_pts: Vec<(u8, u8)> = Vec::with_capacity(transfers.len());
|
||||
let mut dest_pts: Vec<(u8, u8)> = Vec::with_capacity(transfers.len());
|
||||
|
||||
|
@ -77,7 +84,7 @@ impl TransferRegion {
|
|||
match &self.source_region {
|
||||
Region::Rect(c1, c2) => {
|
||||
let mut wells = Vec::<(u8, u8)>::new();
|
||||
let (ul, br) = standardize_rectangle(&c1, &c2);
|
||||
let (ul, br) = standardize_rectangle(c1, c2);
|
||||
let (interleave_i, interleave_j) = self.interleave_source;
|
||||
// NOTE: This will panic if either is 0!
|
||||
// We'll reassign these values (still not mutable) just in case.
|
||||
|
@ -174,8 +181,8 @@ impl TransferRegion {
|
|||
Region::Rect(c1, c2) => {
|
||||
Box::new(move |(i, j)| {
|
||||
if source_wells.contains(&(i, j)) {
|
||||
let possible_destination_wells = create_dense_rectangle(&c1, &c2);
|
||||
let (d_ul, d_br) = standardize_rectangle(&c1, &c2);
|
||||
let possible_destination_wells = create_dense_rectangle(c1, c2);
|
||||
let (d_ul, d_br) = standardize_rectangle(c1, c2);
|
||||
let (s_ul, s_br) =
|
||||
standardize_rectangle(&source_corners.0, &source_corners.1);
|
||||
let s_dims = (
|
||||
|
@ -186,7 +193,7 @@ impl TransferRegion {
|
|||
d_br.0.checked_sub(d_ul.0).unwrap() + 1,
|
||||
d_br.1.checked_sub(d_ul.1).unwrap() + 1,
|
||||
);
|
||||
let N_s = (
|
||||
let number_used_src_wells = (
|
||||
// Number of used source wells
|
||||
(s_dims.0 + il_source.0.unsigned_abs() - 1)
|
||||
.div_euclid(il_source.0.unsigned_abs()),
|
||||
|
@ -197,14 +204,14 @@ impl TransferRegion {
|
|||
// How many times can we replicate?
|
||||
(1..)
|
||||
.position(|n| {
|
||||
n * N_s.0 * il_dest.0.unsigned_abs() - il_dest.0.unsigned_abs()
|
||||
n * number_used_src_wells.0 * il_dest.0.unsigned_abs() - il_dest.0.unsigned_abs()
|
||||
+ 1
|
||||
> d_dims.0
|
||||
})
|
||||
.unwrap() as u8,
|
||||
(1..)
|
||||
.position(|n| {
|
||||
n * N_s.1 * il_dest.1.unsigned_abs() - il_dest.1.unsigned_abs()
|
||||
n * number_used_src_wells.1 * il_dest.1.unsigned_abs() - il_dest.1.unsigned_abs()
|
||||
+ 1
|
||||
> d_dims.1
|
||||
})
|
||||
|
@ -222,26 +229,26 @@ impl TransferRegion {
|
|||
.into_iter()
|
||||
.filter(|(x, _)| {
|
||||
x.checked_sub(d_ul.0).unwrap()
|
||||
% (N_s.0 * il_dest.0.unsigned_abs()) // Counter along x
|
||||
% (number_used_src_wells.0 * il_dest.0.unsigned_abs()) // Counter along x
|
||||
== (il_dest.0.unsigned_abs() *i)
|
||||
% (N_s.0 * il_dest.0.unsigned_abs())
|
||||
% (number_used_src_wells.0 * il_dest.0.unsigned_abs())
|
||||
})
|
||||
.filter(|(_, y)| {
|
||||
y.checked_sub(d_ul.1).unwrap()
|
||||
% (N_s.1 * il_dest.1.unsigned_abs()) // Counter along u
|
||||
% (number_used_src_wells.1 * il_dest.1.unsigned_abs()) // Counter along u
|
||||
== (il_dest.1.unsigned_abs() *j)
|
||||
% (N_s.1 * il_dest.1.unsigned_abs())
|
||||
% (number_used_src_wells.1 * il_dest.1.unsigned_abs())
|
||||
})
|
||||
.filter(|(x, y)| {
|
||||
// How many times have we replicated? < How many are we allowed
|
||||
// to replicate?
|
||||
x.checked_sub(d_ul.0)
|
||||
.unwrap()
|
||||
.div_euclid(N_s.0 * il_dest.0.unsigned_abs())
|
||||
.div_euclid(number_used_src_wells.0 * il_dest.0.unsigned_abs())
|
||||
< count.0
|
||||
&& y.checked_sub(d_ul.1)
|
||||
.unwrap()
|
||||
.div_euclid(N_s.1 * il_dest.1.unsigned_abs())
|
||||
.div_euclid(number_used_src_wells.1 * il_dest.1.unsigned_abs())
|
||||
< count.1
|
||||
})
|
||||
.collect(),
|
|
@ -0,0 +1,70 @@
|
|||
pub fn letters_to_num(letters: &str) -> Option<u8> {
|
||||
let mut num: u8 = 0;
|
||||
for (i, letter) in letters.to_ascii_uppercase().chars().rev().enumerate() {
|
||||
log::debug!("{}, {}", i, letter);
|
||||
let n = letter as u8;
|
||||
if !(65..=90).contains(&n) {
|
||||
return None;
|
||||
}
|
||||
num = num.checked_add((26_i32.pow(i as u32) * (n as i32 - 64)).try_into().ok()?)?;
|
||||
}
|
||||
Some(num)
|
||||
}
|
||||
pub fn num_to_letters(num: u8) -> Option<String> {
|
||||
if num == 0 {
|
||||
return None;
|
||||
} // Otherwise, we will not return none!
|
||||
// As another note, we can't represent higher than "IV" anyway;
|
||||
// thus there's no reason for a loop (26^n with n>1 will NOT occur).
|
||||
let mut text = "".to_string();
|
||||
let mut digit1 = num.div_euclid(26u8);
|
||||
let mut digit2 = num.rem_euclid(26u8);
|
||||
if digit1 > 0 && digit2 == 0u8 {
|
||||
digit1 -= 1;
|
||||
digit2 = 26;
|
||||
}
|
||||
if digit1 != 0 {
|
||||
text.push((64 + digit1) as char)
|
||||
}
|
||||
text.push((64 + digit2) as char);
|
||||
|
||||
Some(text.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{letters_to_num, num_to_letters};
|
||||
|
||||
#[test]
|
||||
fn test_letters_to_num() {
|
||||
assert_eq!(letters_to_num("D"), Some(4));
|
||||
assert_eq!(letters_to_num("d"), None);
|
||||
assert_eq!(letters_to_num("AD"), Some(26 + 4));
|
||||
assert_eq!(letters_to_num("CG"), Some(3 * 26 + 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_to_letters() {
|
||||
println!("27 is {:?}", num_to_letters(27));
|
||||
assert_eq!(num_to_letters(1), Some("A".to_string()));
|
||||
assert_eq!(num_to_letters(26), Some("Z".to_string()));
|
||||
assert_eq!(num_to_letters(27), Some("AA".to_string()));
|
||||
assert_eq!(num_to_letters(111), Some("DG".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_l2n_and_n2l() {
|
||||
assert_eq!(
|
||||
num_to_letters(letters_to_num("A").unwrap()),
|
||||
Some("A".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
num_to_letters(letters_to_num("BJ").unwrap()),
|
||||
Some("BJ".to_string())
|
||||
);
|
||||
for i in 1..=255 {
|
||||
assert_eq!(letters_to_num(&num_to_letters(i).unwrap()), Some(i));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/dist
|
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "plate-tool-web"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
plate-tool-lib = { path = "../plate-tool-lib" }
|
||||
yew = { version = "0.20.0", features = ["csr"] }
|
||||
yewdux = "0.9"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["FormData", "HtmlFormElement",
|
||||
"HtmlDialogElement", "Blob", "Url", "Window",
|
||||
"HtmlAnchorElement", "ReadableStream", "HtmlSelectElement", "HtmlOptionElement", "HtmlButtonElement",
|
||||
"FileReader", "HtmlCollection"] }
|
||||
js-sys = "0.3"
|
||||
log = "0.4"
|
||||
wasm-logger = "0.2"
|
||||
regex = "1"
|
||||
lazy_static = "1.4"
|
||||
uuid = { version = "1.6", features = ["v7", "fast-rng", "macro-diagnostics", "js", "serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
csv = "1.2"
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
|
@ -35,3 +35,16 @@ dialog > form[method="dialog"] {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close_button {
|
||||
color: red;
|
||||
position: absolute;
|
||||
top: 5%;
|
||||
right: 2%;
|
||||
background: rgba(0%,0%,0%,10%);
|
||||
|
||||
&:hover {
|
||||
color: rgb(0%,100%,100%);
|
||||
background: rgba(0%,0%,0%,80%);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
@use "sass:color";
|
||||
@use "../variables" as *;
|
||||
|
||||
div.plate_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
|
||||
border: 2px solid $color-dark;
|
||||
grid-column: right / right;
|
||||
grid-row: upper / 3;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 1%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&>div {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto;
|
||||
grid-template-rows: auto auto;
|
||||
|
||||
&>h2:nth-of-type(1) {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
&>h2:nth-of-type(2) {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
writing-mode: vertical-rl;
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
|
||||
&>div {
|
||||
grid-column:2;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,122 +1,23 @@
|
|||
#![allow(non_snake_case)]
|
||||
use std::collections::HashSet;
|
||||
|
||||
use js_sys::Array;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use wasm_bindgen::{prelude::*, JsCast, JsValue};
|
||||
use wasm_bindgen::{prelude::*, JsCast};
|
||||
use web_sys::{
|
||||
Blob, FileReader, HtmlAnchorElement, HtmlButtonElement, HtmlDialogElement, HtmlFormElement,
|
||||
HtmlInputElement, HtmlOptionElement, HtmlSelectElement, Url,
|
||||
FileReader, HtmlButtonElement, HtmlDialogElement, HtmlElement, HtmlFormElement,
|
||||
HtmlInputElement, HtmlOptionElement, HtmlSelectElement,
|
||||
};
|
||||
use yew::prelude::*;
|
||||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::states::{CurrentTransfer, MainState};
|
||||
use crate::components::transfer_menu::letters_to_num;
|
||||
use crate::components::states::MainState;
|
||||
|
||||
use crate::data::transfer::Transfer;
|
||||
use crate::data::transfer_region::{Region, TransferRegion};
|
||||
use plate_tool_lib::transfer::Transfer;
|
||||
use plate_tool_lib::transfer_region::{Region, TransferRegion};
|
||||
|
||||
use crate::data::csv::{state_to_csv, TransferRecord};
|
||||
use plate_tool_lib::csv::{auto, string_well_to_pt, TransferRecord};
|
||||
|
||||
type NoParamsCallback = Box<dyn Fn(()) -> ()>;
|
||||
use super::main_window_callbacks::create_close_button;
|
||||
|
||||
pub fn toggle_in_transfer_hashes_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
) -> Callback<web_sys::MouseEvent> {
|
||||
let main_dispatch = main_dispatch.clone();
|
||||
Callback::from(move |_| {
|
||||
main_dispatch.reduce_mut(|state| {
|
||||
state.preferences.in_transfer_hashes ^= true;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_plate_dialog_callback(
|
||||
new_plate_dialog_is_open: UseStateHandle<bool>,
|
||||
) -> NoParamsCallback {
|
||||
let new_plate_dialog_is_open = new_plate_dialog_is_open.clone();
|
||||
Box::new(move |_| {
|
||||
new_plate_dialog_is_open.set(false);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn open_new_plate_dialog_callback(
|
||||
new_plate_dialog_is_open: UseStateHandle<bool>,
|
||||
) -> NoParamsCallback {
|
||||
let new_plate_dialog_is_open = new_plate_dialog_is_open.clone();
|
||||
Box::new(move |_| {
|
||||
new_plate_dialog_is_open.set(true);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_button_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
ct_dispatch: Dispatch<CurrentTransfer>,
|
||||
) -> Callback<web_sys::MouseEvent> {
|
||||
Callback::from(move |_| {
|
||||
let window = web_sys::window().unwrap();
|
||||
let confirm =
|
||||
window.confirm_with_message("This will reset all plates and transfers. Proceed?");
|
||||
if let Ok(confirm) = confirm {
|
||||
if confirm {
|
||||
main_dispatch.set(MainState::default());
|
||||
ct_dispatch.set(CurrentTransfer::default());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn save_str(data: &str, name: &str) {
|
||||
let blob =
|
||||
Blob::new_with_str_sequence(&Array::from_iter(std::iter::once(JsValue::from_str(data))));
|
||||
if let Ok(blob) = blob {
|
||||
let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?");
|
||||
// Beneath is the cool hack to download files
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let anchor = document
|
||||
.create_element("a")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlAnchorElement>()
|
||||
.unwrap();
|
||||
anchor.set_download(name);
|
||||
anchor.set_href(&url);
|
||||
anchor.click();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export_csv_button_callback(main_state: std::rc::Rc<MainState>) -> Callback<MouseEvent> {
|
||||
Callback::from(move |_| {
|
||||
if main_state.transfers.is_empty() {
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.alert_with_message("No transfers to export.")
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
web_sys::window().unwrap().alert_with_message("CSV export is currently not importable. Export as JSON if you'd like to back up your work!").unwrap();
|
||||
if let Ok(csv) = state_to_csv(&main_state) {
|
||||
save_str(&csv, "transfers.csv");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn export_json_button_callback(main_state: std::rc::Rc<MainState>) -> Callback<MouseEvent> {
|
||||
Callback::from(move |_| {
|
||||
if let Ok(json) = serde_json::to_string(&main_state) {
|
||||
save_str(&json, "plate-tool-state.json");
|
||||
} else {
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.alert_with_message("Failed to export.")
|
||||
.unwrap();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn input_json_input_callback(
|
||||
pub fn import_transfer_csv_input_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
modal: HtmlDialogElement,
|
||||
) -> Closure<dyn FnMut(Event)> {
|
||||
|
@ -133,16 +34,7 @@ pub fn input_json_input_callback(
|
|||
let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut
|
||||
// trait
|
||||
let modal = modal.clone();
|
||||
let onload = Closure::<dyn FnMut(_)>::new(move |_: Event| {
|
||||
if let Some(value) = &fr1.result().ok().and_then(|v| v.as_string()) {
|
||||
let ms = serde_json::from_str::<MainState>(value);
|
||||
match ms {
|
||||
Ok(ms) => main_dispatch.set(ms),
|
||||
Err(e) => log::debug!("{:?}", e),
|
||||
};
|
||||
modal.close();
|
||||
}
|
||||
});
|
||||
let onload = import_transfer_csv_onload_callback(main_dispatch, fr1, modal);
|
||||
fr.set_onload(Some(onload.as_ref().unchecked_ref()));
|
||||
onload.forget(); // Magic (don't touch)
|
||||
}
|
||||
|
@ -151,7 +43,7 @@ pub fn input_json_input_callback(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn import_json_button_callback(main_dispatch: Dispatch<MainState>) -> Callback<MouseEvent> {
|
||||
pub fn import_transfer_csv_callback(main_dispatch: Dispatch<MainState>) -> Callback<MouseEvent> {
|
||||
Callback::from(move |_| {
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
|
@ -169,7 +61,10 @@ pub fn import_json_button_callback(main_dispatch: Dispatch<MainState>) -> Callba
|
|||
})
|
||||
};
|
||||
modal.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
|
||||
|
||||
let close_button = create_close_button(&onclose_callback);
|
||||
onclose_callback.forget();
|
||||
modal.append_child(&close_button).unwrap();
|
||||
|
||||
let form = document
|
||||
.create_element("form")
|
||||
|
@ -182,13 +77,13 @@ pub fn import_json_button_callback(main_dispatch: Dispatch<MainState>) -> Callba
|
|||
.dyn_into::<HtmlInputElement>()
|
||||
.unwrap();
|
||||
input.set_type("file");
|
||||
input.set_accept(".json");
|
||||
input.set_accept(".csv");
|
||||
form.append_child(&input).unwrap();
|
||||
|
||||
let input_callback = {
|
||||
let main_dispatch = main_dispatch.clone();
|
||||
let modal = modal.clone();
|
||||
input_json_input_callback(main_dispatch, modal)
|
||||
import_transfer_csv_input_callback(main_dispatch, modal)
|
||||
};
|
||||
input.set_onchange(Some(input_callback.as_ref().unchecked_ref()));
|
||||
input_callback.forget(); // Magic straight from the docs, don't touch :(
|
||||
|
@ -199,83 +94,6 @@ pub fn import_json_button_callback(main_dispatch: Dispatch<MainState>) -> Callba
|
|||
})
|
||||
}
|
||||
|
||||
pub fn import_transfer_csv_submit_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
from_source: HtmlSelectElement,
|
||||
to_source: HtmlSelectElement,
|
||||
from_dest: HtmlSelectElement,
|
||||
to_dest: HtmlSelectElement,
|
||||
records: Vec<TransferRecord>,
|
||||
) -> Closure<dyn FnMut(Event)> {
|
||||
Closure::<dyn FnMut(_)>::new(move |_: Event| {
|
||||
let from_source = from_source.value();
|
||||
let to_source = to_source.value();
|
||||
let from_dest = from_dest.value();
|
||||
let to_dest = to_dest.value();
|
||||
|
||||
lazy_static! {
|
||||
static ref REGEX: Regex = Regex::new(r"([A-Z]+)(\d+)").unwrap();
|
||||
}
|
||||
let records: Vec<((u8, u8), (u8, u8))> = records
|
||||
.iter()
|
||||
.filter(|record| record.source_plate == from_source)
|
||||
.filter(|record| record.destination_plate == from_dest)
|
||||
.map(|record| {
|
||||
let c1 = REGEX.captures(&record.source_well).unwrap();
|
||||
let c2 = REGEX.captures(&record.destination_well).unwrap();
|
||||
log::debug!("{} {}", &record.source_well, &record.destination_well);
|
||||
log::debug!("{},{} {},{}", &c1[1], &c1[2], &c2[1], &c2[2]);
|
||||
|
||||
(
|
||||
(
|
||||
letters_to_num(&c1[1]).unwrap(),
|
||||
c1[2].parse::<u8>().unwrap(),
|
||||
),
|
||||
(
|
||||
letters_to_num(&c2[1]).unwrap(),
|
||||
c2[2].parse::<u8>().unwrap(),
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let spi = main_dispatch
|
||||
.get()
|
||||
.source_plates
|
||||
.iter()
|
||||
.find(|src| src.name == to_source)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let dpi = main_dispatch
|
||||
.get()
|
||||
.destination_plates
|
||||
.iter()
|
||||
.find(|dest| dest.name == to_dest)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let custom_region = Region::new_custom(&records);
|
||||
let transfer_region = TransferRegion {
|
||||
source_region: custom_region.clone(),
|
||||
dest_region: custom_region,
|
||||
interleave_source: (1, 1),
|
||||
interleave_dest: (1, 1),
|
||||
source_plate: spi.plate,
|
||||
dest_plate: dpi.plate,
|
||||
};
|
||||
|
||||
let transfer = Transfer::new(spi, dpi, transfer_region, "Custom Transfer".to_string());
|
||||
main_dispatch.reduce_mut(|state| {
|
||||
state.transfers.push(transfer);
|
||||
state.selected_transfer = state
|
||||
.transfers
|
||||
.last()
|
||||
.expect("An element should have just been added")
|
||||
.get_uuid();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pub fn import_transfer_csv_onload_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
file_reader: FileReader,
|
||||
|
@ -283,19 +101,7 @@ pub fn import_transfer_csv_onload_callback(
|
|||
) -> Closure<dyn FnMut(Event)> {
|
||||
Closure::<dyn FnMut(_)>::new(move |_: Event| {
|
||||
if let Some(value) = &file_reader.result().ok().and_then(|v| v.as_string()) {
|
||||
let mut rdr = csv::Reader::from_reader(value.as_bytes());
|
||||
let mut records = Vec::new();
|
||||
for record in rdr.deserialize::<crate::data::csv::TransferRecord>() {
|
||||
match record {
|
||||
Ok(r) => {
|
||||
//log::debug!("{:?}", r);
|
||||
records.push(r);
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
let records = plate_tool_lib::csv::read_csv(value);
|
||||
|
||||
let mut sources: HashSet<String> = HashSet::new();
|
||||
let mut destinations: HashSet<String> = HashSet::new();
|
||||
|
@ -306,6 +112,17 @@ pub fn import_transfer_csv_onload_callback(
|
|||
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
|
||||
let auto_button = document
|
||||
.create_element("button")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlElement>()
|
||||
.unwrap();
|
||||
auto_button.set_inner_text("Auto");
|
||||
let auto_button_callback = auto_callback(main_dispatch.clone(), &records);
|
||||
auto_button.set_onclick(Some(auto_button_callback.as_ref().unchecked_ref()));
|
||||
auto_button_callback.forget();
|
||||
|
||||
let form = document
|
||||
.create_element("form")
|
||||
.unwrap()
|
||||
|
@ -402,32 +219,87 @@ pub fn import_transfer_csv_onload_callback(
|
|||
form.append_child(&to_dest).unwrap();
|
||||
modal.append_child(&submit).unwrap();
|
||||
modal.append_child(&form).unwrap();
|
||||
modal.append_child(&auto_button).unwrap();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn import_transfer_csv_input_callback(
|
||||
pub fn import_transfer_csv_submit_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
modal: HtmlDialogElement,
|
||||
from_source: HtmlSelectElement,
|
||||
to_source: HtmlSelectElement,
|
||||
from_dest: HtmlSelectElement,
|
||||
to_dest: HtmlSelectElement,
|
||||
records: Vec<TransferRecord>,
|
||||
) -> Closure<dyn FnMut(Event)> {
|
||||
Closure::<dyn FnMut(_)>::new(move |e: Event| {
|
||||
if let Some(input) = e.current_target() {
|
||||
let input = input
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.expect("We know this is an input.");
|
||||
if let Some(files) = input.files() {
|
||||
if let Some(file) = files.get(0) {
|
||||
let fr = web_sys::FileReader::new().unwrap();
|
||||
fr.read_as_text(&file).unwrap();
|
||||
let fr1 = fr.clone(); // Clone to avoid outliving closure
|
||||
let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut
|
||||
// trait
|
||||
let modal = modal.clone();
|
||||
let onload = import_transfer_csv_onload_callback(main_dispatch, fr1, modal);
|
||||
fr.set_onload(Some(onload.as_ref().unchecked_ref()));
|
||||
onload.forget(); // Magic (don't touch)
|
||||
}
|
||||
}
|
||||
}
|
||||
Closure::<dyn FnMut(_)>::new(move |_: Event| {
|
||||
let from_source = from_source.value();
|
||||
let to_source = to_source.value();
|
||||
let from_dest = from_dest.value();
|
||||
let to_dest = to_dest.value();
|
||||
|
||||
let records: Vec<((u8, u8), (u8, u8))> = records
|
||||
.iter()
|
||||
.filter(|record| record.source_plate == from_source)
|
||||
.filter(|record| record.destination_plate == from_dest)
|
||||
.map(|record| {
|
||||
(
|
||||
string_well_to_pt(&record.source_well).unwrap(),
|
||||
string_well_to_pt(&record.destination_well).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let spi = main_dispatch
|
||||
.get()
|
||||
.source_plates
|
||||
.iter()
|
||||
.find(|src| src.name == to_source)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let dpi = main_dispatch
|
||||
.get()
|
||||
.destination_plates
|
||||
.iter()
|
||||
.find(|dest| dest.name == to_dest)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let custom_region = Region::new_custom(&records);
|
||||
let transfer_region = TransferRegion {
|
||||
source_region: custom_region.clone(),
|
||||
dest_region: custom_region,
|
||||
interleave_source: (1, 1),
|
||||
interleave_dest: (1, 1),
|
||||
source_plate: spi.plate,
|
||||
dest_plate: dpi.plate,
|
||||
};
|
||||
|
||||
let transfer = Transfer::new(spi, dpi, transfer_region, "Custom Transfer".to_string());
|
||||
main_dispatch.reduce_mut(|state| {
|
||||
state.transfers.push(transfer);
|
||||
state.selected_transfer = state
|
||||
.transfers
|
||||
.last()
|
||||
.expect("An element should have just been added")
|
||||
.get_uuid();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
fn auto_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
records: &[TransferRecord],
|
||||
) -> Closure<dyn FnMut(Event)> {
|
||||
let records = Vec::from(records);
|
||||
Closure::<dyn FnMut(_)>::new(move |_| {
|
||||
let res = auto(&records);
|
||||
main_dispatch.reduce_mut(|state| {
|
||||
state.source_plates.extend(res.sources.into_iter());
|
||||
state
|
||||
.destination_plates
|
||||
.extend(res.destinations.into_iter());
|
||||
state.transfers.extend(res.transfers.into_iter());
|
||||
});
|
||||
})
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
|
||||
use js_sys::Array;
|
||||
|
||||
|
||||
use wasm_bindgen::{prelude::*, JsCast, JsValue};
|
||||
use web_sys::{
|
||||
Blob, HtmlAnchorElement, HtmlDialogElement, HtmlElement,
|
||||
HtmlFormElement, HtmlInputElement, Url,
|
||||
};
|
||||
use yew::prelude::*;
|
||||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::states::{CurrentTransfer, MainState};
|
||||
|
||||
use crate::state_to_csv;
|
||||
|
||||
type NoParamsCallback = Box<dyn Fn(())>;
|
||||
|
||||
pub fn create_close_button(close: &Closure<dyn FnMut(Event)>) -> HtmlElement {
|
||||
let document = web_sys::window().unwrap().document().unwrap();
|
||||
let close_button = document
|
||||
.create_element("button")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlElement>()
|
||||
.unwrap();
|
||||
close_button.set_text_content(Some("✖"));
|
||||
close_button.set_class_name("close_button");
|
||||
close_button.set_onclick(Some(close.as_ref().unchecked_ref()));
|
||||
|
||||
close_button
|
||||
}
|
||||
|
||||
pub fn toggle_in_transfer_hashes_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
) -> Callback<web_sys::MouseEvent> {
|
||||
let main_dispatch = main_dispatch.clone();
|
||||
Callback::from(move |_| {
|
||||
main_dispatch.reduce_mut(|state| {
|
||||
state.preferences.in_transfer_hashes ^= true;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_plate_dialog_callback(
|
||||
new_plate_dialog_is_open: UseStateHandle<bool>,
|
||||
) -> NoParamsCallback {
|
||||
let new_plate_dialog_is_open = new_plate_dialog_is_open.clone();
|
||||
Box::new(move |_| {
|
||||
new_plate_dialog_is_open.set(false);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn open_new_plate_dialog_callback(
|
||||
new_plate_dialog_is_open: UseStateHandle<bool>,
|
||||
) -> NoParamsCallback {
|
||||
let new_plate_dialog_is_open = new_plate_dialog_is_open.clone();
|
||||
Box::new(move |_| {
|
||||
new_plate_dialog_is_open.set(true);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_button_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
ct_dispatch: Dispatch<CurrentTransfer>,
|
||||
) -> Callback<web_sys::MouseEvent> {
|
||||
Callback::from(move |_| {
|
||||
let window = web_sys::window().unwrap();
|
||||
let confirm =
|
||||
window.confirm_with_message("This will reset all plates and transfers. Proceed?");
|
||||
if let Ok(confirm) = confirm {
|
||||
if confirm {
|
||||
main_dispatch.set(MainState::default());
|
||||
ct_dispatch.set(CurrentTransfer::default());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn save_str(data: &str, name: &str) {
|
||||
let blob =
|
||||
Blob::new_with_str_sequence(&Array::from_iter(std::iter::once(JsValue::from_str(data))));
|
||||
if let Ok(blob) = blob {
|
||||
let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?");
|
||||
// Beneath is the cool hack to download files
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let anchor = document
|
||||
.create_element("a")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlAnchorElement>()
|
||||
.unwrap();
|
||||
anchor.set_download(name);
|
||||
anchor.set_href(&url);
|
||||
anchor.click();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export_csv_button_callback(main_state: std::rc::Rc<MainState>) -> Callback<MouseEvent> {
|
||||
Callback::from(move |_| {
|
||||
if main_state.transfers.is_empty() {
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.alert_with_message("No transfers to export.")
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
web_sys::window().unwrap().alert_with_message("CSV export is currently not importable. Export as JSON if you'd like to back up your work!").unwrap();
|
||||
if let Ok(csv) = state_to_csv(&main_state) {
|
||||
save_str(&csv, "transfers.csv");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn export_json_button_callback(main_state: std::rc::Rc<MainState>) -> Callback<MouseEvent> {
|
||||
Callback::from(move |_| {
|
||||
if let Ok(json) = serde_json::to_string(&main_state) {
|
||||
save_str(&json, "plate-tool-state.json");
|
||||
} else {
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.alert_with_message("Failed to export.")
|
||||
.unwrap();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn input_json_input_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
modal: HtmlDialogElement,
|
||||
) -> Closure<dyn FnMut(Event)> {
|
||||
Closure::<dyn FnMut(_)>::new(move |e: Event| {
|
||||
if let Some(input) = e.current_target() {
|
||||
let input = input
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.expect("We know this is an input.");
|
||||
if let Some(files) = input.files() {
|
||||
if let Some(file) = files.get(0) {
|
||||
let fr = web_sys::FileReader::new().unwrap();
|
||||
fr.read_as_text(&file).unwrap();
|
||||
let fr1 = fr.clone(); // Clone to avoid outliving closure
|
||||
let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut
|
||||
// trait
|
||||
let modal = modal.clone();
|
||||
let onload = Closure::<dyn FnMut(_)>::new(move |_: Event| {
|
||||
if let Some(value) = &fr1.result().ok().and_then(|v| v.as_string()) {
|
||||
let ms = serde_json::from_str::<MainState>(value);
|
||||
match ms {
|
||||
Ok(ms) => main_dispatch.set(ms),
|
||||
Err(e) => log::debug!("{:?}", e),
|
||||
};
|
||||
modal.close();
|
||||
}
|
||||
});
|
||||
fr.set_onload(Some(onload.as_ref().unchecked_ref()));
|
||||
onload.forget(); // Magic (don't touch)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn import_json_button_callback(main_dispatch: Dispatch<MainState>) -> Callback<MouseEvent> {
|
||||
Callback::from(move |_| {
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
let modal = document
|
||||
.create_element("dialog")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlDialogElement>()
|
||||
.unwrap();
|
||||
modal.set_text_content(Some("Import File:"));
|
||||
let onclose_callback = {
|
||||
let modal = modal.clone();
|
||||
Closure::<dyn FnMut(_)>::new(move |_: Event| {
|
||||
modal.remove();
|
||||
})
|
||||
};
|
||||
modal.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
|
||||
|
||||
let close_button = create_close_button(&onclose_callback);
|
||||
onclose_callback.forget();
|
||||
modal.append_child(&close_button).unwrap();
|
||||
|
||||
let form = document
|
||||
.create_element("form")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlFormElement>()
|
||||
.unwrap();
|
||||
let input = document
|
||||
.create_element("input")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.unwrap();
|
||||
input.set_type("file");
|
||||
input.set_accept(".json");
|
||||
form.append_child(&input).unwrap();
|
||||
|
||||
let input_callback = {
|
||||
let main_dispatch = main_dispatch.clone();
|
||||
let modal = modal.clone();
|
||||
input_json_input_callback(main_dispatch, modal)
|
||||
};
|
||||
input.set_onchange(Some(input_callback.as_ref().unchecked_ref()));
|
||||
input_callback.forget(); // Magic straight from the docs, don't touch :(
|
||||
|
||||
modal.append_child(&form).unwrap();
|
||||
body.append_child(&modal).unwrap();
|
||||
modal.show_modal().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
pub use super::import_csv_callbacks::import_transfer_csv_callback;
|
||||
|
||||
pub use super::import_csv_callbacks::import_transfer_csv_submit_callback;
|
||||
|
||||
pub use super::import_csv_callbacks::import_transfer_csv_onload_callback;
|
||||
|
||||
pub use super::import_csv_callbacks::import_transfer_csv_input_callback;
|
|
@ -2,3 +2,4 @@ pub mod main_window_callbacks;
|
|||
pub mod new_plate_dialog_callbacks;
|
||||
pub mod transfer_menu_callbacks;
|
||||
pub mod tree_callbacks;
|
||||
mod import_csv_callbacks;
|
|
@ -5,8 +5,8 @@ use wasm_bindgen::JsCast;
|
|||
use web_sys::{EventTarget, FormData, HtmlFormElement};
|
||||
|
||||
use crate::components::states::MainState;
|
||||
use crate::data::plate::*;
|
||||
use crate::data::plate_instances::PlateInstance;
|
||||
use plate_tool_lib::plate::*;
|
||||
use plate_tool_lib::plate_instances::PlateInstance;
|
||||
|
||||
pub fn new_plate_callback(
|
||||
dispatch: Dispatch<MainState>,
|
|
@ -6,7 +6,7 @@ use yew::prelude::*;
|
|||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::transfer_menu::RegionDisplay;
|
||||
use crate::data::{transfer::Transfer, transfer_region::Region};
|
||||
use plate_tool_lib::{transfer::Transfer, transfer_region::Region};
|
||||
|
||||
use crate::components::states::{CurrentTransfer, MainState};
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
use std::rc::Rc;
|
||||
use uuid::Uuid;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{EventTarget, HtmlElement};
|
||||
use web_sys::{EventTarget, HtmlElement, HtmlInputElement, HtmlSelectElement, HtmlOptionElement};
|
||||
use yew::prelude::*;
|
||||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::states::{CurrentTransfer, MainState};
|
||||
use crate::data::transfer_region::Region;
|
||||
use plate_tool_lib::{transfer_region::Region, plate::PlateFormat};
|
||||
|
||||
type NoParamsCallback = Box<dyn Fn(()) -> ()>;
|
||||
type NoParamsCallback = Box<dyn Fn(())>;
|
||||
|
||||
pub fn open_plate_info_callback(
|
||||
plate_menu_id: UseStateHandle<Option<Uuid>>,
|
||||
|
@ -45,6 +45,38 @@ pub fn plate_info_delete_callback(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn rename_onchange(id: Uuid, main_dispatch: Dispatch<MainState>) -> Callback<Event> {
|
||||
Callback::from(move |e: Event| {
|
||||
log::debug!("Changed name");
|
||||
let input = e
|
||||
.target()
|
||||
.expect("Event must have target")
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.unwrap();
|
||||
main_dispatch.reduce_mut(|state| state.rename_plate(id, &input.value()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_onchange(id: Uuid, main_dispatch: Dispatch<MainState>) -> Callback<Event> {
|
||||
Callback::from(move |e: Event| {
|
||||
log::debug!("Changing plate format");
|
||||
let new_format: Option<PlateFormat> = e.target()
|
||||
.expect("Event must have target")
|
||||
.dyn_into::<HtmlSelectElement>()
|
||||
.unwrap()
|
||||
.selected_options()
|
||||
.get_with_index(0)
|
||||
.map(|el| {
|
||||
el.dyn_into::<HtmlOptionElement>().unwrap()
|
||||
})
|
||||
.map(|opt_el| opt_el.value())
|
||||
.and_then(|value| PlateFormat::try_from(value.as_str()).ok());
|
||||
if let Some(format) = new_format {
|
||||
main_dispatch.reduce_mut(|state| state.change_format(id, &format));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn source_plate_select_callback(
|
||||
main_dispatch: Dispatch<MainState>,
|
||||
ct_dispatch: Dispatch<CurrentTransfer>,
|
|
@ -1,8 +1,4 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use js_sys::Array;
|
||||
use wasm_bindgen::{prelude::*, JsCast, JsValue};
|
||||
use web_sys::{Blob, HtmlAnchorElement, HtmlDialogElement, HtmlFormElement, HtmlInputElement, Url};
|
||||
use yew::prelude::*;
|
||||
use yewdux::prelude::*;
|
||||
|
||||
|
@ -12,7 +8,7 @@ use crate::components::states::{CurrentTransfer, MainState};
|
|||
use crate::components::transfer_menu::TransferMenu;
|
||||
use crate::components::tree::Tree;
|
||||
|
||||
use crate::data::plate_instances::PlateInstance;
|
||||
use plate_tool_lib::plate_instances::PlateInstance;
|
||||
|
||||
use crate::components::callbacks::main_window_callbacks;
|
||||
|
||||
|
@ -72,53 +68,8 @@ pub fn MainWindow() -> Html {
|
|||
main_window_callbacks::import_json_button_callback(main_dispatch)
|
||||
};
|
||||
|
||||
let import_transfer_csv_callback = {
|
||||
Callback::from(move |_| {
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
let modal = document
|
||||
.create_element("dialog")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlDialogElement>()
|
||||
.unwrap();
|
||||
modal.set_text_content(Some("Import File:"));
|
||||
let onclose_callback = {
|
||||
let modal = modal.clone();
|
||||
Closure::<dyn FnMut(_)>::new(move |_: Event| {
|
||||
modal.remove();
|
||||
})
|
||||
};
|
||||
modal.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
|
||||
onclose_callback.forget();
|
||||
|
||||
let form = document
|
||||
.create_element("form")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlFormElement>()
|
||||
.unwrap();
|
||||
let input = document
|
||||
.create_element("input")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.unwrap();
|
||||
input.set_type("file");
|
||||
input.set_accept(".csv");
|
||||
form.append_child(&input).unwrap();
|
||||
|
||||
let input_callback = {
|
||||
let main_dispatch = main_dispatch.clone();
|
||||
let modal = modal.clone();
|
||||
main_window_callbacks::import_transfer_csv_input_callback(main_dispatch, modal)
|
||||
};
|
||||
input.set_onchange(Some(input_callback.as_ref().unchecked_ref()));
|
||||
input_callback.forget(); // Magic straight from the docs, don't touch :(
|
||||
|
||||
modal.append_child(&form).unwrap();
|
||||
body.append_child(&modal).unwrap();
|
||||
modal.show_modal().unwrap();
|
||||
})
|
||||
};
|
||||
let import_transfer_csv_callback =
|
||||
main_window_callbacks::import_transfer_csv_callback(main_dispatch.clone());
|
||||
|
||||
html! {
|
||||
<>
|
||||
|
@ -163,22 +114,3 @@ pub fn MainWindow() -> Html {
|
|||
</>
|
||||
}
|
||||
}
|
||||
|
||||
fn save_str(data: &str, name: &str) {
|
||||
let blob =
|
||||
Blob::new_with_str_sequence(&Array::from_iter(std::iter::once(JsValue::from_str(data))));
|
||||
if let Ok(blob) = blob {
|
||||
let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?");
|
||||
// Beneath is the cool hack to download files
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let anchor = document
|
||||
.create_element("a")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlAnchorElement>()
|
||||
.unwrap();
|
||||
anchor.set_download(name);
|
||||
anchor.set_href(&url);
|
||||
anchor.click();
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ use yewdux::prelude::*;
|
|||
use web_sys::HtmlDialogElement;
|
||||
|
||||
use crate::components::states::MainState;
|
||||
use crate::data::plate_instances::PlateInstance;
|
||||
|
||||
use crate::components::callbacks::new_plate_dialog_callbacks;
|
||||
|
||||
|
@ -69,10 +68,3 @@ pub fn NewPlateDialog(props: &NewPlateDialogProps) -> Html {
|
|||
</dialog>
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlateInstance> for String {
|
||||
fn from(value: &PlateInstance) -> Self {
|
||||
// Could have other formatting here
|
||||
format!("{}, {}", value.name, value.plate.plate_format)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pub mod plate_container;
|
||||
mod util;
|
||||
mod plate_callbacks;
|
||||
mod plate_data;
|
||||
mod plate;
|
|
@ -5,66 +5,70 @@ use yew::prelude::*;
|
|||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::states::{CurrentTransfer, MainState};
|
||||
use crate::data::plate_instances::PlateInstance;
|
||||
use crate::data::transfer::Transfer;
|
||||
use crate::data::transfer_region::Region;
|
||||
use plate_tool_lib::plate::PlateType;
|
||||
use plate_tool_lib::transfer::Transfer;
|
||||
use plate_tool_lib::transfer_region::Region;
|
||||
|
||||
// Color Palette for the Source Plates, can be changed here
|
||||
use crate::components::plates::util::Palettes;
|
||||
const PALETTE: super::util::ColorPalette = Palettes::RAINBOW;
|
||||
|
||||
use super::super::transfer_menu::{num_to_letters, RegionDisplay};
|
||||
use plate_tool_lib::util::num_to_letters;
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct SourcePlateProps {
|
||||
pub source_plate: PlateInstance,
|
||||
pub destination_plate: PlateInstance,
|
||||
pub cell_height: f64,
|
||||
}
|
||||
use super::plate_data::*;
|
||||
use super::plate_callbacks;
|
||||
|
||||
#[function_component]
|
||||
pub fn SourcePlate(props: &SourcePlateProps) -> Html {
|
||||
pub fn Plate(props: &PlateProps) -> Html {
|
||||
let (main_state, _) = use_store::<MainState>();
|
||||
let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
|
||||
let m_start_handle: UseStateHandle<Option<(u8, u8)>> = use_state_eq(|| None);
|
||||
let m_end_handle: UseStateHandle<Option<(u8, u8)>> = use_state_eq(|| None);
|
||||
let m_stat_handle: UseStateHandle<bool> = use_state_eq(|| false);
|
||||
let m_start_handle: MStartHandle = use_state_eq(|| None);
|
||||
let m_end_handle: MEndHandle = use_state_eq(|| None);
|
||||
let m_stat_handle: MStatHandle = use_state_eq(|| false);
|
||||
|
||||
if !(*m_stat_handle) {
|
||||
let (pt1, pt2) = match ct_state.transfer.transfer_region.source_region {
|
||||
let region = match props.ptype {
|
||||
PlateType::Source => ct_state.transfer.transfer_region.source_region.clone(),
|
||||
PlateType::Destination => ct_state.transfer.transfer_region.dest_region.clone(),
|
||||
};
|
||||
let (pt1, pt2) = match region {
|
||||
Region::Point((x, y)) => ((x, y), (x, y)),
|
||||
Region::Rect(c1, c2) => (c1, c2),
|
||||
Region::Custom(_) => ((0,0), (0,0)),
|
||||
Region::Custom(_) => ((0, 0), (0, 0)),
|
||||
};
|
||||
m_start_handle.set(Some(pt1));
|
||||
m_end_handle.set(Some(pt2));
|
||||
}
|
||||
|
||||
let transfer_map = {
|
||||
let ts = main_state
|
||||
.transfers
|
||||
.iter()
|
||||
.filter(|t| t.source_id == props.source_plate.get_uuid());
|
||||
let tooltip_map = {
|
||||
let transfers = main_state.transfers.iter().filter(|t| match props.ptype {
|
||||
PlateType::Source => t.source_id == props.source_plate.get_uuid(),
|
||||
PlateType::Destination => t.dest_id == props.destination_plate.get_uuid(),
|
||||
});
|
||||
let mut tooltip_map: HashMap<(u8, u8), Vec<&Transfer>> = HashMap::new();
|
||||
for t in ts {
|
||||
let sws = t.transfer_region.get_source_wells();
|
||||
for sw in sws {
|
||||
if let Some(val) = tooltip_map.get_mut(&sw) {
|
||||
val.push(t);
|
||||
for transfer in transfers {
|
||||
let wells = match props.ptype {
|
||||
PlateType::Source => transfer.transfer_region.get_source_wells(),
|
||||
PlateType::Destination => transfer.transfer_region.get_destination_wells(),
|
||||
};
|
||||
for well in wells {
|
||||
if let Some(val) = tooltip_map.get_mut(&well) {
|
||||
val.push(transfer);
|
||||
} else {
|
||||
tooltip_map.insert(sw, vec![t]);
|
||||
tooltip_map.insert(well, vec![transfer]);
|
||||
}
|
||||
}
|
||||
}
|
||||
tooltip_map
|
||||
};
|
||||
|
||||
let source_wells = ct_state.transfer.transfer_region.get_source_wells();
|
||||
let wells = match props.ptype {
|
||||
PlateType::Source => ct_state.transfer.transfer_region.get_source_wells(),
|
||||
PlateType::Destination => ct_state.transfer.transfer_region.get_destination_wells(),
|
||||
};
|
||||
|
||||
let ordered_ids: Vec<uuid::Uuid> = {
|
||||
let mut ids: Vec<uuid::Uuid> = main_state.transfers.clone().iter()
|
||||
.map(|x| x.id)
|
||||
.collect();
|
||||
let mut ids: Vec<uuid::Uuid> = main_state.transfers.clone().iter().map(|x| x.id).collect();
|
||||
ids.sort_unstable();
|
||||
ids
|
||||
};
|
||||
|
@ -73,37 +77,13 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
|
|||
let m_start_handle = m_start_handle.clone();
|
||||
let m_end_handle = m_end_handle.clone();
|
||||
let m_stat_handle = m_stat_handle.clone();
|
||||
|
||||
Callback::from(move |(i, j, t)| match t {
|
||||
MouseEventType::Mousedown => {
|
||||
m_start_handle.set(Some((i, j)));
|
||||
m_end_handle.set(Some((i, j)));
|
||||
m_stat_handle.set(true);
|
||||
}
|
||||
MouseEventType::Mouseenter => {
|
||||
if *m_stat_handle {
|
||||
m_end_handle.set(Some((i, j)));
|
||||
}
|
||||
}
|
||||
})
|
||||
plate_callbacks::mouse_callback(m_start_handle, m_end_handle, m_stat_handle)
|
||||
};
|
||||
|
||||
let mouseup_callback = {
|
||||
let m_start_handle = m_start_handle.clone();
|
||||
let m_end_handle = m_end_handle.clone();
|
||||
|
||||
Callback::from(move |_: MouseEvent| {
|
||||
m_stat_handle.set(false);
|
||||
if let Some(ul) = *m_start_handle {
|
||||
if let Some(br) = *m_end_handle {
|
||||
if let Ok(rd) = RegionDisplay::try_from((ul.0, ul.1, br.0, br.1)) {
|
||||
ct_dispatch.reduce_mut(|state| {
|
||||
state.transfer.transfer_region.source_region = Region::from(&rd);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
plate_callbacks::mouseup_callback(m_start_handle, m_end_handle, m_stat_handle, ct_dispatch)
|
||||
};
|
||||
|
||||
let mouseleave_callback = Callback::clone(&mouseup_callback);
|
||||
|
@ -112,8 +92,20 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
|
|||
let _ = js_sys::eval("copy_screenshot_src()");
|
||||
});
|
||||
|
||||
let width = match props.ptype {
|
||||
PlateType::Source => props.source_plate.plate.size().1,
|
||||
PlateType::Destination => props.destination_plate.plate.size().1,
|
||||
};
|
||||
let height = match props.ptype {
|
||||
PlateType::Source => props.source_plate.plate.size().0,
|
||||
PlateType::Destination => props.destination_plate.plate.size().0,
|
||||
};
|
||||
let pformat = match props.ptype {
|
||||
PlateType::Source => props.source_plate.plate.plate_format,
|
||||
PlateType::Destination => props.destination_plate.plate.plate_format,
|
||||
};
|
||||
let column_header = {
|
||||
let headers = (1..=props.source_plate.plate.size().1)
|
||||
let headers = (1..=width)
|
||||
.map(|j| {
|
||||
html! {<th>
|
||||
{format!("{:0>2}", j)}
|
||||
|
@ -122,22 +114,23 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
|
|||
.collect::<Html>();
|
||||
html! {<tr><th />{ headers }</tr>}
|
||||
};
|
||||
let rows = (1..=props.source_plate.plate.size().0)
|
||||
let rows =
|
||||
(1..=height)
|
||||
.map(|i| {
|
||||
let row_header = html! {<th>{num_to_letters(i)}</th>};
|
||||
let row = (1..=props.source_plate.plate.size().1)
|
||||
let row = (1..=width)
|
||||
.map(|j| {
|
||||
html! {
|
||||
<SourcePlateCell i={i} j={j}
|
||||
<PlateCell i={i} j={j}
|
||||
selected={in_rect(*m_start_handle.clone(), *m_end_handle.clone(), (i,j))}
|
||||
mouse={mouse_callback.clone()}
|
||||
in_transfer={source_wells.contains(&(i,j)) && main_state.preferences.in_transfer_hashes}
|
||||
color={transfer_map.get(&(i,j))
|
||||
in_transfer={wells.contains(&(i,j)) && main_state.preferences.in_transfer_hashes}
|
||||
color={tooltip_map.get(&(i,j))
|
||||
.and_then(|t| t.last())
|
||||
.map(|t| PALETTE.get_ordered(t.get_uuid(), &ordered_ids))
|
||||
}
|
||||
cell_height={props.cell_height}
|
||||
title={transfer_map.get(&(i,j)).map(|transfers| format!("Used by: {}", transfers.iter().map(|t| t.name.clone())
|
||||
title={tooltip_map.get(&(i,j)).map(|transfers| format!("Used by: {}", transfers.iter().map(|t| t.name.clone())
|
||||
.collect::<Vec<_>>().join(", ")))}
|
||||
/>
|
||||
}
|
||||
|
@ -153,8 +146,11 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
|
|||
|
||||
html! {
|
||||
<div ondblclick={screenshot_callback}
|
||||
class={classes!{"source_plate",
|
||||
"W".to_owned()+&props.source_plate.plate.plate_format.to_string()}}>
|
||||
class={classes!{match props.ptype {
|
||||
PlateType::Source => "source_plate",
|
||||
PlateType::Destination => "dest_plate",
|
||||
},
|
||||
"W".to_owned()+&pformat.to_string()}}>
|
||||
<table
|
||||
onmouseup={move |e| {
|
||||
mouseup_callback.emit(e);
|
||||
|
@ -170,7 +166,7 @@ pub fn SourcePlate(props: &SourcePlateProps) -> Html {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct SourcePlateCellProps {
|
||||
pub struct PlateCellProps {
|
||||
i: u8,
|
||||
j: u8,
|
||||
selected: bool,
|
||||
|
@ -180,14 +176,9 @@ pub struct SourcePlateCellProps {
|
|||
cell_height: f64,
|
||||
title: Option<String>,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum MouseEventType {
|
||||
Mousedown,
|
||||
Mouseenter,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn SourcePlateCell(props: &SourcePlateCellProps) -> Html {
|
||||
fn PlateCell(props: &PlateCellProps) -> Html {
|
||||
let selected_class = match props.selected {
|
||||
true => Some("current_select"),
|
||||
false => None,
|
|
@ -0,0 +1,50 @@
|
|||
use yew::prelude::*;
|
||||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::states::CurrentTransfer;
|
||||
use plate_tool_lib::transfer_region::Region;
|
||||
|
||||
// Color Palette for the Source Plates, can be changed here
|
||||
|
||||
use super::super::transfer_menu::RegionDisplay;
|
||||
|
||||
use super::plate_data::*;
|
||||
|
||||
pub fn mouse_callback(
|
||||
m_start_handle: MStartHandle,
|
||||
m_end_handle: MEndHandle,
|
||||
m_stat_handle: MStatHandle,
|
||||
) -> Callback<(u8, u8, MouseEventType)> {
|
||||
Callback::from(move |(i, j, t)| match t {
|
||||
MouseEventType::Mousedown => {
|
||||
m_start_handle.set(Some((i, j)));
|
||||
m_end_handle.set(Some((i, j)));
|
||||
m_stat_handle.set(true);
|
||||
}
|
||||
MouseEventType::Mouseenter => {
|
||||
if *m_stat_handle {
|
||||
m_end_handle.set(Some((i, j)));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mouseup_callback(
|
||||
m_start_handle: MStartHandle,
|
||||
m_end_handle: MEndHandle,
|
||||
m_stat_handle: MStatHandle,
|
||||
ct_dispatch: Dispatch<CurrentTransfer>,
|
||||
) -> Callback<MouseEvent> {
|
||||
Callback::from(move |_: MouseEvent| {
|
||||
m_stat_handle.set(false);
|
||||
if let Some(ul) = *m_start_handle {
|
||||
if let Some(br) = *m_end_handle {
|
||||
if let Ok(rd) = RegionDisplay::try_from((ul.0, ul.1, br.0, br.1)) {
|
||||
ct_dispatch.reduce_mut(|state| {
|
||||
state.transfer.transfer_region.source_region = Region::from(&rd);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -3,10 +3,12 @@ use wasm_bindgen::prelude::Closure;
|
|||
use wasm_bindgen::JsCast;
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::data::plate_instances::PlateInstance;
|
||||
use plate_tool_lib::plate::PlateType;
|
||||
use plate_tool_lib::plate_instances::PlateInstance;
|
||||
|
||||
use super::destination_plate::DestinationPlate;
|
||||
use super::source_plate::SourcePlate;
|
||||
// use super::destination_plate::DestinationPlate;
|
||||
// use super::source_plate::SourcePlate;
|
||||
use super::plate::Plate;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct PlateContainerProps {
|
||||
|
@ -53,13 +55,15 @@ pub fn PlateContainer(props: &PlateContainerProps) -> Html {
|
|||
if let Some(dpi) = props.destination_dims.clone() {
|
||||
<div class="plate_container--source">
|
||||
<h2>{spi.name.clone()}</h2>
|
||||
<SourcePlate source_plate={spi.clone()} destination_plate={dpi.clone()}
|
||||
cell_height={cell_height}/>
|
||||
<h2>{"Source"}</h2>
|
||||
<Plate source_plate={spi.clone()} destination_plate={dpi.clone()}
|
||||
cell_height={cell_height} ptype={PlateType::Source}/>
|
||||
</div>
|
||||
<div class="plate_container--destination">
|
||||
<h2>{dpi.name.clone()}</h2>
|
||||
<DestinationPlate source_plate={spi.clone()} destination_plate={dpi.clone()}
|
||||
cell_height={cell_height}/>
|
||||
<h2>{"Destination"}</h2>
|
||||
<Plate source_plate={spi.clone()} destination_plate={dpi.clone()}
|
||||
cell_height={cell_height} ptype={PlateType::Destination}/>
|
||||
</div>
|
||||
} else {
|
||||
<h2>{"No Destination Plate Selected"}</h2>
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
use yew::prelude::*;
|
||||
|
||||
use plate_tool_lib::plate_instances::PlateInstance;
|
||||
use plate_tool_lib::plate::PlateType;
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct PlateProps {
|
||||
pub source_plate: PlateInstance,
|
||||
pub destination_plate: PlateInstance,
|
||||
pub cell_height: f64,
|
||||
pub ptype: PlateType,
|
||||
}
|
||||
|
||||
pub type MStartHandle = UseStateHandle<Option<(u8, u8)>>;
|
||||
pub type MEndHandle = UseStateHandle<Option<(u8, u8)>>;
|
||||
pub type MStatHandle = UseStateHandle<bool>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MouseEventType {
|
||||
Mousedown,
|
||||
Mouseenter,
|
||||
}
|
|
@ -2,10 +2,6 @@
|
|||
// https://iquilezles.org/articles/palettes/
|
||||
// http://dev.thi.ng/gradients/
|
||||
|
||||
use rand::prelude::*;
|
||||
use rand::rngs::SmallRng;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub struct ColorPalette {
|
||||
a: [f64; 3],
|
||||
|
@ -41,17 +37,17 @@ impl ColorPalette {
|
|||
// self.get(r.gen_range(0.0..1.0f64))
|
||||
// }
|
||||
|
||||
pub fn get_ordered(&self, t: uuid::Uuid, ordered_uuids: &Vec<uuid::Uuid>)
|
||||
pub fn get_ordered(&self, t: uuid::Uuid, ordered_uuids: &[uuid::Uuid])
|
||||
-> [f64; 3] {
|
||||
let index = ordered_uuids.iter().position(|&x| x == t).expect("uuid must be in list of uuids") + 1;
|
||||
return self.get(Self::space_evenly(index))
|
||||
self.get(Self::space_evenly(index))
|
||||
}
|
||||
|
||||
fn space_evenly(x: usize) -> f64 {
|
||||
let e: usize = (x.ilog2() + 1) as usize;
|
||||
let d: usize = (2usize.pow(e as u32)) as usize;
|
||||
let d: usize = 2usize.pow(e as u32);
|
||||
let n: usize = (2*x + 1) % d;
|
||||
return (n as f64) / (d as f64);
|
||||
(n as f64) / (d as f64)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,9 @@ use serde::{Deserialize, Serialize};
|
|||
use uuid::Uuid;
|
||||
use yewdux::{prelude::*, storage};
|
||||
|
||||
use crate::data::plate::*;
|
||||
use crate::data::plate_instances::PlateInstance;
|
||||
use crate::data::transfer::Transfer;
|
||||
use plate_tool_lib::plate::*;
|
||||
use plate_tool_lib::plate_instances::PlateInstance;
|
||||
use plate_tool_lib::transfer::Transfer;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, Store)]
|
||||
#[store(storage = "session")]
|
||||
|
@ -108,4 +108,13 @@ impl MainState {
|
|||
self.destination_plates[index].change_name(new_name.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_format(&mut self, id: Uuid, new_format: &PlateFormat) {
|
||||
if let Some(index) = self.source_plates.iter().position(|spi| spi.get_uuid() == id) {
|
||||
self.source_plates[index].change_format(new_format);
|
||||
}
|
||||
if let Some(index) = self.destination_plates.iter().position(|dpi| dpi.get_uuid() == id) {
|
||||
self.destination_plates[index].change_format(new_format);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,8 @@ use yew::prelude::*;
|
|||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::callbacks::transfer_menu_callbacks;
|
||||
use crate::data::transfer_region::Region;
|
||||
use plate_tool_lib::transfer_region::Region;
|
||||
use plate_tool_lib::util::{letters_to_num, num_to_letters};
|
||||
|
||||
use super::states::{CurrentTransfer, MainState};
|
||||
|
||||
|
@ -205,7 +206,7 @@ impl TryFrom<&str> for RegionDisplay {
|
|||
lazy_static! {
|
||||
static ref REGION_REGEX: Regex = Regex::new(r"([A-Z]+)(\d+):([A-Z]+)(\d+)").unwrap();
|
||||
}
|
||||
if let Some(captures) = REGION_REGEX.captures(&value) {
|
||||
if let Some(captures) = REGION_REGEX.captures(value) {
|
||||
if captures.len() != 5 {
|
||||
return Err("Not enough capture groups");
|
||||
}
|
||||
|
@ -277,79 +278,12 @@ impl TryFrom<(u8, u8, u8, u8)> for RegionDisplay {
|
|||
})
|
||||
}
|
||||
}
|
||||
pub fn letters_to_num(letters: &str) -> Option<u8> {
|
||||
let mut num: u8 = 0;
|
||||
for (i, letter) in letters.to_ascii_uppercase().chars().rev().enumerate() {
|
||||
log::debug!("{}, {}", i, letter);
|
||||
let n = letter as u8;
|
||||
if !(65..=90).contains(&n) {
|
||||
return None;
|
||||
}
|
||||
num = num.checked_add((26_i32.pow(i as u32) * (n as i32 - 64)).try_into().ok()?)?;
|
||||
}
|
||||
Some(num)
|
||||
}
|
||||
pub fn num_to_letters(num: u8) -> Option<String> {
|
||||
if num == 0 {
|
||||
return None;
|
||||
} // Otherwise, we will not return none!
|
||||
// As another note, we can't represent higher than "IV" anyway;
|
||||
// thus there's no reason for a loop (26^n with n>1 will NOT occur).
|
||||
let mut text = "".to_string();
|
||||
let mut digit1 = num.div_euclid(26u8);
|
||||
let mut digit2 = num.rem_euclid(26u8);
|
||||
if digit1 > 0 && digit2 == 0u8 {
|
||||
digit1 -= 1;
|
||||
digit2 = 26;
|
||||
}
|
||||
if digit1 != 0 {
|
||||
text.push((64 + digit1) as char)
|
||||
}
|
||||
text.push((64 + digit2) as char);
|
||||
|
||||
Some(text.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
use super::{letters_to_num, num_to_letters, RegionDisplay};
|
||||
|
||||
#[test]
|
||||
#[wasm_bindgen_test]
|
||||
fn test_letters_to_num() {
|
||||
assert_eq!(letters_to_num("D"), Some(4));
|
||||
assert_eq!(letters_to_num("d"), None);
|
||||
assert_eq!(letters_to_num("AD"), Some(26 + 4));
|
||||
assert_eq!(letters_to_num("CG"), Some(3 * 26 + 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[wasm_bindgen_test]
|
||||
fn test_num_to_letters() {
|
||||
println!("27 is {:?}", num_to_letters(27));
|
||||
assert_eq!(num_to_letters(1), Some("A".to_string()));
|
||||
assert_eq!(num_to_letters(26), Some("Z".to_string()));
|
||||
assert_eq!(num_to_letters(27), Some("AA".to_string()));
|
||||
assert_eq!(num_to_letters(111), Some("DG".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[wasm_bindgen_test]
|
||||
fn test_l2n_and_n2l() {
|
||||
assert_eq!(
|
||||
num_to_letters(letters_to_num("A").unwrap()),
|
||||
Some("A".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
num_to_letters(letters_to_num("BJ").unwrap()),
|
||||
Some("BJ".to_string())
|
||||
);
|
||||
for i in 1..=255 {
|
||||
assert_eq!(letters_to_num(&num_to_letters(i).unwrap()), Some(i));
|
||||
}
|
||||
}
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[wasm_bindgen_test]
|
|
@ -1,13 +1,14 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use plate_tool_lib::plate::PlateFormat;
|
||||
use uuid::Uuid;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{HtmlDialogElement, HtmlInputElement};
|
||||
use yew::prelude::*;
|
||||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::states::{CurrentTransfer, MainState};
|
||||
use crate::components::callbacks::tree_callbacks;
|
||||
use crate::components::states::{CurrentTransfer, MainState};
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct TreeProps {
|
||||
|
@ -157,23 +158,32 @@ fn PlateInfoModal(props: &PlateInfoModalProps) -> Html {
|
|||
Some(plate) => plate.name.clone(),
|
||||
None => "Not Found".to_string(),
|
||||
};
|
||||
let plate_format = plate.map(|p| p.plate.plate_format);
|
||||
let plate_formats = vec![
|
||||
PlateFormat::W6,
|
||||
PlateFormat::W12,
|
||||
PlateFormat::W24,
|
||||
PlateFormat::W48,
|
||||
PlateFormat::W96,
|
||||
PlateFormat::W384,
|
||||
PlateFormat::W1536,
|
||||
PlateFormat::W3456,
|
||||
];
|
||||
let plate_format_options = plate_formats.iter().map(|v| {
|
||||
let selected = Some(v) == plate_format.as_ref();
|
||||
html!{
|
||||
<option value={v.to_string()} selected={selected}>{ v.to_string() }</option>
|
||||
}
|
||||
});
|
||||
|
||||
let onclose = {
|
||||
let dialog_close_callback = props.dialog_close_callback.clone();
|
||||
move |_| dialog_close_callback.emit(())
|
||||
};
|
||||
|
||||
let rename_onchange = {
|
||||
let id = props.id;
|
||||
Callback::from(move |e: Event| {
|
||||
log::debug!("Changed name");
|
||||
let input = e
|
||||
.target()
|
||||
.expect("Event must have target")
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.unwrap();
|
||||
main_dispatch.reduce_mut(|state| state.rename_plate(id, &input.value()))
|
||||
})
|
||||
};
|
||||
let rename_onchange = tree_callbacks::rename_onchange(props.id, main_dispatch.clone());
|
||||
|
||||
let format_onchange = tree_callbacks::format_onchange(props.id, main_dispatch.clone());
|
||||
|
||||
let delete_onclick = {
|
||||
let delete_button_callback = props.delete_button_callback.clone();
|
||||
|
@ -204,6 +214,11 @@ fn PlateInfoModal(props: &PlateInfoModalProps) -> Html {
|
|||
<h2>{"Plate Info"}</h2>
|
||||
<h3>{"Name: "}<input type="text" value={plate_name} onchange={rename_onchange}/></h3>
|
||||
<button onclick={delete_onclick}>{"Delete"}</button>
|
||||
if let Some(_) = plate_format {
|
||||
<select name="modal_plate_format" onchange={format_onchange}>
|
||||
{ for plate_format_options }
|
||||
</select>
|
||||
}
|
||||
<form class="modal_close" method="dialog"><button /></form>
|
||||
</dialog>
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
#![allow(non_snake_case)]
|
||||
mod components;
|
||||
mod data;
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use components::main_window::MainWindow;
|
||||
use components::states::MainState;
|
||||
use plate_tool_lib::csv::*;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use data::*;
|
||||
use plate_tool_lib::*;
|
||||
|
||||
#[function_component]
|
||||
pub fn App() -> Html {
|
||||
|
@ -35,3 +38,25 @@ pub fn plate_test() {
|
|||
println!("{:?} -> {:?}", w, m(w));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state_to_csv(state: &MainState) -> Result<String, Box<dyn Error>> {
|
||||
let mut records: Vec<TransferRecord> = Vec::new();
|
||||
for transfer in &state.transfers {
|
||||
let src_barcode = state
|
||||
.source_plates
|
||||
.iter()
|
||||
.find(|spi| spi.get_uuid() == transfer.source_id)
|
||||
.ok_or("Found unpurged transfer")?;
|
||||
let dest_barcode = state
|
||||
.destination_plates
|
||||
.iter()
|
||||
.find(|dpi| dpi.get_uuid() == transfer.dest_id)
|
||||
.ok_or("Found unpurged transfer")?;
|
||||
records.append(&mut transfer_to_records(
|
||||
transfer,
|
||||
&src_barcode.name,
|
||||
&dest_barcode.name,
|
||||
))
|
||||
}
|
||||
records_to_csv(records)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use plate_tool::App;
|
||||
use plate_tool_web::App;
|
||||
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::default());
|
|
@ -1,216 +0,0 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use yew::prelude::*;
|
||||
use yewdux::prelude::*;
|
||||
|
||||
use crate::components::states::{CurrentTransfer, MainState};
|
||||
use crate::data::plate_instances::PlateInstance;
|
||||
use crate::data::transfer::Transfer;
|
||||
use crate::data::transfer_region::Region;
|
||||
|
||||
// Color Palette for the Source Plates, can be changed here
|
||||
use crate::components::plates::util::Palettes;
|
||||
const PALETTE: super::util::ColorPalette = Palettes::RAINBOW;
|
||||
|
||||
use super::super::transfer_menu::{num_to_letters, RegionDisplay};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct DestinationPlateProps {
|
||||
pub source_plate: PlateInstance,
|
||||
pub destination_plate: PlateInstance,
|
||||
pub cell_height: f64,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn DestinationPlate(props: &DestinationPlateProps) -> Html {
|
||||
let (main_state, _) = use_store::<MainState>();
|
||||
let (ct_state, ct_dispatch) = use_store::<CurrentTransfer>();
|
||||
let m_start_handle: UseStateHandle<Option<(u8, u8)>> = use_state_eq(|| None);
|
||||
let m_end_handle: UseStateHandle<Option<(u8, u8)>> = use_state_eq(|| None);
|
||||
let m_stat_handle: UseStateHandle<bool> = use_state_eq(|| false);
|
||||
|
||||
if !(*m_stat_handle) {
|
||||
let (pt1, pt2) = match ct_state.transfer.transfer_region.dest_region {
|
||||
Region::Point((x, y)) => ((x, y), (x, y)),
|
||||
Region::Rect(c1, c2) => (c1, c2),
|
||||
Region::Custom(_) => ((0,0), (0,0)),
|
||||
};
|
||||
m_start_handle.set(Some(pt1));
|
||||
m_end_handle.set(Some(pt2));
|
||||
}
|
||||
let destination_wells = ct_state.transfer.transfer_region.get_destination_wells();
|
||||
|
||||
let ordered_ids: Vec<uuid::Uuid> = {
|
||||
let mut ids: Vec<uuid::Uuid> = main_state.transfers.clone().iter()
|
||||
.map(|x| x.id)
|
||||
.collect();
|
||||
ids.sort_unstable();
|
||||
ids
|
||||
};
|
||||
|
||||
let mouse_callback = {
|
||||
let m_start_handle = m_start_handle.clone();
|
||||
let m_end_handle = m_end_handle.clone();
|
||||
let m_stat_handle = m_stat_handle.clone();
|
||||
|
||||
Callback::from(move |(i, j, t)| match t {
|
||||
MouseEventType::Mousedown => {
|
||||
m_start_handle.set(Some((i, j)));
|
||||
m_end_handle.set(Some((i, j)));
|
||||
m_stat_handle.set(true);
|
||||
}
|
||||
MouseEventType::Mouseenter => {
|
||||
if *m_stat_handle {
|
||||
m_end_handle.set(Some((i, j)));
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let transfer_map = {
|
||||
let ts = main_state
|
||||
.transfers
|
||||
.iter()
|
||||
.filter(|t| t.dest_id == props.destination_plate.get_uuid());
|
||||
let mut tooltip_map: HashMap<(u8, u8), Vec<&Transfer>> = HashMap::new();
|
||||
for t in ts {
|
||||
let dws = t.transfer_region.get_destination_wells();
|
||||
for dw in dws {
|
||||
if let Some(val) = tooltip_map.get_mut(&dw) {
|
||||
val.push(t);
|
||||
} else {
|
||||
tooltip_map.insert(dw, vec![t]);
|
||||
}
|
||||
}
|
||||
}
|
||||
tooltip_map
|
||||
};
|
||||
|
||||
let mouseup_callback = {
|
||||
let m_start_handle = m_start_handle.clone();
|
||||
let m_end_handle = m_end_handle.clone();
|
||||
|
||||
Callback::from(move |_: MouseEvent| {
|
||||
m_stat_handle.set(false);
|
||||
if let Some(ul) = *m_start_handle {
|
||||
if let Some(br) = *m_end_handle {
|
||||
if let Ok(rd) = RegionDisplay::try_from((ul.0, ul.1, br.0, br.1)) {
|
||||
ct_dispatch.reduce_mut(|state| {
|
||||
state.transfer.transfer_region.dest_region = Region::from(&rd);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let mouseleave_callback = Callback::clone(&mouseup_callback);
|
||||
|
||||
|
||||
let screenshot_callback = Callback::from(|_| {
|
||||
let _ = js_sys::eval("copy_screenshot_dest()");
|
||||
});
|
||||
|
||||
let column_header = {
|
||||
let headers = (1..=props.destination_plate.plate.size().1)
|
||||
.map(|j| {
|
||||
html! {<th>{format!("{:0>2}", j)}</th>}
|
||||
})
|
||||
.collect::<Html>();
|
||||
html! {<tr><th />{ headers }</tr>}
|
||||
};
|
||||
let rows = (1..=props.destination_plate.plate.size().0)
|
||||
.map(|i| {
|
||||
let row_header = html! {<th>{num_to_letters(i)}</th>};
|
||||
let row = (1..=props.destination_plate.plate.size().1).map(|j| {
|
||||
html! {
|
||||
<DestPlateCell i={i} j={j}
|
||||
selected={super::source_plate::in_rect(*m_start_handle.clone(), *m_end_handle.clone(), (i,j))}
|
||||
mouse={mouse_callback.clone()}
|
||||
in_transfer={destination_wells.contains(&(i,j)) && main_state.preferences.in_transfer_hashes}
|
||||
color={transfer_map.get(&(i,j))
|
||||
.and_then(|t| t.last())
|
||||
.map(|t| PALETTE.get_ordered(t.get_uuid(), &ordered_ids))
|
||||
}
|
||||
cell_height={props.cell_height}
|
||||
title={transfer_map.get(&(i,j)).map(|transfers| format!("Used by: {}", transfers.iter().map(|t| t.name.clone())
|
||||
.collect::<Vec<_>>().join(", ")))}
|
||||
/>
|
||||
}
|
||||
}).collect::<Html>();
|
||||
html! {
|
||||
<tr>
|
||||
{ row_header }{ row }
|
||||
</tr>
|
||||
}
|
||||
})
|
||||
.collect::<Html>();
|
||||
|
||||
html! {
|
||||
<div ondblclick={screenshot_callback}
|
||||
class={classes!{"dest_plate",
|
||||
"W".to_owned()+&props.source_plate.plate.plate_format.to_string()}}>
|
||||
<table
|
||||
onmouseup={move |e| {
|
||||
mouseup_callback.emit(e);
|
||||
}}
|
||||
onmouseleave={move |e| {
|
||||
mouseleave_callback.emit(e);
|
||||
}}>
|
||||
{ column_header }{ rows }
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MouseEventType {
|
||||
Mousedown,
|
||||
Mouseenter,
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct DestPlateCellProps {
|
||||
pub i: u8,
|
||||
pub j: u8,
|
||||
pub selected: bool,
|
||||
pub mouse: Callback<(u8, u8, MouseEventType)>,
|
||||
pub in_transfer: Option<bool>,
|
||||
color: Option<[f64; 3]>,
|
||||
cell_height: f64,
|
||||
title: Option<String>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn DestPlateCell(props: &DestPlateCellProps) -> Html {
|
||||
let selected_class = match props.selected {
|
||||
true => Some("current_select"),
|
||||
false => None,
|
||||
};
|
||||
let in_transfer_class = match props.in_transfer {
|
||||
Some(true) => Some("in_transfer"),
|
||||
_ => None,
|
||||
};
|
||||
let color = props.color.unwrap_or([255.0, 255.0, 255.0]);
|
||||
let mouse = Callback::clone(&props.mouse);
|
||||
let mouse2 = Callback::clone(&props.mouse);
|
||||
let (i, j) = (props.i, props.j);
|
||||
|
||||
html! {
|
||||
<td class={classes!("plate_cell", selected_class, in_transfer_class)}
|
||||
style={format!("height: {}px;", props.cell_height)}
|
||||
onmousedown={move |_| {
|
||||
mouse.emit((i,j, MouseEventType::Mousedown))
|
||||
}}
|
||||
onmouseenter={move |_| {
|
||||
mouse2.emit((i,j, MouseEventType::Mouseenter))
|
||||
}}>
|
||||
<div class="plate_cell_inner"
|
||||
style={format!("background: rgba({},{},{},1);", color[0], color[1], color[2])}
|
||||
title={if let Some(text) = &props.title {
|
||||
text.clone()
|
||||
} else { "".to_string() }}/>
|
||||
</td>
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
pub mod destination_plate;
|
||||
pub mod plate_container;
|
||||
pub mod source_plate;
|
||||
mod util;
|
|
@ -1,81 +0,0 @@
|
|||
use crate::components::states::MainState;
|
||||
use crate::components::transfer_menu::num_to_letters;
|
||||
use crate::data::transfer::Transfer;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::error::Error;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TransferRecord {
|
||||
#[serde(rename = "Source Plate")]
|
||||
pub source_plate: String,
|
||||
#[serde(rename = "Source Well")]
|
||||
pub source_well: String,
|
||||
#[serde(rename = "Dest Plate")]
|
||||
pub destination_plate: String,
|
||||
#[serde(rename = "Destination Well")]
|
||||
pub destination_well: String,
|
||||
#[serde(rename = "Transfer Volume")]
|
||||
pub volume: f32,
|
||||
#[serde(rename = "Concentration")]
|
||||
pub concentration: Option<f32>,
|
||||
}
|
||||
|
||||
pub fn state_to_csv(state: &MainState) -> Result<String, Box<dyn Error>> {
|
||||
let mut records: Vec<TransferRecord> = Vec::new();
|
||||
for transfer in &state.transfers {
|
||||
let src_barcode = state
|
||||
.source_plates
|
||||
.iter()
|
||||
.find(|spi| spi.get_uuid() == transfer.source_id)
|
||||
.ok_or("Found unpurged transfer")?;
|
||||
let dest_barcode = state
|
||||
.destination_plates
|
||||
.iter()
|
||||
.find(|dpi| dpi.get_uuid() == transfer.dest_id)
|
||||
.ok_or("Found unpurged transfer")?;
|
||||
records.append(&mut transfer_to_records(
|
||||
transfer,
|
||||
&src_barcode.name,
|
||||
&dest_barcode.name,
|
||||
))
|
||||
}
|
||||
return records_to_csv(records);
|
||||
}
|
||||
|
||||
fn transfer_to_records(
|
||||
tr: &Transfer,
|
||||
src_barcode: &str,
|
||||
dest_barcode: &str,
|
||||
) -> Vec<TransferRecord> {
|
||||
let source_wells = tr.transfer_region.get_source_wells();
|
||||
let map = tr.transfer_region.calculate_map();
|
||||
|
||||
let mut records: Vec<TransferRecord> = vec![];
|
||||
|
||||
for s_well in source_wells {
|
||||
let dest_wells = map(s_well);
|
||||
if let Some(dest_wells) = dest_wells {
|
||||
for d_well in dest_wells {
|
||||
records.push(TransferRecord {
|
||||
source_plate: src_barcode.to_string(),
|
||||
source_well: format!("{}{}", num_to_letters(s_well.0).unwrap(), s_well.1),
|
||||
destination_plate: dest_barcode.to_string(),
|
||||
destination_well: format!("{}{}", num_to_letters(d_well.0).unwrap(), d_well.1),
|
||||
volume: tr.volume,
|
||||
concentration: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
records
|
||||
}
|
||||
|
||||
fn records_to_csv(trs: Vec<TransferRecord>) -> Result<String, Box<dyn Error>> {
|
||||
let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
|
||||
for record in trs {
|
||||
wtr.serialize(record)?
|
||||
}
|
||||
let data = String::from_utf8(wtr.into_inner()?)?;
|
||||
Ok(data)
|
||||
}
|
Loading…
Reference in New Issue