From 2b7cc00fb652ea598488ae0b99a6cfba56b62d5c Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Thu, 22 Feb 2024 12:55:29 +0000 Subject: [PATCH] Allow for removing now deleted entries on refresh --- src/main.rs | 60 +++++++++++++++++++++++++++++++++++++++-------------- src/poll.rs | 35 +++++++++++++++++-------------- 2 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/main.rs b/src/main.rs index c837785..6ff1b59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ use crate::client::TogglClient; use crate::entity::prelude::TimeEntry; +use crate::entity::time_entry; use crate::types::{Current, Project, ProjectClient, ReportEntry, TogglQuery}; use anyhow::anyhow; +use axum::extract::Query; use axum::http::StatusCode; use axum::response::IntoResponse; use axum::routing::{get, post}; @@ -9,14 +11,15 @@ use axum::{Extension, Json, Router}; use base64::engine::general_purpose::STANDARD; use base64::Engine; use beachhead::{shutdown_signal, Result}; +use chrono::{NaiveDate, NaiveTime}; use clap::Parser; use migration::{Migrator, MigratorTrait}; -use sea_orm::{DatabaseConnection, EntityTrait}; +use sea_orm::sea_query::IntoCondition; +use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter}; +use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; use std::net::SocketAddr; -use axum::extract::Query; -use serde::Deserialize; use tower_http::trace::TraceLayer; use tracing::{debug, instrument}; @@ -54,15 +57,24 @@ pub async fn report( let report = toggl_client.full_report(&query).await?; debug!("Returned results: {:?}", report); - cache_report(&db, &report).await?; + // We don't perform any deletes on report-fetched entries + cache_report(&db, &report, None).await?; Ok(Json(report)) } #[instrument(skip_all)] -async fn cache_report(db: &DatabaseConnection, models: &Vec) -> Result<()> { +async fn cache_report( + db: &DatabaseConnection, + models: &Vec, + exclusive_on: Option, +) -> Result<()> { let models = models.iter().flat_map(|entry| entry.as_models()); let models = models.collect::>(); + let ids = models + .iter() + .map(|entry| entry.toggl_id.clone().unwrap()) + .collect::>(); debug!("Caching report entries: {:?}", models); // TODO: Why is this needed? @@ -75,6 +87,17 @@ async fn cache_report(db: &DatabaseConnection, models: &Vec) -> Res .exec(db) .await?; + if let Some(exclusive_on) = exclusive_on { + TimeEntry::delete_many() + .filter( + Condition::all() + .add(exclusive_on) + .add(time_entry::Column::TogglId.is_in(ids).not()), + ) + .exec(db) + .await?; + } + Ok(()) } @@ -143,26 +166,33 @@ async fn refresh( Extension(db): Extension, Query(RefreshQuery { start_date }): Query, ) -> Result<&'static str> { - let date = chrono::Utc::now() - .naive_utc() - .date() - .format("%Y-%m-%d") - .to_string(); - - let start_date = start_date.unwrap_or(date.clone()); + let end_date = chrono::Utc::now(); + let end_date_query_string = end_date.date_naive().format("%Y-%m-%d").to_string(); + let start_date_query_string = start_date.unwrap_or(end_date_query_string.clone()); + let start_date = NaiveDate::parse_from_str(&start_date_query_string, "%Y-%m-%d")?; let query = TogglQuery { - start_date: Some(start_date), - end_date: Some(date), + start_date: Some(start_date_query_string), + end_date: Some(end_date_query_string), ..Default::default() }; let report = toggl_client.full_report(&query).await?; - cache_report(&db, &report).await?; + let exclusivity_condition = day_exclusivity_condition(start_date, end_date.date_naive()); + cache_report(&db, &report, Some(exclusivity_condition)).await?; Ok("Ok") } +fn day_exclusivity_condition(start: NaiveDate, end: NaiveDate) -> Condition { + time_entry::Column::Start + .between( + start.and_time(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), + end.and_time(NaiveTime::from_hms_opt(23, 59, 59).unwrap()), + ) + .into_condition() +} + #[tokio::main] async fn main() -> Result<()> { // install global collector configured based on RUST_LOG env var. diff --git a/src/poll.rs b/src/poll.rs index abeb194..6cbadc5 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -1,8 +1,9 @@ use crate::client::TogglClient; +use crate::entity::{client, project, time_entry}; use crate::types::{Project, ProjectClient, TogglQuery}; use sea_orm::{DatabaseConnection, EntityTrait, QuerySelect}; use tracing::instrument; -use crate::entity::{project, client}; +use crate::day_exclusivity_condition; #[tracing::instrument(skip(client, db))] pub async fn poll_job(client: TogglClient, db: DatabaseConnection, poll_period: u64) { @@ -33,20 +34,16 @@ pub async fn perform_poll( client: &TogglClient, db: &DatabaseConnection, ) -> beachhead::Result { + let now = chrono::Utc::now(); + let today_string = now + .date_naive() + .format("%Y-%m-%d") + .to_string(); + let report = client .full_report(&TogglQuery { - start_date: Some( - chrono::Utc::now() - .date_naive() - .format("%Y-%m-%d") - .to_string(), - ), - end_date: Some( - chrono::Utc::now() - .date_naive() - .format("%Y-%m-%d") - .to_string(), - ), + start_date: Some(today_string.clone()), + end_date: Some(today_string.clone()), ..Default::default() }) .await?; @@ -58,7 +55,8 @@ pub async fn perform_poll( .all(db) .await?; - let new_projects = report.iter() + let new_projects = report + .iter() .filter_map(|entry| entry.project_id) .any(|project_id| !existing_project_ids.contains(&(project_id as i64))); @@ -78,6 +76,13 @@ pub async fn perform_poll( .await?; } - crate::cache_report(&db, &report).await?; + crate::cache_report( + &db, + &report, + Some( + day_exclusivity_condition(now.date_naive(), now.date_naive()), + ), + ) + .await?; Ok(report.len()) }