Merge branch 'dev' into feature-better-bs-icons
This commit is contained in:
commit
e5fa70b2b0
1227
src-ui/messages.xlf
1227
src-ui/messages.xlf
File diff suppressed because it is too large
Load Diff
@ -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">
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
<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> <ng-container i18n>Clear selection</ng-container>
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
|
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
||||||
|
</svg> <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> <ng-container i18n>{{dismissButtonText}}</ng-container>
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
|
<use xlink:href="assets/bootstrap-icons.svg#check2-all"/>
|
||||||
|
</svg> <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">
|
||||||
@ -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> <ng-container i18n>Dismiss</ng-container>
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
|
<use xlink:href="assets/bootstrap-icons.svg#check"/>
|
||||||
|
</svg> <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> <ng-container i18n>Open Document</ng-container>
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
|
<use xlink:href="assets/bootstrap-icons.svg#file-text"/>
|
||||||
|
</svg> <ng-container i18n>Open Document</ng-container>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -100,7 +115,12 @@
|
|||||||
|
|
||||||
<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> ({{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>
|
||||||
|
@ -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) {
|
||||||
|
@ -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">
|
||||||
|
@ -24,4 +24,10 @@ export class PageHeaderComponent {
|
|||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
subTitle: string = ''
|
subTitle: string = ''
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
info: string
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
infoLink: string
|
||||||
}
|
}
|
||||||
|
@ -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> <ng-container i18n>Add Field</ng-container>
|
<i-bs name="plus-circle"></i-bs> <ng-container i18n>Add Field</ng-container>
|
||||||
</button>
|
</button>
|
||||||
|
@ -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()
|
||||||
|
@ -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 }">
|
||||||
|
@ -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> <ng-container i18n>Add Workflow</ng-container>
|
<i-bs name="plus-circle"></i-bs> <ng-container i18n>Add Workflow</ng-container>
|
||||||
</button>
|
</button>
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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.
BIN
src/documents/tests/data/v1.17.4.model.pickle
Normal file
BIN
src/documents/tests/data/v1.17.4.model.pickle
Normal file
Binary file not shown.
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user