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"
|
||||
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]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
|
@ -494,24 +530,13 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.3.0"
|
||||
name = "cstr"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
|
||||
checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636"
|
||||
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",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -634,12 +659,33 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "edit-distance"
|
||||
version = "2.1.0"
|
||||
|
@ -655,12 +701,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.34"
|
||||
|
@ -728,28 +768,6 @@ dependencies = [
|
|||
"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]]
|
||||
name = "fastrand"
|
||||
version = "2.1.0"
|
||||
|
@ -775,6 +793,12 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-ord"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
|
@ -792,6 +816,58 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
|
@ -801,6 +877,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
|
@ -930,6 +1017,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "gif"
|
||||
version = "0.13.1"
|
||||
|
@ -1176,6 +1273,20 @@ dependencies = [
|
|||
"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]]
|
||||
name = "image"
|
||||
version = "0.25.1"
|
||||
|
@ -1186,7 +1297,7 @@ dependencies = [
|
|||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"gif 0.13.1",
|
||||
"image-webp",
|
||||
"num-traits",
|
||||
"png",
|
||||
|
@ -1242,17 +1353,6 @@ version = "2.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
|
@ -1709,6 +1809,25 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
|
@ -1770,13 +1889,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "plotlib"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9462104f987d8d0f6625f0c7764f1c8b890bd1dc8584d8293e031f25c5a0d242"
|
||||
name = "plotters"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/starlitcanopy/plotters.git?rev=986cd959362a2dbec8d1b25670fd083b904d7b8c#986cd959362a2dbec8d1b25670fd083b904d7b8c"
|
||||
dependencies = [
|
||||
"failure",
|
||||
"svg",
|
||||
"chrono",
|
||||
"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]]
|
||||
|
@ -1833,20 +1984,6 @@ version = "0.2.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.85"
|
||||
|
@ -2168,6 +2305,15 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
|
@ -2243,12 +2389,6 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
|
@ -2420,14 +2560,12 @@ name = "shimmeringmoon"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"csv",
|
||||
"edit-distance",
|
||||
"image",
|
||||
"image 0.25.1",
|
||||
"kd-tree",
|
||||
"num",
|
||||
"plotlib",
|
||||
"plotters",
|
||||
"poise",
|
||||
"prettytable-rs",
|
||||
"sqlx",
|
||||
"tesseract",
|
||||
"tokio",
|
||||
|
@ -2761,12 +2899,6 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "svg"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3685c82a045a6af0c488f0550b0f52b4c77d2a52b0ca8aba719f9d268fa96965"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
@ -2795,18 +2927,6 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
|
@ -2865,17 +2985,6 @@ dependencies = [
|
|||
"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]]
|
||||
name = "tesseract"
|
||||
version = "0.15.1"
|
||||
|
@ -3160,6 +3269,12 @@ version = "0.2.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.21.0"
|
||||
|
@ -3264,18 +3379,6 @@ version = "1.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
|
@ -3702,6 +3805,27 @@ dependencies = [
|
|||
"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]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.34"
|
||||
|
|
|
@ -5,14 +5,12 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
chrono = "0.4.38"
|
||||
csv = "1.3.0"
|
||||
edit-distance = "2.1.0"
|
||||
image = "0.25.1"
|
||||
kd-tree = "0.6.0"
|
||||
num = "0.4.3"
|
||||
plotlib = "0.5.1"
|
||||
plotters = { git="https://github.com/starlitcanopy/plotters.git", rev="986cd959362a2dbec8d1b25670fd083b904d7b8c" }
|
||||
poise = "0.6.1"
|
||||
prettytable-rs = "0.10.0"
|
||||
sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio", "chrono"] }
|
||||
tesseract = "0.15.1"
|
||||
tokio = {version="1.38.0", features=["rt-multi-thread"]}
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
rust-analyzer-nightly
|
||||
ruff
|
||||
imagemagick
|
||||
fontconfig
|
||||
freetype
|
||||
|
||||
clang
|
||||
llvmPackages.clang
|
||||
|
|
|
@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS plays (
|
|||
creation_zeta_ptt INTEGER,
|
||||
|
||||
score INTEGER NOT NULL,
|
||||
zeta_score INTEGER,
|
||||
zeta_score INTEGER NOT NULL,
|
||||
|
||||
max_recall 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;
|
||||
|
||||
// {{{ Difficuly
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sqlx::Type)]
|
||||
pub enum Difficulty {
|
||||
PST,
|
||||
PRS,
|
||||
|
@ -85,17 +85,31 @@ impl CachedSong {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn lookup(&self, difficulty: Difficulty) -> Option<&Chart> {
|
||||
pub fn lookup(&self, difficulty: Difficulty) -> Result<&Chart, Error> {
|
||||
self.charts
|
||||
.get(difficulty.to_index())
|
||||
.and_then(|c| c.as_ref())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Could not find difficulty {:?} for song {}",
|
||||
difficulty, self.song.title
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[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
|
||||
.get_mut(difficulty.to_index())
|
||||
.and_then(|c| c.as_mut())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Could not find difficulty {:?} for song {}",
|
||||
difficulty, self.song.title
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -117,8 +131,26 @@ impl SongCache {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn lookup_mut(&mut self, id: u32) -> Option<&mut CachedSong> {
|
||||
self.songs.get_mut(id as usize).and_then(|i| i.as_mut())
|
||||
pub fn lookup_chart(&self, chart_id: u32) -> Result<(&Song, &Chart), Error> {
|
||||
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]
|
||||
|
|
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 crate::context::{Context, Error};
|
||||
use crate::score::{jacket_rects, CreatePlay, ImageCropper, ImageDimensions, RelativeRect, Score};
|
||||
use crate::user::User;
|
||||
use crate::score::{
|
||||
jacket_rects, CreatePlay, ImageCropper, ImageDimensions, Play, RelativeRect, Score,
|
||||
};
|
||||
use crate::user::{discord_it_to_discord_user, User};
|
||||
use image::imageops::FilterType;
|
||||
use image::ImageFormat;
|
||||
use poise::serenity_prelude::{CreateAttachment, CreateEmbed, CreateMessage};
|
||||
|
@ -36,7 +38,7 @@ pub async fn help(
|
|||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
subcommands("magic", "delete"),
|
||||
subcommands("magic", "delete", "show"),
|
||||
subcommand_required
|
||||
)]
|
||||
pub async fn score(_ctx: Context<'_>) -> Result<(), Error> {
|
||||
|
@ -88,8 +90,8 @@ pub async fn magic(
|
|||
if files.len() == 0 {
|
||||
ctx.reply("No images found attached to message").await?;
|
||||
} else {
|
||||
let mut embeds: Vec<CreateEmbed> = vec![];
|
||||
let mut attachments: Vec<CreateAttachment> = vec![];
|
||||
let mut embeds = Vec::with_capacity(files.len());
|
||||
let mut attachments = Vec::with_capacity(files.len());
|
||||
let handle = ctx
|
||||
.reply(format!("Processed 0/{} scores", files.len()))
|
||||
.await?;
|
||||
|
@ -116,7 +118,7 @@ pub async fn magic(
|
|||
.content(format!("Image {}: reading jacket", i + 1));
|
||||
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
|
||||
let mut ocr_image = image.grayscale().blur(1.);
|
||||
|
@ -151,8 +153,10 @@ pub async fn magic(
|
|||
.content(format!("Image {}: reading title", i + 1));
|
||||
handle.edit(ctx, edited).await?;
|
||||
|
||||
let song_by_name = cropper.read_song(&ocr_image, &ctx.data().song_cache);
|
||||
let cached_song = match (song_by_jacket, song_by_name) {
|
||||
let song_by_name = cropper
|
||||
.read_song(&ocr_image, &ctx.data().song_cache, difficulty)
|
||||
.await;
|
||||
let (song, chart) = match (song_by_jacket, song_by_name) {
|
||||
// {{{ Both errors
|
||||
(Err(err_jacket), Err(err_name)) => {
|
||||
error_with_image(
|
||||
|
@ -196,16 +200,8 @@ Title error: {}
|
|||
.ok_or_else(|| "Could not find jacket area in picture")?
|
||||
.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
|
||||
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_dir = ctx.data().data_dir.join("jackets/user");
|
||||
|
@ -221,42 +217,27 @@ Title error: {}
|
|||
sqlx::query!(
|
||||
"UPDATE charts SET jacket=? WHERE song_id=? AND difficulty=?",
|
||||
jacket,
|
||||
chart.song_id,
|
||||
chart.difficulty,
|
||||
by_name.1.song_id,
|
||||
by_name.1.difficulty,
|
||||
)
|
||||
.execute(&ctx.data().db)
|
||||
.await?;
|
||||
// }}}
|
||||
// {{{ Aquire and use song cache lock
|
||||
{
|
||||
let mut song_cache = ctx
|
||||
.data()
|
||||
.song_cache
|
||||
.lock()
|
||||
.map_err(|_| "Poisoned song cache")?;
|
||||
let mut song_cache = ctx.data().song_cache.lock().await;
|
||||
|
||||
let chart = song_cache
|
||||
.lookup_mut(by_name.song.id)
|
||||
.ok_or_else(|| {
|
||||
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
|
||||
)
|
||||
})?;
|
||||
.lookup_mut(by_name.0.id)?
|
||||
.lookup_mut(difficulty)?;
|
||||
|
||||
if chart.jacket.is_none() {
|
||||
if let Some(chart) = by_name.lookup_mut(difficulty) {
|
||||
chart.jacket = Some(jacket_path.clone());
|
||||
};
|
||||
by_name.1.jacket = Some(jacket_path.clone());
|
||||
chart.jacket = Some(jacket_path);
|
||||
} else {
|
||||
println!(
|
||||
"Jacket not detected for chart {} [{:?}]",
|
||||
by_name.song.id, difficulty
|
||||
by_name.0.id, difficulty
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -267,10 +248,10 @@ Title error: {}
|
|||
// }}}
|
||||
// {{{ Both succeeded
|
||||
(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!(
|
||||
"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()
|
||||
.reply(true)
|
||||
.content(format!("Image {}: reading score", i + 1));
|
||||
|
@ -315,7 +286,7 @@ Title error: {}
|
|||
// {{{ Build play
|
||||
let (score, maybe_fars, score_warning) =
|
||||
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_fars(maybe_fars)
|
||||
.save(&ctx.data())
|
||||
|
@ -323,15 +294,13 @@ Title error: {}
|
|||
// }}}
|
||||
// }}}
|
||||
// {{{ 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 {
|
||||
embed = embed.description(warning);
|
||||
}
|
||||
|
||||
embeds.push(embed);
|
||||
if let Some(attachment) = attachment {
|
||||
attachments.push(attachment);
|
||||
}
|
||||
attachments.extend(attachment);
|
||||
// }}}
|
||||
} else {
|
||||
ctx.reply("One of the attached files is not an image!")
|
||||
|
@ -350,10 +319,8 @@ Title error: {}
|
|||
|
||||
handle.delete(ctx).await?;
|
||||
|
||||
let msg = CreateMessage::new().embeds(embeds);
|
||||
|
||||
ctx.channel_id()
|
||||
.send_files(ctx.http(), attachments, msg)
|
||||
.send_files(ctx.http(), attachments, CreateMessage::new().embeds(embeds))
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
@ -403,3 +370,66 @@ pub async fn delete(
|
|||
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::{
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use sqlx::SqlitePool;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{chart::SongCache, jacket::JacketCache};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![warn(clippy::str_to_string)]
|
||||
#![feature(iter_map_windows)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(async_closure)]
|
||||
|
||||
mod chart;
|
||||
mod commands;
|
||||
|
@ -9,7 +10,6 @@ mod jacket;
|
|||
mod score;
|
||||
mod user;
|
||||
|
||||
use chart::Difficulty;
|
||||
use context::{Error, UserContext};
|
||||
use poise::serenity_prelude::{self as serenity};
|
||||
use sqlx::sqlite::SqlitePoolOptions;
|
||||
|
@ -38,7 +38,11 @@ async fn main() {
|
|||
|
||||
// {{{ Poise options
|
||||
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 {
|
||||
stripped_dynamic_prefix: Some(|_ctx, message, _user_ctx| {
|
||||
Box::pin(async {
|
||||
|
|
1222
src/score.rs
1222
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};
|
||||
|
||||
#[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