about summary refs log tree commit diff
path: root/src/database.rs
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2025-08-03 16:40:40 -0500
committergennyble <gen@nyble.dev>2025-08-03 16:40:40 -0500
commitdc1f9d30968fe393a15ccc0dfc79af237259b9de (patch)
tree9d7c1c148db626be8d864a8d6daf720bfcd34e39 /src/database.rs
parent0150fce1af73affcbe0d58a439fc7277be027059 (diff)
downloadleaberblord-dc1f9d30968fe393a15ccc0dfc79af237259b9de.tar.gz
leaberblord-dc1f9d30968fe393a15ccc0dfc79af237259b9de.zip
break ties by timestamp
Diffstat (limited to 'src/database.rs')
-rw-r--r--src/database.rs54
1 files changed, 47 insertions, 7 deletions
diff --git a/src/database.rs b/src/database.rs
index 0fb6987..d5ccf92 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -72,18 +72,19 @@ impl Database {
 	pub fn get_leaderboard(&self, guild_id: u64) -> Result<Vec<BoardRow>, Error> {
 		// Don't deadlock!
 		let lb = self.leaderboard_id(guild_id)?;
+		let query = lb.sql_leaderboard_query();
 		let conn = self.conn.lock().unwrap();
 
-		let mut query = conn
-			.prepare(&lb.sql("SELECT * FROM leaderboard_LBID ORDER BY points DESC"))
-			.unwrap();
+		// The query that get's run sorts ties by preferring who got to that score first
+		let mut query = conn.prepare(&lb.sql(query)).unwrap();
+
 		let vec = query
 			.query_map((), |row| {
 				Ok(BoardRow {
-					user_id: row.get(0)?,
-					user_handle: row.get(1)?,
-					user_nickname: row.get(2)?,
-					points: row.get(3)?,
+					user_id: row.get(1)?,
+					user_handle: row.get(2)?,
+					user_nickname: row.get(3)?,
+					points: row.get(4)?,
 				})
 			})
 			.optional()
@@ -232,6 +233,15 @@ const CREATE_TABLE_LEADERBOARD_HISTORY: &'static str = "\
 		points INTEGER NOT NULL DEFAULT 0
 	);";
 
+const CREATE_TRIGGER_LDBCHANGETIME_UPDATE: &'static str = "\
+	CREATE TRIGGER ldbchangetime_update_LBID
+		AFTER UPDATE ON leaderboard_LBID
+		FOR EACH ROW
+		WHEN NEW.score_updated < OLD.score_updated OR OLD.score_updated IS NULL
+		BEGIN
+			UPDATE leaderboard_LBID SET score_updated=CURRENT_TIMESTAMP WHERE user_id=OLD.user_id;
+		END;";
+
 const CREATE_TRIGGER_LDBHIST_UPDATE: &'static str = "\
 	CREATE TRIGGER ldbdhist_update_LBID BEFORE UPDATE ON leaderboard_LBID
 		BEGIN
@@ -244,6 +254,19 @@ const CREATE_TRIGGER_LDBHIST_INSERT: &'static str = "\
 			INSERT INTO leaderboard_history_LBID(user_id, points) VALUES(new.user_id, new.points);
 		END;";
 
+const LEADERBOARD_QUERY: &'static str = "\
+	SELECT
+		history.timestamp,
+		leaderboard_LBID.*
+	FROM leaderboard_LBID
+	JOIN (
+	SELECT user_id, max(timestamp) as timestamp
+	FROM leaderboard_history_LBID
+	GROUP BY user_id
+	) history
+	ON history.user_id = leaderboard_LBID.user_id
+	ORDER BY leaderboard_LBID.points DESC, history.timestamp ASC;";
+
 struct LeaderboardTable {
 	id: usize,
 }
@@ -272,6 +295,10 @@ impl LeaderboardTable {
 	pub fn sql_create_insert_trigger_history_table(&self) -> String {
 		CREATE_TRIGGER_LDBHIST_INSERT.replace("LBID", &self.id.to_string())
 	}
+
+	pub fn sql_leaderboard_query(&self) -> String {
+		LEADERBOARD_QUERY.replace("LBID", &self.id.to_string())
+	}
 }
 
 #[derive(Clone, Debug)]
@@ -282,6 +309,19 @@ pub struct BoardRow {
 	pub points: i64,
 }
 
+#[derive(Clone, Debug)]
+pub struct Placement {
+	pub row: BoardRow,
+
+	/// Absolute placement sorting by points and then
+	/// TODO: sort by date points attained
+	pub placement: usize,
+	/// Placement allowing ties, and placing ties in the same place
+	pub placement_tie: usize,
+	/// Whether or not this placement was first of a tie. True if untied.
+	pub placement_first_of_tie: bool,
+}
+
 #[derive(Debug)]
 pub enum Error {
 	TableNotExist,