mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-08-17 08:13:19 +00:00
Feature: Redesign the task system (#12584)
* feat(tasks): replace PaperlessTask model with structured redesign Drop the old string-based PaperlessTask table and recreate it with Status/TaskType/TriggerSource enums, JSONField result storage, and duration tracking fields. Update all call sites to use the new API. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(tasks): rewrite signal handlers to track all task types Replace the old consume_file-only handler with a full rewrite that tracks 6 task types (consume_file, train_classifier, sanity_check, index_optimize, llm_index, mail_fetch) with proper trigger source detection, input data extraction, legacy result string parsing, duration/wait time recording, and structured error capture on failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tasks): add traceback and revoked state coverage to signal tests * refactor(tasks): remove manual PaperlessTask creation and scheduled/auto params All task records are now created exclusively via Celery signals (Task 2). Removed PaperlessTask creation/update from train_classifier, sanity_check, llmindex_index, and check_sanity. Removed scheduled= and auto= parameters from all 7 call sites. Updated apply_async callers to use trigger_source headers instead. Exceptions now propagate naturally from task functions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(tasks): auto-inject trigger_source=scheduled header for all beat tasks Inject `headers: {"trigger_source": "scheduled"}` into every Celery beat schedule entry so signal handlers can identify scheduler-originated tasks without per-task instrumentation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(tasks): update serializer, filter, and viewset with v9 backwards compat - Replace TasksViewSerializer/RunTaskViewSerializer with TaskSerializerV10 (new field names), TaskSerializerV9 (v9 compat), TaskSummarySerializer, and RunTaskSerializer - Add AcknowledgeTasksViewSerializer unchanged (kept existing validation) - Expand PaperlessTaskFilterSet with MultipleChoiceFilter for task_type, trigger_source, status; add is_complete, date_created_after/before filters - Replace TasksViewSet.get_serializer_class() to branch on request.version - Add get_queryset() v9 compat for task_name/type query params - Add acknowledge_all, summary, active actions to TasksViewSet - Rewrite run action to use apply_async with trigger_source header - Add timedelta import to views.py; add MultipleChoiceFilter/DateTimeFilter to filters.py imports Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tasks): add read_only_fields to TaskSerializerV9, enforce admin via permission_classes on run action * test(tasks): rewrite API task tests for redesigned model and v9 compat Replaces the old Django TestCase-based tests with pytest-style classes using PaperlessTaskFactory. Covers v10 field names, v9 backwards-compat field mapping, filtering, ordering, acknowledge, acknowledge_all, summary, active, and run endpoints. Also adds PaperlessTaskFactory to factories.py and fixes a redundant source= kwarg in TaskSerializerV10.related_document_ids. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tasks): fix two spec gaps in task API test suite Move test_list_is_owner_aware to TestGetTasksV10 (it tests GET /api/tasks/, not acknowledge). Add test_related_document_ids_includes_duplicate_of to cover the duplicate_of path in the related_document_ids property. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tasks): address code quality review findings Remove trivial field-existence tests per project conventions. Fix potentially flaky ordering test to use explicit date_created values. Add is_complete=false filter test, v9 type filter input direction test, and tighten TestActive second test to target REVOKED specifically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(tasks): update TaskAdmin for redesigned model Add date_created, duration_seconds to list_display; add trigger_source to list_filter; add input_data, duration_seconds, wait_time_seconds to readonly_fields. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(tasks): update Angular types and service for task redesign Replace PaperlessTaskName/PaperlessTaskType/PaperlessTaskStatus enums with new PaperlessTaskType, PaperlessTaskTriggerSource, PaperlessTaskStatus enums. Update PaperlessTask interface to new field names (task_type, trigger_source, input_data, result_message, related_document_ids). Update TasksService to filter by task_type instead of task_name. Update tasks component and system-status-dialog to use new field names. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(tasks): remove django-celery-results PaperlessTask now tracks all task results via Celery signals. The django-celery-results DB backend was write-only -- nothing reads from it. Drop the package and add a migration to clean up the orphaned tables. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: fix remaining tests broken by task system redesign Update all tests that created PaperlessTask objects with old field names to use PaperlessTaskFactory and new field names (task_type, trigger_source, status, result_message). Use apply_async instead of delay where mocked. Drop TestCheckSanityTaskRecording — tests PaperlessTask creation that was intentionally removed from check_sanity(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tasks): improve test_api_tasks.py structure and add api marker - Move admin_client, v9_client, user_client fixtures to conftest.py so they can be reused by other API tests; all three now build on the rest_api_client fixture instead of creating APIClient() directly - Move regular_user fixture to conftest.py (was already done, now also used by the new client fixtures) - Add docstrings to every test method describing the behaviour under test - Move timedelta/timezone imports to module level - Register 'api' pytest marker in pyproject.toml and apply pytestmark to the entire file so all 40 tests are selectable via -m api Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(tasks): simplify task tracking code after redesign - Extract COMPLETE_STATUSES as a class constant on PaperlessTask, eliminating the repeated status tuple across models.py, views.py (3×), and filters.py - Extract _CELERY_STATE_TO_STATUS as a module-level constant instead of rebuilding the dict on every task_postrun - Extract _V9_TYPE_TO_TRIGGER_SOURCE and _RUNNABLE_TASKS as class constants on TasksViewSet instead of rebuilding on every request - Extract _TRIGGER_SOURCE_TO_V9_TYPE as a class constant on TaskSerializerV9 instead of rebuilding per serialized object - Extract _get_consume_args helper to deduplicate identical arg extraction logic in _extract_input_data, _determine_trigger_source, and _extract_owner_id - Move inline imports (re, traceback) and Avg to module level - Fix _DOCUMENT_SOURCE_TO_TRIGGER type annotation key type to DocumentSource instead of Any - Remove redundant truthiness checks in SystemStatusView branches already guarded by an is-None check Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(tasks): add docstrings and rename _parse_legacy_result - Add docstrings to _extract_input_data, _determine_trigger_source, _extract_owner_id explaining what each helper does and why - Rename _parse_legacy_result -> _parse_consume_result: the function parses current consume_file string outputs (consumer.py returns "New document id N created" and "It is a duplicate of X (#N)"), not legacy data; the old name was misleading Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(tasks): extend and harden the task system redesign - TaskType: add EMPTY_TRASH, CHECK_WORKFLOWS, CLEANUP_SHARE_LINKS; remove INDEX_REBUILD (no backing task — beat schedule uses index_optimize) - TRACKED_TASKS: wire up all nine task types including the three new ones and llmindex_index / process_mail_accounts - Add task_revoked_handler so cancelled/expired tasks are marked REVOKED - Fix double-write: task_postrun_handler no longer overwrites result_data when status is already FAILURE (task_failure_handler owns that write) - v9 serialiser: map EMAIL_CONSUME and FOLDER_CONSUME to AUTO_TASK - views: scope task list to owner for regular users, admins see all; validate ?days= query param and return 400 on bad input - tests: add test_list_admin_sees_all_tasks; rename/fix test_parses_duplicate_string (duplicates produce SUCCESS, not FAILURE); use PaperlessTaskFactory in modified tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tasks): fix MAIL_FETCH null input_data and postrun double-query - _extract_input_data: return {} instead of {"account_ids": None} when process_mail_accounts is called without an explicit account list (the normal beat-scheduled path); add test to cover this path - task_postrun_handler: replace filter().first() + filter().update() with get() + save(update_fields=[...]) — single fetch, single write, consistent with task_prerun_handler Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tasks): add queryset stub to satisfy drf-spectacular schema generation TasksViewSet.get_queryset() accesses request.user, which drf-spectacular cannot provide during static schema generation. Adding a class-level queryset = PaperlessTask.objects.none() gives spectacular a model to introspect without invoking get_queryset(), eliminating both warnings and the test_valid_schema failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tasks): fill coverage gaps in task system - test_task_signals: add TestTaskRevokedHandler (marks REVOKED, ignores None request, ignores unknown id); switch existing direct PaperlessTask.objects.create calls to PaperlessTaskFactory; import pytest_mock and use MockerFixture typing on mocker params - test_api_tasks: add test_rejects_invalid_days_param to TestSummary - tasks.service.spec: add dismissAllTasks test (POST acknowledge_all + reload) - models: add pragma: no cover to __str__, is_complete, and related_document_ids (trivial delegates, covered indirectly) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Well, that was a bad push. * Fixes v9 API compatability with testing coverage * fix(tasks): restore INDEX_OPTIMIZE enum and remove no-op run button INDEX_OPTIMIZE was dropped from the TaskType enum but still referenced in _RUNNABLE_TASKS (views.py) and the frontend system-status-dialog, causing an AttributeError at import time. Restore the enum value in the model and migration so the serializer accepts it, but remove it from _RUNNABLE_TASKS since index_optimize is a Tantivy no-op. Remove the frontend "Run Task" button for index optimization accordingly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(tasks): v9 type filter now matches all equivalent trigger sources The v9 ?type= query param mapped each value to a single TriggerSource, but the serializer maps multiple sources to the same v9 type value. A task serialized as "auto_task" would not appear when filtering by ?type=auto_task if its trigger_source was email_consume or folder_consume. Same issue for "manual_task" missing web_ui and api_upload sources. Changed to trigger_source__in with the full set of sources for each v9 type value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(tasks): give task_failure_handler full ownership of FAILURE path task_postrun_handler now early-returns for FAILURE states instead of redundantly writing status and date_done. task_failure_handler now computes duration_seconds and wait_time_seconds so failed tasks get complete timing data. This eliminates a wasted .get() + .save() round trip on every failed task and gives each handler a clean, non-overlapping responsibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(tasks): resolve trigger_source header via TriggerSource enum lookup Replace two hardcoded string comparisons ("scheduled", "system") with a single TriggerSource(header_source) lookup so the enum values are the single source of truth. Any valid TriggerSource DB value passed in the header is accepted; invalid values fall through to the document-source / MANUAL logic. Update tests to pass enum values in headers rather than raw strings, and add a test for the invalid-header fallback path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tasks): use TriggerSource enum values at all apply_async call sites Replace raw strings ("system", "manual") with PaperlessTask.TriggerSource enum values in the three callers that can import models. The settings file remains a raw string (models cannot be imported at settings load time) with a comment pointing to the enum value it must match. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(tasks): parametrize repetitive test cases in task test files test_api_tasks.py: - Collapse six trigger_source->v9-type tests into one parametrized test, adding the previously untested API_UPLOAD case - Collapse three task_name mapping tests (two remaps + pass-through) into one parametrized test - Collapse two acknowledge_all status tests into one parametrized test - Collapse two run-endpoint 400 tests into one parametrized test - Update run/ assertions to use TriggerSource enum values test_task_signals.py: - Collapse three trigger_source header tests into one parametrized test - Collapse two DocumentSource->TriggerSource mapping tests into one parametrized test - Collapse two prerun ignore-invalid-id tests into one parametrized test All parametrize cases use pytest.param with descriptive ids. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Handle JSON serialization for datetime and Path. Further restrist the v9 permissions as Copilot suggests * That should fix the generated schema/browser * Use XSerializer for the schema * A few more basic cases I see no value in covering * Drops the migration related stuff too. Just in case we want it again or it confuses people * fix: annotate tasks_summary_retrieve as array of TaskSummarySerializer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: annotate tasks_active_retrieve as array of TaskSerializerV10 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Restore task running to superuser only * Removes the acknowledge/dismiss all stuff * Aligns v10 and v9 task permissions with each other * Short blurb just to warn users about the tasks being cleared --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
20aa0937e8
commit
8e67828bd7
@@ -76,33 +76,27 @@
|
||||
<label class="form-check-label" for="task{{task.id}}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td class="overflow-auto name-col">{{ task.task_file_name }}</td>
|
||||
<td class="overflow-auto name-col">{{ task.input_data?.filename }}</td>
|
||||
<td class="d-none d-lg-table-cell">{{ task.date_created | customDate:'short' }}</td>
|
||||
@if (activeTab !== 'started' && activeTab !== 'queued') {
|
||||
<td class="d-none d-lg-table-cell">
|
||||
@if (task.result?.length > 50) {
|
||||
@if (task.result_message?.length > 50) {
|
||||
<div class="result" (click)="expandTask(task); $event.stopPropagation();"
|
||||
[ngbPopover]="resultPopover" popoverClass="shadow small mobile" triggers="mouseenter:mouseleave" container="body">
|
||||
<span class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result | slice:0:50 }}…</span>
|
||||
<span class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result_message | slice:0:50 }}…</span>
|
||||
</div>
|
||||
}
|
||||
@if (task.result?.length <= 50) {
|
||||
<span class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result }}</span>
|
||||
@if (task.result_message?.length <= 50) {
|
||||
<span class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result_message }}</span>
|
||||
}
|
||||
<ng-template #resultPopover>
|
||||
<pre class="small mb-0">{{ task.result | slice:0:300 }}@if (task.result.length > 300) {
|
||||
<pre class="small mb-0">{{ task.result_message | slice:0:300 }}@if (task.result_message.length > 300) {
|
||||
…
|
||||
}</pre>
|
||||
@if (task.result?.length > 300) {
|
||||
@if (task.result_message?.length > 300) {
|
||||
<br/><em>(<ng-container i18n>click for full output</ng-container>)</em>
|
||||
}
|
||||
</ng-template>
|
||||
@if (task.duplicate_documents?.length > 0) {
|
||||
<div class="small text-warning-emphasis d-flex align-items-center gap-1">
|
||||
<i-bs class="lh-1" width="1em" height="1em" name="exclamation-triangle"></i-bs>
|
||||
<span i18n>Duplicate(s) detected</span>
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
<td class="d-lg-none">
|
||||
@@ -116,7 +110,7 @@
|
||||
<i-bs name="check" class="me-1"></i-bs><ng-container i18n>Dismiss</ng-container>
|
||||
</button>
|
||||
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
|
||||
@if (task.related_document) {
|
||||
@if (task.related_document_ids?.[0]) {
|
||||
<button class="btn btn-sm btn-outline-primary" (click)="dismissAndGo(task); $event.stopPropagation();">
|
||||
<i-bs name="file-text" class="me-1"></i-bs><ng-container i18n>Open Document</ng-container>
|
||||
</button>
|
||||
@@ -127,7 +121,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="p-0" [class.border-0]="expandedTask !== task.id" colspan="5">
|
||||
<pre #collapse="ngbCollapse" [ngbCollapse]="expandedTask !== task.id" class="small mb-0"><div class="small p-1 p-lg-3 ms-lg-3">{{ task.result }}</div></pre>
|
||||
<pre #collapse="ngbCollapse" [ngbCollapse]="expandedTask !== task.id" class="small mb-0"><div class="small p-1 p-lg-3 ms-lg-3">{{ task.result_message }}</div></pre>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import { throwError } from 'rxjs'
|
||||
import { routes } from 'src/app/app-routing.module'
|
||||
import {
|
||||
PaperlessTask,
|
||||
PaperlessTaskName,
|
||||
PaperlessTaskStatus,
|
||||
PaperlessTaskTriggerSource,
|
||||
PaperlessTaskType,
|
||||
} from 'src/app/data/paperless-task'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
@@ -39,81 +39,100 @@ const tasks: PaperlessTask[] = [
|
||||
{
|
||||
id: 467,
|
||||
task_id: '11ca1a5b-9f81-442c-b2c8-7e4ae53657f1',
|
||||
task_file_name: 'test.pdf',
|
||||
input_data: { filename: 'test.pdf' },
|
||||
date_created: new Date('2023-03-01T10:26:03.093116Z'),
|
||||
date_done: new Date('2023-03-01T10:26:07.223048Z'),
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Failed,
|
||||
result: 'test.pd: Not consuming test.pdf: It is a duplicate of test (#100)',
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
task_type_display: 'Consume File',
|
||||
trigger_source: PaperlessTaskTriggerSource.FolderConsume,
|
||||
trigger_source_display: 'Folder Consume',
|
||||
status: PaperlessTaskStatus.Failure,
|
||||
status_display: 'Failure',
|
||||
result_message:
|
||||
'test.pd: Not consuming test.pdf: It is a duplicate of test (#100)',
|
||||
acknowledged: false,
|
||||
related_document: null,
|
||||
related_document_ids: [],
|
||||
},
|
||||
{
|
||||
id: 466,
|
||||
task_id: '10ca1a5b-3c08-442c-b2c8-7e4ae53657f1',
|
||||
task_file_name: '191092.pdf',
|
||||
input_data: { filename: '191092.pdf' },
|
||||
date_created: new Date('2023-03-01T09:26:03.093116Z'),
|
||||
date_done: new Date('2023-03-01T09:26:07.223048Z'),
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Failed,
|
||||
result:
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
task_type_display: 'Consume File',
|
||||
trigger_source: PaperlessTaskTriggerSource.FolderConsume,
|
||||
trigger_source_display: 'Folder Consume',
|
||||
status: PaperlessTaskStatus.Failure,
|
||||
status_display: 'Failure',
|
||||
result_message:
|
||||
'191092.pd: Not consuming 191092.pdf: It is a duplicate of 191092 (#311)',
|
||||
acknowledged: false,
|
||||
related_document: null,
|
||||
related_document_ids: [],
|
||||
},
|
||||
{
|
||||
id: 465,
|
||||
task_id: '3612d477-bb04-44e3-985b-ac580dd496d8',
|
||||
task_file_name: 'Scan Jun 6, 2023 at 3.19 PM.pdf',
|
||||
input_data: { filename: 'Scan Jun 6, 2023 at 3.19 PM.pdf' },
|
||||
date_created: new Date('2023-06-06T15:22:05.722323-07:00'),
|
||||
date_done: new Date('2023-06-06T15:22:14.564305-07:00'),
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
task_type_display: 'Consume File',
|
||||
trigger_source: PaperlessTaskTriggerSource.FolderConsume,
|
||||
trigger_source_display: 'Folder Consume',
|
||||
status: PaperlessTaskStatus.Pending,
|
||||
result: null,
|
||||
status_display: 'Pending',
|
||||
result_message: null,
|
||||
acknowledged: false,
|
||||
related_document: null,
|
||||
related_document_ids: [],
|
||||
},
|
||||
{
|
||||
id: 464,
|
||||
task_id: '2eac4716-2aa6-4dcd-9953-264e11656d7e',
|
||||
task_file_name: 'paperless-mail-l4dkg8ir',
|
||||
input_data: { filename: 'paperless-mail-l4dkg8ir' },
|
||||
date_created: new Date('2023-06-04T11:24:32.898089-07:00'),
|
||||
date_done: new Date('2023-06-04T11:24:44.678605-07:00'),
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Complete,
|
||||
result: 'Success. New document id 422 created',
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
task_type_display: 'Consume File',
|
||||
trigger_source: PaperlessTaskTriggerSource.EmailConsume,
|
||||
trigger_source_display: 'Email Consume',
|
||||
status: PaperlessTaskStatus.Success,
|
||||
status_display: 'Success',
|
||||
result_message: 'Success. New document id 422 created',
|
||||
acknowledged: false,
|
||||
related_document: 422,
|
||||
related_document_ids: [422],
|
||||
},
|
||||
{
|
||||
id: 463,
|
||||
task_id: '28125528-1575-4d6b-99e6-168906e8fa5c',
|
||||
task_file_name: 'onlinePaymentSummary.pdf',
|
||||
input_data: { filename: 'onlinePaymentSummary.pdf' },
|
||||
date_created: new Date('2023-06-01T13:49:51.631305-07:00'),
|
||||
date_done: new Date('2023-06-01T13:49:54.190220-07:00'),
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Complete,
|
||||
result: 'Success. New document id 421 created',
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
task_type_display: 'Consume File',
|
||||
trigger_source: PaperlessTaskTriggerSource.FolderConsume,
|
||||
trigger_source_display: 'Folder Consume',
|
||||
status: PaperlessTaskStatus.Success,
|
||||
status_display: 'Success',
|
||||
result_message: 'Success. New document id 421 created',
|
||||
acknowledged: false,
|
||||
related_document: 421,
|
||||
related_document_ids: [421],
|
||||
},
|
||||
{
|
||||
id: 462,
|
||||
task_id: 'a5b9ca47-0c8e-490f-a04c-6db5d5fc09e5',
|
||||
task_file_name: 'paperless-mail-_rrpmqk6',
|
||||
input_data: { filename: 'paperless-mail-_rrpmqk6' },
|
||||
date_created: new Date('2023-06-07T02:54:35.694916Z'),
|
||||
date_done: null,
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
task_type_display: 'Consume File',
|
||||
trigger_source: PaperlessTaskTriggerSource.EmailConsume,
|
||||
trigger_source_display: 'Email Consume',
|
||||
status: PaperlessTaskStatus.Started,
|
||||
result: null,
|
||||
status_display: 'Started',
|
||||
result_message: null,
|
||||
acknowledged: false,
|
||||
related_document: null,
|
||||
related_document_ids: [],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -167,7 +186,7 @@ describe('TasksComponent', () => {
|
||||
fixture.detectChanges()
|
||||
httpTestingController
|
||||
.expectOne(
|
||||
`${environment.apiBaseUrl}tasks/?task_name=consume_file&acknowledged=false`
|
||||
`${environment.apiBaseUrl}tasks/?task_type=consume_file&acknowledged=false`
|
||||
)
|
||||
.flush(tasks)
|
||||
})
|
||||
@@ -176,7 +195,7 @@ describe('TasksComponent', () => {
|
||||
const tabButtons = fixture.debugElement.queryAll(By.directive(NgbNavItem))
|
||||
|
||||
let currentTasksLength = tasks.filter(
|
||||
(t) => t.status === PaperlessTaskStatus.Failed
|
||||
(t) => t.status === PaperlessTaskStatus.Failure
|
||||
).length
|
||||
component.activeTab = TaskTab.Failed
|
||||
fixture.detectChanges()
|
||||
@@ -188,7 +207,7 @@ describe('TasksComponent', () => {
|
||||
).toHaveLength(currentTasksLength + 1)
|
||||
|
||||
currentTasksLength = tasks.filter(
|
||||
(t) => t.status === PaperlessTaskStatus.Complete
|
||||
(t) => t.status === PaperlessTaskStatus.Success
|
||||
).length
|
||||
component.activeTab = TaskTab.Completed
|
||||
fixture.detectChanges()
|
||||
@@ -308,7 +327,7 @@ describe('TasksComponent', () => {
|
||||
expect(component.selectedTasks).toEqual(
|
||||
new Set(
|
||||
tasks
|
||||
.filter((t) => t.status === PaperlessTaskStatus.Failed)
|
||||
.filter((t) => t.status === PaperlessTaskStatus.Failure)
|
||||
.map((t) => t.id)
|
||||
)
|
||||
)
|
||||
@@ -322,7 +341,7 @@ describe('TasksComponent', () => {
|
||||
component.dismissAndGo(tasks[3])
|
||||
expect(routerSpy).toHaveBeenCalledWith([
|
||||
'documents',
|
||||
tasks[3].related_document,
|
||||
tasks[3].related_document_ids?.[0],
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ export class TasksComponent
|
||||
|
||||
dismissAndGo(task: PaperlessTask) {
|
||||
this.dismissTask(task)
|
||||
this.router.navigate(['documents', task.related_document])
|
||||
this.router.navigate(['documents', task.related_document_ids?.[0]])
|
||||
}
|
||||
|
||||
expandTask(task: PaperlessTask) {
|
||||
@@ -207,11 +207,13 @@ export class TasksComponent
|
||||
if (this._filterText.length) {
|
||||
tasks = tasks.filter((t) => {
|
||||
if (this.filterTargetID == TaskFilterTargetID.Name) {
|
||||
return t.task_file_name
|
||||
.toLowerCase()
|
||||
return (t.input_data?.filename as string)
|
||||
?.toLowerCase()
|
||||
.includes(this._filterText.toLowerCase())
|
||||
} else if (this.filterTargetID == TaskFilterTargetID.Result) {
|
||||
return t.result.toLowerCase().includes(this._filterText.toLowerCase())
|
||||
return t.result_message
|
||||
?.toLowerCase()
|
||||
.includes(this._filterText.toLowerCase())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+6
-16
@@ -168,16 +168,6 @@
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1"></i-bs>
|
||||
}
|
||||
</button>
|
||||
@if (currentUserIsSuperUser) {
|
||||
@if (isRunning(PaperlessTaskName.IndexOptimize)) {
|
||||
<div class="spinner-border spinner-border-sm ms-2" role="status"></div>
|
||||
} @else {
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskName.IndexOptimize)">
|
||||
<i-bs name="play-fill" class="me-1"></i-bs>
|
||||
<ng-container i18n>Run Task</ng-container>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</dd>
|
||||
<ng-template #indexStatus>
|
||||
@if (status.tasks.index_status === 'OK') {
|
||||
@@ -203,10 +193,10 @@
|
||||
}
|
||||
</button>
|
||||
@if (currentUserIsSuperUser) {
|
||||
@if (isRunning(PaperlessTaskName.TrainClassifier)) {
|
||||
@if (isRunning(PaperlessTaskType.TrainClassifier)) {
|
||||
<div class="spinner-border spinner-border-sm ms-2" role="status"></div>
|
||||
} @else {
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskName.TrainClassifier)">
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskType.TrainClassifier)">
|
||||
<i-bs name="play-fill" class="me-1"></i-bs>
|
||||
<ng-container i18n>Run Task</ng-container>
|
||||
</button>
|
||||
@@ -237,10 +227,10 @@
|
||||
}
|
||||
</button>
|
||||
@if (currentUserIsSuperUser) {
|
||||
@if (isRunning(PaperlessTaskName.SanityCheck)) {
|
||||
@if (isRunning(PaperlessTaskType.SanityCheck)) {
|
||||
<div class="spinner-border spinner-border-sm ms-2" role="status"></div>
|
||||
} @else {
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskName.SanityCheck)">
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskType.SanityCheck)">
|
||||
<i-bs name="play-fill" class="me-1"></i-bs>
|
||||
<ng-container i18n>Run Task</ng-container>
|
||||
</button>
|
||||
@@ -285,10 +275,10 @@
|
||||
}
|
||||
</button>
|
||||
@if (currentUserIsSuperUser) {
|
||||
@if (isRunning(PaperlessTaskName.LLMIndexUpdate)) {
|
||||
@if (isRunning(PaperlessTaskType.LlmIndex)) {
|
||||
<div class="spinner-border spinner-border-sm ms-2" role="status"></div>
|
||||
} @else {
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskName.LLMIndexUpdate)">
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskType.LlmIndex)">
|
||||
<i-bs name="play-fill" class="me-1"></i-bs>
|
||||
<ng-container i18n>Run Task</ng-container>
|
||||
</button>
|
||||
|
||||
+10
-10
@@ -25,7 +25,7 @@ import {
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { Subject, of, throwError } from 'rxjs'
|
||||
import { PaperlessTaskName } from 'src/app/data/paperless-task'
|
||||
import { PaperlessTaskType } from 'src/app/data/paperless-task'
|
||||
import {
|
||||
InstallType,
|
||||
SystemStatus,
|
||||
@@ -138,9 +138,9 @@ describe('SystemStatusDialogComponent', () => {
|
||||
})
|
||||
|
||||
it('should check if task is running', () => {
|
||||
component.runTask(PaperlessTaskName.IndexOptimize)
|
||||
expect(component.isRunning(PaperlessTaskName.IndexOptimize)).toBeTruthy()
|
||||
expect(component.isRunning(PaperlessTaskName.SanityCheck)).toBeFalsy()
|
||||
component.runTask(PaperlessTaskType.SanityCheck)
|
||||
expect(component.isRunning(PaperlessTaskType.SanityCheck)).toBeTruthy()
|
||||
expect(component.isRunning(PaperlessTaskType.TrainClassifier)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support running tasks, refresh status and show toasts', () => {
|
||||
@@ -151,22 +151,22 @@ describe('SystemStatusDialogComponent', () => {
|
||||
|
||||
// fail first
|
||||
runSpy.mockReturnValue(throwError(() => new Error('error')))
|
||||
component.runTask(PaperlessTaskName.IndexOptimize)
|
||||
expect(runSpy).toHaveBeenCalledWith(PaperlessTaskName.IndexOptimize)
|
||||
component.runTask(PaperlessTaskType.SanityCheck)
|
||||
expect(runSpy).toHaveBeenCalledWith(PaperlessTaskType.SanityCheck)
|
||||
expect(toastErrorSpy).toHaveBeenCalledWith(
|
||||
`Failed to start task ${PaperlessTaskName.IndexOptimize}, see the logs for more details`,
|
||||
`Failed to start task ${PaperlessTaskType.SanityCheck}, see the logs for more details`,
|
||||
expect.any(Error)
|
||||
)
|
||||
|
||||
// succeed
|
||||
runSpy.mockReturnValue(of({}))
|
||||
getStatusSpy.mockReturnValue(of(status))
|
||||
component.runTask(PaperlessTaskName.IndexOptimize)
|
||||
expect(runSpy).toHaveBeenCalledWith(PaperlessTaskName.IndexOptimize)
|
||||
component.runTask(PaperlessTaskType.SanityCheck)
|
||||
expect(runSpy).toHaveBeenCalledWith(PaperlessTaskType.SanityCheck)
|
||||
|
||||
expect(getStatusSpy).toHaveBeenCalled()
|
||||
expect(toastSpy).toHaveBeenCalledWith(
|
||||
`Task ${PaperlessTaskName.IndexOptimize} started`
|
||||
`Task ${PaperlessTaskType.SanityCheck} started`
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
+5
-5
@@ -8,7 +8,7 @@ import {
|
||||
} from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { Subject, takeUntil } from 'rxjs'
|
||||
import { PaperlessTaskName } from 'src/app/data/paperless-task'
|
||||
import { PaperlessTaskType } from 'src/app/data/paperless-task'
|
||||
import {
|
||||
SystemStatus,
|
||||
SystemStatusItemStatus,
|
||||
@@ -49,14 +49,14 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy {
|
||||
private settingsService = inject(SettingsService)
|
||||
|
||||
public SystemStatusItemStatus = SystemStatusItemStatus
|
||||
public PaperlessTaskName = PaperlessTaskName
|
||||
public PaperlessTaskType = PaperlessTaskType
|
||||
public status: SystemStatus
|
||||
public frontendVersion: string = environment.version
|
||||
public versionMismatch: boolean = false
|
||||
|
||||
public copied: boolean = false
|
||||
|
||||
private runningTasks: Set<PaperlessTaskName> = new Set()
|
||||
private runningTasks: Set<PaperlessTaskType> = new Set()
|
||||
private unsubscribeNotifier: Subject<any> = new Subject()
|
||||
|
||||
get currentUserIsSuperUser(): boolean {
|
||||
@@ -107,11 +107,11 @@ export class SystemStatusDialogComponent implements OnInit, OnDestroy {
|
||||
return now.getTime() - date.getTime() > hours * 60 * 60 * 1000
|
||||
}
|
||||
|
||||
public isRunning(taskName: PaperlessTaskName): boolean {
|
||||
public isRunning(taskName: PaperlessTaskType): boolean {
|
||||
return this.runningTasks.has(taskName)
|
||||
}
|
||||
|
||||
public runTask(taskName: PaperlessTaskName) {
|
||||
public runTask(taskName: PaperlessTaskType) {
|
||||
this.runningTasks.add(taskName)
|
||||
this.toastService.showInfo(`Task ${taskName} started`)
|
||||
this.tasksService.run(taskName).subscribe({
|
||||
|
||||
@@ -1,49 +1,67 @@
|
||||
import { Document } from './document'
|
||||
import { ObjectWithId } from './object-with-id'
|
||||
|
||||
export enum PaperlessTaskType {
|
||||
Auto = 'auto_task',
|
||||
ScheduledTask = 'scheduled_task',
|
||||
ManualTask = 'manual_task',
|
||||
}
|
||||
|
||||
export enum PaperlessTaskName {
|
||||
ConsumeFile = 'consume_file',
|
||||
TrainClassifier = 'train_classifier',
|
||||
SanityCheck = 'check_sanity',
|
||||
IndexOptimize = 'index_optimize',
|
||||
LLMIndexUpdate = 'llmindex_update',
|
||||
SanityCheck = 'sanity_check',
|
||||
MailFetch = 'mail_fetch',
|
||||
LlmIndex = 'llm_index',
|
||||
EmptyTrash = 'empty_trash',
|
||||
CheckWorkflows = 'check_workflows',
|
||||
BulkUpdate = 'bulk_update',
|
||||
ReprocessDocument = 'reprocess_document',
|
||||
BuildShareLink = 'build_share_link',
|
||||
BulkDelete = 'bulk_delete',
|
||||
}
|
||||
|
||||
export enum PaperlessTaskTriggerSource {
|
||||
Scheduled = 'scheduled',
|
||||
WebUI = 'web_ui',
|
||||
ApiUpload = 'api_upload',
|
||||
FolderConsume = 'folder_consume',
|
||||
EmailConsume = 'email_consume',
|
||||
System = 'system',
|
||||
Manual = 'manual',
|
||||
}
|
||||
|
||||
export enum PaperlessTaskStatus {
|
||||
Pending = 'PENDING',
|
||||
Started = 'STARTED',
|
||||
Complete = 'SUCCESS',
|
||||
Failed = 'FAILURE',
|
||||
Pending = 'pending',
|
||||
Started = 'started',
|
||||
Success = 'success',
|
||||
Failure = 'failure',
|
||||
Revoked = 'revoked',
|
||||
}
|
||||
|
||||
export interface PaperlessTask extends ObjectWithId {
|
||||
type: PaperlessTaskType
|
||||
|
||||
status: PaperlessTaskStatus
|
||||
|
||||
acknowledged: boolean
|
||||
|
||||
task_id: string
|
||||
|
||||
task_file_name: string
|
||||
|
||||
task_name: PaperlessTaskName
|
||||
|
||||
task_type: PaperlessTaskType
|
||||
task_type_display: string
|
||||
trigger_source: PaperlessTaskTriggerSource
|
||||
trigger_source_display: string
|
||||
status: PaperlessTaskStatus
|
||||
status_display: string
|
||||
date_created: Date
|
||||
|
||||
date_started?: Date
|
||||
date_done?: Date
|
||||
|
||||
result?: string
|
||||
|
||||
related_document?: number
|
||||
|
||||
duplicate_documents?: Document[]
|
||||
|
||||
duration_seconds?: number
|
||||
wait_time_seconds?: number
|
||||
input_data: Record<string, unknown>
|
||||
result_data?: Record<string, unknown>
|
||||
result_message?: string
|
||||
related_document_ids: number[]
|
||||
acknowledged: boolean
|
||||
owner?: number
|
||||
}
|
||||
|
||||
export interface PaperlessTaskSummary {
|
||||
task_type: PaperlessTaskType
|
||||
total_count: number
|
||||
pending_count: number
|
||||
success_count: number
|
||||
failure_count: number
|
||||
avg_duration_seconds: number | null
|
||||
avg_wait_time_seconds: number | null
|
||||
last_run: Date | null
|
||||
last_success: Date | null
|
||||
last_failure: Date | null
|
||||
}
|
||||
|
||||
@@ -5,11 +5,7 @@ import {
|
||||
} from '@angular/common/http/testing'
|
||||
import { TestBed } from '@angular/core/testing'
|
||||
import { environment } from 'src/environments/environment'
|
||||
import {
|
||||
PaperlessTaskName,
|
||||
PaperlessTaskStatus,
|
||||
PaperlessTaskType,
|
||||
} from '../data/paperless-task'
|
||||
import { PaperlessTaskStatus, PaperlessTaskType } from '../data/paperless-task'
|
||||
import { TasksService } from './tasks.service'
|
||||
|
||||
describe('TasksService', () => {
|
||||
@@ -37,7 +33,7 @@ describe('TasksService', () => {
|
||||
it('calls tasks api endpoint on reload', () => {
|
||||
tasksService.reload()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}tasks/?task_name=consume_file&acknowledged=false`
|
||||
`${environment.apiBaseUrl}tasks/?task_type=consume_file&acknowledged=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
})
|
||||
@@ -46,7 +42,7 @@ describe('TasksService', () => {
|
||||
tasksService.loading = true
|
||||
tasksService.reload()
|
||||
httpTestingController.expectNone(
|
||||
`${environment.apiBaseUrl}tasks/?task_name=consume_file&acknowledged=false`
|
||||
`${environment.apiBaseUrl}tasks/?task_type=consume_file&acknowledged=false`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -63,7 +59,7 @@ describe('TasksService', () => {
|
||||
// reload is then called
|
||||
httpTestingController
|
||||
.expectOne(
|
||||
`${environment.apiBaseUrl}tasks/?task_name=consume_file&acknowledged=false`
|
||||
`${environment.apiBaseUrl}tasks/?task_type=consume_file&acknowledged=false`
|
||||
)
|
||||
.flush([])
|
||||
})
|
||||
@@ -72,56 +68,56 @@ describe('TasksService', () => {
|
||||
expect(tasksService.total).toEqual(0)
|
||||
const mockTasks = [
|
||||
{
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Complete,
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Success,
|
||||
acknowledged: false,
|
||||
task_id: '1234',
|
||||
task_file_name: 'file1.pdf',
|
||||
input_data: { filename: 'file1.pdf' },
|
||||
date_created: new Date(),
|
||||
related_document_ids: [],
|
||||
},
|
||||
{
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Failed,
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Failure,
|
||||
acknowledged: false,
|
||||
task_id: '1235',
|
||||
task_file_name: 'file2.pdf',
|
||||
input_data: { filename: 'file2.pdf' },
|
||||
date_created: new Date(),
|
||||
related_document_ids: [],
|
||||
},
|
||||
{
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Pending,
|
||||
acknowledged: false,
|
||||
task_id: '1236',
|
||||
task_file_name: 'file3.pdf',
|
||||
input_data: { filename: 'file3.pdf' },
|
||||
date_created: new Date(),
|
||||
related_document_ids: [],
|
||||
},
|
||||
{
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Started,
|
||||
acknowledged: false,
|
||||
task_id: '1237',
|
||||
task_file_name: 'file4.pdf',
|
||||
input_data: { filename: 'file4.pdf' },
|
||||
date_created: new Date(),
|
||||
related_document_ids: [],
|
||||
},
|
||||
{
|
||||
type: PaperlessTaskType.Auto,
|
||||
task_name: PaperlessTaskName.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Complete,
|
||||
task_type: PaperlessTaskType.ConsumeFile,
|
||||
status: PaperlessTaskStatus.Success,
|
||||
acknowledged: false,
|
||||
task_id: '1238',
|
||||
task_file_name: 'file5.pdf',
|
||||
input_data: { filename: 'file5.pdf' },
|
||||
date_created: new Date(),
|
||||
related_document_ids: [],
|
||||
},
|
||||
]
|
||||
|
||||
tasksService.reload()
|
||||
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}tasks/?task_name=consume_file&acknowledged=false`
|
||||
`${environment.apiBaseUrl}tasks/?task_type=consume_file&acknowledged=false`
|
||||
)
|
||||
|
||||
req.flush(mockTasks)
|
||||
@@ -134,9 +130,9 @@ describe('TasksService', () => {
|
||||
})
|
||||
|
||||
it('supports running tasks', () => {
|
||||
tasksService.run(PaperlessTaskName.SanityCheck).subscribe((res) => {
|
||||
tasksService.run(PaperlessTaskType.SanityCheck).subscribe((res) => {
|
||||
expect(res).toEqual({
|
||||
result: 'success',
|
||||
task_id: 'abc-123',
|
||||
})
|
||||
})
|
||||
const req = httpTestingController.expectOne(
|
||||
@@ -144,7 +140,7 @@ describe('TasksService', () => {
|
||||
)
|
||||
expect(req.request.method).toEqual('POST')
|
||||
req.flush({
|
||||
result: 'success',
|
||||
task_id: 'abc-123',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,8 +4,8 @@ import { Observable, Subject } from 'rxjs'
|
||||
import { first, takeUntil, tap } from 'rxjs/operators'
|
||||
import {
|
||||
PaperlessTask,
|
||||
PaperlessTaskName,
|
||||
PaperlessTaskStatus,
|
||||
PaperlessTaskType,
|
||||
} from 'src/app/data/paperless-task'
|
||||
import { environment } from 'src/environments/environment'
|
||||
|
||||
@@ -18,7 +18,7 @@ export class TasksService {
|
||||
private baseUrl: string = environment.apiBaseUrl
|
||||
private endpoint: string = 'tasks'
|
||||
|
||||
public loading: boolean
|
||||
public loading: boolean = false
|
||||
|
||||
private fileTasks: PaperlessTask[] = []
|
||||
|
||||
@@ -33,21 +33,27 @@ export class TasksService {
|
||||
}
|
||||
|
||||
public get queuedFileTasks(): PaperlessTask[] {
|
||||
return this.fileTasks.filter((t) => t.status == PaperlessTaskStatus.Pending)
|
||||
return this.fileTasks.filter(
|
||||
(t) => t.status === PaperlessTaskStatus.Pending
|
||||
)
|
||||
}
|
||||
|
||||
public get startedFileTasks(): PaperlessTask[] {
|
||||
return this.fileTasks.filter((t) => t.status == PaperlessTaskStatus.Started)
|
||||
return this.fileTasks.filter(
|
||||
(t) => t.status === PaperlessTaskStatus.Started
|
||||
)
|
||||
}
|
||||
|
||||
public get completedFileTasks(): PaperlessTask[] {
|
||||
return this.fileTasks.filter(
|
||||
(t) => t.status == PaperlessTaskStatus.Complete
|
||||
(t) => t.status === PaperlessTaskStatus.Success
|
||||
)
|
||||
}
|
||||
|
||||
public get failedFileTasks(): PaperlessTask[] {
|
||||
return this.fileTasks.filter((t) => t.status == PaperlessTaskStatus.Failed)
|
||||
return this.fileTasks.filter(
|
||||
(t) => t.status === PaperlessTaskStatus.Failure
|
||||
)
|
||||
}
|
||||
|
||||
public reload() {
|
||||
@@ -56,18 +62,16 @@ export class TasksService {
|
||||
|
||||
this.http
|
||||
.get<PaperlessTask[]>(
|
||||
`${this.baseUrl}${this.endpoint}/?task_name=consume_file&acknowledged=false`
|
||||
`${this.baseUrl}${this.endpoint}/?task_type=${PaperlessTaskType.ConsumeFile}&acknowledged=false`
|
||||
)
|
||||
.pipe(takeUntil(this.unsubscribeNotifer), first())
|
||||
.subscribe((r) => {
|
||||
this.fileTasks = r.filter(
|
||||
(t) => t.task_name == PaperlessTaskName.ConsumeFile
|
||||
)
|
||||
this.fileTasks = r
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
public dismissTasks(task_ids: Set<number>) {
|
||||
public dismissTasks(task_ids: Set<number>): Observable<any> {
|
||||
return this.http
|
||||
.post(`${this.baseUrl}tasks/acknowledge/`, {
|
||||
tasks: [...task_ids],
|
||||
@@ -85,12 +89,10 @@ export class TasksService {
|
||||
this.unsubscribeNotifer.next(true)
|
||||
}
|
||||
|
||||
public run(taskName: PaperlessTaskName): Observable<any> {
|
||||
return this.http.post<any>(
|
||||
public run(taskType: PaperlessTaskType): Observable<{ task_id: string }> {
|
||||
return this.http.post<{ task_id: string }>(
|
||||
`${environment.apiBaseUrl}${this.endpoint}/run/`,
|
||||
{
|
||||
task_name: taskName,
|
||||
}
|
||||
{ task_type: taskType }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user