Merge branch 'dev' into feature-better-bs-icons

This commit is contained in:
shamoon 2024-01-15 15:47:43 -08:00
commit e5fa70b2b0
18 changed files with 909 additions and 742 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,10 @@
<pngx-page-header title="Application Configuration" subTitle="Global Paperless-ngx configuration options" i18n-title i18n-subTitle></pngx-page-header> <pngx-page-header
title="Application Configuration"
i18n-title
info="Global app configuration options which apply to <strong>every</strong> user of this install of Paperless-ngx. Options can also be set using environment variables or the configuration file but the value here will always take precedence."
i18n-info
infoLink="configuration">
</pngx-page-header>
<form [formGroup]="configForm" (ngSubmit)="saveConfig()" class="pb-4"> <form [formGroup]="configForm" (ngSubmit)="saveConfig()" class="pb-4">

View File

@ -1,4 +1,8 @@
<pngx-page-header title="Logs" i18n-title> <pngx-page-header
title="Logs"
i18n-title
info="Review the log files for the application and for email checking."
i18n-info>
<div class="form-check form-switch" (click)="toggleAutoRefresh()"> <div class="form-check form-switch" (click)="toggleAutoRefresh()">
<input class="form-check-input" type="checkbox" role="switch" id="autoRefreshSwitch" [attr.checked]="autoRefreshInterval"> <input class="form-check-input" type="checkbox" role="switch" id="autoRefreshSwitch" [attr.checked]="autoRefreshInterval">
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label> <label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>

View File

@ -1,4 +1,9 @@
<pngx-page-header title="Settings" i18n-title> <pngx-page-header
title="Settings"
i18n-title
info="Options to customize appearance, notifications, saved views and more. Settings apply to the <strong>current user only</strong>."
i18n-info
>
<button class="btn btn-sm btn-outline-primary" (click)="tourService.start()"><ng-container i18n>Start tour</ng-container></button> <button class="btn btn-sm btn-outline-primary" (click)="tourService.start()"><ng-container i18n>Start tour</ng-container></button>
<a *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }" class="btn btn-sm btn-primary ms-3" href="admin/" target="_blank"> <a *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }" class="btn btn-sm btn-primary ms-3" href="admin/" target="_blank">
<ng-container i18n>Open Django Admin</ng-container> <ng-container i18n>Open Django Admin</ng-container>

View File

