Commit Graph

318 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] f8c5dd539d feat(imap): add IMAP_READONLY_MODE feature flag to safeguard shared mailboxes
When enabled, IMAP processing will fetch and process attachments but
will NOT modify the mailbox state (no starring, labeling, deleting,
or flag changes). This allows preprod instances to safely share a
Gmail inbox with production without interfering with production
email processing.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-01 14:28:51 +00:00
Christian Krakau-Louis 96b14ec74f Merge pull request #440 from christianlouis/copilot/update-search-functionality
feat(search): add content-finding filters, saved searches, and text quality to Search view
2026-03-01 15:19:57 +01:00
copilot-swe-agent[bot] 83c3405c98 feat: persist ocr_quality_score and use it for numeric filtering
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-01 13:46:43 +00:00
copilot-swe-agent[bot] 5bf0a4c0b9 feat(search): add content-finding filters, saved searches, and text quality to Search view
- Add tags, sender, text_quality filters to search API and Meilisearch client
- Add sender and ocr_text_length to Meilisearch filterable attributes
- Expand saved search allowed filter keys to include q, document_type, language, sender, text_quality
- Add filters panel and saved searches UI to the Search view template
- Add tests for new search filters, saved search keys, and search view elements

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-01 13:46:15 +00:00
copilot-swe-agent[bot] 705b970522 feat: add bulk download, cloud OCR, and basic OCR quality filter
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-01 13:38:42 +00:00
Christian Krakau-Louis 05c35a6775 Merge pull request #437 from christianlouis/copilot/add-file-download-endpoint
fix(api): change download endpoint default version from `original` to `processed`
2026-03-01 13:30:30 +01:00
copilot-swe-agent[bot] e5a4c6c64a feat(api): add advanced filtering and saved searches
- Add date range (date_from/date_to), storage provider, and tags filters to GET /api/files
- Add SavedSearch model and migration (005_add_saved_searches)
- Add CRUD API endpoints for saved searches at /api/saved-searches
- Update files.html template with new filter controls and saved searches UI
- Update files view to pass new filter parameters to template
- Add comprehensive tests for all new functionality (26 tests)

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-01 11:22:07 +00:00
copilot-swe-agent[bot] 7b5494bafb feat(api): default download endpoint to processed file version
- Change default `version` param from 'original' to 'processed' so
  GET /api/files/{id}/download (no param) returns the processed file
- Update docstring to reflect new default
- Add tests: ?version=processed, default→processed, invalid→400
- Fix test_file_download_missing_mime_type to use explicit ?version=original
- Add File Download section to docs/API.md

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-01 11:15:14 +00:00
copilot-swe-agent[bot] d095e30467 fix: address code review feedback on tests and placeholder messages
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-01 07:51:07 +00:00
copilot-swe-agent[bot] 7a8ccba9be test(preview): add 23 tests for document preview features
Also updates UserGuide.md with documentation for pdf.js viewer,
image zoom/pan, text preview, and the file-list preview modal.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-01 07:42:48 +00:00
copilot-swe-agent[bot] 2f442b0906 test: improve upload retry tests with legacy path assertions and priority verification
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-28 12:57:50 +00:00
copilot-swe-agent[bot] bb98afac82 fix(api): use processed_file_path from DB in upload retry path resolution
The upload retry logic now checks file_record.processed_file_path first
(the GPT-suggested filename stored during finalization), before falling
back to legacy hash-based and original-filename-based path patterns.

This fixes the case where the processed file has a different name than
the original (e.g., '2023-10-01_Unknown.pdf' vs 'cable_graphic.pdf')
and the retry couldn't find the file on disk.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-28 12:56:01 +00:00
copilot-swe-agent[bot] 4fdb1b8d85 fix: address code review feedback - restrict terminal step check to success only, add test assertions
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-27 16:39:32 +00:00
copilot-swe-agent[bot] 2c55f076be fix(tasks): resolve files stuck in Pending status despite completed processing
The status calculation required ALL steps to be explicitly marked as
success/skipped before a file could be "completed". This failed for
dynamic pipelines where:

