Report update statistics
This commit is contained in:
parent
7759632848
commit
479389dde6
14
src/poll.rs
14
src/poll.rs
@ -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())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user