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:
dawnsystem 2025-11-15 23:59:08 +01:00
parent 1a572b6db6
commit 52f08daa00
21 changed files with 1345 additions and 155 deletions

View file

@ -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)
}

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -1,6 +0,0 @@
// Component-specific styles for deletion requests
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

View file

@ -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

View file

@ -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',
}

View file

@ -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
*/

View file

@ -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
})
)
}