1. check_for_duplicates was logged before the file record existed (no
   file_id), so its FileProcessingStep was never updated from "pending"
2. extract_text was not marked as "skipped" for non-PDF files that go
   through PDF conversion first

Fix:
- Move check_for_duplicates success log to after initialize_file_steps()
  with the correct file_id so the step actually gets updated
- Mark extract_text as "skipped" for non-PDF files
- Add terminal-step fallback: if send_to_all_destinations is "success",
  the file is "completed" even if intermediate steps remain "pending"
  (handles any other dynamic pipeline edge cases)

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-27 16:38:10 +00:00
Christian Krakau-Louis c0d19a5398 Merge pull request #429 from christianlouis/copilot/fix-complete-status-error
fix(tests): align "completed" test fixtures with terminal-step guard semantics
2026-02-27 10:22:39 +01:00
copilot-swe-agent[bot] aba47af30a test: add send_to_all_destinations step to test_status_filter_completed
The terminal-step guard requires send_to_all_destinations: success before
a file is considered "completed". Update test_status_filter_completed to
include this step so it matches the new semantics.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-27 01:08:26 +00:00
copilot-swe-agent[bot] 9795ca0d52 test: update 3 tests broken by terminal-step completed guard
The previous fix (requiring send_to_all_destinations to be present
before marking a file as completed) broke 3 tests that used only
partial step sets and expected "completed":

- test_coverage_polish.py::TestFileQueriesDeduplicationEnabled::
  test_deduplication_enabled_adds_check_for_duplicates
- test_file_listing.py::TestFileListingPagination::
  test_processing_status_included
- test_file_listing.py::TestFileDetailEndpoint::
  test_file_detail_status_determination

Add send_to_all_destinations: success to each test's dataset so
"completed" status is reached correctly under the new semantics.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-27 01:04:42 +00:00
copilot-swe-agent[bot] e6dd39c27d fix(status): prevent false Completed status when mandatory pipeline steps have not run
Add a terminal-step guard (send_to_all_destinations) to all status
calculation paths so that files are only marked Completed once the
entire processing pipeline has been recorded.

- get_file_overall_status: require TERMINAL_STEP to be present
- get_files_processing_status: same guard for bulk status
- get_step_summary: count missing terminal step as queued so
  total_main_steps > main_completed when pipeline is incomplete
- apply_status_filter: SQL sub-query requires terminal step for
  completed filter
- process_document: call initialize_file_steps after creating a new
  file record so all mandatory steps are pre-created as pending

Define TERMINAL_STEP constant in step_manager.py and reference it in
file_status.py and file_queries.py to avoid magic strings.

Tests updated: add send_to_all_destinations to completed-file
fixtures; add test verifying initialize_file_steps is called for
new files.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-27 00:51:14 +00:00
copilot-swe-agent[bot] 4dd018f210 refactor(queue): address code review feedback — extract constants and sync refresh interval
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-27 00:47:58 +00:00
copilot-swe-agent[bot] c7c5718f78 feat(queue): add queue monitoring dashboard and pending banner on files page
- Add /api/queue/stats endpoint with Redis queue lengths, Celery worker
  inspection, and DB processing summaries
- Add /api/queue/pending-count lightweight endpoint for the files page banner
- Add /admin/queue admin-only view with auto-refreshing queue dashboard
- Add queue pending banner on /files page showing queued/processing count
- Add Queue Monitor link to admin dropdown in navigation (desktop + mobile)
- Add comprehensive tests for all new endpoints and views

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-27 00:42:55 +00:00
copilot-swe-agent[bot] 4a251fd3ca fix(test): update drop overlay text assertion to match new directory-upload message
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-26 23:50:46 +00:00
copilot-swe-agent[bot] a05690bd5f feat(upload): adaptive 429 backoff, full Gotenberg file types, directory traversal
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-26 23:24:26 +00:00
copilot-swe-agent[bot] 36b668020b fix(tests): fix OAuth integration tests failing due to Docker registry timeout
The mock_oauth_server session fixture tried to pull ghcr.io/navikt/mock-oauth2-server:2.1.1
from Docker, which times out in sandboxed CI, causing all 14 OAuth integration tests to ERROR.

