diff --git a/youmubot-db-sql/.sqlx/query-246e26a34c042872a77f53a84d62da31db069cced20e3b0f96a40c3c7dd99783.json b/youmubot-db-sql/.sqlx/query-246e26a34c042872a77f53a84d62da31db069cced20e3b0f96a40c3c7dd99783.json new file mode 100644 index 0000000..1ad9d55 --- /dev/null +++ b/youmubot-db-sql/.sqlx/query-246e26a34c042872a77f53a84d62da31db069cced20e3b0f96a40c3c7dd99783.json @@ -0,0 +1,38 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n failures as \"failures: u8\"\n FROM osu_users", + "describe": { + "columns": [ + { + "name": "user_id: i64", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "username", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "id: i64", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "failures: u8", + "ordinal": 3, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + true, + false, + false + ] + }, + "hash": "246e26a34c042872a77f53a84d62da31db069cced20e3b0f96a40c3c7dd99783" +} diff --git a/youmubot-db-sql/.sqlx/query-6ef67ca385287a4cef9fdd47bf4258ec9de4802d90dbb2ab48de32c1a4ada601.json b/youmubot-db-sql/.sqlx/query-6ef67ca385287a4cef9fdd47bf4258ec9de4802d90dbb2ab48de32c1a4ada601.json deleted file mode 100644 index 9935fbd..0000000 --- a/youmubot-db-sql/.sqlx/query-6ef67ca385287a4cef9fdd47bf4258ec9de4802d90dbb2ab48de32c1a4ada601.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "db_name": "SQLite", - "query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n last_update as \"last_update: DateTime\",\n pp_std, pp_taiko, pp_mania, pp_catch,\n failures as \"failures: u8\",\n std_weighted_map_length\n FROM osu_users", - "describe": { - "columns": [ - { - "name": "user_id: i64", - "ordinal": 0, - "type_info": "Int64" - }, - { - "name": "username", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "id: i64", - "ordinal": 2, - "type_info": "Int64" - }, - { - "name": "last_update: DateTime", - "ordinal": 3, - "type_info": "Datetime" - }, - { - "name": "pp_std", - "ordinal": 4, - "type_info": "Float" - }, - { - "name": "pp_taiko", - "ordinal": 5, - "type_info": "Float" - }, - { - "name": "pp_mania", - "ordinal": 6, - "type_info": "Float" - }, - { - "name": "pp_catch", - "ordinal": 7, - "type_info": "Float" - }, - { - "name": "failures: u8", - "ordinal": 8, - "type_info": "Int64" - }, - { - "name": "std_weighted_map_length", - "ordinal": 9, - "type_info": "Float" - } - ], - "parameters": { - "Right": 0 - }, - "nullable": [ - false, - true, - false, - false, - true, - true, - true, - true, - false, - true - ] - }, - "hash": "6ef67ca385287a4cef9fdd47bf4258ec9de4802d90dbb2ab48de32c1a4ada601" -} diff --git a/youmubot-db-sql/.sqlx/query-9b7788f4d7144fe00f4bc9004c88dc8562ff3d7a931fc3f1dc039cc55fe3195a.json b/youmubot-db-sql/.sqlx/query-9b7788f4d7144fe00f4bc9004c88dc8562ff3d7a931fc3f1dc039cc55fe3195a.json new file mode 100644 index 0000000..9f4d11e --- /dev/null +++ b/youmubot-db-sql/.sqlx/query-9b7788f4d7144fe00f4bc9004c88dc8562ff3d7a931fc3f1dc039cc55fe3195a.json @@ -0,0 +1,38 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n failures as \"failures: u8\"\n FROM osu_users WHERE id = ?", + "describe": { + "columns": [ + { + "name": "user_id: i64", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "username", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "id: i64", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "failures: u8", + "ordinal": 3, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + true, + false, + false + ] + }, + "hash": "9b7788f4d7144fe00f4bc9004c88dc8562ff3d7a931fc3f1dc039cc55fe3195a" +} diff --git a/youmubot-db-sql/.sqlx/query-a06efa1b12c2c7c9cf5b83bff796c0e59d61596cb609c4bb952edc2d64cec868.json b/youmubot-db-sql/.sqlx/query-a06efa1b12c2c7c9cf5b83bff796c0e59d61596cb609c4bb952edc2d64cec868.json deleted file mode 100644 index f69c8f9..0000000 --- a/youmubot-db-sql/.sqlx/query-a06efa1b12c2c7c9cf5b83bff796c0e59d61596cb609c4bb952edc2d64cec868.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "INSERT\n INTO osu_users(user_id, username, id, last_update, pp_std, pp_taiko, pp_mania, pp_catch, failures, std_weighted_map_length)\n VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (user_id) WHERE id = ? DO UPDATE\n SET\n last_update = excluded.last_update,\n username = excluded.username,\n pp_std = excluded.pp_std,\n pp_taiko = excluded.pp_taiko,\n pp_mania = excluded.pp_mania,\n pp_catch = excluded.pp_catch,\n failures = excluded.failures,\n std_weighted_map_length = excluded.std_weighted_map_length\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 11 - }, - "nullable": [] - }, - "hash": "a06efa1b12c2c7c9cf5b83bff796c0e59d61596cb609c4bb952edc2d64cec868" -} diff --git a/youmubot-db-sql/.sqlx/query-a247c7a73bdbf1042fdd8363da1f6b51e283c83b1ba7fa17e5cb056b220f7889.json b/youmubot-db-sql/.sqlx/query-a247c7a73bdbf1042fdd8363da1f6b51e283c83b1ba7fa17e5cb056b220f7889.json new file mode 100644 index 0000000..bb0e861 --- /dev/null +++ b/youmubot-db-sql/.sqlx/query-a247c7a73bdbf1042fdd8363da1f6b51e283c83b1ba7fa17e5cb056b220f7889.json @@ -0,0 +1,44 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n mode as \"mode: u8\",\n pp,\n map_length,\n map_age,\n last_update as \"last_update: DateTime\"\n FROM osu_user_mode_stats\n WHERE user_id = ?\n ORDER BY mode ASC", + "describe": { + "columns": [ + { + "name": "mode: u8", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "pp", + "ordinal": 1, + "type_info": "Float" + }, + { + "name": "map_length", + "ordinal": 2, + "type_info": "Float" + }, + { + "name": "map_age", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "last_update: DateTime", + "ordinal": 4, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "a247c7a73bdbf1042fdd8363da1f6b51e283c83b1ba7fa17e5cb056b220f7889" +} diff --git a/youmubot-db-sql/.sqlx/query-a5d8dccaaf80b2673c5c0e689c01a90861788ca84221baaaf19cd159ed3062c9.json b/youmubot-db-sql/.sqlx/query-a5d8dccaaf80b2673c5c0e689c01a90861788ca84221baaaf19cd159ed3062c9.json new file mode 100644 index 0000000..df1a510 --- /dev/null +++ b/youmubot-db-sql/.sqlx/query-a5d8dccaaf80b2673c5c0e689c01a90861788ca84221baaaf19cd159ed3062c9.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT\n INTO osu_users(user_id, username, id, failures)\n VALUES(?, ?, ?, ?)\n ON CONFLICT (user_id) WHERE id = ? DO UPDATE\n SET\n username = excluded.username,\n failures = excluded.failures\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 5 + }, + "nullable": [] + }, + "hash": "a5d8dccaaf80b2673c5c0e689c01a90861788ca84221baaaf19cd159ed3062c9" +} diff --git a/youmubot-db-sql/.sqlx/query-aa6db751abe51922548d6c040d40ef57cae1cf6338523fd2b801b482164d54fa.json b/youmubot-db-sql/.sqlx/query-aa6db751abe51922548d6c040d40ef57cae1cf6338523fd2b801b482164d54fa.json new file mode 100644 index 0000000..3e9a7a2 --- /dev/null +++ b/youmubot-db-sql/.sqlx/query-aa6db751abe51922548d6c040d40ef57cae1cf6338523fd2b801b482164d54fa.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "DELETE FROM osu_user_mode_stats WHERE user_id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "aa6db751abe51922548d6c040d40ef57cae1cf6338523fd2b801b482164d54fa" +} diff --git a/youmubot-db-sql/.sqlx/query-ae7f57eb92e0bec8439e682ab3ca10732991ffe803d05b09e908ecb4a74c0566.json b/youmubot-db-sql/.sqlx/query-ae7f57eb92e0bec8439e682ab3ca10732991ffe803d05b09e908ecb4a74c0566.json new file mode 100644 index 0000000..02e0cc0 --- /dev/null +++ b/youmubot-db-sql/.sqlx/query-ae7f57eb92e0bec8439e682ab3ca10732991ffe803d05b09e908ecb4a74c0566.json @@ -0,0 +1,38 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n failures as \"failures: u8\"\n FROM osu_users WHERE user_id = ?", + "describe": { + "columns": [ + { + "name": "user_id: i64", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "username", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "id: i64", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "failures: u8", + "ordinal": 3, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + true, + false, + false + ] + }, + "hash": "ae7f57eb92e0bec8439e682ab3ca10732991ffe803d05b09e908ecb4a74c0566" +} diff --git a/youmubot-db-sql/.sqlx/query-b098282e73cc6fd435330f6ecd446b1a1cd2aeb89517b7ee09e7e6f8d6e0cd79.json b/youmubot-db-sql/.sqlx/query-b098282e73cc6fd435330f6ecd446b1a1cd2aeb89517b7ee09e7e6f8d6e0cd79.json deleted file mode 100644 index 9d44a8a..0000000 --- a/youmubot-db-sql/.sqlx/query-b098282e73cc6fd435330f6ecd446b1a1cd2aeb89517b7ee09e7e6f8d6e0cd79.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "db_name": "SQLite", - "query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n last_update as \"last_update: DateTime\",\n pp_std, pp_taiko, pp_mania, pp_catch,\n failures as \"failures: u8\",\n std_weighted_map_length\n FROM osu_users WHERE id = ?", - "describe": { - "columns": [ - { - "name": "user_id: i64", - "ordinal": 0, - "type_info": "Int64" - }, - { - "name": "username", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "id: i64", - "ordinal": 2, - "type_info": "Int64" - }, - { - "name": "last_update: DateTime", - "ordinal": 3, - "type_info": "Datetime" - }, - { - "name": "pp_std", - "ordinal": 4, - "type_info": "Float" - }, - { - "name": "pp_taiko", - "ordinal": 5, - "type_info": "Float" - }, - { - "name": "pp_mania", - "ordinal": 6, - "type_info": "Float" - }, - { - "name": "pp_catch", - "ordinal": 7, - "type_info": "Float" - }, - { - "name": "failures: u8", - "ordinal": 8, - "type_info": "Int64" - }, - { - "name": "std_weighted_map_length", - "ordinal": 9, - "type_info": "Float" - } - ], - "parameters": { - "Right": 1 - }, - "nullable": [ - false, - true, - false, - false, - true, - true, - true, - true, - false, - true - ] - }, - "hash": "b098282e73cc6fd435330f6ecd446b1a1cd2aeb89517b7ee09e7e6f8d6e0cd79" -} diff --git a/youmubot-db-sql/.sqlx/query-d2c0856276174653a9dd428a2f89e7841b2083ee4b18ea83eedcd46bc4356599.json b/youmubot-db-sql/.sqlx/query-d2c0856276174653a9dd428a2f89e7841b2083ee4b18ea83eedcd46bc4356599.json new file mode 100644 index 0000000..77e4dc3 --- /dev/null +++ b/youmubot-db-sql/.sqlx/query-d2c0856276174653a9dd428a2f89e7841b2083ee4b18ea83eedcd46bc4356599.json @@ -0,0 +1,50 @@ +{ + "db_name": "SQLite", + "query": "SELECT\n user_id as \"user_id: i64\",\n mode as \"mode: u8\",\n pp,\n map_length,\n map_age,\n last_update as \"last_update: DateTime\"\n FROM osu_user_mode_stats\n ORDER BY user_id ASC, mode ASC", + "describe": { + "columns": [ + { + "name": "user_id: i64", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "mode: u8", + "ordinal": 1, + "type_info": "Int64" + }, + { + "name": "pp", + "ordinal": 2, + "type_info": "Float" + }, + { + "name": "map_length", + "ordinal": 3, + "type_info": "Float" + }, + { + "name": "map_age", + "ordinal": 4, + "type_info": "Int64" + }, + { + "name": "last_update: DateTime", + "ordinal": 5, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false, + false, + false, + false + ] + }, + "hash": "d2c0856276174653a9dd428a2f89e7841b2083ee4b18ea83eedcd46bc4356599" +} diff --git a/youmubot-db-sql/.sqlx/query-df0aa5065268e59c68990ab46ab4a90ec3137398e83b3d0c626209306804399a.json b/youmubot-db-sql/.sqlx/query-df0aa5065268e59c68990ab46ab4a90ec3137398e83b3d0c626209306804399a.json deleted file mode 100644 index bbca50f..0000000 --- a/youmubot-db-sql/.sqlx/query-df0aa5065268e59c68990ab46ab4a90ec3137398e83b3d0c626209306804399a.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "db_name": "SQLite", - "query": "SELECT\n user_id as \"user_id: i64\",\n username,\n id as \"id: i64\",\n last_update as \"last_update: DateTime\",\n pp_std, pp_taiko, pp_mania, pp_catch,\n failures as \"failures: u8\",\n std_weighted_map_length\n FROM osu_users WHERE user_id = ?", - "describe": { - "columns": [ - { - "name": "user_id: i64", - "ordinal": 0, - "type_info": "Int64" - }, - { - "name": "username", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "id: i64", - "ordinal": 2, - "type_info": "Int64" - }, - { - "name": "last_update: DateTime", - "ordinal": 3, - "type_info": "Datetime" - }, - { - "name": "pp_std", - "ordinal": 4, - "type_info": "Float" - }, - { - "name": "pp_taiko", - "ordinal": 5, - "type_info": "Float" - }, - { - "name": "pp_mania", - "ordinal": 6, - "type_info": "Float" - }, - { - "name": "pp_catch", - "ordinal": 7, - "type_info": "Float" - }, - { - "name": "failures: u8", - "ordinal": 8, - "type_info": "Int64" - }, - { - "name": "std_weighted_map_length", - "ordinal": 9, - "type_info": "Float" - } - ], - "parameters": { - "Right": 1 - }, - "nullable": [ - false, - true, - false, - false, - true, - true, - true, - true, - false, - true - ] - }, - "hash": "df0aa5065268e59c68990ab46ab4a90ec3137398e83b3d0c626209306804399a" -} diff --git a/youmubot-db-sql/.sqlx/query-f356ab7ef8b7c320d29ad9cce03f66defcfbe67a7c4ccac16dd41508bc2d829d.json b/youmubot-db-sql/.sqlx/query-f356ab7ef8b7c320d29ad9cce03f66defcfbe67a7c4ccac16dd41508bc2d829d.json new file mode 100644 index 0000000..3ae964b --- /dev/null +++ b/youmubot-db-sql/.sqlx/query-f356ab7ef8b7c320d29ad9cce03f66defcfbe67a7c4ccac16dd41508bc2d829d.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO osu_user_mode_stats (user_id, mode, pp, map_length, map_age, last_update) VALUES (?, ?, ?, ?, ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 6 + }, + "nullable": [] + }, + "hash": "f356ab7ef8b7c320d29ad9cce03f66defcfbe67a7c4ccac16dd41508bc2d829d" +} diff --git a/youmubot-db-sql/migrations/20240724031746_move_osu_mode_data_to_own_tables.sql b/youmubot-db-sql/migrations/20240724031746_move_osu_mode_data_to_own_tables.sql new file mode 100644 index 0000000..c20af3a --- /dev/null +++ b/youmubot-db-sql/migrations/20240724031746_move_osu_mode_data_to_own_tables.sql @@ -0,0 +1,60 @@ +-- Add migration script here + +CREATE TABLE osu_user_mode_stats ( + user_id INT NOT NULL REFERENCES osu_users (user_id), + mode INT NOT NULL, + pp REAL NOT NULL DEFAULT 0, + map_length REAL NOT NULL DEFAULT 0, + map_age INT NOT NULL DEFAULT 0, + last_update INT NOT NULL, + PRIMARY KEY (user_id, mode), + CHECK (mode >= 0 AND mode < 4) +) STRICT; + +-- Try to move data to new table + +INSERT INTO osu_user_mode_stats (user_id, mode, pp, map_length, last_update) + SELECT + u.user_id, + 0 as mode, + u.pp_std as pp, + u.std_weighted_map_length as map_length, + unixepoch(u.last_update) as last_update + FROM osu_users u + WHERE u.pp_std IS NOT NULL AND u.std_weighted_map_length IS NOT NULL; + +INSERT INTO osu_user_mode_stats (user_id, mode, pp, last_update) + SELECT + u.user_id, + 1 as mode, + u.pp_taiko as pp, + unixepoch(u.last_update) as last_update + FROM osu_users u + WHERE u.pp_taiko IS NOT NULL; + +INSERT INTO osu_user_mode_stats (user_id, mode, pp, last_update) + SELECT + u.user_id, + 2 as mode, + u.pp_catch as pp, + unixepoch(u.last_update) as last_update + FROM osu_users u + WHERE u.pp_catch IS NOT NULL; + +INSERT INTO osu_user_mode_stats (user_id, mode, pp, last_update) + SELECT + u.user_id, + 3 as mode, + u.pp_mania as pp, + unixepoch(u.last_update) as last_update + FROM osu_users u + WHERE u.pp_mania IS NOT NULL; + +-- Clean up old table + +ALTER TABLE osu_users DROP COLUMN last_update; +ALTER TABLE osu_users DROP COLUMN pp_std; +ALTER TABLE osu_users DROP COLUMN pp_taiko; +ALTER TABLE osu_users DROP COLUMN pp_catch; +ALTER TABLE osu_users DROP COLUMN pp_mania; +ALTER TABLE osu_users DROP COLUMN std_weighted_map_length; diff --git a/youmubot-db-sql/src/models/mod.rs b/youmubot-db-sql/src/models/mod.rs index b71839b..59f7ecf 100644 --- a/youmubot-db-sql/src/models/mod.rs +++ b/youmubot-db-sql/src/models/mod.rs @@ -1,5 +1,4 @@ use crate::*; -use futures_util::stream::{Stream, StreamExt}; use sqlx::{query, query_as, Executor}; /// The DateTime used in the package. @@ -7,16 +6,3 @@ pub type DateTime = chrono::DateTime; pub mod osu; pub mod osu_user; - -/// Map a `fetch_many` result to a normal result. -pub(crate) async fn map_many_result( - item: Result, E>, -) -> Option> -where - E: Into, -{ - match item { - Ok(v) => v.right().map(Ok), - Err(e) => Some(Err(e.into())), - } -} diff --git a/youmubot-db-sql/src/models/osu_user.rs b/youmubot-db-sql/src/models/osu_user.rs index f743e19..74af102 100644 --- a/youmubot-db-sql/src/models/osu_user.rs +++ b/youmubot-db-sql/src/models/osu_user.rs @@ -1,5 +1,6 @@ use super::*; -use sqlx::{query, query_as, Executor}; +use sqlx::{query, query_as, Executor, Transaction}; +use std::collections::HashMap as Map; /// An osu user, as represented in the SQL. #[derive(Debug, Clone)] @@ -7,120 +8,219 @@ pub struct OsuUser { pub user_id: i64, pub username: Option, // should always be there pub id: i64, - pub last_update: DateTime, - pub pp_std: Option, - pub pp_taiko: Option, - pub pp_mania: Option, - pub pp_catch: Option, + pub modes: Map, /// Number of consecutive update failures pub failures: u8, +} - pub std_weighted_map_length: Option, +/// Stats for a single user and mode. +#[derive(Debug, Clone)] +pub struct OsuUserMode { + pub pp: f64, + pub map_length: f64, + pub map_age: i64, + pub last_update: DateTime, +} + +impl OsuUserMode { + async fn from_user<'a, E>(id: i64, conn: E) -> Result> + where + E: Executor<'a, Database = Database>, + { + Ok(query!( + r#"SELECT + mode as "mode: u8", + pp, + map_length, + map_age, + last_update as "last_update: DateTime" + FROM osu_user_mode_stats + WHERE user_id = ? + ORDER BY mode ASC"#, + id + ) + .fetch_all(conn) + .await? + .into_iter() + .map(|row| { + ( + row.mode, + Self { + pp: row.pp, + map_length: row.map_length, + map_age: row.map_age, + last_update: row.last_update, + }, + ) + }) + .collect()) + } + + async fn fetch_all<'a, E>(conn: E) -> Result>> + where + E: Executor<'a, Database = Database>, + { + let mut res: Map> = Map::new(); + query!( + r#"SELECT + user_id as "user_id: i64", + mode as "mode: u8", + pp, + map_length, + map_age, + last_update as "last_update: DateTime" + FROM osu_user_mode_stats + ORDER BY user_id ASC, mode ASC"#, + ) + .fetch_all(conn) + .await? + .into_iter() + .for_each(|v| { + let modes = res.entry(v.user_id).or_default(); + modes.insert( + v.mode, + Self { + pp: v.pp, + map_length: v.map_length, + map_age: v.map_age, + last_update: v.last_update, + }, + ); + }); + Ok(res) + } +} + +mod raw { + #[derive(Debug)] + pub struct OsuUser { + pub user_id: i64, + pub username: Option, // should always be there + pub id: i64, + pub failures: u8, + } } impl OsuUser { + fn from_raw(r: raw::OsuUser, modes: Map) -> Self { + Self { + user_id: r.user_id, + username: r.username, + id: r.id, + modes, + failures: r.failures, + } + } /// Query an user by their user id. - pub async fn by_user_id<'a, E>(user_id: i64, conn: &'a mut E) -> Result> - where - &'a mut E: Executor<'a, Database = Database>, - { - let u = query_as!( - Self, + pub async fn by_user_id(user_id: i64, conn: &Pool) -> Result> { + let u = match query_as!( + raw::OsuUser, r#"SELECT user_id as "user_id: i64", username, id as "id: i64", - last_update as "last_update: DateTime", - pp_std, pp_taiko, pp_mania, pp_catch, - failures as "failures: u8", - std_weighted_map_length + failures as "failures: u8" FROM osu_users WHERE user_id = ?"#, user_id ) .fetch_optional(conn) - .await?; - Ok(u) + .await? + { + Some(v) => v, + None => return Ok(None), + }; + let modes = OsuUserMode::from_user(u.user_id, conn).await?; + Ok(Some(Self::from_raw(u, modes))) } /// Query an user by their osu id. - pub async fn by_osu_id<'a, E>(osu_id: i64, conn: &'a mut E) -> Result> - where - &'a mut E: Executor<'a, Database = Database>, - { - let u = query_as!( - Self, + pub async fn by_osu_id(osu_id: i64, conn: &Pool) -> Result> { + let u = match query_as!( + raw::OsuUser, r#"SELECT user_id as "user_id: i64", username, id as "id: i64", - last_update as "last_update: DateTime", - pp_std, pp_taiko, pp_mania, pp_catch, - failures as "failures: u8", - std_weighted_map_length + failures as "failures: u8" FROM osu_users WHERE id = ?"#, osu_id ) .fetch_optional(conn) - .await?; - Ok(u) + .await? + { + Some(v) => v, + None => return Ok(None), + }; + let modes = OsuUserMode::from_user(u.user_id, conn).await?; + Ok(Some(Self::from_raw(u, modes))) } /// Query all users. - pub fn all<'a, E>(conn: &'a mut E) -> impl Stream> + 'a - where - &'a mut E: Executor<'a, Database = Database>, - { - query_as!( - Self, + pub async fn all(conn: &Pool) -> Result> { + // last_update as "last_update: DateTime", + let us = query_as!( + raw::OsuUser, r#"SELECT user_id as "user_id: i64", username, id as "id: i64", - last_update as "last_update: DateTime", - pp_std, pp_taiko, pp_mania, pp_catch, - failures as "failures: u8", - std_weighted_map_length + failures as "failures: u8" FROM osu_users"#, ) - .fetch_many(conn) - .filter_map(map_many_result) + .fetch_all(conn) + .await?; + let mut modes = OsuUserMode::fetch_all(conn).await?; + Ok(us + .into_iter() + .map(|u| { + let m = modes.remove(&u.user_id).unwrap_or_default(); + Self::from_raw(u, m) + }) + .collect()) } } impl OsuUser { /// Stores the user. - pub async fn store<'a, E>(&self, conn: &'a mut E) -> Result<()> - where - &'a mut E: Executor<'a, Database = Database>, - { + pub async fn store<'a>(&self, conn: &mut Transaction<'a, Database>) -> Result<()> { query!( r#"INSERT - INTO osu_users(user_id, username, id, last_update, pp_std, pp_taiko, pp_mania, pp_catch, failures, std_weighted_map_length) - VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + INTO osu_users(user_id, username, id, failures) + VALUES(?, ?, ?, ?) ON CONFLICT (user_id) WHERE id = ? DO UPDATE SET - last_update = excluded.last_update, username = excluded.username, - pp_std = excluded.pp_std, - pp_taiko = excluded.pp_taiko, - pp_mania = excluded.pp_mania, - pp_catch = excluded.pp_catch, - failures = excluded.failures, - std_weighted_map_length = excluded.std_weighted_map_length + failures = excluded.failures "#, self.user_id, self.username, self.id, - self.last_update, - self.pp_std, - self.pp_taiko, - self.pp_mania, - self.pp_catch, self.failures, - self.std_weighted_map_length, - self.user_id, - ).execute(conn).await?; + ) + .execute(&mut **conn) + .await?; + // Store the modes + query!( + "DELETE FROM osu_user_mode_stats WHERE user_id = ?", + self.user_id + ) + .execute(&mut **conn) + .await?; + for (mode, stats) in &self.modes { + let ts = stats.last_update.timestamp(); + query!( + "INSERT INTO osu_user_mode_stats (user_id, mode, pp, map_length, map_age, last_update) VALUES (?, ?, ?, ?, ?, ?)", + self.user_id, + *mode, + stats.pp, + stats.map_length, + stats.map_age, + ts, + ) + .execute(&mut **conn) + .await?; + } Ok(()) }