@ -1,24 +1,33 @@
<pngx-page-header title="File Tasks" i18n-title> <pngx-page-header
title="File Tasks"
i18n-title
info="File Tasks shows you documents that have been consumed, are waiting to be, or may have failed during the process."
i18n-info
>
<div class="btn-toolbar col col-md-auto align-items-center"> <div class="btn-toolbar col col-md-auto align-items-center">
<button class="btn btn-sm btn-outline-secondary me-2" (click)="clearSelection()" [hidden]="selectedTasks.size === 0"> <button class="btn btn-sm btn-outline-secondary me-2" (click)="clearSelection()" [hidden]="selectedTasks.size === 0">
<i-bs name="x"></i-bs>&nbsp;<ng-container i18n>Clear selection</ng-container> <svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>&nbsp;<ng-container i18n>Clear selection</ng-container>
</button> </button>
<button class="btn btn-sm btn-outline-primary me-4" (click)="dismissTasks()" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }" [disabled]="tasksService.total === 0"> <button class="btn btn-sm btn-outline-primary me-4" (click)="dismissTasks()" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }" [disabled]="tasksService.total === 0">
<i-bs name="check2-all"></i-bs>&nbsp;<ng-container i18n>{{dismissButtonText}}</ng-container> <svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#check2-all"/>
</svg>&nbsp;<ng-container i18n>{{dismissButtonText}}</ng-container>
</button> </button>
<div class="form-check form-switch mb-0" (click)="toggleAutoRefresh()"> <div class="form-check form-switch mb-0" (click)="toggleAutoRefresh()">
<input class="form-check-input" type="checkbox" role="switch" id="autoRefreshSwitch" [attr.checked]="autoRefreshInterval"> <input class="form-check-input" type="checkbox" role="switch" id="autoRefreshSwitch" [attr.checked]="autoRefreshInterval">
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label> <label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>
</div> </div>
</div> </div>
</pngx-page-header> </pngx-page-header>
@if (!tasksService.completedFileTasks && tasksService.loading) { @if (!tasksService.completedFileTasks && tasksService.loading) {
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div> <div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div>
<div class="visually-hidden" i18n>Loading...</div> <div class="visually-hidden" i18n>Loading...</div>
} }
<ng-template let-tasks="tasks" #tasksTemplate> <ng-template let-tasks="tasks" #tasksTemplate>
<table class="table table-striped align-middle border shadow-sm"> <table class="table table-striped align-middle border shadow-sm">
<thead> <thead>
<tr> <tr>
@ -71,18 +80,24 @@
} }
<td class="d-lg-none"> <td class="d-lg-none">
<button class="btn btn-link" (click)="expandTask(task); $event.stopPropagation();"> <button class="btn btn-link" (click)="expandTask(task); $event.stopPropagation();">
<i-bs class="" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16" name="info-circle"></i-bs> <svg fill="currentColor" class="" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#info-circle" />
</svg>
</button> </button>
</td> </td>
<td scope="row"> <td scope="row">
<div class="btn-group" role="group"> <div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }"> <button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }">
<i-bs name="check"></i-bs>&nbsp;<ng-container i18n>Dismiss</ng-container> <svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#check"/>
</svg>&nbsp;<ng-container i18n>Dismiss</ng-container>
</button> </button>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"> <ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
@if (task.related_document) { @if (task.related_document) {
<button class="btn btn-sm btn-outline-primary" (click)="dismissAndGo(task); $event.stopPropagation();"> <button class="btn btn-sm btn-outline-primary" (click)="dismissAndGo(task); $event.stopPropagation();">
<i-bs name="file-text"></i-bs>&nbsp;<ng-container i18n>Open Document</ng-container> <svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#file-text"/>
</svg>&nbsp;<ng-container i18n>Open Document</ng-container>
</button> </button>
} }
</ng-container> </ng-container>
@ -100,46 +115,51 @@
<div class="pb-3 d-sm-flex justify-content-between align-items-center"> <div class="pb-3 d-sm-flex justify-content-between align-items-center">
@if (tasks.length > 0) { @if (tasks.length > 0) {
<div class="pb-2 pb-sm-0" i18n>{tasks.length, plural, =1 {One {{this.activeTabLocalized}} task} other {{{tasks.length || 0}} total {{this.activeTabLocalized}} tasks}}</div> <div class="pb-2 pb-sm-0">
<ng-container i18n>{tasks.length, plural, =1 {One {{this.activeTabLocalized}} task} other {{{tasks.length || 0}} total {{this.activeTabLocalized}} tasks}}</ng-container>
@if (selectedTasks.size > 0) {
<ng-container i18n>&nbsp;({{selectedTasks.size}} selected)</ng-container>
}
</div>
} }
@if (tasks.length > pageSize) { @if (tasks.length > pageSize) {
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="tasks.length" maxSize="8" size="sm"></ngb-pagination> <ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="tasks.length" maxSize="8" size="sm"></ngb-pagination>
} }
</div> </div>
</ng-template> </ng-template>
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTab" class="nav-tabs" (hidden)="duringTabChange($event)"> <ul ngbNav #nav="ngbNav" [(activeId)]="activeTab" class="nav-tabs" (hidden)="duringTabChange($event)">
<li ngbNavItem="failed"> <li ngbNavItem="failed">
<a ngbNavLink i18n>Failed@if (tasksService.failedFileTasks.length > 0) { <a ngbNavLink i18n>Failed@if (tasksService.failedFileTasks.length > 0) {
<span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span> <span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span>
}</a> }</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.failedFileTasks}"></ng-container> <ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.failedFileTasks}"></ng-container>
</ng-template> </ng-template>
</li> </li>
<li ngbNavItem="completed"> <li ngbNavItem="completed">
<a ngbNavLink i18n>Complete@if (tasksService.completedFileTasks.length > 0) { <a ngbNavLink i18n>Complete@if (tasksService.completedFileTasks.length > 0) {
<span class="badge bg-secondary ms-2">{{tasksService.completedFileTasks.length}}</span> <span class="badge bg-secondary ms-2">{{tasksService.completedFileTasks.length}}</span>
}</a> }</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.completedFileTasks}"></ng-container> <ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.completedFileTasks}"></ng-container>
</ng-template> </ng-template>
</li> </li>
<li ngbNavItem="started"> <li ngbNavItem="started">
<a ngbNavLink i18n>Started@if (tasksService.startedFileTasks.length > 0) { <a ngbNavLink i18n>Started@if (tasksService.startedFileTasks.length > 0) {
<span class="badge bg-secondary ms-2">{{tasksService.startedFileTasks.length}}</span> <span class="badge bg-secondary ms-2">{{tasksService.startedFileTasks.length}}</span>
}</a> }</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.startedFileTasks}"></ng-container> <ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.startedFileTasks}"></ng-container>
</ng-template> </ng-template>
</li> </li>
<li ngbNavItem="queued"> <li ngbNavItem="queued">
<a ngbNavLink i18n>Queued@if (tasksService.queuedFileTasks.length > 0) { <a ngbNavLink i18n>Queued@if (tasksService.queuedFileTasks.length > 0) {
<span class="badge bg-secondary ms-2">{{tasksService.queuedFileTasks.length}}</span> <span class="badge bg-secondary ms-2">{{tasksService.queuedFileTasks.length}}</span>
}</a> }</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.queuedFileTasks}"></ng-container> <ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.queuedFileTasks}"></ng-container>
</ng-template> </ng-template>
</li> </li>
</ul> </ul>
<div [ngbNavOutlet]="nav"></div> <div [ngbNavOutlet]="nav"></div>

