Allow for removing now deleted entries on refresh

This commit is contained in:
Joshua Coles 2024-02-22 12:55:29 +00:00
parent bc95081399
commit 2b7cc00fb6
2 changed files with 65 additions and 30 deletions

View File

@ -1,7 +1,9 @@
use crate::client::TogglClient; use crate::client::TogglClient;
use crate::entity::prelude::TimeEntry; use crate::entity::prelude::TimeEntry;
use crate::entity::time_entry;
use crate::types::{Current, Project, ProjectClient, ReportEntry, TogglQuery}; use crate::types::{Current, Project, ProjectClient, ReportEntry, TogglQuery};
use anyhow::anyhow; use anyhow::anyhow;
use axum::extract::Query;
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::response::IntoResponse; use axum::response::IntoResponse;
use axum::routing::{get, post}; use axum::routing::{get, post};
@ -9,14 +11,15 @@ use axum::{Extension, Json, Router};
use base64::engine::general_purpose::STANDARD; use base64::engine::general_purpose::STANDARD;
use base64::Engine; use base64::Engine;
use beachhead::{shutdown_signal, Result}; use beachhead::{shutdown_signal, Result};
use chrono::{NaiveDate, NaiveTime};
use clap::Parser; use clap::Parser;
use migration::{Migrator, MigratorTrait}; 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 serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use std::net::SocketAddr; use std::net::SocketAddr;
use axum::extract::Query;
use serde::Deserialize;
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
@ -54,15 +57,24 @@ pub async fn report(
let report = toggl_client.full_report(&query).await?; let report = toggl_client.full_report(&query).await?;
debug!("Returned results: {:?}", report); 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)) Ok(Json(report))
} }
#[instrument(skip_all)] #[instrument(skip_all)]
async fn cache_report(db: &DatabaseConnection, models: &Vec<ReportEntry>) -> Result<()> { async fn cache_report(
db: &DatabaseConnection,
models: &Vec<ReportEntry>,
exclusive_on: Option<Condition>,
) -> Result<()> {
let models = models.iter().flat_map(|entry| entry.as_models()); let models = models.iter().flat_map(|entry| entry.as_models());
let models = models.collect::<Vec<_>>(); let models = models.collect::<Vec<_>>();
let ids = models
.iter()
.map(|entry| entry.toggl_id.clone().unwrap())
.collect::<Vec<_>>();
debug!("Caching report entries: {:?}", models); debug!("Caching report entries: {:?}", models);
// TODO: Why is this needed? // TODO: Why is this needed?
@ -75,6 +87,17 @@ async fn cache_report(db: &DatabaseConnection, models: &Vec<ReportEntry>) -> Res
.exec(db) .exec(db)
.await?; .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(()) Ok(())
} }
@ -143,26 +166,33 @@ async fn refresh(
Extension(db): Extension<DatabaseConnection>, Extension(db): Extension<DatabaseConnection>,
Query(RefreshQuery { start_date }): Query<RefreshQuery>, Query(RefreshQuery { start_date }): Query<RefreshQuery>,
) -> Result<&'static str> { ) -> Result<&'static str> {
let date = chrono::Utc::now() let end_date = chrono::Utc::now();
.naive_utc() let end_date_query_string = end_date.date_naive().format("%Y-%m-%d").to_string();
.date() let start_date_query_string = start_date.unwrap_or(end_date_query_string.clone());
.format("%Y-%m-%d") let start_date = NaiveDate::parse_from_str(&start_date_query_string, "%Y-%m-%d")?;
.to_string();
let start_date = start_date.unwrap_or(date.clone());
let query = TogglQuery { let query = TogglQuery {
start_date: Some(start_date), start_date: Some(start_date_query_string),
end_date: Some(date), end_date: Some(end_date_query_string),
..Default::default() ..Default::default()
}; };
let report = toggl_client.full_report(&query).await?; 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") 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] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
// install global collector configured based on RUST_LOG env var. // install global collector configured based on RUST_LOG env var.

View File

@ -1,8 +1,9 @@
use crate::client::TogglClient; use crate::client::TogglClient;
use crate::entity::{client, project, time_entry};
use crate::types::{Project, ProjectClient, TogglQuery}; use crate::types::{Project, ProjectClient, TogglQuery};
use sea_orm::{DatabaseConnection, EntityTrait, QuerySelect}; use sea_orm::{DatabaseConnection, EntityTrait, QuerySelect};
use tracing::instrument; use tracing::instrument;
use crate::entity::{project, client}; use crate::day_exclusivity_condition;
#[tracing::instrument(skip(client, db))] #[tracing::instrument(skip(client, db))]
pub async fn poll_job(client: TogglClient, db: DatabaseConnection, poll_period: u64) { pub async fn poll_job(client: TogglClient, db: DatabaseConnection, poll_period: u64) {
@ -33,20 +34,16 @@ pub async fn perform_poll(
client: &TogglClient, client: &TogglClient,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> beachhead::Result<usize> { ) -> beachhead::Result<usize> {
let now = chrono::Utc::now();
let today_string = now
.date_naive()
.format("%Y-%m-%d")
.to_string();
let report = client let report = client
.full_report(&TogglQuery { .full_report(&TogglQuery {
start_date: Some( start_date: Some(today_string.clone()),
chrono::Utc::now() end_date: Some(today_string.clone()),
.date_naive()
.format("%Y-%m-%d")
.to_string(),
),
end_date: Some(
chrono::Utc::now()
.date_naive()
.format("%Y-%m-%d")
.to_string(),
),
..Default::default() ..Default::default()
}) })
.await?; .await?;
@ -58,7 +55,8 @@ pub async fn perform_poll(
.all(db) .all(db)
.await?; .await?;
let new_projects = report.iter() let new_projects = report
.iter()
.filter_map(|entry| entry.project_id) .filter_map(|entry| entry.project_id)
.any(|project_id| !existing_project_ids.contains(&(project_id as i64))); .any(|project_id| !existing_project_ids.contains(&(project_id as i64)));
@ -78,6 +76,13 @@ pub async fn perform_poll(
.await?; .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()) Ok(report.len())
} }