Changes to tests/conftest_oauth.py:
- mock_oauth_server: catch container startup exceptions, attempt cleanup, yield None
  instead of propagating (static fallback config is used instead)
- oauth_config: add elif mock_oauth_server is None branch returning a static hardcoded
  config (mode="static") using module-level URL constants
- oauth_enabled_app: use authorize_url/access_token_url directly (no HTTP metadata
  discovery), clear/restore authlib _clients/_registry cache per test, add cleanup in teardown
- Extract _STATIC_OAUTH_* constants to avoid URL duplication

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-26 14:21:58 +00:00
copilot-swe-agent[bot] 44dcabd1f3 fix(tests): fix TestGetFullConfigException using PropertyMock on module-level settings
The test was using patch.object(type(settings), "onedrive_client_id", property(...))
to make settings.onedrive_client_id raise. Pydantic v2 Settings fields are not plain
Python descriptors so this approach raises AttributeError.

Fix: patch app.api.onedrive.settings with a MagicMock whose onedrive_client_id
is a PropertyMock(side_effect=Exception), which correctly triggers the except
branch in get_onedrive_full_config and returns {"status": "error"}.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-26 12:04:38 +00:00
copilot-swe-agent[bot] b4e93c00b4 fix(tests): fix test_save_settings_outer_exception in OneDrive coverage tests
The test was patching os.path.join, which is called inside an inner
try/except block in save_onedrive_settings. This meant the exception
was silently caught and logged, never reaching the outer exception
handler that returns HTTP 500.

Fix by patching notify_settings_updated instead, which is called in
the outer try block, so exceptions correctly propagate to the outer
handler.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-26 11:02:16 +00:00
Christian Krakau-Louis a64de0a6cb Merge pull request #420 from christianlouis/copilot/improve-code-coverage-tests
fix(tests): correct mock patch targets in google_drive coverage tests
2026-02-26 11:46:03 +01:00
copilot-swe-agent[bot] 09a6337c6f fix(ui): prevent search input from clearing on each keystroke and add dedicated /search page
The debounceSearch() function in files.html called clearFullTextSearch()
when the query was shorter than 2 characters. Since clearFullTextSearch()
sets input.value = '', every single keystroke was immediately erased —
users could paste text but not type.

Fix: debounceSearch now only hides the results panel for short queries
without touching the input value.

Also adds a dedicated /search page with Google-style results showing
content previews (document title, filename, type badges, tag badges,
sender, and OCR text snippets with highlighted matches).

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-26 09:18:28 +00:00
copilot-swe-agent[bot] c6ac6b50e9 fix(tests): use targeted settings mock instead of broad getattr patch
Replace module-level getattr patch with a MagicMock that has a property
raising on google_drive_use_oauth access, as suggested by code review.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-26 09:16:49 +00:00
copilot-swe-agent[bot] 096aedb8a0 fix(tests): correct mock patch targets in google_drive coverage tests
- Change @patch("app.api.google_drive.get_google_drive_service") to
  @patch("app.tasks.upload_to_google_drive.get_google_drive_service")
  because the function is imported locally inside the endpoint function
  body, not at module level
- Replace patch.object(type(settings), "google_drive_use_oauth", ...)
  with patch("app.api.google_drive.getattr", ...) because Pydantic v2
  models don't expose fields as regular class attributes

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-26 09:14:08 +00:00
copilot-swe-agent[bot] ffa0e1eb7b test(coverage): add celery_worker coverage and remove dead config_validator.py
- Rewrite tests/test_celery_worker.py to mock check_credentials.apply_async
  at import time, enabling all 35 statements to be covered without Redis
- Add tests for conditional beat schedule entries (IMAP, Uptime Kuma)
- Delete app/utils/config_validator.py — dead code shadowed by the
  config_validator/ package directory (Python gives packages precedence)
- Remove both files from coverage omit in pyproject.toml
- celery_worker.py now at 100% coverage (was 0%)

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 21:08:24 +00:00
Christian Krakau-Louis e58b58537d Merge pull request #415 from christianlouis/copilot/increase-test-coverage-modules
test(coverage): add coverage tests for onedrive and google_drive API modules
2026-02-25 17:43:04 +01:00
Christian Krakau-Louis 3635e97dbd Merge pull request #416 from christianlouis/copilot/improve-test-coverage-mid-range-files
test: improve coverage for process_with_ocr, api/settings, and settings_service to 90%+
2026-02-25 17:42:03 +01:00
Christian Krakau-Louis 1bb463f719 Merge pull request #417 from christianlouis/copilot/polish-coverage-to-90-percent
test(coverage): push coverage to 95%+ for 11 target files
2026-02-25 17:41:43 +01:00
github-actions[bot] 69cb16c522 style: apply ruff auto-fix
- Auto-formatted code with ruff format
- Applied ruff linting fixes with --fix

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-25 15:43:40 +00:00
copilot-swe-agent[bot] e3cd687757 test(coverage): add coverage tests for app/api/onedrive.py and app/api/google_drive.py
Add targeted tests covering uncovered lines:
- OneDrive: token refresh failure, token rotation with .env update,
  DB persist failure, user info failure, exception handlers
- Google Drive: service account test-token paths, update settings
  exception handler, get-token-info outer exception

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 15:41:01 +00:00
copilot-swe-agent[bot] 759647f957 test(coverage): add tests to push coverage above 95% for 11 target files
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 15:36:46 +00:00
copilot-swe-agent[bot] d9abbc1c99 test: improve coverage for process_with_ocr, api/settings, and settings_service to 90%+
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 15:24:42 +00:00
copilot-swe-agent[bot] 0821e2f989 feat(privacy): implement GDPR and global privacy compliance
- Add dismissable cookie notice banner to base template (essential
  cookies only, ePrivacy Directive compliant, localStorage persistence)
- Expand Privacy Notice to cover all target markets: EU/GDPR,
  UK GDPR, Switzerland nFADP, Ukraine, US CCPA/CPRA, Canada PIPEDA/
  Law 25, Brazil LGPD/Latin America, and Asia-Pacific & Japan (APPI,
  Australia Privacy Act, South Korea PIPA, Singapore PDPA, India DPDP)
- Add International Data Transfers section (SCCs, IDTAs, adequacy
  decisions) and Data Minimization & Purpose Limitation section
- Update Cookie Policy with precise cookie table, ePrivacy exemption
  rationale, and localStorage notice dismissal documentation
- Create docs/PrivacyCompliance.md: full multi-market compliance guide
  covering cookie strategy, data transfer mechanisms, data subject
  rights handling matrix with response timelines, and market-specific
  notes for all supported regions
- Add docs/PrivacyCompliance.md to mkdocs.yml Compliance nav section
- Add 10 new targeted tests to test_views_general.py validating all
  key compliance content areas

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 15:15:24 +00:00
copilot-swe-agent[bot] db581ba355 fix(database): add runtime migrations for ocr_text, ai_metadata, document_title columns
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 14:37:56 +00:00
copilot-swe-agent[bot] c428a7ec00 feat(ui): add dark mode support with system preference detection and localStorage persistence
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 13:59:49 +00:00
github-actions[bot] 093076515e style: apply ruff auto-fix
- Auto-formatted code with ruff format
- Applied ruff linting fixes with --fix

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-25 13:39:01 +00:00
copilot-swe-agent[bot] ff96340154 fix(config): default meilisearch_url to http://meilisearch:7700 for Docker/K8s service discovery
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 13:27:09 +00:00
copilot-swe-agent[bot] 89d5df71c8 feat(ocr): fine-tune OCR quality criteria with stricter threshold and head-to-head comparison
- Raise quality acceptance threshold from 65→85 (configurable via TEXT_QUALITY_THRESHOLD)
- Reject text with significant issues (excessive_typos, garbage_characters,
  incoherent_text, fragmented_sentences) even when score is above threshold
  (configurable via TEXT_QUALITY_SIGNIFICANT_ISSUES)