View File

@ -1,4 +1,10 @@
<pngx-page-header title="Users & Groups" i18n-title> <pngx-page-header
title="Users & Groups"
i18n-title
info="Create, delete and edit users and groups."
i18n-info
infoLink="usage/#users-and-groups"
>
</pngx-page-header> </pngx-page-header>
@if (users) { @if (users) {

View File

@ -5,6 +5,22 @@
@if (subTitle) { @if (subTitle) {
<span class="h6 mb-0 d-block d-md-inline fw-normal ms-md-3 text-truncate" style="line-height: 1.4">{{subTitle}}</span> <span class="h6 mb-0 d-block d-md-inline fw-normal ms-md-3 text-truncate" style="line-height: 1.4">{{subTitle}}</span>
} }
@if (info) {
<button class="btn btn-sm btn-link text-muted me-auto p-0 p-md-2" title="What's this?" i18n-title type="button" [ngbPopover]="infoPopover" [autoClose]="true">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#question-circle"/>
</svg>
</button>
<ng-template #infoPopover>
<p [class.mb-0]="!infoLink" [innerHTML]="info"></p>
@if (infoLink) {
<a href="https://docs.paperless-ngx.com/{{infoLink}}" target="_blank" referrerpolicy="noopener noreferrer" i18n>Read more</a>
<svg class="sidebaricon-sm text-muted ms-1" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#box-arrow-up-right"/>
</svg>
}
</ng-template>
}
</h3> </h3>
</div> </div>
<div class="btn-toolbar col col-md-auto"> <div class="btn-toolbar col col-md-auto">

View File

@ -24,4 +24,10 @@ export class PageHeaderComponent {
@Input() @Input()
subTitle: string = '' subTitle: string = ''
@Input()
info: string
@Input()
infoLink: string
} }

View File

@ -1,4 +1,10 @@
<pngx-page-header title="Custom Fields" i18n-title> <pngx-page-header
title="Custom Fields"
i18n-title
info="Customize the data fields that can be attached to documents."
i18n-info
infoLink="usage/#custom-fields"
>
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editField()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.CustomField }"> <button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editField()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.CustomField }">
<i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Field</ng-container> <i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Field</ng-container>
</button> </button>

View File

