Figured out plotting!
Signed-off-by: prescientmoon <git@moonythm.dev>
This commit is contained in:
parent
b2e88e703b
commit
49d50bf88b
378
Cargo.lock
generated
378
Cargo.lock
generated
|
@ -401,6 +401,42 @@ version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-graphics"
|
||||||
|
version = "0.23.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"core-foundation",
|
||||||
|
"core-graphics-types",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-graphics-types"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"core-foundation",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-text"
|
||||||
|
version = "20.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation",
|
||||||
|
"core-graphics",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
|
@ -494,24 +530,13 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "csv"
|
name = "cstr"
|
||||||
version = "1.3.0"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
|
checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"csv-core",
|
"proc-macro2",
|
||||||
"itoa",
|
"quote",
|
||||||
"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]]
|
[[package]]
|
||||||
|
@ -634,12 +659,33 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dlib"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||||
|
dependencies = [
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dotenvy"
|
name = "dotenvy"
|
||||||
version = "0.15.7"
|
version = "0.15.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dwrote"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
"wio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "edit-distance"
|
name = "edit-distance"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -655,12 +701,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "encode_unicode"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.34"
|
version = "0.8.34"
|
||||||
|
@ -728,28 +768,6 @@ dependencies = [
|
||||||
"zune-inflate",
|
"zune-inflate",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "failure"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
|
|
||||||
dependencies = [
|
|
||||||
"backtrace",
|
|
||||||
"failure_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "failure_derive"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
"synstructure",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -775,6 +793,12 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-ord"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -792,6 +816,58 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "font-kit"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2845a73bbd781e691ab7c2a028c579727cd254942e8ced57ff73e0eafd60de87"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.5.0",
|
||||||
|
"byteorder",
|
||||||
|
"core-foundation",
|
||||||
|
"core-graphics",
|
||||||
|
"core-text",
|
||||||
|
"dirs-next",
|
||||||
|
"dwrote",
|
||||||
|
"float-ord",
|
||||||
|
"freetype-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"pathfinder_geometry",
|
||||||
|
"pathfinder_simd",
|
||||||
|
"walkdir",
|
||||||
|
"winapi",
|
||||||
|
"yeslogic-fontconfig-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-macros",
|
||||||
|
"foreign-types-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-macros"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
@ -801,6 +877,17 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "freetype-sys"
|
||||||
|
version = "0.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
|
@ -930,6 +1017,16 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gif"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
|
||||||
|
dependencies = [
|
||||||
|
"color_quant",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gif"
|
name = "gif"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
|
@ -1176,6 +1273,20 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.24.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"jpeg-decoder",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
|
@ -1186,7 +1297,7 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"color_quant",
|
"color_quant",
|
||||||
"exr",
|
"exr",
|
||||||
"gif",
|
"gif 0.13.1",
|
||||||
"image-webp",
|
"image-webp",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"png",
|
"png",
|
||||||
|
@ -1242,17 +1353,6 @@ version = "2.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "is-terminal"
|
|
||||||
version = "0.4.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -1709,6 +1809,25 @@ version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathfinder_geometry"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pathfinder_simd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathfinder_simd"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebf45976c56919841273f2a0fc684c28437e2f304e264557d9c72be5d5a718be"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peeking_take_while"
|
name = "peeking_take_while"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -1770,13 +1889,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotlib"
|
name = "plotters"
|
||||||
version = "0.5.1"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/starlitcanopy/plotters.git?rev=986cd959362a2dbec8d1b25670fd083b904d7b8c#986cd959362a2dbec8d1b25670fd083b904d7b8c"
|
||||||
checksum = "9462104f987d8d0f6625f0c7764f1c8b890bd1dc8584d8293e031f25c5a0d242"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"failure",
|
"chrono",
|
||||||
"svg",
|
"font-kit",
|
||||||
|
"image 0.24.9",
|
||||||
|
"lazy_static",
|
||||||
|
"num-traits",
|
||||||
|
"pathfinder_geometry",
|
||||||
|
"plotters-backend",
|
||||||
|
"plotters-bitmap",
|
||||||
|
"plotters-svg",
|
||||||
|
"ttf-parser",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-backend"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "git+https://github.com/starlitcanopy/plotters.git?rev=986cd959362a2dbec8d1b25670fd083b904d7b8c#986cd959362a2dbec8d1b25670fd083b904d7b8c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-bitmap"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "git+https://github.com/starlitcanopy/plotters.git?rev=986cd959362a2dbec8d1b25670fd083b904d7b8c#986cd959362a2dbec8d1b25670fd083b904d7b8c"
|
||||||
|
dependencies = [
|
||||||
|
"gif 0.11.4",
|
||||||
|
"image 0.24.9",
|
||||||
|
"plotters-backend",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-svg"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "git+https://github.com/starlitcanopy/plotters.git?rev=986cd959362a2dbec8d1b25670fd083b904d7b8c#986cd959362a2dbec8d1b25670fd083b904d7b8c"
|
||||||
|
dependencies = [
|
||||||
|
"plotters-backend",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1833,20 +1984,6 @@ version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "prettytable-rs"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a"
|
|
||||||
dependencies = [
|
|
||||||
"csv",
|
|
||||||
"encode_unicode",
|
|
||||||
"is-terminal",
|
|
||||||
"lazy_static",
|
|
||||||
"term",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.85"
|
version = "1.0.85"
|
||||||
|
@ -2168,6 +2305,15 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.34"
|
version = "0.38.34"
|
||||||
|
@ -2243,12 +2389,6 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
@ -2420,14 +2560,12 @@ name = "shimmeringmoon"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"csv",
|
|
||||||
"edit-distance",
|
"edit-distance",
|
||||||
"image",
|
"image 0.25.1",
|
||||||
"kd-tree",
|
"kd-tree",
|
||||||
"num",
|
"num",
|
||||||
"plotlib",
|
"plotters",
|
||||||
"poise",
|
"poise",
|
||||||
"prettytable-rs",
|
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tesseract",
|
"tesseract",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -2761,12 +2899,6 @@ version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "svg"
|
|
||||||
version = "0.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3685c82a045a6af0c488f0550b0f52b4c77d2a52b0ca8aba719f9d268fa96965"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
|
@ -2795,18 +2927,6 @@ version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "synstructure"
|
|
||||||
version = "0.12.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
"unicode-xid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-configuration"
|
name = "system-configuration"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -2865,17 +2985,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "term"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
|
||||||
dependencies = [
|
|
||||||
"dirs-next",
|
|
||||||
"rustversion",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tesseract"
|
name = "tesseract"
|
||||||
version = "0.15.1"
|
version = "0.15.1"
|
||||||
|
@ -3160,6 +3269,12 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ttf-parser"
|
||||||
|
version = "0.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
|
@ -3264,18 +3379,6 @@ version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-width"
|
|
||||||
version = "0.1.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-xid"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode_categories"
|
name = "unicode_categories"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -3702,6 +3805,27 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wio"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yeslogic-fontconfig-sys"
|
||||||
|
version = "5.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffb6b23999a8b1a997bf47c7bb4d19ad4029c3327bb3386ebe0a5ff584b33c7a"
|
||||||
|
dependencies = [
|
||||||
|
"cstr",
|
||||||
|
"dlib",
|
||||||
|
"once_cell",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.34"
|
version = "0.7.34"
|
||||||
|
|
|
@ -5,14 +5,12 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
csv = "1.3.0"
|
|
||||||
edit-distance = "2.1.0"
|
edit-distance = "2.1.0"
|
||||||
image = "0.25.1"
|
image = "0.25.1"
|
||||||
kd-tree = "0.6.0"
|
kd-tree = "0.6.0"
|
||||||
num = "0.4.3"
|
num = "0.4.3"
|
||||||
plotlib = "0.5.1"
|
plotters = { git="https://github.com/starlitcanopy/plotters.git", rev="986cd959362a2dbec8d1b25670fd083b904d7b8c" }
|
||||||
poise = "0.6.1"
|
poise = "0.6.1"
|
||||||
prettytable-rs = "0.10.0"
|
|
||||||
sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio", "chrono"] }
|
sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio", "chrono"] }
|
||||||
tesseract = "0.15.1"
|
tesseract = "0.15.1"
|
||||||
tokio = {version="1.38.0", features=["rt-multi-thread"]}
|
tokio = {version="1.38.0", features=["rt-multi-thread"]}
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
rust-analyzer-nightly
|
rust-analyzer-nightly
|
||||||
ruff
|
ruff
|
||||||
imagemagick
|
imagemagick
|
||||||
|
fontconfig
|
||||||
|
freetype
|
||||||
|
|
||||||
clang
|
clang
|
||||||
llvmPackages.clang
|
llvmPackages.clang
|
||||||
|
|
|
@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS plays (
|
||||||
creation_zeta_ptt INTEGER,
|
creation_zeta_ptt INTEGER,
|
||||||
|
|
||||||
score INTEGER NOT NULL,
|
score INTEGER NOT NULL,
|
||||||
zeta_score INTEGER,
|
zeta_score INTEGER NOT NULL,
|
||||||
|
|
||||||
max_recall INTEGER,
|
max_recall INTEGER,
|
||||||
far_notes INTEGER,
|
far_notes INTEGER,
|
||||||
|
|
42
src/chart.rs
42
src/chart.rs
|
@ -5,7 +5,7 @@ use sqlx::{prelude::FromRow, SqlitePool};
|
||||||
use crate::context::Error;
|
use crate::context::Error;
|
||||||
|
|
||||||
// {{{ Difficuly
|
// {{{ Difficuly
|
||||||
#[derive(Debug, Clone, Copy, sqlx::Type)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sqlx::Type)]
|
||||||
pub enum Difficulty {
|
pub enum Difficulty {
|
||||||
PST,
|
PST,
|
||||||
PRS,
|
PRS,
|
||||||
|
@ -85,17 +85,31 @@ impl CachedSong {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lookup(&self, difficulty: Difficulty) -> Option<&Chart> {
|
pub fn lookup(&self, difficulty: Difficulty) -> Result<&Chart, Error> {
|
||||||
self.charts
|
self.charts
|
||||||
.get(difficulty.to_index())
|
.get(difficulty.to_index())
|
||||||
.and_then(|c| c.as_ref())
|
.and_then(|c| c.as_ref())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"Could not find difficulty {:?} for song {}",
|
||||||
|
difficulty, self.song.title
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lookup_mut(&mut self, difficulty: Difficulty) -> Option<&mut Chart> {
|
pub fn lookup_mut(&mut self, difficulty: Difficulty) -> Result<&mut Chart, Error> {
|
||||||
self.charts
|
self.charts
|
||||||
.get_mut(difficulty.to_index())
|
.get_mut(difficulty.to_index())
|
||||||
.and_then(|c| c.as_mut())
|
.and_then(|c| c.as_mut())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"Could not find difficulty {:?} for song {}",
|
||||||
|
difficulty, self.song.title
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -117,8 +131,26 @@ impl SongCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lookup_mut(&mut self, id: u32) -> Option<&mut CachedSong> {
|
pub fn lookup_chart(&self, chart_id: u32) -> Result<(&Song, &Chart), Error> {
|
||||||
self.songs.get_mut(id as usize).and_then(|i| i.as_mut())
|
self.songs()
|
||||||
|
.find_map(|item| {
|
||||||
|
item.charts().find_map(|chart| {
|
||||||
|
if chart.id == chart_id {
|
||||||
|
Some((&item.song, chart))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.ok_or_else(|| format!("Could not find chart with id {}", chart_id).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn lookup_mut(&mut self, id: u32) -> Result<&mut CachedSong, Error> {
|
||||||
|
self.songs
|
||||||
|
.get_mut(id as usize)
|
||||||
|
.and_then(|i| i.as_mut())
|
||||||
|
.ok_or_else(|| format!("Could not find song with id {}", id).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
2
src/commands/mod.rs
Normal file
2
src/commands/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod score;
|
||||||
|
pub mod stats;
|
|
@ -1,8 +1,10 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::context::{Context, Error};
|
use crate::context::{Context, Error};
|
||||||
use crate::score::{jacket_rects, CreatePlay, ImageCropper, ImageDimensions, RelativeRect, Score};
|
use crate::score::{
|
||||||
use crate::user::User;
|
jacket_rects, CreatePlay, ImageCropper, ImageDimensions, Play, RelativeRect, Score,
|
||||||
|
};
|
||||||
|
use crate::user::{discord_it_to_discord_user, User};
|
||||||
use image::imageops::FilterType;
|
use image::imageops::FilterType;
|
||||||
use image::ImageFormat;
|
use image::ImageFormat;
|
||||||
use poise::serenity_prelude::{CreateAttachment, CreateEmbed, CreateMessage};
|
use poise::serenity_prelude::{CreateAttachment, CreateEmbed, CreateMessage};
|
||||||
|
@ -36,7 +38,7 @@ pub async fn help(
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
prefix_command,
|
prefix_command,
|
||||||
slash_command,
|
slash_command,
|
||||||
subcommands("magic", "delete"),
|
subcommands("magic", "delete", "show"),
|
||||||
subcommand_required
|
subcommand_required
|
||||||
)]
|
)]
|
||||||
pub async fn score(_ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn score(_ctx: Context<'_>) -> Result<(), Error> {
|
||||||
|
@ -88,8 +90,8 @@ pub async fn magic(
|
||||||
if files.len() == 0 {
|
if files.len() == 0 {
|
||||||
ctx.reply("No images found attached to message").await?;
|
ctx.reply("No images found attached to message").await?;
|
||||||
} else {
|
} else {
|
||||||
let mut embeds: Vec<CreateEmbed> = vec![];
|
let mut embeds = Vec::with_capacity(files.len());
|
||||||
let mut attachments: Vec<CreateAttachment> = vec![];
|
let mut attachments = Vec::with_capacity(files.len());
|
||||||
let handle = ctx
|
let handle = ctx
|
||||||
.reply(format!("Processed 0/{} scores", files.len()))
|
.reply(format!("Processed 0/{} scores", files.len()))
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -116,7 +118,7 @@ pub async fn magic(
|
||||||
.content(format!("Image {}: reading jacket", i + 1));
|
.content(format!("Image {}: reading jacket", i + 1));
|
||||||
handle.edit(ctx, edited).await?;
|
handle.edit(ctx, edited).await?;
|
||||||
|
|
||||||
let song_by_jacket = cropper.read_jacket(ctx.data(), &image);
|
let song_by_jacket = cropper.read_jacket(ctx.data(), &image).await;
|
||||||
|
|
||||||
// This makes OCR more likely to work
|
// This makes OCR more likely to work
|
||||||
let mut ocr_image = image.grayscale().blur(1.);
|
let mut ocr_image = image.grayscale().blur(1.);
|
||||||
|
@ -151,8 +153,10 @@ pub async fn magic(
|
||||||
.content(format!("Image {}: reading title", i + 1));
|
.content(format!("Image {}: reading title", i + 1));
|
||||||
handle.edit(ctx, edited).await?;
|
handle.edit(ctx, edited).await?;
|
||||||
|
|
||||||
let song_by_name = cropper.read_song(&ocr_image, &ctx.data().song_cache);
|
let song_by_name = cropper
|
||||||
let cached_song = match (song_by_jacket, song_by_name) {
|
.read_song(&ocr_image, &ctx.data().song_cache, difficulty)
|
||||||
|
.await;
|
||||||
|
let (song, chart) = match (song_by_jacket, song_by_name) {
|
||||||
// {{{ Both errors
|
// {{{ Both errors
|
||||||
(Err(err_jacket), Err(err_name)) => {
|
(Err(err_jacket), Err(err_name)) => {
|
||||||
error_with_image(
|
error_with_image(
|
||||||
|
@ -196,16 +200,8 @@ Title error: {}
|
||||||
.ok_or_else(|| "Could not find jacket area in picture")?
|
.ok_or_else(|| "Could not find jacket area in picture")?
|
||||||
.to_absolute();
|
.to_absolute();
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Find chart
|
|
||||||
let chart = by_name.lookup(difficulty).ok_or_else(|| {
|
|
||||||
format!(
|
|
||||||
"Cannot find difficulty {:?} for chart {:?}",
|
|
||||||
difficulty, by_name.song.title
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
// }}}
|
|
||||||
// {{{ Build path
|
// {{{ Build path
|
||||||
let filename = format!("{}-{}", by_name.song.id, chart.id);
|
let filename = format!("{}-{}", by_name.0.id, by_name.1.id);
|
||||||
let jacket = format!("user/{}", filename);
|
let jacket = format!("user/{}", filename);
|
||||||
|
|
||||||
let jacket_dir = ctx.data().data_dir.join("jackets/user");
|
let jacket_dir = ctx.data().data_dir.join("jackets/user");
|
||||||
|
@ -221,42 +217,27 @@ Title error: {}
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE charts SET jacket=? WHERE song_id=? AND difficulty=?",
|
"UPDATE charts SET jacket=? WHERE song_id=? AND difficulty=?",
|
||||||
jacket,
|
jacket,
|
||||||
chart.song_id,
|
by_name.1.song_id,
|
||||||
chart.difficulty,
|
by_name.1.difficulty,
|
||||||
)
|
)
|
||||||
.execute(&ctx.data().db)
|
.execute(&ctx.data().db)
|
||||||
.await?;
|
.await?;
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Aquire and use song cache lock
|
// {{{ Aquire and use song cache lock
|
||||||
{
|
{
|
||||||
let mut song_cache = ctx
|
let mut song_cache = ctx.data().song_cache.lock().await;
|
||||||
.data()
|
|
||||||
.song_cache
|
|
||||||
.lock()
|
|
||||||
.map_err(|_| "Poisoned song cache")?;
|
|
||||||
|
|
||||||
let chart = song_cache
|
let chart = song_cache
|
||||||
.lookup_mut(by_name.song.id)
|
.lookup_mut(by_name.0.id)?
|
||||||
.ok_or_else(|| {
|
.lookup_mut(difficulty)?;
|
||||||
format!("Could not find song for id {}", by_name.song.id)
|
|
||||||
})?
|
|
||||||
.lookup_mut(difficulty)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
format!(
|
|
||||||
"Could not find difficulty {:?} for song {}",
|
|
||||||
difficulty, by_name.song.title
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if chart.jacket.is_none() {
|
if chart.jacket.is_none() {
|
||||||
if let Some(chart) = by_name.lookup_mut(difficulty) {
|
by_name.1.jacket = Some(jacket_path.clone());
|
||||||
chart.jacket = Some(jacket_path.clone());
|
|
||||||
};
|
|
||||||
chart.jacket = Some(jacket_path);
|
chart.jacket = Some(jacket_path);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"Jacket not detected for chart {} [{:?}]",
|
"Jacket not detected for chart {} [{:?}]",
|
||||||
by_name.song.id, difficulty
|
by_name.0.id, difficulty
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -267,10 +248,10 @@ Title error: {}
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Both succeeded
|
// {{{ Both succeeded
|
||||||
(Ok(by_jacket), Ok(by_name)) => {
|
(Ok(by_jacket), Ok(by_name)) => {
|
||||||
if by_name.song.id != by_jacket.song.id {
|
if by_name.0.id != by_jacket.0.id {
|
||||||
println!(
|
println!(
|
||||||
"Got diverging choices between '{:?}' and '{:?}'",
|
"Got diverging choices between '{:?}' and '{:?}'",
|
||||||
by_jacket.song.id, by_name.song.id
|
by_jacket.0.id, by_name.0.id
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -278,16 +259,6 @@ Title error: {}
|
||||||
} // }}}
|
} // }}}
|
||||||
};
|
};
|
||||||
|
|
||||||
// {{{ Build chart
|
|
||||||
let song = &cached_song.song;
|
|
||||||
let chart = cached_song.lookup(difficulty).ok_or_else(|| {
|
|
||||||
format!(
|
|
||||||
"Could not find difficulty {:?} for song {}",
|
|
||||||
difficulty, song.title
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
let edited = CreateReply::default()
|
let edited = CreateReply::default()
|
||||||
.reply(true)
|
.reply(true)
|
||||||
.content(format!("Image {}: reading score", i + 1));
|
.content(format!("Image {}: reading score", i + 1));
|
||||||
|
@ -315,7 +286,7 @@ Title error: {}
|
||||||
// {{{ Build play
|
// {{{ Build play
|
||||||
let (score, maybe_fars, score_warning) =
|
let (score, maybe_fars, score_warning) =
|
||||||
Score::resolve_ambiguities(score_possibilities, None, chart.note_count)?;
|
Score::resolve_ambiguities(score_possibilities, None, chart.note_count)?;
|
||||||
let play = CreatePlay::new(score, chart, &user)
|
let play = CreatePlay::new(score, &chart, &user)
|
||||||
.with_attachment(file)
|
.with_attachment(file)
|
||||||
.with_fars(maybe_fars)
|
.with_fars(maybe_fars)
|
||||||
.save(&ctx.data())
|
.save(&ctx.data())
|
||||||
|
@ -323,15 +294,13 @@ Title error: {}
|
||||||
// }}}
|
// }}}
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Deliver embed
|
// {{{ Deliver embed
|
||||||
let (mut embed, attachment) = play.to_embed(&song, &chart, i).await?;
|
let (mut embed, attachment) = play.to_embed(&song, &chart, i, None).await?;
|
||||||
if let Some(warning) = score_warning {
|
if let Some(warning) = score_warning {
|
||||||
embed = embed.description(warning);
|
embed = embed.description(warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
embeds.push(embed);
|
embeds.push(embed);
|
||||||
if let Some(attachment) = attachment {
|
attachments.extend(attachment);
|
||||||
attachments.push(attachment);
|
|
||||||
}
|
|
||||||
// }}}
|
// }}}
|
||||||
} else {
|
} else {
|
||||||
ctx.reply("One of the attached files is not an image!")
|
ctx.reply("One of the attached files is not an image!")
|
||||||
|
@ -350,10 +319,8 @@ Title error: {}
|
||||||
|
|
||||||
handle.delete(ctx).await?;
|
handle.delete(ctx).await?;
|
||||||
|
|
||||||
let msg = CreateMessage::new().embeds(embeds);
|
|
||||||
|
|
||||||
ctx.channel_id()
|
ctx.channel_id()
|
||||||
.send_files(ctx.http(), attachments, msg)
|
.send_files(ctx.http(), attachments, CreateMessage::new().embeds(embeds))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,3 +370,66 @@ pub async fn delete(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
// {{{ Score show
|
||||||
|
/// Show scores given their ides
|
||||||
|
#[poise::command(prefix_command, slash_command)]
|
||||||
|
pub async fn show(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "Ids of score to show"] ids: Vec<u32>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if ids.len() == 0 {
|
||||||
|
ctx.reply("Empty ID list provided").await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let lock = ctx.data().song_cache.lock().await;
|
||||||
|
|
||||||
|
let mut embeds = Vec::with_capacity(ids.len());
|
||||||
|
let mut attachments = Vec::with_capacity(ids.len());
|
||||||
|
for (i, id) in ids.iter().enumerate() {
|
||||||
|
let res = query!(
|
||||||
|
"
|
||||||
|
SELECT
|
||||||
|
p.id,p.chart_id,p.user_id,p.score,p.zeta_score,
|
||||||
|
p.max_recall,p.created_at,p.far_notes,
|
||||||
|
u.discord_id
|
||||||
|
FROM plays p
|
||||||
|
JOIN users u ON p.user_id = u.id
|
||||||
|
WHERE p.id=?
|
||||||
|
",
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.fetch_one(&ctx.data().db)
|
||||||
|
.await
|
||||||
|
.map_err(|_| format!("Could not find play with id {}", id))?;
|
||||||
|
|
||||||
|
let play = Play {
|
||||||
|
id: res.id as u32,
|
||||||
|
chart_id: res.chart_id as u32,
|
||||||
|
user_id: res.user_id as u32,
|
||||||
|
score: Score(res.score as u32),
|
||||||
|
zeta_score: Score(res.zeta_score as u32),
|
||||||
|
max_recall: res.max_recall.map(|r| r as u32),
|
||||||
|
far_notes: res.far_notes.map(|r| r as u32),
|
||||||
|
created_at: res.created_at,
|
||||||
|
discord_attachment_id: None,
|
||||||
|
creation_ptt: None,
|
||||||
|
creation_zeta_ptt: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let user = discord_it_to_discord_user(&ctx, &res.discord_id).await?;
|
||||||
|
|
||||||
|
let (song, chart) = lock.lookup_chart(play.chart_id)?;
|
||||||
|
let (embed, attachment) = play.to_embed(song, chart, i, Some(&user)).await?;
|
||||||
|
|
||||||
|
embeds.push(embed);
|
||||||
|
attachments.extend(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.channel_id()
|
||||||
|
.send_files(ctx.http(), attachments, CreateMessage::new().embeds(embeds))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// }}}
|
245
src/commands/stats.rs
Normal file
245
src/commands/stats.rs
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use chrono::{DateTime, NaiveDateTime};
|
||||||
|
use image::{ImageBuffer, Rgb};
|
||||||
|
use plotters::{
|
||||||
|
backend::{BitMapBackend, PixelFormat, RGBPixel},
|
||||||
|
chart::{ChartBuilder, LabelAreaPosition},
|
||||||
|
drawing::IntoDrawingArea,
|
||||||
|
element::Circle,
|
||||||
|
series::LineSeries,
|
||||||
|
style::{
|
||||||
|
text_anchor::{HPos, Pos, VPos},
|
||||||
|
Color, FontTransform, IntoFont, TextStyle, BLUE, WHITE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use poise::{
|
||||||
|
serenity_prelude::{CreateAttachment, CreateMessage},
|
||||||
|
CreateReply,
|
||||||
|
};
|
||||||
|
use sqlx::query_as;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
chart::Difficulty,
|
||||||
|
context::{Context, Error},
|
||||||
|
score::{guess_chart_name, DbPlay, Score},
|
||||||
|
user::{discord_it_to_discord_user, User},
|
||||||
|
};
|
||||||
|
|
||||||
|
// {{{ Stats
|
||||||
|
/// Stats display
|
||||||
|
#[poise::command(
|
||||||
|
prefix_command,
|
||||||
|
slash_command,
|
||||||
|
subcommands("chart"),
|
||||||
|
subcommand_required
|
||||||
|
)]
|
||||||
|
pub async fn stats(_ctx: Context<'_>) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
|
// {{{ Chart
|
||||||
|
/// Chart-related stats
|
||||||
|
#[poise::command(
|
||||||
|
prefix_command,
|
||||||
|
slash_command,
|
||||||
|
subcommands("best", "plot"),
|
||||||
|
subcommand_required
|
||||||
|
)]
|
||||||
|
pub async fn chart(_ctx: Context<'_>) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
|
// {{{ Best score
|
||||||
|
/// Show the best score on a given chart
|
||||||
|
#[poise::command(prefix_command, slash_command)]
|
||||||
|
pub async fn best(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[rest]
|
||||||
|
#[description = "Name of chart to show (difficulty at the end)"]
|
||||||
|
name: String,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let user = match User::from_context(&ctx).await {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(_) => {
|
||||||
|
ctx.say("You are not an user in my database, sorry!")
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = name.trim();
|
||||||
|
let (name, difficulty) = name
|
||||||
|
.strip_suffix("PST")
|
||||||
|
.zip(Some(Difficulty::PST))
|
||||||
|
.or_else(|| name.strip_suffix("PRS").zip(Some(Difficulty::PRS)))
|
||||||
|
.or_else(|| name.strip_suffix("FTR").zip(Some(Difficulty::FTR)))
|
||||||
|
.or_else(|| name.strip_suffix("ETR").zip(Some(Difficulty::ETR)))
|
||||||
|
.or_else(|| name.strip_suffix("BYD").zip(Some(Difficulty::BYD)))
|
||||||
|
.unwrap_or((&name, Difficulty::FTR));
|
||||||
|
|
||||||
|
let (song, chart) = guess_chart_name(name, &ctx.data().song_cache, difficulty).await?;
|
||||||
|
|
||||||
|
let play = query_as!(
|
||||||
|
DbPlay,
|
||||||
|
"
|
||||||
|
SELECT * FROM plays
|
||||||
|
WHERE user_id=?
|
||||||
|
AND chart_id=?
|
||||||
|
ORDER BY score DESC
|
||||||
|
",
|
||||||
|
user.id,
|
||||||
|
chart.id
|
||||||
|
)
|
||||||
|
.fetch_one(&ctx.data().db)
|
||||||
|
.await
|
||||||
|
.map_err(|_| format!("Could not find any scores for chart"))?
|
||||||
|
.to_play();
|
||||||
|
|
||||||
|
let (embed, attachment) = play
|
||||||
|
.to_embed(
|
||||||
|
&song,
|
||||||
|
&chart,
|
||||||
|
0,
|
||||||
|
Some(&discord_it_to_discord_user(&ctx, &user.discord_id).await?),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
ctx.channel_id()
|
||||||
|
.send_files(ctx.http(), attachment, CreateMessage::new().embed(embed))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
|
// Score plot
|
||||||
|
/// Show the best score on a given chart
|
||||||
|
#[poise::command(prefix_command, slash_command)]
|
||||||
|
pub async fn plot(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[rest]
|
||||||
|
#[description = "Name of chart to show (difficulty at the end)"]
|
||||||
|
name: String,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let user = match User::from_context(&ctx).await {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(_) => {
|
||||||
|
ctx.say("You are not an user in my database, sorry!")
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = name.trim();
|
||||||
|
let (name, difficulty) = name
|
||||||
|
.strip_suffix("PST")
|
||||||
|
.zip(Some(Difficulty::PST))
|
||||||
|
.or_else(|| name.strip_suffix("PRS").zip(Some(Difficulty::PRS)))
|
||||||
|
.or_else(|| name.strip_suffix("FTR").zip(Some(Difficulty::FTR)))
|
||||||
|
.or_else(|| name.strip_suffix("ETR").zip(Some(Difficulty::ETR)))
|
||||||
|
.or_else(|| name.strip_suffix("BYD").zip(Some(Difficulty::BYD)))
|
||||||
|
.unwrap_or((&name, Difficulty::FTR));
|
||||||
|
|
||||||
|
let (song, chart) = guess_chart_name(name, &ctx.data().song_cache, difficulty).await?;
|
||||||
|
|
||||||
|
let plays = query_as!(
|
||||||
|
DbPlay,
|
||||||
|
"
|
||||||
|
SELECT * FROM plays
|
||||||
|
WHERE user_id=?
|
||||||
|
AND chart_id=?
|
||||||
|
ORDER BY created_at ASC
|
||||||
|
",
|
||||||
|
user.id,
|
||||||
|
chart.id
|
||||||
|
)
|
||||||
|
.fetch_all(&ctx.data().db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if plays.len() == 0 {
|
||||||
|
ctx.reply("No plays found").await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let min_time = plays.iter().map(|p| p.created_at).min().unwrap();
|
||||||
|
let max_time = plays.iter().map(|p| p.created_at).max().unwrap();
|
||||||
|
let mut min_score = plays.iter().map(|p| p.score).min().unwrap();
|
||||||
|
|
||||||
|
if min_score > 9_900_000 {
|
||||||
|
min_score = 9_800_000;
|
||||||
|
} else if min_score > 9_800_000 {
|
||||||
|
min_score = 9_800_000;
|
||||||
|
} else if min_score > 9_500_000 {
|
||||||
|
min_score = 9_500_000;
|
||||||
|
} else {
|
||||||
|
min_score = 9_000_000
|
||||||
|
};
|
||||||
|
|
||||||
|
let max_score = 10_010_000;
|
||||||
|
let width = 1024;
|
||||||
|
let height = 768;
|
||||||
|
|
||||||
|
let mut buffer = vec![u8::MAX; RGBPixel::PIXEL_SIZE * (width * height) as usize];
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut root = BitMapBackend::with_buffer(&mut buffer, (width, height)).into_drawing_area();
|
||||||
|
|
||||||
|
let mut chart = ChartBuilder::on(&root)
|
||||||
|
.margin(25)
|
||||||
|
.caption(
|
||||||
|
format!("{} [{:?}]", song.title, chart.difficulty),
|
||||||
|
("sans-serif", 40),
|
||||||
|
)
|
||||||
|
.set_label_area_size(LabelAreaPosition::Left, 100)
|
||||||
|
.set_label_area_size(LabelAreaPosition::Bottom, 40)
|
||||||
|
.build_cartesian_2d(
|
||||||
|
min_time.and_utc().timestamp_millis()..max_time.and_utc().timestamp_millis(),
|
||||||
|
min_score..max_score,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
chart
|
||||||
|
.configure_mesh()
|
||||||
|
.light_line_style(WHITE)
|
||||||
|
.y_label_formatter(&|s| format!("{}", Score(*s as u32)))
|
||||||
|
.y_desc("Score")
|
||||||
|
.x_label_formatter(&|d| {
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
DateTime::from_timestamp_millis(*d).unwrap().date_naive()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.y_label_style(TextStyle::from(("sans-serif", 20).into_font()))
|
||||||
|
.x_label_style(TextStyle::from(("sans-serif", 20).into_font()))
|
||||||
|
.draw()?;
|
||||||
|
|
||||||
|
let mut points: Vec<_> = plays
|
||||||
|
.iter()
|
||||||
|
.map(|play| (play.created_at.and_utc().timestamp_millis(), play.score))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
points.sort();
|
||||||
|
points.dedup();
|
||||||
|
|
||||||
|
chart.draw_series(LineSeries::new(points.iter().map(|(t, s)| (*t, *s)), &BLUE))?;
|
||||||
|
|
||||||
|
chart.draw_series(
|
||||||
|
points
|
||||||
|
.iter()
|
||||||
|
.map(|(t, s)| Circle::new((*t, *s), 3, BLUE.filled())),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
root.present()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let image: ImageBuffer<Rgb<u8>, _> = ImageBuffer::from_raw(width, height, buffer).unwrap();
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
let mut cursor = Cursor::new(&mut buffer);
|
||||||
|
image.write_to(&mut cursor, image::ImageFormat::Png)?;
|
||||||
|
|
||||||
|
let reply = CreateReply::default().attachment(CreateAttachment::bytes(buffer, "plot.png"));
|
||||||
|
ctx.send(reply).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
//
|
|
@ -1,9 +1,7 @@
|
||||||
use std::{
|
use std::{path::PathBuf, sync::Arc};
|
||||||
path::PathBuf,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::{chart::SongCache, jacket::JacketCache};
|
use crate::{chart::SongCache, jacket::JacketCache};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![warn(clippy::str_to_string)]
|
#![warn(clippy::str_to_string)]
|
||||||
#![feature(iter_map_windows)]
|
#![feature(iter_map_windows)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![feature(async_closure)]
|
||||||
|
|
||||||
mod chart;
|
mod chart;
|
||||||
mod commands;
|
mod commands;
|
||||||
|
@ -9,7 +10,6 @@ mod jacket;
|
||||||
mod score;
|
mod score;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
use chart::Difficulty;
|
|
||||||
use context::{Error, UserContext};
|
use context::{Error, UserContext};
|
||||||
use poise::serenity_prelude::{self as serenity};
|
use poise::serenity_prelude::{self as serenity};
|
||||||
use sqlx::sqlite::SqlitePoolOptions;
|
use sqlx::sqlite::SqlitePoolOptions;
|
||||||
|
@ -38,7 +38,11 @@ async fn main() {
|
||||||
|
|
||||||
// {{{ Poise options
|
// {{{ Poise options
|
||||||
let options = poise::FrameworkOptions {
|
let options = poise::FrameworkOptions {
|
||||||
commands: vec![commands::help(), commands::score()],
|
commands: vec![
|
||||||
|
commands::score::help(),
|
||||||
|
commands::score::score(),
|
||||||
|
commands::stats::stats(),
|
||||||
|
],
|
||||||
prefix_options: poise::PrefixFrameworkOptions {
|
prefix_options: poise::PrefixFrameworkOptions {
|
||||||
stripped_dynamic_prefix: Some(|_ctx, message, _user_ctx| {
|
stripped_dynamic_prefix: Some(|_ctx, message, _user_ctx| {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
|
|
1210
src/score.rs
1210
src/score.rs
File diff suppressed because it is too large
Load diff
15
src/user.rs
15
src/user.rs
|
@ -1,3 +1,7 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use poise::serenity_prelude::UserId;
|
||||||
|
|
||||||
use crate::context::{Context, Error};
|
use crate::context::{Context, Error};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -21,3 +25,14 @@ impl User {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub async fn discord_it_to_discord_user(
|
||||||
|
&ctx: &Context<'_>,
|
||||||
|
discord_id: &str,
|
||||||
|
) -> Result<poise::serenity_prelude::User, Error> {
|
||||||
|
UserId::from_str(discord_id)?
|
||||||
|
.to_user(ctx.http())
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue