(toggl-portal): Poll

This commit is contained in:
Joshua Coles 2023-11-03 19:50:43 +00:00
parent d47174efee
commit 7e2848a0c1
3 changed files with 78 additions and 14 deletions

View File

@ -1,4 +1,5 @@
use crate::client::TogglClient;
use crate::entity::time_entry::{self, ActiveModel, Entity as TimeEntry};
use crate::types::{Current, ReportEntry, TogglQuery};
use anyhow::anyhow;
use axum::http::StatusCode;
@ -9,19 +10,19 @@ use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use beachhead::{shutdown_signal, Result};
use clap::Parser;
use migration::{Migrator, MigratorTrait};
use sea_orm::sea_query::OnConflict;
use sea_orm::{DatabaseConnection, EntityTrait};
use serde_json::Value;
use std::collections::HashMap;
use std::net::SocketAddr;
use sea_orm::{DatabaseConnection, EntityTrait};
use sea_orm::sea_query::OnConflict;
use tower_http::trace::TraceLayer;
use migration::{Migrator, MigratorTrait};
use crate::entity::time_entry::{self, Entity as TimeEntry};
mod client;
mod types;
mod db;
mod entity;
mod poll;
mod types;
#[derive(Debug, Clone, Parser)]
struct Config {
@ -44,7 +45,16 @@ pub async fn report(
Json(query): Json<TogglQuery>,
) -> Result<Json<Vec<ReportEntry>>> {
let report = toggl_client.full_report(&query).await?;
let models = report.iter().flat_map(|entry| entry.as_models());
cache_report(&db, &report).await?;
Ok(Json(report))
}
async fn cache_report(
db: &DatabaseConnection,
models: &Vec<ReportEntry>,
) -> Result<()> {
let models = models.iter().flat_map(|entry| entry.as_models());
TimeEntry::insert_many(models)
.on_conflict(
@ -55,10 +65,12 @@ pub async fn report(
time_entry::Column::Start,
time_entry::Column::Stop,
time_entry::Column::RawJson,
]).to_owned()
).exec(&db).await?;
Ok(Json(report))
])
.to_owned(),
)
.exec(db)
.await?;
Ok(())
}
pub async fn current(
@ -99,9 +111,9 @@ async fn main() -> Result<()> {
.await
.unwrap();
Migrator::up(&db, None)
.await
.expect("Failed to migrate");
Migrator::up(&db, None).await.expect("Failed to migrate");
tokio::spawn(poll::poll_job(toggl_client.clone(), db.clone()));
// build our application with a route
let app = Router::new()

52
src/poll.rs Normal file
View File

@ -0,0 +1,52 @@
use crate::client::TogglClient;
use crate::types::TogglQuery;
use sea_orm::DatabaseConnection;
use tracing::instrument;
#[tracing::instrument(skip(client, db))]
pub async fn poll_job(client: TogglClient, db: DatabaseConnection) {
// Every 2h, poll the Toggl API for new time entries for today to cache them in the database
let period = tokio::time::Duration::from_secs(60 * 60 * 2);
loop {
tracing::info!("Polling Toggl API");
match perform_poll(&client, &db).await {
Ok(report_entries_count) => {
tracing::info!(
"Successfully polled Toggl API: {:?} entries retrieved",
report_entries_count
);
}
Err(error) => {
tracing::error!("Failed to poll Toggl API: {:?}", error);
}
}
tokio::time::sleep(period).await;
}
}
#[instrument(skip(client, db))]
async fn perform_poll(client: &TogglClient, db: &DatabaseConnection) -> beachhead::Result<usize> {
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(),
),
..Default::default()
})
.await?;
crate::cache_report(&db, &report).await?;
Ok(report.len())
}

View File

@ -58,7 +58,7 @@ pub struct Project {
#[allow(non_snake_case)]
#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct TogglQuery {
pub billable: Option<bool>,
pub client_ids: Option<Vec<u64>>,