@ -90,7 +90,7 @@ describe('CustomFieldsComponent', () => {
const toastInfoSpy = jest.spyOn(toastService, 'showInfo') const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
const reloadSpy = jest.spyOn(component, 'reload') const reloadSpy = jest.spyOn(component, 'reload')
const createButton = fixture.debugElement.queryAll(By.css('button'))[0] const createButton = fixture.debugElement.queryAll(By.css('button'))[1]
createButton.triggerEventHandler('click') createButton.triggerEventHandler('click')
expect(modal).not.toBeUndefined() expect(modal).not.toBeUndefined()
@ -114,7 +114,7 @@ describe('CustomFieldsComponent', () => {
const toastInfoSpy = jest.spyOn(toastService, 'showInfo') const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
const reloadSpy = jest.spyOn(component, 'reload') const reloadSpy = jest.spyOn(component, 'reload')
const editButton = fixture.debugElement.queryAll(By.css('button'))[1] const editButton = fixture.debugElement.queryAll(By.css('button'))[2]
editButton.triggerEventHandler('click') editButton.triggerEventHandler('click')
expect(modal).not.toBeUndefined() expect(modal).not.toBeUndefined()
@ -139,7 +139,7 @@ describe('CustomFieldsComponent', () => {
const deleteSpy = jest.spyOn(customFieldsService, 'delete') const deleteSpy = jest.spyOn(customFieldsService, 'delete')
const reloadSpy = jest.spyOn(component, 'reload') const reloadSpy = jest.spyOn(component, 'reload')
const deleteButton = fixture.debugElement.queryAll(By.css('button'))[3] const deleteButton = fixture.debugElement.queryAll(By.css('button'))[4]
deleteButton.triggerEventHandler('click') deleteButton.triggerEventHandler('click')
expect(modal).not.toBeUndefined() expect(modal).not.toBeUndefined()

View File

@ -1,4 +1,10 @@
<pngx-page-header title="Mail Settings" i18n-title> <pngx-page-header
title="Mail Settings"
i18n-title
info="Manage e-mail accounts and rules for automatically importing documents."
i18n-info
infoLink="usage/#usage-email"
>
</pngx-page-header> </pngx-page-header>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.MailAccount }"> <ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.MailAccount }">

View File

@ -1,4 +1,10 @@
<pngx-page-header title="Workflows" i18n-title> <pngx-page-header
title="Workflows"
i18n-title
info="Use workflows to customize the behavior of Paperless-ngx when events 'trigger' a workflow."
i18n-info
infoLink="usage/#workflows"
>
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editWorkflow()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.Workflow }"> <button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editWorkflow()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.Workflow }">
<i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Workflow</ng-container> <i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Workflow</ng-container>
</button> </button>

View File

@ -125,7 +125,7 @@ describe('WorkflowsComponent', () => {
const toastInfoSpy = jest.spyOn(toastService, 'showInfo') const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
const reloadSpy = jest.spyOn(component, 'reload') const reloadSpy = jest.spyOn(component, 'reload')
const createButton = fixture.debugElement.queryAll(By.css('button'))[0] const createButton = fixture.debugElement.queryAll(By.css('button'))[1]
createButton.triggerEventHandler('click') createButton.triggerEventHandler('click')
expect(modal).not.toBeUndefined() expect(modal).not.toBeUndefined()
@ -149,7 +149,7 @@ describe('WorkflowsComponent', () => {
const toastInfoSpy = jest.spyOn(toastService, 'showInfo') const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
const reloadSpy = jest.spyOn(component, 'reload') const reloadSpy = jest.spyOn(component, 'reload')
const editButton = fixture.debugElement.queryAll(By.css('button'))[1] const editButton = fixture.debugElement.queryAll(By.css('button'))[2]
editButton.triggerEventHandler('click') editButton.triggerEventHandler('click')
expect(modal).not.toBeUndefined() expect(modal).not.toBeUndefined()
@ -174,7 +174,7 @@ describe('WorkflowsComponent', () => {
const deleteSpy = jest.spyOn(workflowService, 'delete') const deleteSpy = jest.spyOn(workflowService, 'delete')
const reloadSpy = jest.spyOn(component, 'reload') const reloadSpy = jest.spyOn(component, 'reload')
const deleteButton = fixture.debugElement.queryAll(By.css('button'))[3] const deleteButton = fixture.debugElement.queryAll(By.css('button'))[4]
deleteButton.triggerEventHandler('click') deleteButton.triggerEventHandler('click')
expect(modal).not.toBeUndefined() expect(modal).not.toBeUndefined()

View File

@ -10,6 +10,7 @@ from pathlib import Path
from typing import Optional from typing import Optional
from django.conf import settings from django.conf import settings
from sklearn.exceptions import InconsistentVersionWarning
from documents.models import Document from documents.models import Document
from documents.models import MatchingModel from documents.models import MatchingModel
@ -18,7 +19,9 @@ logger = logging.getLogger("paperless.classifier")
class IncompatibleClassifierVersionError(Exception): class IncompatibleClassifierVersionError(Exception):
pass def __init__(self, message: str, *args: object) -> None:
self.message = message
super().__init__(*args)
class ClassifierModelCorruptError(Exception): class ClassifierModelCorruptError(Exception):
@ -37,8 +40,8 @@ def load_classifier() -> Optional["DocumentClassifier"]:
try: try:
classifier.load() classifier.load()
except IncompatibleClassifierVersionError: except IncompatibleClassifierVersionError as e:
logger.info("Classifier version updated, will re-train") logger.info(f"Classifier version incompatible: {e.message}, will re-train")
os.unlink(settings.MODEL_FILE) os.unlink(settings.MODEL_FILE)
classifier = None classifier = None
except ClassifierModelCorruptError: except ClassifierModelCorruptError:
@ -114,10 +117,12 @@ class DocumentClassifier:
"#security-maintainability-limitations" "#security-maintainability-limitations"
) )
for warning in w: for warning in w:
if issubclass(warning.category, UserWarning): # The warning is inconsistent, the MLPClassifier is a specific warning, others have not updated yet
w_msg = str(warning.message) if issubclass(warning.category, InconsistentVersionWarning) or (
if sk_learn_warning_url in w_msg: issubclass(warning.category, UserWarning)
raise IncompatibleClassifierVersionError and sk_learn_warning_url in str(warning.message)
):
raise IncompatibleClassifierVersionError("sklearn version update")
def save(self): def save(self):
target_file: Path = settings.MODEL_FILE target_file: Path = settings.MODEL_FILE

View File

@ -114,6 +114,8 @@ class SharedByUser(Filter):
ctype = ContentType.objects.get_for_model(self.model) ctype = ContentType.objects.get_for_model(self.model)
UserObjectPermission = get_user_obj_perms_model() UserObjectPermission = get_user_obj_perms_model()
GroupObjectPermission = get_group_obj_perms_model() GroupObjectPermission = get_group_obj_perms_model()
# see https://github.com/paperless-ngx/paperless-ngx/issues/5392, we limit subqueries
# to 1 because Postgres doesn't like returning > 1 row, but all we care about is > 0
return ( return (
qs.filter( qs.filter(
owner_id=value, owner_id=value,
@ -123,7 +125,7 @@ class SharedByUser(Filter):
UserObjectPermission.objects.filter( UserObjectPermission.objects.filter(
content_type=ctype, content_type=ctype,
object_pk=Cast(OuterRef("pk"), CharField()), object_pk=Cast(OuterRef("pk"), CharField()),
).values("user_id"), ).values("user_id")[:1],
), ),
) )
.annotate( .annotate(
@ -131,7 +133,7 @@ class SharedByUser(Filter):
GroupObjectPermission.objects.filter( GroupObjectPermission.objects.filter(
content_type=ctype, content_type=ctype,
object_pk=Cast(OuterRef("pk"), CharField()), object_pk=Cast(OuterRef("pk"), CharField()),
).values("group_id"), ).values("group_id")[:1],
), ),
) )
.filter( .filter(

Binary file not shown.

View File

@ -1,5 +1,6 @@
import os import os
import re import re
import shutil
from pathlib import Path from pathlib import Path
from unittest import mock from unittest import mock
@ -649,7 +650,7 @@ class TestClassifier(DirectoriesMixin, TestCase):
Path(settings.MODEL_FILE).touch() Path(settings.MODEL_FILE).touch()
self.assertTrue(os.path.exists(settings.MODEL_FILE)) self.assertTrue(os.path.exists(settings.MODEL_FILE))
load.side_effect = IncompatibleClassifierVersionError() load.side_effect = IncompatibleClassifierVersionError("Dummey Error")
self.assertIsNone(load_classifier()) self.assertIsNone(load_classifier())
self.assertFalse(os.path.exists(settings.MODEL_FILE)) self.assertFalse(os.path.exists(settings.MODEL_FILE))
@ -661,3 +662,14 @@ class TestClassifier(DirectoriesMixin, TestCase):
load.side_effect = OSError() load.side_effect = OSError()
self.assertIsNone(load_classifier()) self.assertIsNone(load_classifier())
self.assertTrue(os.path.exists(settings.MODEL_FILE)) self.assertTrue(os.path.exists(settings.MODEL_FILE))
def test_load_old_classifier_version(self):
shutil.copy(
os.path.join(os.path.dirname(__file__), "data", "v1.17.4.model.pickle"),
self.dirs.scratch_dir,
)
with override_settings(
MODEL_FILE=self.dirs.scratch_dir / "v1.17.4.model.pickle",
):
classifier = load_classifier()
self.assertIsNone(classifier)