main: more date logic

This commit is contained in:
nobody 2024-08-17 06:42:12 +02:00 committed by murmeldin
parent e4aae07113
commit 88450b7a61

View file

@ -179,22 +179,23 @@ fn main() -> Result<(), Box<dyn Error>> {
&config["wiki-http-password"], &config["wiki-http-password"],
is_dry_run(), is_dry_run(),
); );
// get next plenum days
let today = Local::now().date_naive();
let plenum_spec = date::parse_spec(&config["date-spec"])?;
let nearest_plenum_days = date::get_matching_dates_around(today, plenum_spec);
// figure out where we are // figure out where we are
let mut last_state = ProgramState::parse(&config["state-name"]); let mut last_state = ProgramState::parse(&config["state-name"]);
let last_run = config.get("state-last-run").unwrap_or_default(); let last_run = config.get("state-last-run").unwrap_or_default();
let last_run = NaiveDate::parse_from_str(&last_run, "%Y-%m-%d").unwrap_or_default(); let last_run = NaiveDate::parse_from_str(&last_run, "%Y-%m-%d").unwrap_or_default();
// figure out where we should be // figure out where we should be
let today = Local::now().date_naive();
if (today - last_run).num_days() > 10 { if (today - last_run).num_days() > 10 {
if !matches!(last_state, ProgramState::Normal) { if !matches!(last_state, ProgramState::Normal) {
eprintln!("WARNING: last run was a long time ago, resetting state."); eprintln!("WARNING: last run was a long time ago, resetting state.");
last_state = ProgramState::Normal; last_state = ProgramState::Normal;
do_cleanup(999, &config, &hedgedoc, &email, &wiki)?; do_cleanup(999, &today, &config, &hedgedoc, &email, &wiki)?;
} }
} }
let last_state = last_state; let last_state = last_state;
let plenum_spec = date::parse_spec(&config["date-spec"])?;
let nearest_plenum_days = date::get_matching_dates_around(today, plenum_spec);
// deltas has either 2 or 3 days, if 3 then the middle one is == 0 (i.e. today) // deltas has either 2 or 3 days, if 3 then the middle one is == 0 (i.e. today)
let deltas: Vec<i64> = nearest_plenum_days.iter().map(|&d| (d - today).num_days()).collect(); let deltas: Vec<i64> = nearest_plenum_days.iter().map(|&d| (d - today).num_days()).collect();
// find the relevant one: // find the relevant one:
@ -212,6 +213,7 @@ fn main() -> Result<(), Box<dyn Error>> {
}, },
} }
.unwrap(); // always has at least 2 elems .unwrap(); // always has at least 2 elems
let plenum_day = today.checked_add_signed(chrono::TimeDelta::days(delta)).unwrap();
let intended_state = if delta > 3 { let intended_state = if delta > 3 {
ProgramState::Normal // nothing to do 3+ days in advance ProgramState::Normal // nothing to do 3+ days in advance
} else if delta > 1 { } else if delta > 1 {
@ -224,8 +226,8 @@ fn main() -> Result<(), Box<dyn Error>> {
ProgramState::Logged // after that, we want to log it to the list & the wiki ProgramState::Logged // after that, we want to log it to the list & the wiki
}; };
let action: TransitionFunction = TRANSITION_LUT[last_state.index()][intended_state.index()]; let action: TransitionFunction = TRANSITION_LUT[last_state as usize][intended_state as usize];
action(delta, &config, &hedgedoc, &email, &wiki)?; action(delta, &plenum_day, &config, &hedgedoc, &email, &wiki)?;
// TODO: cleanup / write new state // TODO: cleanup / write new state
@ -513,31 +515,62 @@ fn try_to_remove_top_instructions(pad_content: String) -> String {
result.to_string() // Wenn es nicht geklappt hat, wird einfach das Pad mit dem Kommentar zurückgegeben result.to_string() // Wenn es nicht geklappt hat, wird einfach das Pad mit dem Kommentar zurückgegeben
} }
/* ***** formatting helpers ***** */
fn relative_date( ttp: i64 ) -> String {
if ttp.abs() > 2 {
if ttp.is_negative() {
format!( "vor {} Tagen", -ttp )
} else {
format!( "in {} Tagen", ttp )
}
} else {
match ttp {
2 => "übermorgen",
1 => "morgen",
0 => "heute",
-1 => "gestern",
-2 => "vorgestern",
_ => unreachable!(),
}.to_string()
}
}
fn upper_first(s: &str) -> String {
let mut c = s.chars();
match c.next() {
Some(fst) => fst.to_uppercase().collect::<String>() + c.as_str(),
None => String::new(),
}
}
/* ***** transition actions ***** */ /* ***** transition actions ***** */
fn do_announcement( fn do_announcement(
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki, ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
// use TTP to adjust text if needed (in {ttp} days) // use TTP to adjust text if needed (in {ttp} days)
todo!() todo!()
} }
fn do_reminder( fn do_reminder(
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki, ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
// use TTP to adjust text if needed (tomorrow or today / in {ttp} days) // use TTP to adjust text if needed (tomorrow or today / in {ttp} days)
todo!() todo!()
} }
fn do_protocol( fn do_protocol(
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki, ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
// use TTP to adjust text if needed ({-ttp} days ago) // use TTP to adjust text if needed ({-ttp} days ago)
todo!() todo!()
} }
/// General cleanup function. Call as `(999, today, …)` for a complete reset
/// based on today as the base date.
fn do_cleanup( fn do_cleanup(
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki, ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
todo!() todo!()
} }
@ -546,6 +579,7 @@ fn do_cleanup(
type TransitionFunction = fn( type TransitionFunction = fn(
ttp: i64, ttp: i64,
plenum_day: &NaiveDate,
config: &KV, config: &KV,
hedgedoc: &HedgeDoc, hedgedoc: &HedgeDoc,
email: &SimpleEmail, email: &SimpleEmail,
@ -562,22 +596,22 @@ const TRANSITION_LUT: [[TransitionFunction; 5]; 5] = [
/* LOGGED */ [do_cleanup, do_clean_announcement, do_clean_reminder, nop, do_cleanup], /* LOGGED */ [do_cleanup, do_clean_announcement, do_clean_reminder, nop, do_cleanup],
]; ];
fn nop(_: i64, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &Mediawiki) -> Result<(), Box<dyn Error>> { fn nop(_: i64, _: &NaiveDate, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &Mediawiki) -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
fn do_clean_announcement( fn do_clean_announcement(
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki, ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
do_cleanup(ttp, config, hedgedoc, email, wiki)?; do_cleanup(ttp, plenum_day, config, hedgedoc, email, wiki)?;
do_announcement(ttp, config, hedgedoc, email, wiki) do_announcement(ttp, plenum_day, config, hedgedoc, email, wiki)
} }
fn do_clean_reminder( fn do_clean_reminder(
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki, ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
do_cleanup(ttp, config, hedgedoc, email, wiki)?; do_cleanup(ttp, plenum_day, config, hedgedoc, email, wiki)?;
do_reminder(ttp, config, hedgedoc, email, wiki) do_reminder(ttp, plenum_day, config, hedgedoc, email, wiki)
} }
/// State machine type for the announcement logic, ensuring we can deal with /// State machine type for the announcement logic, ensuring we can deal with
@ -590,30 +624,32 @@ fn do_clean_reminder(
/// - Waiting just a day of delay (after it makes sense to send a reminder) /// - Waiting just a day of delay (after it makes sense to send a reminder)
/// - Logged protocol was written to wiki & mailing list /// - Logged protocol was written to wiki & mailing list
/// ///
/// The bot knows in which state it is at all times. It knows this because it /// > The bot knows in which state it is at all times. It knows this because
/// knows in which state it isn't. By comparing where it is with where it /// > it knows in which state it isn't. By comparing where it is with where
/// isn't, it obtains a difference, or deviation. The program logic uses /// > it isn't, it obtains a difference, or deviation. The program logic uses
/// deviations to generate corrective commands to drive the bot from a state /// > deviations to generate corrective commands to drive the bot from a state
/// where it is to a state where it isn't, and arriving in a state where it /// > where it is to a state where it isn't, and arriving in a state where it
/// wasn't, it now is. Consequently, the state where it is, is now the state /// > wasn't, it now is. Consequently, the state where it is, is now the state
/// that it wasn't, and it follows that the state that it was in, is now the /// > that it wasn't, and it follows that the state that it was in, is now the
/// state that it isn't in. /// > state that it isn't in.
/// /// >
/// In the event that the state that it is in is not the state that it wasn't, /// > In the event that the state that it is in is not the state that it wasn't,
/// the system has acquired a variation, the variation being the difference /// > the system has acquired a variation, the variation being the difference
/// between where the bot is, and where it wasn't. If variation is considered /// > between where the bot is, and where it wasn't. If variation is considered
/// to be a significant factor, it too may be corrected by the program logic. /// > to be a significant factor, it too may be corrected by the program logic.
/// However, the bot must also know where it was. /// > However, the bot must also know where it was.
/// /// >
/// The program logic works as follows. Because a delay has modified some /// > The program logic works as follows. Because a delay has modified some
/// of the information the bot has obtained, it is not sure just when it is. /// > of the information the bot has obtained, it is not sure just when it is.
/// However, it is sure when it isn't, within reason, and it knows when it was. /// > However, it is sure when it isn't, within reason, and it knows when it was.
/// It now subtracts when it should be from when it wasn't, or vice-versa, and /// > It now subtracts when it should be from when it wasn't, or vice-versa, and
/// by differentiating this from the algebraic sum of when it shouldn't be, /// > by differentiating this from the algebraic sum of when it shouldn't be,
/// and when it was, it is able to obtain the deviation and its variation, /// > and when it was, it is able to obtain the deviation and its variation,
/// which is called error. /// > which is called error.
#[derive(Default, Debug)]
enum ProgramState { enum ProgramState {
/// Normal is the default state, with no actions currently outstanding. /// Normal is the default state, with no actions currently outstanding.
#[default]
Normal, Normal,
/// There is an upcoming event, and the first announcement has been sent. /// There is an upcoming event, and the first announcement has been sent.
Announced, Announced,
@ -636,16 +672,6 @@ impl ProgramState {
_ => ProgramState::Normal, _ => ProgramState::Normal,
} }
} }
fn index(&self) -> usize {
match self {
ProgramState::Normal => 0,
ProgramState::Announced => 1,
ProgramState::Reminded => 2,
ProgramState::Waiting => 3,
ProgramState::Logged => 4,
}
}
} }
impl std::fmt::Display for ProgramState { impl std::fmt::Display for ProgramState {