- Add compare_text_quality() for AI-powered head-to-head comparison of
  original embedded text vs fresh OCR output
- Update process_document to pass original text to OCR task for comparison
- Update process_with_ocr to run comparison and keep the higher-quality text
- Add new settings to settings_service.py metadata
- Update docs/ConfigurationGuide.md with new settings
- Add comprehensive tests for new threshold and comparison logic

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-25 13:03:21 +00:00
copilot-swe-agent[bot] 44c9f6dc38 test: fix test_retry_azure_ocr_success to patch process_with_ocr instead of legacy azure task
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-24 22:55:46 +00:00
copilot-swe-agent[bot] 0c500e1ec6 fix(ocr): address code review: add model fallback default, remove unused variable
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-24 18:44:20 +00:00
copilot-swe-agent[bot] b03bfb5e02 feat(ocr): add AI-based embedded text quality check with automatic OCR fallback
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-24 18:41:27 +00:00
Christian Krakau-Louis a5985fb135 Merge pull request #396 from christianlouis/copilot/achieve-code-coverage-goals
test: raise overall coverage from 92.6% to 95.4%
2026-02-24 19:25:41 +01:00
copilot-swe-agent[bot] 02ad558330 test: achieve 90%+ code coverage across codebase
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-24 18:17:22 +00:00
copilot-swe-agent[bot] 2b2a97c2fa fix(ocr): ensure Tesseract language data for embed_text_layer regardless of active OCR provider
Root cause: ensure_ocr_languages_from_settings() only downloaded tessdata
when the 'tesseract' provider was active, but embed_text_layer() uses
ocrmypdf (which needs tessdata) as a fallback for ALL OCR providers.

- embed_text_layer(): call ensure_tesseract_languages(language) after
  confirming ocrmypdf is on PATH, so language data is present before
  ocrmypdf is invoked (prevents exit code 3 for fra/deu/etc.)
- ensure_ocr_languages_from_settings(): extend the condition from
  'tesseract' in active_providers to also trigger when ocrmypdf is
  on PATH, enabling proactive pre-download at startup for any config
- Tests: mock shutil.which and ensure_tesseract_languages in affected
  test cases; rename azure-only test and add new test for ocrmypdf case

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-24 18:02:19 +00:00
copilot-swe-agent[bot] 8a2a4dc2b3 feat(ocr): auto-install Tesseract/EasyOCR languages from settings
- Add app/utils/ocr_language_manager.py: detects tessdata dir, downloads
  missing .traineddata files via wget/curl from tessdata_fast GitHub repo,
  pre-downloads EasyOCR models, exposes async background-thread helper
- TesseractOCRProvider.process() calls ensure_tesseract_languages() before
  running pytesseract; raises clear error if languages remain unavailable
- EasyOCRProvider.process() logs informational message when models download
- app/main.py: calls ensure_ocr_languages_async() at startup
- app/utils/settings_sync.py: triggers language re-check after every
  settings reload so UI changes take effect without container restart
- app/api/settings.py: adds POST /api/settings/install-ocr-languages
  endpoint for on-demand language installation from the admin UI
- Dockerfile: adds wget for runtime tessdata downloads
- docs/ConfigurationGuide.md: documents automatic language download
- tests/test_ocr_language_manager.py: 29 unit tests

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-24 16:28:14 +00:00