diff --git a/src-ui/src/app/components/admin/tasks/tasks.component.html b/src-ui/src/app/components/admin/tasks/tasks.component.html index 39f3aebbf..4ea9747cd 100644 --- a/src-ui/src/app/components/admin/tasks/tasks.component.html +++ b/src-ui/src/app/components/admin/tasks/tasks.component.html @@ -82,9 +82,7 @@
@if (task.status === PaperlessTaskStatus.Failed) { - + } +
+
+
    +
  • + +
  • +
+
+ +
+
+
+
+ diff --git a/src-ui/src/app/components/admin/tasks/tasks.component.scss b/src-ui/src/app/components/admin/tasks/tasks.component.scss index 60f8f2297..1c0cbfc37 100644 --- a/src-ui/src/app/components/admin/tasks/tasks.component.scss +++ b/src-ui/src/app/components/admin/tasks/tasks.component.scss @@ -26,3 +26,7 @@ pre { max-width: 150px; } } + +.retry-dropdown { + width: 300px; +} diff --git a/src-ui/src/app/components/admin/tasks/tasks.component.spec.ts b/src-ui/src/app/components/admin/tasks/tasks.component.spec.ts index 435e95ac6..9e1d42e01 100644 --- a/src-ui/src/app/components/admin/tasks/tasks.component.spec.ts +++ b/src-ui/src/app/components/admin/tasks/tasks.component.spec.ts @@ -33,6 +33,7 @@ import { FormsModule } from '@angular/forms' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { ToastService } from 'src/app/services/toast.service' import { of, throwError } from 'rxjs' +import { CheckComponent } from '../../common/input/check/check.component' const tasks: PaperlessTask[] = [ { @@ -128,6 +129,7 @@ describe('TasksComponent', () => { IfPermissionsDirective, CustomDatePipe, ConfirmDialogComponent, + CheckComponent, ], imports: [ NgbModule, @@ -177,8 +179,10 @@ describe('TasksComponent', () => { `Failed${currentTasksLength}` ) expect( - fixture.debugElement.queryAll(By.css('table input[type="checkbox"]')) - ).toHaveLength(currentTasksLength + 1) + fixture.debugElement.queryAll( + By.css('table td > .form-check input[type="checkbox"]') + ) + ).toHaveLength(currentTasksLength) currentTasksLength = tasks.filter( (t) => t.status === PaperlessTaskStatus.Complete @@ -300,7 +304,7 @@ describe('TasksComponent', () => { const toastErrorSpy = jest.spyOn(toastService, 'showError') retrySpy.mockReturnValueOnce(of({ task_id: '123' })) component.retryTask(tasks[0]) - expect(retrySpy).toHaveBeenCalledWith(tasks[0]) + expect(retrySpy).toHaveBeenCalledWith(tasks[0], false) expect(toastInfoSpy).toHaveBeenCalledWith('Retrying task...') retrySpy.mockReturnValueOnce(throwError(() => new Error('test'))) component.retryTask(tasks[0]) diff --git a/src-ui/src/app/components/admin/tasks/tasks.component.ts b/src-ui/src/app/components/admin/tasks/tasks.component.ts index 854c13142..ec4c75c94 100644 --- a/src-ui/src/app/components/admin/tasks/tasks.component.ts +++ b/src-ui/src/app/components/admin/tasks/tasks.component.ts @@ -28,6 +28,8 @@ export class TasksComponent public autoRefreshInterval: any + public retryClean: boolean = false + get dismissButtonText(): string { return this.selectedTasks.size > 0 ? $localize`Dismiss selected` @@ -87,7 +89,7 @@ export class TasksComponent } retryTask(task: PaperlessTask) { - this.tasksService.retryTask(task).subscribe({ + this.tasksService.retryTask(task, this.retryClean).subscribe({ next: () => { this.toastService.showInfo($localize`Retrying task...`) }, diff --git a/src-ui/src/app/services/tasks.service.spec.ts b/src-ui/src/app/services/tasks.service.spec.ts index 60492bde0..0bd4ea8be 100644 --- a/src-ui/src/app/services/tasks.service.spec.ts +++ b/src-ui/src/app/services/tasks.service.spec.ts @@ -130,14 +130,14 @@ describe('TasksService', () => { date_created: new Date(), } - tasksService.retryTask(task).subscribe() + tasksService.retryTask(task, true).subscribe() const reloadSpy = jest.spyOn(tasksService, 'reload') const req = httpTestingController.expectOne( `${environment.apiBaseUrl}tasks/${task.id}/retry/` ) expect(req.request.method).toEqual('POST') expect(req.request.body).toEqual({ - task_id: task.id, + clean: true, }) req.flush({ task_id: 12345 }) expect(reloadSpy).toHaveBeenCalled() diff --git a/src-ui/src/app/services/tasks.service.ts b/src-ui/src/app/services/tasks.service.ts index 52c8b1a3e..9ccfb9ba8 100644 --- a/src-ui/src/app/services/tasks.service.ts +++ b/src-ui/src/app/services/tasks.service.ts @@ -73,10 +73,10 @@ export class TasksService { }) } - public retryTask(task: PaperlessTask): Observable { + public retryTask(task: PaperlessTask, clean: boolean): Observable { return this.http .post(`${this.baseUrl}tasks/${task.id}/retry/`, { - task_id: task.id, + clean, }) .pipe( takeUntil(this.unsubscribeNotifer), diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index aeb901f81..6a830f2b5 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -1585,6 +1585,14 @@ class TasksViewSerializer(serializers.ModelSerializer): return result +class RetryTaskSerializer(serializers.Serializer): + clean = serializers.BooleanField( + default=False, + write_only=True, + required=False, + ) + + class AcknowledgeTasksViewSerializer(serializers.Serializer): tasks = serializers.ListField( required=True, diff --git a/src/documents/views.py b/src/documents/views.py index 1f0b3ad98..4985971da 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -136,6 +136,7 @@ from documents.serialisers import DocumentListSerializer from documents.serialisers import DocumentSerializer from documents.serialisers import DocumentTypeSerializer from documents.serialisers import PostDocumentSerializer +from documents.serialisers import RetryTaskSerializer from documents.serialisers import SavedViewSerializer from documents.serialisers import SearchResultSerializer from documents.serialisers import ShareLinkSerializer @@ -1722,9 +1723,16 @@ class TasksViewSet(ReadOnlyModelViewSet): @action(methods=["post"], detail=True) def retry(self, request, pk=None): task = self.get_object() + + serializer = RetryTaskSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + clean = serializer.validated_data.get("clean") + try: - new_task_id = retry_failed_file(task.task_id, True) + new_task_id = retry_failed_file(task.task_id, clean) return Response({"task_id": new_task_id}) + except FileNotFoundError: + return HttpResponseBadRequest("Original file not found") except Exception as e: logger.warning(f"An error occurred retrying task: {e!s}") return HttpResponseBadRequest(