scoring starter code, and flake to stable
This commit is contained in:
parent
544e380835
commit
5487a1801f
6 changed files with 98 additions and 7 deletions
8
flake.lock
generated
8
flake.lock
generated
|
|
@ -2,16 +2,16 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1768127708,
|
"lastModified": 1769933782,
|
||||||
"narHash": "sha256-1Sm77VfZh3mU0F5OqKABNLWxOuDeHIlcFjsXeeiPazs=",
|
"narHash": "sha256-GlZemJ2dxhXMMq6TNyt588OFv4/jIt3J1QVBO9MspBE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ffbc9f8cbaacfb331b6017d5a5abb21a492c9a38",
|
"rev": "64728753f1a42c81c5688a136a6bee173665acc9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-unstable",
|
"ref": "nixos-25.11-small",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
description = "Koucha rust flake";
|
description = "Koucha rust flake";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11-small";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs }:
|
outputs = { self, nixpkgs }:
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@ CREATE TABLE feeds (
|
||||||
CREATE TABLE feed_channels (
|
CREATE TABLE feed_channels (
|
||||||
feed_id INTEGER NOT NULL,
|
feed_id INTEGER NOT NULL,
|
||||||
channel_id INTEGER NOT NULL,
|
channel_id INTEGER NOT NULL,
|
||||||
-- Decay settings will go here
|
initial_score INTEGER,
|
||||||
|
gravity INTEGER,
|
||||||
|
boost INTEGER,
|
||||||
PRIMARY KEY (feed_id, channel_id),
|
PRIMARY KEY (feed_id, channel_id),
|
||||||
FOREIGN KEY (feed_id) REFERENCES feeds(id),
|
FOREIGN KEY (feed_id) REFERENCES feeds(id),
|
||||||
FOREIGN KEY (channel_id) REFERENCES channels(id)
|
FOREIGN KEY (channel_id) REFERENCES channels(id)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ pub use channel::Channel;
|
||||||
mod item;
|
mod item;
|
||||||
pub use item::Item;
|
pub use item::Item;
|
||||||
|
|
||||||
|
|
||||||
macro_rules! define_id {
|
macro_rules! define_id {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ type Result<T> = std::result::Result<T, Box<dyn Error>>;
|
||||||
|
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod fetch;
|
pub mod fetch;
|
||||||
|
pub mod score;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test_utils;
|
pub mod test_utils;
|
||||||
|
|
|
||||||
89
koucha/src/score.rs
Normal file
89
koucha/src/score.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
use chrono::{DateTime, Utc, TimeDelta};
|
||||||
|
|
||||||
|
mod default {
|
||||||
|
pub const INITIAL_SCORE: i64 = 70;
|
||||||
|
pub const GRAVITY: i64 = -10;
|
||||||
|
pub const BOOST: i64 = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! rich_i64 {
|
||||||
|
($name:ident, $default:expr) => {
|
||||||
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
|
pub struct $name(i64);
|
||||||
|
impl From<$name> for i64 { fn from(id: $name) -> Self { id.0 } }
|
||||||
|
impl $name {
|
||||||
|
pub fn new(value: Option<i64>) -> Self {
|
||||||
|
Self(value.unwrap_or($default))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
rich_i64!(Gravity, default::GRAVITY);
|
||||||
|
rich_i64!(Boost, default::BOOST);
|
||||||
|
impl Boost {
|
||||||
|
fn should_apply(
|
||||||
|
old_boost_time: Option<DateTime<Utc>>, new_boost_time: DateTime<Utc>
|
||||||
|
) -> bool {
|
||||||
|
old_boost_time.map_or(true, |obs| {
|
||||||
|
let time_delta = obs - &new_boost_time;
|
||||||
|
time_delta.num_days() >= 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct Score {
|
||||||
|
value: i64,
|
||||||
|
last_updated: DateTime<Utc>,
|
||||||
|
last_boosted: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Score {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::new_at_time(Utc::now())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_at_time(creation_time: DateTime<Utc>) -> Self {
|
||||||
|
Score {
|
||||||
|
value: default::INITIAL_SCORE,
|
||||||
|
last_updated: creation_time,
|
||||||
|
last_boosted: Some(creation_time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(self) -> Self {
|
||||||
|
self.update_at_time(Utc::now())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_at_time(self, update_time: DateTime<Utc>) -> Self {
|
||||||
|
let to_elapse_from = self.last_boosted.map_or(self.last_updated, |lb| {
|
||||||
|
std::cmp::max(self.last_updated, lb + TimeDelta::days(1))
|
||||||
|
});
|
||||||
|
let elapsed_time = update_time.signed_duration_since(to_elapse_from);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn boost(self, boost: Boost) -> Self {
|
||||||
|
self.boost_at_time(boost, Utc::now())
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function does not "fast-forward" the score's value to the time of
|
||||||
|
// boost, but still updates `last_updated`. Meaning that hours of gravity may
|
||||||
|
// not be applied.
|
||||||
|
// The expectation is that a boost is a user interaction, and that the user
|
||||||
|
// should be seeing a "fresh" score. We wouldn't want a boost to end up
|
||||||
|
// applying lots of gravity. But care should be taken to ensure that only
|
||||||
|
// "fresh" scores are boosted. Or that boost_time reflects the state of the
|
||||||
|
// user experience.
|
||||||
|
pub fn boost_at_time(self, boost: Boost, boost_time: DateTime<Utc>) -> Self {
|
||||||
|
if Boost::should_apply(self.last_boosted, boost_time) {
|
||||||
|
Self {
|
||||||
|
value: self.value + i64::from(boost),
|
||||||
|
last_updated: boost_time,
|
||||||
|
last_boosted: Some(boost_time),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue