Report update statistics

This commit is contained in:
Joshua Coles 2024-03-03 20:49:27 +00:00
parent 7759632848
commit 479389dde6
3 changed files with 48 additions and 20 deletions

View File

@ -13,7 +13,7 @@ use sea_orm::{
use std::ops::Sub; use std::ops::Sub;
use migration::Order; use migration::Order;
use tracing::instrument; use tracing::instrument;
use crate::sync_service::update_database; use crate::sync_service::{update_database, UpdateStats};
#[tracing::instrument(skip(client, db))] #[tracing::instrument(skip(client, db))]
pub async fn poll_job(client: TogglApiClient, db: DatabaseConnection, poll_period: u64) { pub async fn poll_job(client: TogglApiClient, db: DatabaseConnection, poll_period: u64) {
@ -23,10 +23,10 @@ pub async fn poll_job(client: TogglApiClient, db: DatabaseConnection, poll_perio
loop { loop {
tracing::info!("Polling Toggl API"); tracing::info!("Polling Toggl API");
match perform_poll(&client, &db).await { match perform_poll(&client, &db).await {
Ok(report_entries_count) => { Ok(poll_update_data) => {
tracing::info!( tracing::info!(
"Successfully polled Toggl API: {:?} entries retrieved", "Successfully polled Toggl API: {:?}",
report_entries_count poll_update_data
); );
} }
@ -43,7 +43,7 @@ pub async fn poll_job(client: TogglApiClient, db: DatabaseConnection, poll_perio
pub async fn perform_poll( pub async fn perform_poll(
toggl_client: &TogglApiClient, toggl_client: &TogglApiClient,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> utils::Result<usize> { ) -> utils::Result<UpdateStats> {
let since = time_entry::Entity::find() let since = time_entry::Entity::find()
.select_only() .select_only()
.column(time_entry::Column::ServerUpdatedAt) .column(time_entry::Column::ServerUpdatedAt)
@ -68,7 +68,5 @@ pub async fn perform_poll(
toggl_client, toggl_client,
&time_entries, &time_entries,
None, None,
).await?; ).await
Ok(time_entries.len())
} }

View File

@ -12,6 +12,7 @@ use serde::Deserialize;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use tracing::{instrument}; use tracing::{instrument};
use crate::sync_service::UpdateStats;
#[instrument(skip(db, toggl_client))] #[instrument(skip(db, toggl_client))]
pub async fn report( pub async fn report(
@ -119,7 +120,7 @@ pub async fn refresh(
Extension(toggl_client): Extension<TogglApiClient>, Extension(toggl_client): Extension<TogglApiClient>,
Extension(db): Extension<DatabaseConnection>, Extension(db): Extension<DatabaseConnection>,
Query(RefreshQuery { start_date, end_date }): Query<RefreshQuery>, Query(RefreshQuery { start_date, end_date }): Query<RefreshQuery>,
) -> utils::Result<&'static str> { ) -> utils::Result<Json<UpdateStats>> {
let time_entries = match (start_date, end_date) { let time_entries = match (start_date, end_date) {
(Some(start_date), Some(end_date)) => { (Some(start_date), Some(end_date)) => {
toggl_client.fetch_time_entries_in_range(start_date, end_date).await? toggl_client.fetch_time_entries_in_range(start_date, end_date).await?
@ -144,7 +145,5 @@ pub async fn refresh(
&toggl_client, &toggl_client,
&time_entries, &time_entries,
None, None,
).await?; ).await.map(Json)
Ok("Ok")
} }

View File

@ -4,29 +4,52 @@ use crate::toggl_api::types::{Client, Project, ReportRow, TimeEntry as ToggleApi
use crate::utils; use crate::utils;
use migration::Condition; use migration::Condition;
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect}; use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect};
use tracing::{debug, instrument}; use serde::Serialize;
use crate::toggl_api::TogglApiClient; use crate::toggl_api::TogglApiClient;
#[derive(Debug, Serialize)]
pub struct UpdateStats {
retrieved: UpdateStatsInner,
written: UpdateStatsInner,
}
#[derive(Debug, Serialize)]
pub struct UpdateStatsInner {
updated: usize,
deleted: usize,
}
pub async fn update_database( pub async fn update_database(
db: &DatabaseConnection, db: &DatabaseConnection,
toggl_client: &TogglApiClient, toggl_client: &TogglApiClient,
time_entries: &[ToggleApiTimeEntry], time_entries: &[ToggleApiTimeEntry],
exclusive_on: Option<Condition>, exclusive_on: Option<Condition>,
) -> utils::Result<()> { ) -> utils::Result<UpdateStats> {
let (deleted_entries, time_entries) = time_entries let (deleted_entries, time_entries) = time_entries
.iter() .iter()
.partition::<Vec<_>, _>(|entry| entry.server_deleted_at.is_some()); .partition::<Vec<_>, _>(|entry| entry.server_deleted_at.is_some());
let retrieved = UpdateStatsInner {
updated: time_entries.len(),
deleted: deleted_entries.len(),
};
let mut written = UpdateStatsInner {
updated: 0,
deleted: 0,
};
let deleted_ids = deleted_entries.iter() let deleted_ids = deleted_entries.iter()
.map(|entry| entry.id) .map(|entry| entry.id)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if !deleted_ids.is_empty() { if !deleted_ids.is_empty() {
debug!("Deleting time entries: {:?}", deleted_ids); let delete_result = TimeEntry::delete_many()
TimeEntry::delete_many()
.filter(time_entry::Column::TogglId.is_in(deleted_ids)) .filter(time_entry::Column::TogglId.is_in(deleted_ids))
.exec(db) .exec(db)
.await?; .await?;
written.deleted = delete_result.rows_affected as usize;
} }
let existing_project_ids = project::Entity::find() let existing_project_ids = project::Entity::find()
@ -67,14 +90,19 @@ pub async fn update_database(
// TODO: Why is this needed? // TODO: Why is this needed?
if models.is_empty() { if models.is_empty() {
return Ok(()); return Ok(UpdateStats {
retrieved,
written,
});
} }
TimeEntry::insert_many(models) let insert_result = TimeEntry::insert_many(models)
.on_conflict(ToggleApiTimeEntry::grafting_conflict_statement()) .on_conflict(ToggleApiTimeEntry::grafting_conflict_statement())
.exec(db) .exec_without_returning(db)
.await?; .await?;
written.updated = insert_result as usize;
if let Some(exclusive_on) = exclusive_on { if let Some(exclusive_on) = exclusive_on {
TimeEntry::delete_many() TimeEntry::delete_many()
.filter( .filter(
@ -86,5 +114,8 @@ pub async fn update_database(
.await?; .await?;
} }
Ok(()) Ok(UpdateStats {
retrieved,
written,
})
} }