From 7652648980b959866d2ef8294af9f53a90a4481b Mon Sep 17 00:00:00 2001 From: perf3ct Date: Fri, 27 Jun 2025 20:51:01 +0000 Subject: [PATCH 1/2] feat(tests): reorganize unit and integration tests --- ...ionality_tests.rs => integration_admin_functionality_tests.rs} | 0 tests/{auto_resume_tests.rs => integration_auto_resume_tests.rs} | 0 .../{cancellation_tests.rs => integration_cancellation_tests.rs} | 0 ...ts.rs => integration_comprehensive_source_management_tests.rs} | 0 tests/{debug_ocr_test.rs => integration_debug_ocr_test.rs} | 0 ...ests.rs => integration_document_deletion_integration_tests.rs} | 0 ...sts.rs => integration_document_upload_hash_duplicate_tests.rs} | 0 ...es_tests.rs => integration_error_handling_edge_cases_tests.rs} | 0 ...dpoints_tests.rs => integration_failed_ocr_endpoints_tests.rs} | 0 ...ine_tests.rs => integration_file_processing_pipeline_tests.rs} | 0 ...ion_tests.rs => integration_hash_duplicate_detection_tests.rs} | 0 ...on_tests.rs => integration_ignored_files_integration_tests.rs} | 0 tests/{integration_tests.rs => integration_integration_tests.rs} | 0 ..._empty_content.rs => integration_investigate_empty_content.rs} | 0 ...tegration_tests.rs => integration_labels_integration_tests.rs} | 0 ...older_sync_tests.rs => integration_local_folder_sync_tests.rs} | 0 ...cr_corruption_tests.rs => integration_ocr_corruption_tests.rs} | 0 ...ounting_tests.rs => integration_ocr_failure_counting_tests.rs} | 0 ...ation_test.rs => integration_ocr_pipeline_integration_test.rs} | 0 ...agement_tests.rs => integration_ocr_queue_management_tests.rs} | 0 ...rmance_load_tests.rs => integration_performance_load_tests.rs} | 0 ...s_metrics_tests.rs => integration_prometheus_metrics_tests.rs} | 0 ...ol_tests.rs => integration_role_based_access_control_tests.rs} | 0 tests/{s3_sync_tests.rs => integration_s3_sync_tests.rs} | 0 ...e_throttling_test.rs => integration_simple_throttling_test.rs} | 0 ...mple_tests.rs => integration_source_scheduler_simple_tests.rs} | 0 ...e_scheduler_tests.rs => integration_source_scheduler_tests.rs} | 0 ...e_tests.rs => integration_source_sync_hash_duplicate_tests.rs} | 0 ...{source_update_tests.rs => integration_source_update_tests.rs} | 0 ...lity_tests.rs => integration_stop_sync_functionality_tests.rs} | 0 ...est_image_ocr_tests.rs => integration_test_image_ocr_tests.rs} | 0 ...separation_tests.rs => integration_thread_separation_tests.rs} | 0 ...e_sync_tests.rs => integration_universal_source_sync_tests.rs} | 0 ...hensive_tests.rs => integration_webdav_comprehensive_tests.rs} | 0 ...licate_tests.rs => integration_webdav_hash_duplicate_tests.rs} | 0 ...tegration_tests.rs => integration_webdav_integration_tests.rs} | 0 tests/{webdav_sync_tests.rs => integration_webdav_sync_tests.rs} | 0 tests/{basic_sync_tests.rs => unit_basic_sync_tests.rs} | 0 tests/{oidc_unit_tests.rs => unit_oidc_unit_tests.rs} | 0 tests/{unit_tests.rs => unit_unit_tests.rs} | 0 ..._enhanced_unit_tests.rs => unit_webdav_enhanced_unit_tests.rs} | 0 tests/{webdav_unit_tests.rs => unit_webdav_unit_tests.rs} | 0 42 files changed, 0 insertions(+), 0 deletions(-) rename tests/{admin_functionality_tests.rs => integration_admin_functionality_tests.rs} (100%) rename tests/{auto_resume_tests.rs => integration_auto_resume_tests.rs} (100%) rename tests/{cancellation_tests.rs => integration_cancellation_tests.rs} (100%) rename tests/{comprehensive_source_management_tests.rs => integration_comprehensive_source_management_tests.rs} (100%) rename tests/{debug_ocr_test.rs => integration_debug_ocr_test.rs} (100%) rename tests/{document_deletion_integration_tests.rs => integration_document_deletion_integration_tests.rs} (100%) rename tests/{document_upload_hash_duplicate_tests.rs => integration_document_upload_hash_duplicate_tests.rs} (100%) rename tests/{error_handling_edge_cases_tests.rs => integration_error_handling_edge_cases_tests.rs} (100%) rename tests/{failed_ocr_endpoints_tests.rs => integration_failed_ocr_endpoints_tests.rs} (100%) rename tests/{file_processing_pipeline_tests.rs => integration_file_processing_pipeline_tests.rs} (100%) rename tests/{hash_duplicate_detection_tests.rs => integration_hash_duplicate_detection_tests.rs} (100%) rename tests/{ignored_files_integration_tests.rs => integration_ignored_files_integration_tests.rs} (100%) rename tests/{integration_tests.rs => integration_integration_tests.rs} (100%) rename tests/{investigate_empty_content.rs => integration_investigate_empty_content.rs} (100%) rename tests/{labels_integration_tests.rs => integration_labels_integration_tests.rs} (100%) rename tests/{local_folder_sync_tests.rs => integration_local_folder_sync_tests.rs} (100%) rename tests/{ocr_corruption_tests.rs => integration_ocr_corruption_tests.rs} (100%) rename tests/{ocr_failure_counting_tests.rs => integration_ocr_failure_counting_tests.rs} (100%) rename tests/{ocr_pipeline_integration_test.rs => integration_ocr_pipeline_integration_test.rs} (100%) rename tests/{ocr_queue_management_tests.rs => integration_ocr_queue_management_tests.rs} (100%) rename tests/{performance_load_tests.rs => integration_performance_load_tests.rs} (100%) rename tests/{prometheus_metrics_tests.rs => integration_prometheus_metrics_tests.rs} (100%) rename tests/{role_based_access_control_tests.rs => integration_role_based_access_control_tests.rs} (100%) rename tests/{s3_sync_tests.rs => integration_s3_sync_tests.rs} (100%) rename tests/{simple_throttling_test.rs => integration_simple_throttling_test.rs} (100%) rename tests/{source_scheduler_simple_tests.rs => integration_source_scheduler_simple_tests.rs} (100%) rename tests/{source_scheduler_tests.rs => integration_source_scheduler_tests.rs} (100%) rename tests/{source_sync_hash_duplicate_tests.rs => integration_source_sync_hash_duplicate_tests.rs} (100%) rename tests/{source_update_tests.rs => integration_source_update_tests.rs} (100%) rename tests/{stop_sync_functionality_tests.rs => integration_stop_sync_functionality_tests.rs} (100%) rename tests/{test_image_ocr_tests.rs => integration_test_image_ocr_tests.rs} (100%) rename tests/{thread_separation_tests.rs => integration_thread_separation_tests.rs} (100%) rename tests/{universal_source_sync_tests.rs => integration_universal_source_sync_tests.rs} (100%) rename tests/{webdav_comprehensive_tests.rs => integration_webdav_comprehensive_tests.rs} (100%) rename tests/{webdav_hash_duplicate_tests.rs => integration_webdav_hash_duplicate_tests.rs} (100%) rename tests/{webdav_integration_tests.rs => integration_webdav_integration_tests.rs} (100%) rename tests/{webdav_sync_tests.rs => integration_webdav_sync_tests.rs} (100%) rename tests/{basic_sync_tests.rs => unit_basic_sync_tests.rs} (100%) rename tests/{oidc_unit_tests.rs => unit_oidc_unit_tests.rs} (100%) rename tests/{unit_tests.rs => unit_unit_tests.rs} (100%) rename tests/{webdav_enhanced_unit_tests.rs => unit_webdav_enhanced_unit_tests.rs} (100%) rename tests/{webdav_unit_tests.rs => unit_webdav_unit_tests.rs} (100%) diff --git a/tests/admin_functionality_tests.rs b/tests/integration_admin_functionality_tests.rs similarity index 100% rename from tests/admin_functionality_tests.rs rename to tests/integration_admin_functionality_tests.rs diff --git a/tests/auto_resume_tests.rs b/tests/integration_auto_resume_tests.rs similarity index 100% rename from tests/auto_resume_tests.rs rename to tests/integration_auto_resume_tests.rs diff --git a/tests/cancellation_tests.rs b/tests/integration_cancellation_tests.rs similarity index 100% rename from tests/cancellation_tests.rs rename to tests/integration_cancellation_tests.rs diff --git a/tests/comprehensive_source_management_tests.rs b/tests/integration_comprehensive_source_management_tests.rs similarity index 100% rename from tests/comprehensive_source_management_tests.rs rename to tests/integration_comprehensive_source_management_tests.rs diff --git a/tests/debug_ocr_test.rs b/tests/integration_debug_ocr_test.rs similarity index 100% rename from tests/debug_ocr_test.rs rename to tests/integration_debug_ocr_test.rs diff --git a/tests/document_deletion_integration_tests.rs b/tests/integration_document_deletion_integration_tests.rs similarity index 100% rename from tests/document_deletion_integration_tests.rs rename to tests/integration_document_deletion_integration_tests.rs diff --git a/tests/document_upload_hash_duplicate_tests.rs b/tests/integration_document_upload_hash_duplicate_tests.rs similarity index 100% rename from tests/document_upload_hash_duplicate_tests.rs rename to tests/integration_document_upload_hash_duplicate_tests.rs diff --git a/tests/error_handling_edge_cases_tests.rs b/tests/integration_error_handling_edge_cases_tests.rs similarity index 100% rename from tests/error_handling_edge_cases_tests.rs rename to tests/integration_error_handling_edge_cases_tests.rs diff --git a/tests/failed_ocr_endpoints_tests.rs b/tests/integration_failed_ocr_endpoints_tests.rs similarity index 100% rename from tests/failed_ocr_endpoints_tests.rs rename to tests/integration_failed_ocr_endpoints_tests.rs diff --git a/tests/file_processing_pipeline_tests.rs b/tests/integration_file_processing_pipeline_tests.rs similarity index 100% rename from tests/file_processing_pipeline_tests.rs rename to tests/integration_file_processing_pipeline_tests.rs diff --git a/tests/hash_duplicate_detection_tests.rs b/tests/integration_hash_duplicate_detection_tests.rs similarity index 100% rename from tests/hash_duplicate_detection_tests.rs rename to tests/integration_hash_duplicate_detection_tests.rs diff --git a/tests/ignored_files_integration_tests.rs b/tests/integration_ignored_files_integration_tests.rs similarity index 100% rename from tests/ignored_files_integration_tests.rs rename to tests/integration_ignored_files_integration_tests.rs diff --git a/tests/integration_tests.rs b/tests/integration_integration_tests.rs similarity index 100% rename from tests/integration_tests.rs rename to tests/integration_integration_tests.rs diff --git a/tests/investigate_empty_content.rs b/tests/integration_investigate_empty_content.rs similarity index 100% rename from tests/investigate_empty_content.rs rename to tests/integration_investigate_empty_content.rs diff --git a/tests/labels_integration_tests.rs b/tests/integration_labels_integration_tests.rs similarity index 100% rename from tests/labels_integration_tests.rs rename to tests/integration_labels_integration_tests.rs diff --git a/tests/local_folder_sync_tests.rs b/tests/integration_local_folder_sync_tests.rs similarity index 100% rename from tests/local_folder_sync_tests.rs rename to tests/integration_local_folder_sync_tests.rs diff --git a/tests/ocr_corruption_tests.rs b/tests/integration_ocr_corruption_tests.rs similarity index 100% rename from tests/ocr_corruption_tests.rs rename to tests/integration_ocr_corruption_tests.rs diff --git a/tests/ocr_failure_counting_tests.rs b/tests/integration_ocr_failure_counting_tests.rs similarity index 100% rename from tests/ocr_failure_counting_tests.rs rename to tests/integration_ocr_failure_counting_tests.rs diff --git a/tests/ocr_pipeline_integration_test.rs b/tests/integration_ocr_pipeline_integration_test.rs similarity index 100% rename from tests/ocr_pipeline_integration_test.rs rename to tests/integration_ocr_pipeline_integration_test.rs diff --git a/tests/ocr_queue_management_tests.rs b/tests/integration_ocr_queue_management_tests.rs similarity index 100% rename from tests/ocr_queue_management_tests.rs rename to tests/integration_ocr_queue_management_tests.rs diff --git a/tests/performance_load_tests.rs b/tests/integration_performance_load_tests.rs similarity index 100% rename from tests/performance_load_tests.rs rename to tests/integration_performance_load_tests.rs diff --git a/tests/prometheus_metrics_tests.rs b/tests/integration_prometheus_metrics_tests.rs similarity index 100% rename from tests/prometheus_metrics_tests.rs rename to tests/integration_prometheus_metrics_tests.rs diff --git a/tests/role_based_access_control_tests.rs b/tests/integration_role_based_access_control_tests.rs similarity index 100% rename from tests/role_based_access_control_tests.rs rename to tests/integration_role_based_access_control_tests.rs diff --git a/tests/s3_sync_tests.rs b/tests/integration_s3_sync_tests.rs similarity index 100% rename from tests/s3_sync_tests.rs rename to tests/integration_s3_sync_tests.rs diff --git a/tests/simple_throttling_test.rs b/tests/integration_simple_throttling_test.rs similarity index 100% rename from tests/simple_throttling_test.rs rename to tests/integration_simple_throttling_test.rs diff --git a/tests/source_scheduler_simple_tests.rs b/tests/integration_source_scheduler_simple_tests.rs similarity index 100% rename from tests/source_scheduler_simple_tests.rs rename to tests/integration_source_scheduler_simple_tests.rs diff --git a/tests/source_scheduler_tests.rs b/tests/integration_source_scheduler_tests.rs similarity index 100% rename from tests/source_scheduler_tests.rs rename to tests/integration_source_scheduler_tests.rs diff --git a/tests/source_sync_hash_duplicate_tests.rs b/tests/integration_source_sync_hash_duplicate_tests.rs similarity index 100% rename from tests/source_sync_hash_duplicate_tests.rs rename to tests/integration_source_sync_hash_duplicate_tests.rs diff --git a/tests/source_update_tests.rs b/tests/integration_source_update_tests.rs similarity index 100% rename from tests/source_update_tests.rs rename to tests/integration_source_update_tests.rs diff --git a/tests/stop_sync_functionality_tests.rs b/tests/integration_stop_sync_functionality_tests.rs similarity index 100% rename from tests/stop_sync_functionality_tests.rs rename to tests/integration_stop_sync_functionality_tests.rs diff --git a/tests/test_image_ocr_tests.rs b/tests/integration_test_image_ocr_tests.rs similarity index 100% rename from tests/test_image_ocr_tests.rs rename to tests/integration_test_image_ocr_tests.rs diff --git a/tests/thread_separation_tests.rs b/tests/integration_thread_separation_tests.rs similarity index 100% rename from tests/thread_separation_tests.rs rename to tests/integration_thread_separation_tests.rs diff --git a/tests/universal_source_sync_tests.rs b/tests/integration_universal_source_sync_tests.rs similarity index 100% rename from tests/universal_source_sync_tests.rs rename to tests/integration_universal_source_sync_tests.rs diff --git a/tests/webdav_comprehensive_tests.rs b/tests/integration_webdav_comprehensive_tests.rs similarity index 100% rename from tests/webdav_comprehensive_tests.rs rename to tests/integration_webdav_comprehensive_tests.rs diff --git a/tests/webdav_hash_duplicate_tests.rs b/tests/integration_webdav_hash_duplicate_tests.rs similarity index 100% rename from tests/webdav_hash_duplicate_tests.rs rename to tests/integration_webdav_hash_duplicate_tests.rs diff --git a/tests/webdav_integration_tests.rs b/tests/integration_webdav_integration_tests.rs similarity index 100% rename from tests/webdav_integration_tests.rs rename to tests/integration_webdav_integration_tests.rs diff --git a/tests/webdav_sync_tests.rs b/tests/integration_webdav_sync_tests.rs similarity index 100% rename from tests/webdav_sync_tests.rs rename to tests/integration_webdav_sync_tests.rs diff --git a/tests/basic_sync_tests.rs b/tests/unit_basic_sync_tests.rs similarity index 100% rename from tests/basic_sync_tests.rs rename to tests/unit_basic_sync_tests.rs diff --git a/tests/oidc_unit_tests.rs b/tests/unit_oidc_unit_tests.rs similarity index 100% rename from tests/oidc_unit_tests.rs rename to tests/unit_oidc_unit_tests.rs diff --git a/tests/unit_tests.rs b/tests/unit_unit_tests.rs similarity index 100% rename from tests/unit_tests.rs rename to tests/unit_unit_tests.rs diff --git a/tests/webdav_enhanced_unit_tests.rs b/tests/unit_webdav_enhanced_unit_tests.rs similarity index 100% rename from tests/webdav_enhanced_unit_tests.rs rename to tests/unit_webdav_enhanced_unit_tests.rs diff --git a/tests/webdav_unit_tests.rs b/tests/unit_webdav_unit_tests.rs similarity index 100% rename from tests/webdav_unit_tests.rs rename to tests/unit_webdav_unit_tests.rs From a75fca0c28ad0eec6efcdddd6209e7fd8f35fa26 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Fri, 27 Jun 2025 21:32:50 +0000 Subject: [PATCH 2/2] feat(client/server): add a new badge for each source that shows the number of documents stored from each source --- frontend/src/pages/SourcesPage.tsx | 89 +++++++++++++++++++++--------- src/db/documents.rs | 55 ++++++++++++++++++ src/models.rs | 9 +++ src/routes/sources.rs | 63 +++++++++++++++++++-- 4 files changed, 186 insertions(+), 30 deletions(-) diff --git a/frontend/src/pages/SourcesPage.tsx b/frontend/src/pages/SourcesPage.tsx index 2621a74..172b4e2 100644 --- a/frontend/src/pages/SourcesPage.tsx +++ b/frontend/src/pages/SourcesPage.tsx @@ -65,6 +65,8 @@ import { Storage as ServerIcon, Pause as PauseIcon, PlayArrow as ResumeIcon, + TextSnippet as DocumentIcon, + Visibility as OcrIcon, } from '@mui/icons-material'; import { useNavigate } from 'react-router-dom'; import api, { queueService } from '../services/api'; @@ -84,6 +86,8 @@ interface Source { total_files_synced: number; total_files_pending: number; total_size_bytes: number; + total_documents: number; + total_documents_ocr: number; created_at: string; updated_at: string; } @@ -699,7 +703,7 @@ const SourcesPage: React.FC = () => { {source.name} - + { fontWeight: 600, }} /> + } + label={`${source.total_documents} docs`} + size="small" + sx={{ + borderRadius: 2, + bgcolor: alpha(theme.palette.info.main, 0.1), + color: theme.palette.info.main, + border: `1px solid ${alpha(theme.palette.info.main, 0.3)}`, + fontSize: '0.75rem', + fontWeight: 600, + }} + /> + } + label={`${source.total_documents_ocr} OCR'd`} + size="small" + sx={{ + borderRadius: 2, + bgcolor: alpha(theme.palette.success.main, 0.1), + color: theme.palette.success.main, + border: `1px solid ${alpha(theme.palette.success.main, 0.3)}`, + fontSize: '0.75rem', + fontWeight: 600, + }} + /> {!source.enabled && ( { {/* Stats Grid */} - + } - label="Files Processed" - value={source.total_files_synced} + icon={} + label="Documents Stored" + value={source.total_documents} + color="info" + tooltip="Total number of documents currently stored from this source" + /> + + + } + label="OCR Processed" + value={source.total_documents_ocr} color="success" - tooltip="Files attempted to be synced, including duplicates and skipped files" + tooltip="Number of documents that have been successfully OCR'd" /> - - } - label="Files Pending" - value={source.total_files_pending} - color="warning" - tooltip="Files discovered but not yet processed during sync" - /> - - - } - label="Total Size (Downloaded)" - value={formatBytes(source.total_size_bytes)} - color="primary" - tooltip="Total size of files successfully downloaded from this source" - /> - - + } label="Last Sync" @@ -852,6 +873,24 @@ const SourcesPage: React.FC = () => { tooltip="When this source was last synchronized" /> + + } + label="Files Pending" + value={source.total_files_pending} + color="warning" + tooltip="Files discovered but not yet processed during sync" + /> + + + } + label="Total Size" + value={formatBytes(source.total_size_bytes)} + color="primary" + tooltip="Total size of files successfully downloaded from this source" + /> + {/* Error Alert */} diff --git a/src/db/documents.rs b/src/db/documents.rs index 3bdec41..4e2b993 100644 --- a/src/db/documents.rs +++ b/src/db/documents.rs @@ -1509,4 +1509,59 @@ impl Database { Ok(deleted_documents) } + + pub async fn count_documents_for_source(&self, source_id: Uuid) -> Result<(i64, i64)> { + let row = sqlx::query( + r#" + SELECT + COUNT(*) as total_documents, + COUNT(CASE WHEN ocr_status = 'completed' AND ocr_text IS NOT NULL THEN 1 END) as total_documents_ocr + FROM documents + WHERE source_id = $1 + "# + ) + .bind(source_id) + .fetch_one(&self.pool) + .await?; + + let total_documents: i64 = row.get("total_documents"); + let total_documents_ocr: i64 = row.get("total_documents_ocr"); + + Ok((total_documents, total_documents_ocr)) + } + + pub async fn count_documents_for_sources(&self, source_ids: &[Uuid]) -> Result> { + if source_ids.is_empty() { + return Ok(vec![]); + } + + let query = format!( + r#" + SELECT + source_id, + COUNT(*) as total_documents, + COUNT(CASE WHEN ocr_status = 'completed' AND ocr_text IS NOT NULL THEN 1 END) as total_documents_ocr + FROM documents + WHERE source_id = ANY($1) + GROUP BY source_id + "# + ); + + let rows = sqlx::query(&query) + .bind(source_ids) + .fetch_all(&self.pool) + .await?; + + let results = rows + .into_iter() + .map(|row| { + let source_id: Uuid = row.get("source_id"); + let total_documents: i64 = row.get("total_documents"); + let total_documents_ocr: i64 = row.get("total_documents_ocr"); + (source_id, total_documents, total_documents_ocr) + }) + .collect(); + + Ok(results) + } } \ No newline at end of file diff --git a/src/models.rs b/src/models.rs index 1de61fe..22cc34c 100644 --- a/src/models.rs +++ b/src/models.rs @@ -862,6 +862,12 @@ pub struct SourceResponse { pub total_size_bytes: i64, pub created_at: DateTime, pub updated_at: DateTime, + /// Total number of documents/files currently stored from this source + #[serde(default)] + pub total_documents: i64, + /// Total number of documents that have been OCR'd from this source + #[serde(default)] + pub total_documents_ocr: i64, } #[derive(Debug, Serialize, Deserialize, ToSchema)] @@ -903,6 +909,9 @@ impl From for SourceResponse { total_size_bytes: source.total_size_bytes, created_at: source.created_at, updated_at: source.updated_at, + // These will be populated separately when needed + total_documents: 0, + total_documents_ocr: 0, } } } diff --git a/src/routes/sources.rs b/src/routes/sources.rs index 3d48654..b42fd19 100644 --- a/src/routes/sources.rs +++ b/src/routes/sources.rs @@ -50,7 +50,33 @@ async fn list_sources( .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let responses: Vec = sources.into_iter().map(|s| s.into()).collect(); + // Get source IDs for batch counting + let source_ids: Vec = sources.iter().map(|s| s.id).collect(); + + // Get document counts for all sources in one query + let counts = state + .db + .count_documents_for_sources(&source_ids) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + + // Create a map for quick lookup + let count_map: std::collections::HashMap = counts + .into_iter() + .map(|(id, total, ocr)| (id, (total, ocr))) + .collect(); + + let responses: Vec = sources + .into_iter() + .map(|s| { + let (total_docs, total_ocr) = count_map.get(&s.id).copied().unwrap_or((0, 0)); + let mut response: SourceResponse = s.into(); + response.total_documents = total_docs; + response.total_documents_ocr = total_ocr; + response + }) + .collect(); + Ok(Json(responses)) } @@ -90,7 +116,12 @@ async fn create_source( StatusCode::INTERNAL_SERVER_ERROR })?; - Ok(Json(source.into())) + let mut response: SourceResponse = source.into(); + // New sources have no documents yet + response.total_documents = 0; + response.total_documents_ocr = 0; + + Ok(Json(response)) } #[utoipa::path( @@ -129,6 +160,13 @@ async fn get_source( .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + // Get document counts + let (total_documents, total_documents_ocr) = state + .db + .count_documents_for_source(source_id) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + // Calculate sync progress let sync_progress = if source.total_files_pending > 0 { Some( @@ -140,8 +178,12 @@ async fn get_source( None }; + let mut source_response: SourceResponse = source.into(); + source_response.total_documents = total_documents; + source_response.total_documents_ocr = total_documents_ocr; + let response = SourceWithStats { - source: source.into(), + source: source_response, recent_documents: recent_documents.into_iter().map(|d| d.into()).collect(), sync_progress, }; @@ -202,8 +244,19 @@ async fn update_source( StatusCode::INTERNAL_SERVER_ERROR })?; - info!("Successfully updated source {}: {}", source_id, source.name); - Ok(Json(source.into())) + // Get document counts + let (total_documents, total_documents_ocr) = state + .db + .count_documents_for_source(source_id) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + + let mut response: SourceResponse = source.into(); + response.total_documents = total_documents; + response.total_documents_ocr = total_documents_ocr; + + info!("Successfully updated source {}: {}", source_id, response.name); + Ok(Json(response)) } #[utoipa::path(