Initial db sync work

This commit is contained in:
2024-07-27 17:58:34 +01:00
parent 5969a1b3d8
commit 5887ae4946
4 changed files with 260 additions and 42 deletions
+136 -8
View File
@@ -1,21 +1,149 @@
use std::ops::Sub;
use chrono::NaiveDate;
use chrono::{NaiveDate, TimeDelta, Utc};
use sqlx::{Connection, PgConnection};
use toggl::TogglApi;
mod toggl;
mod sensitive;
#[derive(Debug, thiserror::Error)]
enum AppError {
#[error("Database error: {0}")]
SqlxError(#[from] sqlx::Error),
#[error("Toggl error: {0}")]
TogglError(#[from] toggl::TogglError),
}
struct Worker {
db: PgConnection,
toggl_api: TogglApi,
}
struct TableSummary {
client_ids: Vec<u64>,
workspace_ids: Vec<u64>,
project_ids: Vec<u64>,
tag_ids: Vec<u64>,
}
impl Worker {
async fn get_ids(&mut self) -> Result<TableSummary, AppError> {
let client_ids = sqlx::query!("select id from tracking_clients")
.fetch_all(&mut self.db)
.await?;
let workspace_ids = sqlx::query!("select id from workspaces")
.fetch_all(&mut self.db)
.await?;
let project_ids = sqlx::query!("select id from projects")
.fetch_all(&mut self.db)
.await?;
let tag_ids = sqlx::query!("select id from tags")
.fetch_all(&mut self.db)
.await?;
Ok(TableSummary {
client_ids: client_ids.iter().map(|row| row.id as u64).collect(),
workspace_ids: workspace_ids.iter().map(|row| row.id as u64).collect(),
project_ids: project_ids.iter().map(|row| row.id as u64).collect(),
tag_ids: tag_ids.iter().map(|row| row.id as u64).collect(),
})
}
async fn update(&mut self, default_look_back: TimeDelta) -> Result<(), AppError> {
let result = sqlx::query!("select max(updated_at) as last_updated_at from time_entries")
.fetch_one(&mut self.db)
.await
.expect("Could not fetch max updated_at from time_entries");
let existing_ids = self.get_ids().await?;
let fetch_since = result.last_updated_at
.unwrap_or_else(|| Utc::now() - default_look_back);
let time_entries = self.toggl_api
.get_time_entries_for_user_modified_since(fetch_since).await?;
let refetch_projects = time_entries.iter()
.map(|entry| entry.project_id)
.filter_map(|project_id| project_id)
.any(|project_id| !existing_ids.project_ids.contains(&project_id));
let refetch_tags = time_entries.iter()
.flat_map(|entry| entry.tag_ids.iter())
.any(|tag| !existing_ids.tag_ids.contains(&tag));
Ok(())
}
async fn update_projects(&mut self) -> Result<(), AppError> {
let projects = self.toggl_api.get_projects().await?;
for project in projects {
sqlx::query!(
r#"
INSERT INTO projects (id, workspace_id, client_id, name, color, status, active, updated_at, start_date, created_at, server_deleted_at, actual_hours, actual_seconds, can_track_time, permissions)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
ON CONFLICT (id) DO UPDATE SET
workspace_id = excluded.workspace_id,
client_id = excluded.client_id,
name = excluded.name,
color = excluded.color,
status = excluded.status,
active = excluded.active,
updated_at = excluded.updated_at,
start_date = excluded.start_date,
created_at = excluded.created_at,
server_deleted_at = excluded.server_deleted_at,
actual_hours = excluded.actual_hours,
actual_seconds = excluded.actual_seconds,
can_track_time = excluded.can_track_time,
permissions = excluded.permissions
"#,
project.id,
project.workspace_id,
project.client_id,
project.name,
project.color,
project.status.to_string(),
project.active,
project.updated_at,
project.start_date,
project.created_at,
project.server_deleted_at,
project.actual_hours,
project.actual_seconds,
project.can_track_time,
project.permissions,
)
.execute(&mut self.db)
.await?;
}
Ok(())
}
}
#[tokio::main]
async fn main() {
dotenv::dotenv()
.expect("Failed to load .env file");
let api = TogglApi::new(
sensitive::API_TOKEN,
sensitive::WORKSPACE_ID,
);
dbg!(api.search(toggl::types::TogglReportFilters {
start_date: Some(NaiveDate::from_ymd_opt(2024, 07, 10).unwrap()),
end_date: Some(NaiveDate::from_ymd_opt(2024, 07, 16).unwrap()),
enrich_response: Some(true),
..Default::default()
}).await);
let database_url = std::env::var("DATABASE_URL")
.expect("DATABASE_URL must be set");
let mut worker = Worker {
db: PgConnection::connect(&database_url).await.unwrap(),
toggl_api: api,
};
worker.update_projects()
.await
.unwrap();
}