mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-12-09 00:05:21 +01:00
Update BITACORA_MAESTRA.md to correct duplicate timestamps and log recent project review session. Enhance AI scanner confidence thresholds in ai_scanner.py, improve model loading safety in model_cache.py, and refine security checks in security.py. Update numpy dependency in pyproject.toml. Remove unused styles and clean up component code in the UI. Implement proper cleanup in Angular components to prevent memory leaks.
This commit is contained in:
parent
1a572b6db6
commit
52f08daa00
21 changed files with 1345 additions and 155 deletions
|
|
@ -7,6 +7,7 @@ import { ToastService } from 'src/app/services/toast.service'
|
|||
import { CheckComponent } from '../../../common/input/check/check.component'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { environment } from 'src/environments/environment'
|
||||
|
||||
interface MLModel {
|
||||
value: string
|
||||
|
|
@ -107,14 +108,16 @@ export class AiSettingsComponent implements OnInit {
|
|||
})
|
||||
|
||||
// Log mock test results
|
||||
console.log('AI Scanner Test Results:', {
|
||||
scannerEnabled: this.settingsForm.get('aiScannerEnabled')?.value,
|
||||
mlEnabled: this.settingsForm.get('aiMlFeaturesEnabled')?.value,
|
||||
ocrEnabled: this.settingsForm.get('aiAdvancedOcrEnabled')?.value,
|
||||
autoApplyThreshold: this.autoApplyThreshold,
|
||||
suggestThreshold: this.suggestThreshold,
|
||||
model: this.settingsForm.get('aiMlModel')?.value,
|
||||
})
|
||||
if (!environment.production) {
|
||||
console.log('AI Scanner Test Results:', {
|
||||
scannerEnabled: this.settingsForm.get('aiScannerEnabled')?.value,
|
||||
mlEnabled: this.settingsForm.get('aiMlFeaturesEnabled')?.value,
|
||||
ocrEnabled: this.settingsForm.get('aiAdvancedOcrEnabled')?.value,
|
||||
autoApplyThreshold: this.autoApplyThreshold,
|
||||
suggestThreshold: this.suggestThreshold,
|
||||
model: this.settingsForm.get('aiMlModel')?.value,
|
||||
})
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,15 @@ import {
|
|||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
inject,
|
||||
} from '@angular/core'
|
||||
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { Subject } from 'rxjs'
|
||||
import { takeUntil } from 'rxjs/operators'
|
||||
import {
|
||||
AISuggestion,
|
||||
AISuggestionStatus,
|
||||
|
|
@ -61,7 +64,7 @@ import { ToastService } from 'src/app/services/toast.service'
|
|||
]),
|
||||
],
|
||||
})
|
||||
export class AiSuggestionsPanelComponent implements OnChanges {
|
||||
export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
|
||||
private tagService = inject(TagService)
|
||||
private correspondentService = inject(CorrespondentService)
|
||||
private documentTypeService = inject(DocumentTypeService)
|
||||
|
|
@ -92,6 +95,7 @@ export class AiSuggestionsPanelComponent implements OnChanges {
|
|||
private documentTypes: DocumentType[] = []
|
||||
private storagePaths: StoragePath[] = []
|
||||
private customFields: CustomField[] = []
|
||||
private destroy$ = new Subject<void>()
|
||||
|
||||
public AISuggestionType = AISuggestionType
|
||||
public AISuggestionStatus = AISuggestionStatus
|
||||
|
|
@ -129,7 +133,7 @@ export class AiSuggestionsPanelComponent implements OnChanges {
|
|||
(s) => s.type === AISuggestionType.Tag
|
||||
)
|
||||
if (tagSuggestions.length > 0) {
|
||||
this.tagService.listAll().subscribe((tags) => {
|
||||
this.tagService.listAll().pipe(takeUntil(this.destroy$)).subscribe((tags) => {
|
||||
this.tags = tags.results
|
||||
this.updateSuggestionLabels()
|
||||
})
|
||||
|
|
@ -140,7 +144,7 @@ export class AiSuggestionsPanelComponent implements OnChanges {
|
|||
(s) => s.type === AISuggestionType.Correspondent
|
||||
)
|
||||
if (correspondentSuggestions.length > 0) {
|
||||
this.correspondentService.listAll().subscribe((correspondents) => {
|
||||
this.correspondentService.listAll().pipe(takeUntil(this.destroy$)).subscribe((correspondents) => {
|
||||
this.correspondents = correspondents.results
|
||||
this.updateSuggestionLabels()
|
||||
})
|
||||
|
|
@ -151,7 +155,7 @@ export class AiSuggestionsPanelComponent implements OnChanges {
|
|||
(s) => s.type === AISuggestionType.DocumentType
|
||||
)
|
||||
if (documentTypeSuggestions.length > 0) {
|
||||
this.documentTypeService.listAll().subscribe((documentTypes) => {
|
||||
this.documentTypeService.listAll().pipe(takeUntil(this.destroy$)).subscribe((documentTypes) => {
|
||||
this.documentTypes = documentTypes.results
|
||||
this.updateSuggestionLabels()
|
||||
})
|
||||
|
|
@ -162,7 +166,7 @@ export class AiSuggestionsPanelComponent implements OnChanges {
|
|||
(s) => s.type === AISuggestionType.StoragePath
|
||||
)
|
||||
if (storagePathSuggestions.length > 0) {
|
||||
this.storagePathService.listAll().subscribe((storagePaths) => {
|
||||
this.storagePathService.listAll().pipe(takeUntil(this.destroy$)).subscribe((storagePaths) => {
|
||||
this.storagePaths = storagePaths.results
|
||||
this.updateSuggestionLabels()
|
||||
})
|
||||
|
|
@ -173,7 +177,7 @@ export class AiSuggestionsPanelComponent implements OnChanges {
|
|||
(s) => s.type === AISuggestionType.CustomField
|
||||
)
|
||||
if (customFieldSuggestions.length > 0) {
|
||||
this.customFieldsService.listAll().subscribe((customFields) => {
|
||||
this.customFieldsService.listAll().pipe(takeUntil(this.destroy$)).subscribe((customFields) => {
|
||||
this.customFields = customFields.results
|
||||
this.updateSuggestionLabels()
|
||||
})
|
||||
|
|
@ -378,4 +382,9 @@ export class AiSuggestionsPanelComponent implements OnChanges {
|
|||
public get suggestionTypes(): AISuggestionType[] {
|
||||
return Array.from(this.groupedSuggestions.keys())
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next()
|
||||
this.destroy$.complete()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
// Detail component styles
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { CommonModule } from '@angular/common'
|
||||
import { Component, inject, Input } from '@angular/core'
|
||||
import { Component, inject, Input, OnDestroy } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { Subject } from 'rxjs'
|
||||
import { takeUntil } from 'rxjs/operators'
|
||||
import {
|
||||
DeletionRequest,
|
||||
DeletionRequestStatus,
|
||||
|
|
@ -21,9 +23,8 @@ import { ToastService } from 'src/app/services/toast.service'
|
|||
CustomDatePipe,
|
||||
],
|
||||
templateUrl: './deletion-request-detail.component.html',
|
||||
styleUrls: ['./deletion-request-detail.component.scss'],
|
||||
})
|
||||
export class DeletionRequestDetailComponent {
|
||||
export class DeletionRequestDetailComponent implements OnDestroy {
|
||||
@Input() deletionRequest: DeletionRequest
|
||||
|
||||
public DeletionRequestStatus = DeletionRequestStatus
|
||||
|
|
@ -33,6 +34,7 @@ export class DeletionRequestDetailComponent {
|
|||
|
||||
public reviewComment: string = ''
|
||||
public isProcessing: boolean = false
|
||||
private destroy$ = new Subject<void>()
|
||||
|
||||
approve(): void {
|
||||
if (this.isProcessing) return
|
||||
|
|
@ -40,6 +42,7 @@ export class DeletionRequestDetailComponent {
|
|||
this.isProcessing = true
|
||||
this.deletionRequestService
|
||||
.approve(this.deletionRequest.id, this.reviewComment)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
this.toastService.showInfo(
|
||||
|
|
@ -64,6 +67,7 @@ export class DeletionRequestDetailComponent {
|
|||
this.isProcessing = true
|
||||
this.deletionRequestService
|
||||
.reject(this.deletionRequest.id, this.reviewComment)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
this.toastService.showInfo(
|
||||
|
|
@ -85,4 +89,9 @@ export class DeletionRequestDetailComponent {
|
|||
canModify(): boolean {
|
||||
return this.deletionRequest.status === DeletionRequestStatus.Pending
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next()
|
||||
this.destroy$.complete()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
// Component-specific styles for deletion requests
|
||||
.text-truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
@ -34,7 +34,6 @@ import { DeletionRequestDetailComponent } from './deletion-request-detail/deleti
|
|||
CustomDatePipe,
|
||||
],
|
||||
templateUrl: './deletion-requests.component.html',
|
||||
styleUrls: ['./deletion-requests.component.scss'],
|
||||
})
|
||||
export class DeletionRequestsComponent
|
||||
extends LoadingComponentWithPermissions
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { DeletionRequest, DeletionRequestStatus } from './deletion-request'
|
||||
|
||||
/**
|
||||
* Represents the AI scanner status and statistics
|
||||
*/
|
||||
|
|
@ -37,27 +39,3 @@ export interface AIStatus {
|
|||
*/
|
||||
version?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a pending deletion request initiated by AI
|
||||
*/
|
||||
export interface DeletionRequest {
|
||||
id: number
|
||||
document_id: number
|
||||
document_title: string
|
||||
reason: string
|
||||
confidence: number
|
||||
created_at: string
|
||||
status: DeletionRequestStatus
|
||||
}
|
||||
|
||||
/**
|
||||
* Status of a deletion request
|
||||
*/
|
||||
export enum DeletionRequestStatus {
|
||||
Pending = 'pending',
|
||||
Approved = 'approved',
|
||||
Rejected = 'rejected',
|
||||
Cancelled = 'cancelled',
|
||||
Completed = 'completed',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { HttpClient } from '@angular/common/http'
|
||||
import { Injectable, inject } from '@angular/core'
|
||||
import { BehaviorSubject, Observable, interval } from 'rxjs'
|
||||
import { BehaviorSubject, Observable, interval, Subscription } from 'rxjs'
|
||||
import { catchError, map, startWith, switchMap } from 'rxjs/operators'
|
||||
import { AIStatus } from 'src/app/data/ai-status'
|
||||
import { environment } from 'src/environments/environment'
|
||||
|
|
@ -21,12 +21,13 @@ export class AIStatusService {
|
|||
})
|
||||
|
||||
public loading: boolean = false
|
||||
private pollingSubscription?: Subscription
|
||||
|
||||
// Poll every 30 seconds for AI status updates
|
||||
private readonly POLL_INTERVAL = 30000
|
||||
|
||||
constructor() {
|
||||
this.startPolling()
|
||||
// Polling is now controlled manually via startPolling()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -46,8 +47,11 @@ export class AIStatusService {
|
|||
/**
|
||||
* Start polling for AI status updates
|
||||
*/
|
||||
private startPolling(): void {
|
||||
interval(this.POLL_INTERVAL)
|
||||
public startPolling(): void {
|
||||
if (this.pollingSubscription) {
|
||||
return // Already running
|
||||
}
|
||||
this.pollingSubscription = interval(this.POLL_INTERVAL)
|
||||
.pipe(
|
||||
startWith(0), // Emit immediately on subscription
|
||||
switchMap(() => this.fetchAIStatus())
|
||||
|
|
@ -57,6 +61,16 @@ export class AIStatusService {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop polling for AI status updates
|
||||
*/
|
||||
public stopPolling(): void {
|
||||
if (this.pollingSubscription) {
|
||||
this.pollingSubscription.unsubscribe()
|
||||
this.pollingSubscription = undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch AI status from the backend
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { HttpClient } from '@angular/common/http'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Observable } from 'rxjs'
|
||||
import { tap } from 'rxjs/operators'
|
||||
import { tap, catchError } from 'rxjs/operators'
|
||||
import { DeletionRequest } from 'src/app/data/deletion-request'
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service'
|
||||
|
||||
|
|
@ -28,6 +28,10 @@ export class DeletionRequestService extends AbstractPaperlessService<DeletionReq
|
|||
.pipe(
|
||||
tap(() => {
|
||||
this._loading = false
|
||||
}),
|
||||
catchError((error) => {
|
||||
this._loading = false
|
||||
throw error
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
@ -46,6 +50,10 @@ export class DeletionRequestService extends AbstractPaperlessService<DeletionReq
|
|||
.pipe(
|
||||
tap(() => {
|
||||
this._loading = false
|
||||
}),
|
||||
catchError((error) => {
|
||||
this._loading = false
|
||||
throw error
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue