mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-02-11 23:59:31 -06:00
Compare commits
5 Commits
chore/swit
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c70d3f8a8 | ||
|
|
491b5a4355 | ||
|
|
d41d4e12bf | ||
|
|
775e32bf3b | ||
|
|
e8e027abc0 |
@@ -277,6 +277,8 @@ src/documents/management/commands/document_exporter.py:0: error: Skipping analyz
|
|||||||
src/documents/management/commands/document_exporter.py:0: error: Skipping analyzing "auditlog.models": module is installed, but missing library stubs or py.typed marker [import-untyped]
|
src/documents/management/commands/document_exporter.py:0: error: Skipping analyzing "auditlog.models": module is installed, but missing library stubs or py.typed marker [import-untyped]
|
||||||
src/documents/management/commands/document_fuzzy_match.py:0: error: Function is missing a type annotation [no-untyped-def]
|
src/documents/management/commands/document_fuzzy_match.py:0: error: Function is missing a type annotation [no-untyped-def]
|
||||||
src/documents/management/commands/document_fuzzy_match.py:0: error: Function is missing a type annotation [no-untyped-def]
|
src/documents/management/commands/document_fuzzy_match.py:0: error: Function is missing a type annotation [no-untyped-def]
|
||||||
|
src/documents/management/commands/document_importer.py:0: error: Argument 1 to "create_source_path_directory" has incompatible type "Path | None"; expected "Path" [arg-type]
|
||||||
|
src/documents/management/commands/document_importer.py:0: error: Argument 2 to "copy_file_with_basic_stats" has incompatible type "Path | None"; expected "Path | str" [arg-type]
|
||||||
src/documents/management/commands/document_importer.py:0: error: Attribute "version" already defined on line 0 [no-redef]
|
src/documents/management/commands/document_importer.py:0: error: Attribute "version" already defined on line 0 [no-redef]
|
||||||
src/documents/management/commands/document_importer.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
|
src/documents/management/commands/document_importer.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
|
||||||
src/documents/management/commands/document_importer.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
|
src/documents/management/commands/document_importer.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ dependencies = [
|
|||||||
"setproctitle~=1.3.4",
|
"setproctitle~=1.3.4",
|
||||||
"tika-client~=0.10.0",
|
"tika-client~=0.10.0",
|
||||||
"torch~=2.10.0",
|
"torch~=2.10.0",
|
||||||
|
"tqdm~=4.67.1",
|
||||||
"watchfiles>=1.1.1",
|
"watchfiles>=1.1.1",
|
||||||
"whitenoise~=6.11",
|
"whitenoise~=6.11",
|
||||||
"whoosh-reloaded>=2.7.5",
|
"whoosh-reloaded>=2.7.5",
|
||||||
@@ -149,6 +150,7 @@ typing = [
|
|||||||
"types-pytz",
|
"types-pytz",
|
||||||
"types-redis",
|
"types-redis",
|
||||||
"types-setuptools",
|
"types-setuptools",
|
||||||
|
"types-tqdm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.uv]
|
[tool.uv]
|
||||||
|
|||||||
@@ -7917,7 +7917,7 @@
|
|||||||
<trans-unit id="4958946940233632319" datatype="html">
|
<trans-unit id="4958946940233632319" datatype="html">
|
||||||
<source>No entries found.</source>
|
<source>No entries found.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-history/document-history.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-history/document-history.component.html</context>
|
||||||
<context context-type="linenumber">10</context>
|
<context context-type="linenumber">10</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|||||||
@@ -116,9 +116,9 @@ import {
|
|||||||
} from '../common/pdf-viewer/pdf-viewer.types'
|
} from '../common/pdf-viewer/pdf-viewer.types'
|
||||||
import { ShareLinksDialogComponent } from '../common/share-links-dialog/share-links-dialog.component'
|
import { ShareLinksDialogComponent } from '../common/share-links-dialog/share-links-dialog.component'
|
||||||
import { SuggestionsDropdownComponent } from '../common/suggestions-dropdown/suggestions-dropdown.component'
|
import { SuggestionsDropdownComponent } from '../common/suggestions-dropdown/suggestions-dropdown.component'
|
||||||
import { DocumentHistoryComponent } from '../document-history/document-history.component'
|
|
||||||
import { DocumentNotesComponent } from '../document-notes/document-notes.component'
|
import { DocumentNotesComponent } from '../document-notes/document-notes.component'
|
||||||
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
|
||||||
|
import { DocumentHistoryComponent } from './document-history/document-history.component'
|
||||||
import { MetadataCollapseComponent } from './metadata-collapse/metadata-collapse.component'
|
import { MetadataCollapseComponent } from './metadata-collapse/metadata-collapse.component'
|
||||||
|
|
||||||
enum DocumentDetailNavIDs {
|
enum DocumentDetailNavIDs {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@if (loading) {
|
@if (loading) {
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="spinner-border spinner-border-sm fw-normal" role="status"></div>
|
<output class="spinner-border spinner-border-sm fw-normal" role="status"></output>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
@@ -83,8 +83,22 @@ describe('DocumentHistoryComponent', () => {
|
|||||||
expect(result).toBe(correspondentName)
|
expect(result).toBe(correspondentName)
|
||||||
})
|
})
|
||||||
expect(getCachedSpy).toHaveBeenCalledWith(parseInt(correspondentId))
|
expect(getCachedSpy).toHaveBeenCalledWith(parseInt(correspondentId))
|
||||||
// no correspondent found
|
})
|
||||||
getCachedSpy.mockReturnValue(of(null))
|
|
||||||
|
it('getPrettyName should memoize results to avoid resubscribe loops', () => {
|
||||||
|
const correspondentId = '1'
|
||||||
|
const getCachedSpy = jest
|
||||||
|
.spyOn(correspondentService, 'getCached')
|
||||||
|
.mockReturnValue(of({ name: 'John Doe' }))
|
||||||
|
const a = component.getPrettyName(DataType.Correspondent, correspondentId)
|
||||||
|
const b = component.getPrettyName(DataType.Correspondent, correspondentId)
|
||||||
|
expect(a).toBe(b)
|
||||||
|
expect(getCachedSpy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getPrettyName should fall back to the correspondent id when missing', () => {
|
||||||
|
const correspondentId = '1'
|
||||||
|
jest.spyOn(correspondentService, 'getCached').mockReturnValue(of(null))
|
||||||
component
|
component
|
||||||
.getPrettyName(DataType.Correspondent, correspondentId)
|
.getPrettyName(DataType.Correspondent, correspondentId)
|
||||||
.subscribe((result) => {
|
.subscribe((result) => {
|
||||||
@@ -104,8 +118,11 @@ describe('DocumentHistoryComponent', () => {
|
|||||||
expect(result).toBe(documentTypeName)
|
expect(result).toBe(documentTypeName)
|
||||||
})
|
})
|
||||||
expect(getCachedSpy).toHaveBeenCalledWith(parseInt(documentTypeId))
|
expect(getCachedSpy).toHaveBeenCalledWith(parseInt(documentTypeId))
|
||||||
// no document type found
|
})
|
||||||
getCachedSpy.mockReturnValue(of(null))
|
|
||||||
|
it('getPrettyName should fall back to the document type id when missing', () => {
|
||||||
|
const documentTypeId = '1'
|
||||||
|
jest.spyOn(documentTypeService, 'getCached').mockReturnValue(of(null))
|
||||||
component
|
component
|
||||||
.getPrettyName(DataType.DocumentType, documentTypeId)
|
.getPrettyName(DataType.DocumentType, documentTypeId)
|
||||||
.subscribe((result) => {
|
.subscribe((result) => {
|
||||||
@@ -125,8 +142,11 @@ describe('DocumentHistoryComponent', () => {
|
|||||||
expect(result).toBe(storagePath)
|
expect(result).toBe(storagePath)
|
||||||
})
|
})
|
||||||
expect(getCachedSpy).toHaveBeenCalledWith(parseInt(storagePathId))
|
expect(getCachedSpy).toHaveBeenCalledWith(parseInt(storagePathId))
|
||||||
// no storage path found
|
})
|
||||||
getCachedSpy.mockReturnValue(of(null))
|
|
||||||
|
it('getPrettyName should fall back to the storage path id when missing', () => {
|
||||||
|
const storagePathId = '1'
|
||||||
|
jest.spyOn(storagePathService, 'getCached').mockReturnValue(of(null))
|
||||||
component
|
component
|
||||||
.getPrettyName(DataType.StoragePath, storagePathId)
|
.getPrettyName(DataType.StoragePath, storagePathId)
|
||||||
.subscribe((result) => {
|
.subscribe((result) => {
|
||||||
@@ -144,8 +164,11 @@ describe('DocumentHistoryComponent', () => {
|
|||||||
expect(result).toBe(ownerUsername)
|
expect(result).toBe(ownerUsername)
|
||||||
})
|
})
|
||||||
expect(getCachedSpy).toHaveBeenCalledWith(parseInt(ownerId))
|
expect(getCachedSpy).toHaveBeenCalledWith(parseInt(ownerId))
|
||||||
// no user found
|
})
|
||||||
getCachedSpy.mockReturnValue(of(null))
|
|
||||||
|
it('getPrettyName should fall back to the owner id when missing', () => {
|
||||||
|
const ownerId = '1'
|
||||||
|
jest.spyOn(userService, 'getCached').mockReturnValue(of(null))
|
||||||
component.getPrettyName('owner', ownerId).subscribe((result) => {
|
component.getPrettyName('owner', ownerId).subscribe((result) => {
|
||||||
expect(result).toBe(ownerId)
|
expect(result).toBe(ownerId)
|
||||||
})
|
})
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import { AsyncPipe, KeyValuePipe, TitleCasePipe } from '@angular/common'
|
||||||
|
import { Component, Input, OnInit, inject } from '@angular/core'
|
||||||
|
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
|
import { Observable, first, map, of, shareReplay } from 'rxjs'
|
||||||
|
import { AuditLogAction, AuditLogEntry } from 'src/app/data/auditlog-entry'
|
||||||
|
import { DataType } from 'src/app/data/datatype'
|
||||||
|
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||||
|
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
|
||||||
|
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||||
|
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||||
|
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
||||||
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'pngx-document-history',
|
||||||
|
templateUrl: './document-history.component.html',
|
||||||
|
styleUrl: './document-history.component.scss',
|
||||||
|
imports: [
|
||||||
|
CustomDatePipe,
|
||||||
|
NgbTooltipModule,
|
||||||
|
AsyncPipe,
|
||||||
|
KeyValuePipe,
|
||||||
|
TitleCasePipe,
|
||||||
|
NgxBootstrapIconsModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class DocumentHistoryComponent implements OnInit {
|
||||||
|
private documentService = inject(DocumentService)
|
||||||
|
private correspondentService = inject(CorrespondentService)
|
||||||
|
private storagePathService = inject(StoragePathService)
|
||||||
|
private documentTypeService = inject(DocumentTypeService)
|
||||||
|
private userService = inject(UserService)
|
||||||
|
|
||||||
|
public AuditLogAction = AuditLogAction
|
||||||
|
|
||||||
|
private _documentId: number
|
||||||
|
@Input()
|
||||||
|
set documentId(id: number) {
|
||||||
|
if (this._documentId !== id) {
|
||||||
|
this._documentId = id
|
||||||
|
this.prettyNameCache.clear()
|
||||||
|
this.loadHistory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public loading: boolean = true
|
||||||
|
public entries: AuditLogEntry[] = []
|
||||||
|
|
||||||
|
private readonly prettyNameCache = new Map<string, Observable<string>>()
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.loadHistory()
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadHistory(): void {
|
||||||
|
if (this._documentId) {
|
||||||
|
this.loading = true
|
||||||
|
this.documentService.getHistory(this._documentId).subscribe((entries) => {
|
||||||
|
this.entries = entries
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrettyName(type: DataType | string, id: string): Observable<string> {
|
||||||
|
const cacheKey = `${type}:${id}`
|
||||||
|
const cached = this.prettyNameCache.get(cacheKey)
|
||||||
|
if (cached) {
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
|
||||||
|
const idInt = parseInt(id, 10)
|
||||||
|
const fallback$ = of(id)
|
||||||
|
|
||||||
|
let result$: Observable<string>
|
||||||
|
if (!Number.isFinite(idInt)) {
|
||||||
|
result$ = fallback$
|
||||||
|
} else {
|
||||||
|
switch (type) {
|
||||||
|
case DataType.Correspondent:
|
||||||
|
result$ = this.correspondentService.getCached(idInt).pipe(
|
||||||
|
first(),
|
||||||
|
map((correspondent) => correspondent?.name ?? id)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case DataType.DocumentType:
|
||||||
|
result$ = this.documentTypeService.getCached(idInt).pipe(
|
||||||
|
first(),
|
||||||
|
map((documentType) => documentType?.name ?? id)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case DataType.StoragePath:
|
||||||
|
result$ = this.storagePathService.getCached(idInt).pipe(
|
||||||
|
first(),
|
||||||
|
map((storagePath) => storagePath?.path ?? id)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case 'owner':
|
||||||
|
result$ = this.userService.getCached(idInt).pipe(
|
||||||
|
first(),
|
||||||
|
map((user) => user?.username ?? id)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
result$ = fallback$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const shared$ = result$.pipe(shareReplay({ bufferSize: 1, refCount: true }))
|
||||||
|
this.prettyNameCache.set(cacheKey, shared$)
|
||||||
|
return shared$
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
import { AsyncPipe, KeyValuePipe, TitleCasePipe } from '@angular/common'
|
|
||||||
import { Component, Input, OnInit, inject } from '@angular/core'
|
|
||||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
|
||||||
import { Observable, first, map, of } from 'rxjs'
|
|
||||||
import { AuditLogAction, AuditLogEntry } from 'src/app/data/auditlog-entry'
|
|
||||||
import { DataType } from 'src/app/data/datatype'
|
|
||||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
|
||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
|
|
||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
|
||||||
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'pngx-document-history',
|
|
||||||
templateUrl: './document-history.component.html',
|
|
||||||
styleUrl: './document-history.component.scss',
|
|
||||||
imports: [
|
|
||||||
CustomDatePipe,
|
|
||||||
NgbTooltipModule,
|
|
||||||
AsyncPipe,
|
|
||||||
KeyValuePipe,
|
|
||||||
TitleCasePipe,
|
|
||||||
NgxBootstrapIconsModule,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class DocumentHistoryComponent implements OnInit {
|
|
||||||
private documentService = inject(DocumentService)
|
|
||||||
private correspondentService = inject(CorrespondentService)
|
|
||||||
private storagePathService = inject(StoragePathService)
|
|
||||||
private documentTypeService = inject(DocumentTypeService)
|
|
||||||
private userService = inject(UserService)
|
|
||||||
|
|
||||||
public AuditLogAction = AuditLogAction
|
|
||||||
|
|
||||||
private _documentId: number
|
|
||||||
@Input()
|
|
||||||
set documentId(id: number) {
|
|
||||||
this._documentId = id
|
|
||||||
this.ngOnInit()
|
|
||||||
}
|
|
||||||
|
|
||||||
public loading: boolean = true
|
|
||||||
public entries: AuditLogEntry[] = []
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
if (this._documentId) {
|
|
||||||
this.loading = true
|
|
||||||
this.documentService
|
|
||||||
.getHistory(this._documentId)
|
|
||||||
.subscribe((auditLogEntries) => {
|
|
||||||
this.entries = auditLogEntries
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getPrettyName(type: DataType | string, id: string): Observable<string> {
|
|
||||||
switch (type) {
|
|
||||||
case DataType.Correspondent:
|
|
||||||
return this.correspondentService.getCached(parseInt(id, 10)).pipe(
|
|
||||||
first(),
|
|
||||||
map((correspondent) => correspondent?.name ?? id)
|
|
||||||
)
|
|
||||||
case DataType.DocumentType:
|
|
||||||
return this.documentTypeService.getCached(parseInt(id, 10)).pipe(
|
|
||||||
first(),
|
|
||||||
map((documentType) => documentType?.name ?? id)
|
|
||||||
)
|
|
||||||
case DataType.StoragePath:
|
|
||||||
return this.storagePathService.getCached(parseInt(id, 10)).pipe(
|
|
||||||
first(),
|
|
||||||
map((storagePath) => storagePath?.path ?? id)
|
|
||||||
)
|
|
||||||
case 'owner':
|
|
||||||
return this.userService.getCached(parseInt(id, 10)).pipe(
|
|
||||||
first(),
|
|
||||||
map((user) => user?.username ?? id)
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return of(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
|
||||||
|
import tqdm
|
||||||
from django import db
|
from django import db
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.management.commands.mixins import MultiProcessMixin
|
from documents.management.commands.mixins import MultiProcessMixin
|
||||||
from documents.management.commands.mixins import ProgressBarMixin
|
from documents.management.commands.mixins import ProgressBarMixin
|
||||||
@@ -79,24 +75,20 @@ class Command(MultiProcessMixin, ProgressBarMixin, BaseCommand):
|
|||||||
try:
|
try:
|
||||||
logging.getLogger().handlers[0].level = logging.ERROR
|
logging.getLogger().handlers[0].level = logging.ERROR
|
||||||
|
|
||||||
with Progress(
|
if self.process_count == 1:
|
||||||
TextColumn("[progress.description]{task.description}"),
|
for doc_id in document_ids:
|
||||||
BarColumn(),
|
update_document_content_maybe_archive_file(doc_id)
|
||||||
TaskProgressColumn(),
|
else: # pragma: no cover
|
||||||
TimeRemainingColumn(),
|
with multiprocessing.Pool(self.process_count) as pool:
|
||||||
disable=self.no_progress_bar,
|
list(
|
||||||
) as progress:
|
tqdm.tqdm(
|
||||||
task = progress.add_task("Archiving documents", total=len(document_ids))
|
pool.imap_unordered(
|
||||||
if self.process_count == 1:
|
update_document_content_maybe_archive_file,
|
||||||
for doc_id in document_ids:
|
document_ids,
|
||||||
update_document_content_maybe_archive_file(doc_id)
|
),
|
||||||
progress.update(task, advance=1)
|
total=len(document_ids),
|
||||||
else: # pragma: no cover
|
disable=self.no_progress_bar,
|
||||||
with multiprocessing.Pool(self.process_count) as pool:
|
),
|
||||||
for _ in pool.imap_unordered(
|
)
|
||||||
update_document_content_maybe_archive_file,
|
|
||||||
document_ids,
|
|
||||||
):
|
|
||||||
progress.update(task, advance=1)
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self.stdout.write(self.style.NOTICE("Aborting..."))
|
self.stdout.write(self.style.NOTICE("Aborting..."))
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import tempfile
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import tqdm
|
||||||
from allauth.mfa.models import Authenticator
|
from allauth.mfa.models import Authenticator
|
||||||
from allauth.socialaccount.models import SocialAccount
|
from allauth.socialaccount.models import SocialAccount
|
||||||
from allauth.socialaccount.models import SocialApp
|
from allauth.socialaccount.models import SocialApp
|
||||||
@@ -23,11 +24,6 @@ from django.utils import timezone
|
|||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
from guardian.models import GroupObjectPermission
|
from guardian.models import GroupObjectPermission
|
||||||
from guardian.models import UserObjectPermission
|
from guardian.models import UserObjectPermission
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
@@ -313,64 +309,54 @@ class Command(CryptMixin, BaseCommand):
|
|||||||
document_manifest = manifest_dict["documents"]
|
document_manifest = manifest_dict["documents"]
|
||||||
|
|
||||||
# 3. Export files from each document
|
# 3. Export files from each document
|
||||||
with Progress(
|
for index, document_dict in tqdm.tqdm(
|
||||||
TextColumn("[progress.description]{task.description}"),
|
enumerate(document_manifest),
|
||||||
BarColumn(),
|
total=len(document_manifest),
|
||||||
TaskProgressColumn(),
|
|
||||||
TimeRemainingColumn(),
|
|
||||||
disable=self.no_progress_bar,
|
disable=self.no_progress_bar,
|
||||||
) as progress:
|
):
|
||||||
task = progress.add_task(
|
document = document_map[document_dict["pk"]]
|
||||||
"Exporting documents",
|
|
||||||
total=len(document_manifest),
|
# 3.1. generate a unique filename
|
||||||
|
base_name = self.generate_base_name(document)
|
||||||
|
|
||||||
|
# 3.2. write filenames into manifest
|
||||||
|
original_target, thumbnail_target, archive_target = (
|
||||||
|
self.generate_document_targets(document, base_name, document_dict)
|
||||||
)
|
)
|
||||||
for index, document_dict in enumerate(document_manifest):
|
|
||||||
document = document_map[document_dict["pk"]]
|
|
||||||
|
|
||||||
# 3.1. generate a unique filename
|
# 3.3. write files to target folder
|
||||||
base_name = self.generate_base_name(document)
|
if not self.data_only:
|
||||||
|
self.copy_document_files(
|
||||||
# 3.2. write filenames into manifest
|
document,
|
||||||
original_target, thumbnail_target, archive_target = (
|
original_target,
|
||||||
self.generate_document_targets(document, base_name, document_dict)
|
thumbnail_target,
|
||||||
|
archive_target,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3.3. write files to target folder
|
if self.split_manifest:
|
||||||
if not self.data_only:
|
manifest_name = base_name.with_name(f"{base_name.stem}-manifest.json")
|
||||||
self.copy_document_files(
|
if self.use_folder_prefix:
|
||||||
document,
|
manifest_name = Path("json") / manifest_name
|
||||||
original_target,
|
manifest_name = (self.target / manifest_name).resolve()
|
||||||
thumbnail_target,
|
manifest_name.parent.mkdir(parents=True, exist_ok=True)
|
||||||
archive_target,
|
content = [document_manifest[index]]
|
||||||
)
|
content += list(
|
||||||
|
filter(
|
||||||
|
lambda d: d["fields"]["document"] == document_dict["pk"],
|
||||||
|
manifest_dict["notes"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
content += list(
|
||||||
|
filter(
|
||||||
|
lambda d: d["fields"]["document"] == document_dict["pk"],
|
||||||
|
manifest_dict["custom_field_instances"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if self.split_manifest:
|
self.check_and_write_json(
|
||||||
manifest_name = base_name.with_name(
|
content,
|
||||||
f"{base_name.stem}-manifest.json",
|
manifest_name,
|
||||||
)
|
)
|
||||||
if self.use_folder_prefix:
|
|
||||||
manifest_name = Path("json") / manifest_name
|
|
||||||
manifest_name = (self.target / manifest_name).resolve()
|
|
||||||
manifest_name.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
content = [document_manifest[index]]
|
|
||||||
content += list(
|
|
||||||
filter(
|
|
||||||
lambda d: d["fields"]["document"] == document_dict["pk"],
|
|
||||||
manifest_dict["notes"],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
content += list(
|
|
||||||
filter(
|
|
||||||
lambda d: d["fields"]["document"] == document_dict["pk"],
|
|
||||||
manifest_dict["custom_field_instances"],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.check_and_write_json(
|
|
||||||
content,
|
|
||||||
manifest_name,
|
|
||||||
)
|
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|
||||||
# These were exported already
|
# These were exported already
|
||||||
if self.split_manifest:
|
if self.split_manifest:
|
||||||
|
|||||||
@@ -3,13 +3,9 @@ import multiprocessing
|
|||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
import rapidfuzz
|
import rapidfuzz
|
||||||
|
import tqdm
|
||||||
from django.core.management import BaseCommand
|
from django.core.management import BaseCommand
|
||||||
from django.core.management import CommandError
|
from django.core.management import CommandError
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.management.commands.mixins import MultiProcessMixin
|
from documents.management.commands.mixins import MultiProcessMixin
|
||||||
from documents.management.commands.mixins import ProgressBarMixin
|
from documents.management.commands.mixins import ProgressBarMixin
|
||||||
@@ -110,25 +106,19 @@ class Command(MultiProcessMixin, ProgressBarMixin, BaseCommand):
|
|||||||
work_pkgs.append(_WorkPackage(first_doc, second_doc))
|
work_pkgs.append(_WorkPackage(first_doc, second_doc))
|
||||||
|
|
||||||
# Don't spin up a pool of 1 process
|
# Don't spin up a pool of 1 process
|
||||||
with Progress(
|
if self.process_count == 1:
|
||||||
TextColumn("[progress.description]{task.description}"),
|
results = []
|
||||||
BarColumn(),
|
for work in tqdm.tqdm(work_pkgs, disable=self.no_progress_bar):
|
||||||
TaskProgressColumn(),
|
results.append(_process_and_match(work))
|
||||||
TimeRemainingColumn(),
|
else: # pragma: no cover
|
||||||
disable=self.no_progress_bar,
|
with multiprocessing.Pool(processes=self.process_count) as pool:
|
||||||
) as progress:
|
results = list(
|
||||||
task = progress.add_task("Fuzzy matching documents", total=len(work_pkgs))
|
tqdm.tqdm(
|
||||||
if self.process_count == 1:
|
pool.imap_unordered(_process_and_match, work_pkgs),
|
||||||
results = []
|
total=len(work_pkgs),
|
||||||
for work in work_pkgs:
|
disable=self.no_progress_bar,
|
||||||
results.append(_process_and_match(work))
|
),
|
||||||
progress.update(task, advance=1)
|
)
|
||||||
else: # pragma: no cover
|
|
||||||
with multiprocessing.Pool(processes=self.process_count) as pool:
|
|
||||||
results = []
|
|
||||||
for result in pool.imap_unordered(_process_and_match, work_pkgs):
|
|
||||||
results.append(result)
|
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|
||||||
# Check results
|
# Check results
|
||||||
messages = []
|
messages = []
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import tempfile
|
|||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
from zipfile import is_zipfile
|
from zipfile import is_zipfile
|
||||||
|
|
||||||
|
import tqdm
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@@ -23,11 +23,6 @@ from django.db import transaction
|
|||||||
from django.db.models.signals import m2m_changed
|
from django.db.models.signals import m2m_changed
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.file_handling import create_source_path_directory
|
from documents.file_handling import create_source_path_directory
|
||||||
from documents.management.commands.mixins import CryptMixin
|
from documents.management.commands.mixins import CryptMixin
|
||||||
@@ -370,72 +365,58 @@ class Command(CryptMixin, BaseCommand):
|
|||||||
filter(lambda r: r["model"] == "documents.document", self.manifest),
|
filter(lambda r: r["model"] == "documents.document", self.manifest),
|
||||||
)
|
)
|
||||||
|
|
||||||
with Progress(
|
for record in tqdm.tqdm(manifest_documents, disable=self.no_progress_bar):
|
||||||
TextColumn("[progress.description]{task.description}"),
|
document = Document.objects.get(pk=record["pk"])
|
||||||
BarColumn(),
|
|
||||||
TaskProgressColumn(),
|
|
||||||
TimeRemainingColumn(),
|
|
||||||
disable=self.no_progress_bar,
|
|
||||||
) as progress:
|
|
||||||
task = progress.add_task(
|
|
||||||
"Importing documents",
|
|
||||||
total=len(manifest_documents),
|
|
||||||
)
|
|
||||||
for record in manifest_documents:
|
|
||||||
document = Document.objects.get(pk=record["pk"])
|
|
||||||
|
|
||||||
doc_file = record[EXPORTER_FILE_NAME]
|
doc_file = record[EXPORTER_FILE_NAME]
|
||||||
document_path = self.source / doc_file
|
document_path = self.source / doc_file
|
||||||
|
|
||||||
if EXPORTER_THUMBNAIL_NAME in record:
|
if EXPORTER_THUMBNAIL_NAME in record:
|
||||||
thumb_file = record[EXPORTER_THUMBNAIL_NAME]
|
thumb_file = record[EXPORTER_THUMBNAIL_NAME]
|
||||||
thumbnail_path = (self.source / thumb_file).resolve()
|
thumbnail_path = (self.source / thumb_file).resolve()
|
||||||
else:
|
else:
|
||||||
thumbnail_path = None
|
thumbnail_path = None
|
||||||
|
|
||||||
if EXPORTER_ARCHIVE_NAME in record:
|
if EXPORTER_ARCHIVE_NAME in record:
|
||||||
archive_file = record[EXPORTER_ARCHIVE_NAME]
|
archive_file = record[EXPORTER_ARCHIVE_NAME]
|
||||||
archive_path = self.source / archive_file
|
archive_path = self.source / archive_file
|
||||||
else:
|
else:
|
||||||
archive_path = None
|
archive_path = None
|
||||||
|
|
||||||
with FileLock(settings.MEDIA_LOCK):
|
with FileLock(settings.MEDIA_LOCK):
|
||||||
if Path(document.source_path).is_file():
|
if Path(document.source_path).is_file():
|
||||||
raise FileExistsError(document.source_path)
|
raise FileExistsError(document.source_path)
|
||||||
|
|
||||||
create_source_path_directory(document.source_path)
|
create_source_path_directory(document.source_path)
|
||||||
|
|
||||||
copy_file_with_basic_stats(document_path, document.source_path)
|
copy_file_with_basic_stats(document_path, document.source_path)
|
||||||
|
|
||||||
if thumbnail_path:
|
if thumbnail_path:
|
||||||
if thumbnail_path.suffix in {".png", ".PNG"}:
|
if thumbnail_path.suffix in {".png", ".PNG"}:
|
||||||
run_convert(
|
run_convert(
|
||||||
density=300,
|
density=300,
|
||||||
scale="500x5000>",
|
scale="500x5000>",
|
||||||
alpha="remove",
|
alpha="remove",
|
||||||
strip=True,
|
strip=True,
|
||||||
trim=False,
|
trim=False,
|
||||||
auto_orient=True,
|
auto_orient=True,
|
||||||
input_file=f"{thumbnail_path}[0]",
|
input_file=f"{thumbnail_path}[0]",
|
||||||
output_file=str(document.thumbnail_path),
|
output_file=str(document.thumbnail_path),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
copy_file_with_basic_stats(
|
copy_file_with_basic_stats(
|
||||||
thumbnail_path,
|
thumbnail_path,
|
||||||
document.thumbnail_path,
|
document.thumbnail_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
if archive_path:
|
if archive_path:
|
||||||
if TYPE_CHECKING:
|
create_source_path_directory(document.archive_path)
|
||||||
assert document.archive_path is not None
|
# TODO: this assumes that the export is valid and
|
||||||
create_source_path_directory(document.archive_path)
|
# archive_filename is present on all documents with
|
||||||
# TODO: this assumes that the export is valid and
|
# archived files
|
||||||
# archive_filename is present on all documents with
|
copy_file_with_basic_stats(archive_path, document.archive_path)
|
||||||
# archived files
|
|
||||||
copy_file_with_basic_stats(archive_path, document.archive_path)
|
|
||||||
|
|
||||||
document.save()
|
document.save()
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|
||||||
def decrypt_secret_fields(self) -> None:
|
def decrypt_secret_fields(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import tqdm
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.management.commands.mixins import ProgressBarMixin
|
from documents.management.commands.mixins import ProgressBarMixin
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
@@ -22,15 +18,8 @@ class Command(ProgressBarMixin, BaseCommand):
|
|||||||
self.handle_progress_bar_mixin(**options)
|
self.handle_progress_bar_mixin(**options)
|
||||||
logging.getLogger().handlers[0].level = logging.ERROR
|
logging.getLogger().handlers[0].level = logging.ERROR
|
||||||
|
|
||||||
documents = Document.objects.all()
|
for document in tqdm.tqdm(
|
||||||
with Progress(
|
Document.objects.all(),
|
||||||
TextColumn("[progress.description]{task.description}"),
|
|
||||||
BarColumn(),
|
|
||||||
TaskProgressColumn(),
|
|
||||||
TimeRemainingColumn(),
|
|
||||||
disable=self.no_progress_bar,
|
disable=self.no_progress_bar,
|
||||||
) as progress:
|
):
|
||||||
task = progress.add_task("Renaming documents", total=documents.count())
|
post_save.send(Document, instance=document, created=False)
|
||||||
for document in documents:
|
|
||||||
post_save.send(Document, instance=document, created=False)
|
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import tqdm
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.classifier import load_classifier
|
from documents.classifier import load_classifier
|
||||||
from documents.management.commands.mixins import ProgressBarMixin
|
from documents.management.commands.mixins import ProgressBarMixin
|
||||||
@@ -88,62 +84,53 @@ class Command(ProgressBarMixin, BaseCommand):
|
|||||||
|
|
||||||
classifier = load_classifier()
|
classifier = load_classifier()
|
||||||
|
|
||||||
with Progress(
|
for document in tqdm.tqdm(documents, disable=self.no_progress_bar):
|
||||||
TextColumn("[progress.description]{task.description}"),
|
if options["correspondent"]:
|
||||||
BarColumn(),
|
set_correspondent(
|
||||||
TaskProgressColumn(),
|
sender=None,
|
||||||
TimeRemainingColumn(),
|
document=document,
|
||||||
disable=self.no_progress_bar,
|
classifier=classifier,
|
||||||
) as progress:
|
replace=options["overwrite"],
|
||||||
task = progress.add_task("Retagging documents", total=documents.count())
|
use_first=options["use_first"],
|
||||||
for document in documents:
|
suggest=options["suggest"],
|
||||||
if options["correspondent"]:
|
base_url=options["base_url"],
|
||||||
set_correspondent(
|
stdout=self.stdout,
|
||||||
sender=None,
|
style_func=self.style,
|
||||||
document=document,
|
)
|
||||||
classifier=classifier,
|
|
||||||
replace=options["overwrite"],
|
|
||||||
use_first=options["use_first"],
|
|
||||||
suggest=options["suggest"],
|
|
||||||
base_url=options["base_url"],
|
|
||||||
stdout=self.stdout,
|
|
||||||
style_func=self.style,
|
|
||||||
)
|
|
||||||
|
|
||||||
if options["document_type"]:
|
if options["document_type"]:
|
||||||
set_document_type(
|
set_document_type(
|
||||||
sender=None,
|
sender=None,
|
||||||
document=document,
|
document=document,
|
||||||
classifier=classifier,
|
classifier=classifier,
|
||||||
replace=options["overwrite"],
|
replace=options["overwrite"],
|
||||||
use_first=options["use_first"],
|
use_first=options["use_first"],
|
||||||
suggest=options["suggest"],
|
suggest=options["suggest"],
|
||||||
base_url=options["base_url"],
|
base_url=options["base_url"],
|
||||||
stdout=self.stdout,
|
stdout=self.stdout,
|
||||||
style_func=self.style,
|
style_func=self.style,
|
||||||
)
|
)
|
||||||
|
|
||||||
if options["tags"]:
|
if options["tags"]:
|
||||||
set_tags(
|
set_tags(
|
||||||
sender=None,
|
sender=None,
|
||||||
document=document,
|
document=document,
|
||||||
classifier=classifier,
|
classifier=classifier,
|
||||||
replace=options["overwrite"],
|
replace=options["overwrite"],
|
||||||
suggest=options["suggest"],
|
suggest=options["suggest"],
|
||||||
base_url=options["base_url"],
|
base_url=options["base_url"],
|
||||||
stdout=self.stdout,
|
stdout=self.stdout,
|
||||||
style_func=self.style,
|
style_func=self.style,
|
||||||
)
|
)
|
||||||
if options["storage_path"]:
|
if options["storage_path"]:
|
||||||
set_storage_path(
|
set_storage_path(
|
||||||
sender=None,
|
sender=None,
|
||||||
document=document,
|
document=document,
|
||||||
classifier=classifier,
|
classifier=classifier,
|
||||||
replace=options["overwrite"],
|
replace=options["overwrite"],
|
||||||
use_first=options["use_first"],
|
use_first=options["use_first"],
|
||||||
suggest=options["suggest"],
|
suggest=options["suggest"],
|
||||||
base_url=options["base_url"],
|
base_url=options["base_url"],
|
||||||
stdout=self.stdout,
|
stdout=self.stdout,
|
||||||
style_func=self.style,
|
style_func=self.style,
|
||||||
)
|
)
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|||||||
@@ -2,13 +2,9 @@ import logging
|
|||||||
import multiprocessing
|
import multiprocessing
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
import tqdm
|
||||||
from django import db
|
from django import db
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.management.commands.mixins import MultiProcessMixin
|
from documents.management.commands.mixins import MultiProcessMixin
|
||||||
from documents.management.commands.mixins import ProgressBarMixin
|
from documents.management.commands.mixins import ProgressBarMixin
|
||||||
@@ -74,19 +70,15 @@ class Command(MultiProcessMixin, ProgressBarMixin, BaseCommand):
|
|||||||
# with postgres.
|
# with postgres.
|
||||||
db.connections.close_all()
|
db.connections.close_all()
|
||||||
|
|
||||||
with Progress(
|
if self.process_count == 1:
|
||||||
TextColumn("[progress.description]{task.description}"),
|
for doc_id in ids:
|
||||||
BarColumn(),
|
_process_document(doc_id)
|
||||||
TaskProgressColumn(),
|
else: # pragma: no cover
|
||||||
TimeRemainingColumn(),
|
with multiprocessing.Pool(processes=self.process_count) as pool:
|
||||||
disable=self.no_progress_bar,
|
list(
|
||||||
) as progress:
|
tqdm.tqdm(
|
||||||
task = progress.add_task("Generating thumbnails", total=len(ids))
|
pool.imap_unordered(_process_document, ids),
|
||||||
if self.process_count == 1:
|
total=len(ids),
|
||||||
for doc_id in ids:
|
disable=self.no_progress_bar,
|
||||||
_process_document(doc_id)
|
),
|
||||||
progress.update(task, advance=1)
|
)
|
||||||
else: # pragma: no cover
|
|
||||||
with multiprocessing.Pool(processes=self.process_count) as pool:
|
|
||||||
for _ in pool.imap_unordered(_process_document, ids):
|
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
from auditlog.models import LogEntry
|
from auditlog.models import LogEntry
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from rich.console import Console
|
from tqdm import tqdm
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.management.commands.mixins import ProgressBarMixin
|
from documents.management.commands.mixins import ProgressBarMixin
|
||||||
|
|
||||||
@@ -23,37 +18,22 @@ class Command(BaseCommand, ProgressBarMixin):
|
|||||||
|
|
||||||
def handle(self, **options):
|
def handle(self, **options):
|
||||||
self.handle_progress_bar_mixin(**options)
|
self.handle_progress_bar_mixin(**options)
|
||||||
console = Console()
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
log_entries = LogEntry.objects.all()
|
for log_entry in tqdm(LogEntry.objects.all(), disable=self.no_progress_bar):
|
||||||
with Progress(
|
model_class = log_entry.content_type.model_class()
|
||||||
TextColumn("[progress.description]{task.description}"),
|
# use global_objects for SoftDeleteModel
|
||||||
BarColumn(),
|
objects = (
|
||||||
TaskProgressColumn(),
|
model_class.global_objects
|
||||||
TimeRemainingColumn(),
|
if hasattr(model_class, "global_objects")
|
||||||
console=console,
|
else model_class.objects
|
||||||
disable=self.no_progress_bar,
|
|
||||||
) as progress:
|
|
||||||
task = progress.add_task(
|
|
||||||
"Pruning audit logs",
|
|
||||||
total=log_entries.count(),
|
|
||||||
)
|
)
|
||||||
for log_entry in log_entries:
|
if (
|
||||||
model_class = log_entry.content_type.model_class()
|
log_entry.object_id
|
||||||
# use global_objects for SoftDeleteModel
|
and not objects.filter(pk=log_entry.object_id).exists()
|
||||||
objects = (
|
):
|
||||||
model_class.global_objects
|
log_entry.delete()
|
||||||
if hasattr(model_class, "global_objects")
|
tqdm.write(
|
||||||
else model_class.objects
|
self.style.NOTICE(
|
||||||
|
f"Deleted audit log entry for {model_class.__name__} #{log_entry.object_id}",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if (
|
|
||||||
log_entry.object_id
|
|
||||||
and not objects.filter(pk=log_entry.object_id).exists()
|
|
||||||
):
|
|
||||||
log_entry.delete()
|
|
||||||
console.print(
|
|
||||||
self.style.NOTICE(
|
|
||||||
f"Deleted audit log entry for {model_class.__name__} #{log_entry.object_id}",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|||||||
227
src/documents/migrations/0011_optimize_integer_field_sizes.py
Normal file
227
src/documents/migrations/0011_optimize_integer_field_sizes.py
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
# Generated by Django 5.2.11 on 2026-02-09 16:37
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("documents", "0010_alter_document_content_length"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="correspondent",
|
||||||
|
name="matching_algorithm",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(0, "None"),
|
||||||
|
(1, "Any word"),
|
||||||
|
(2, "All words"),
|
||||||
|
(3, "Exact match"),
|
||||||
|
(4, "Regular expression"),
|
||||||
|
(5, "Fuzzy word"),
|
||||||
|
(6, "Automatic"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="matching algorithm",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="documenttype",
|
||||||
|
name="matching_algorithm",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(0, "None"),
|
||||||
|
(1, "Any word"),
|
||||||
|
(2, "All words"),
|
||||||
|
(3, "Exact match"),
|
||||||
|
(4, "Regular expression"),
|
||||||
|
(5, "Fuzzy word"),
|
||||||
|
(6, "Automatic"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="matching algorithm",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="savedviewfilterrule",
|
||||||
|
name="rule_type",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(0, "title contains"),
|
||||||
|
(1, "content contains"),
|
||||||
|
(2, "ASN is"),
|
||||||
|
(3, "correspondent is"),
|
||||||
|
(4, "document type is"),
|
||||||
|
(5, "is in inbox"),
|
||||||
|
(6, "has tag"),
|
||||||
|
(7, "has any tag"),
|
||||||
|
(8, "created before"),
|
||||||
|
(9, "created after"),
|
||||||
|
(10, "created year is"),
|
||||||
|
(11, "created month is"),
|
||||||
|
(12, "created day is"),
|
||||||
|
(13, "added before"),
|
||||||
|
(14, "added after"),
|
||||||
|
(15, "modified before"),
|
||||||
|
(16, "modified after"),
|
||||||
|
(17, "does not have tag"),
|
||||||
|
(18, "does not have ASN"),
|
||||||
|
(19, "title or content contains"),
|
||||||
|
(20, "fulltext query"),
|
||||||
|
(21, "more like this"),
|
||||||
|
(22, "has tags in"),
|
||||||
|
(23, "ASN greater than"),
|
||||||
|
(24, "ASN less than"),
|
||||||
|
(25, "storage path is"),
|
||||||
|
(26, "has correspondent in"),
|
||||||
|
(27, "does not have correspondent in"),
|
||||||
|
(28, "has document type in"),
|
||||||
|
(29, "does not have document type in"),
|
||||||
|
(30, "has storage path in"),
|
||||||
|
(31, "does not have storage path in"),
|
||||||
|
(32, "owner is"),
|
||||||
|
(33, "has owner in"),
|
||||||
|
(34, "does not have owner"),
|
||||||
|
(35, "does not have owner in"),
|
||||||
|
(36, "has custom field value"),
|
||||||
|
(37, "is shared by me"),
|
||||||
|
(38, "has custom fields"),
|
||||||
|
(39, "has custom field in"),
|
||||||
|
(40, "does not have custom field in"),
|
||||||
|
(41, "does not have custom field"),
|
||||||
|
(42, "custom fields query"),
|
||||||
|
(43, "created to"),
|
||||||
|
(44, "created from"),
|
||||||
|
(45, "added to"),
|
||||||
|
(46, "added from"),
|
||||||
|
(47, "mime type is"),
|
||||||
|
],
|
||||||
|
verbose_name="rule type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="storagepath",
|
||||||
|
name="matching_algorithm",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(0, "None"),
|
||||||
|
(1, "Any word"),
|
||||||
|
(2, "All words"),
|
||||||
|
(3, "Exact match"),
|
||||||
|
(4, "Regular expression"),
|
||||||
|
(5, "Fuzzy word"),
|
||||||
|
(6, "Automatic"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="matching algorithm",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="tag",
|
||||||
|
name="matching_algorithm",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(0, "None"),
|
||||||
|
(1, "Any word"),
|
||||||
|
(2, "All words"),
|
||||||
|
(3, "Exact match"),
|
||||||
|
(4, "Regular expression"),
|
||||||
|
(5, "Fuzzy word"),
|
||||||
|
(6, "Automatic"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="matching algorithm",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflow",
|
||||||
|
name="order",
|
||||||
|
field=models.SmallIntegerField(default=0, verbose_name="order"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="order",
|
||||||
|
field=models.PositiveSmallIntegerField(default=0, verbose_name="order"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="type",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Assignment"),
|
||||||
|
(2, "Removal"),
|
||||||
|
(3, "Email"),
|
||||||
|
(4, "Webhook"),
|
||||||
|
(5, "Password removal"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="Workflow Action Type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflowrun",
|
||||||
|
name="type",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Consumption Started"),
|
||||||
|
(2, "Document Added"),
|
||||||
|
(3, "Document Updated"),
|
||||||
|
(4, "Scheduled"),
|
||||||
|
],
|
||||||
|
null=True,
|
||||||
|
verbose_name="workflow trigger type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflowtrigger",
|
||||||
|
name="matching_algorithm",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(0, "None"),
|
||||||
|
(1, "Any word"),
|
||||||
|
(2, "All words"),
|
||||||
|
(3, "Exact match"),
|
||||||
|
(4, "Regular expression"),
|
||||||
|
(5, "Fuzzy word"),
|
||||||
|
],
|
||||||
|
default=0,
|
||||||
|
verbose_name="matching algorithm",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflowtrigger",
|
||||||
|
name="schedule_offset_days",
|
||||||
|
field=models.SmallIntegerField(
|
||||||
|
default=0,
|
||||||
|
help_text="The number of days to offset the schedule trigger by.",
|
||||||
|
verbose_name="schedule offset days",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflowtrigger",
|
||||||
|
name="schedule_recurring_interval_days",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
default=1,
|
||||||
|
help_text="The number of days between recurring schedule triggers.",
|
||||||
|
validators=[django.core.validators.MinValueValidator(1)],
|
||||||
|
verbose_name="schedule recurring delay in days",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workflowtrigger",
|
||||||
|
name="type",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Consumption Started"),
|
||||||
|
(2, "Document Added"),
|
||||||
|
(3, "Document Updated"),
|
||||||
|
(4, "Scheduled"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="Workflow Trigger Type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -67,7 +67,7 @@ class MatchingModel(ModelWithOwner):
|
|||||||
|
|
||||||
match = models.CharField(_("match"), max_length=256, blank=True)
|
match = models.CharField(_("match"), max_length=256, blank=True)
|
||||||
|
|
||||||
matching_algorithm = models.PositiveIntegerField(
|
matching_algorithm = models.PositiveSmallIntegerField(
|
||||||
_("matching algorithm"),
|
_("matching algorithm"),
|
||||||
choices=MATCHING_ALGORITHMS,
|
choices=MATCHING_ALGORITHMS,
|
||||||
default=MATCH_ANY,
|
default=MATCH_ANY,
|
||||||
@@ -547,7 +547,7 @@ class SavedViewFilterRule(models.Model):
|
|||||||
verbose_name=_("saved view"),
|
verbose_name=_("saved view"),
|
||||||
)
|
)
|
||||||
|
|
||||||
rule_type = models.PositiveIntegerField(_("rule type"), choices=RULE_TYPES)
|
rule_type = models.PositiveSmallIntegerField(_("rule type"), choices=RULE_TYPES)
|
||||||
|
|
||||||
value = models.CharField(_("value"), max_length=255, blank=True, null=True)
|
value = models.CharField(_("value"), max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
@@ -1102,7 +1102,7 @@ class WorkflowTrigger(models.Model):
|
|||||||
MODIFIED = "modified", _("Modified")
|
MODIFIED = "modified", _("Modified")
|
||||||
CUSTOM_FIELD = "custom_field", _("Custom Field")
|
CUSTOM_FIELD = "custom_field", _("Custom Field")
|
||||||
|
|
||||||
type = models.PositiveIntegerField(
|
type = models.PositiveSmallIntegerField(
|
||||||
_("Workflow Trigger Type"),
|
_("Workflow Trigger Type"),
|
||||||
choices=WorkflowTriggerType.choices,
|
choices=WorkflowTriggerType.choices,
|
||||||
default=WorkflowTriggerType.CONSUMPTION,
|
default=WorkflowTriggerType.CONSUMPTION,
|
||||||
@@ -1148,7 +1148,7 @@ class WorkflowTrigger(models.Model):
|
|||||||
|
|
||||||
match = models.CharField(_("match"), max_length=256, blank=True)
|
match = models.CharField(_("match"), max_length=256, blank=True)
|
||||||
|
|
||||||
matching_algorithm = models.PositiveIntegerField(
|
matching_algorithm = models.PositiveSmallIntegerField(
|
||||||
_("matching algorithm"),
|
_("matching algorithm"),
|
||||||
choices=WorkflowTriggerMatching.choices,
|
choices=WorkflowTriggerMatching.choices,
|
||||||
default=WorkflowTriggerMatching.NONE,
|
default=WorkflowTriggerMatching.NONE,
|
||||||
@@ -1249,7 +1249,7 @@ class WorkflowTrigger(models.Model):
|
|||||||
help_text=_("JSON-encoded custom field query expression."),
|
help_text=_("JSON-encoded custom field query expression."),
|
||||||
)
|
)
|
||||||
|
|
||||||
schedule_offset_days = models.IntegerField(
|
schedule_offset_days = models.SmallIntegerField(
|
||||||
_("schedule offset days"),
|
_("schedule offset days"),
|
||||||
default=0,
|
default=0,
|
||||||
help_text=_(
|
help_text=_(
|
||||||
@@ -1265,7 +1265,7 @@ class WorkflowTrigger(models.Model):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
schedule_recurring_interval_days = models.PositiveIntegerField(
|
schedule_recurring_interval_days = models.PositiveSmallIntegerField(
|
||||||
_("schedule recurring delay in days"),
|
_("schedule recurring delay in days"),
|
||||||
default=1,
|
default=1,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
@@ -1410,13 +1410,13 @@ class WorkflowAction(models.Model):
|
|||||||
_("Password removal"),
|
_("Password removal"),
|
||||||
)
|
)
|
||||||
|
|
||||||
type = models.PositiveIntegerField(
|
type = models.PositiveSmallIntegerField(
|
||||||
_("Workflow Action Type"),
|
_("Workflow Action Type"),
|
||||||
choices=WorkflowActionType.choices,
|
choices=WorkflowActionType.choices,
|
||||||
default=WorkflowActionType.ASSIGNMENT,
|
default=WorkflowActionType.ASSIGNMENT,
|
||||||
)
|
)
|
||||||
|
|
||||||
order = models.PositiveIntegerField(_("order"), default=0)
|
order = models.PositiveSmallIntegerField(_("order"), default=0)
|
||||||
|
|
||||||
assign_title = models.TextField(
|
assign_title = models.TextField(
|
||||||
_("assign title"),
|
_("assign title"),
|
||||||
@@ -1658,7 +1658,7 @@ class WorkflowAction(models.Model):
|
|||||||
class Workflow(models.Model):
|
class Workflow(models.Model):
|
||||||
name = models.CharField(_("name"), max_length=256, unique=True)
|
name = models.CharField(_("name"), max_length=256, unique=True)
|
||||||
|
|
||||||
order = models.IntegerField(_("order"), default=0)
|
order = models.SmallIntegerField(_("order"), default=0)
|
||||||
|
|
||||||
triggers = models.ManyToManyField(
|
triggers = models.ManyToManyField(
|
||||||
WorkflowTrigger,
|
WorkflowTrigger,
|
||||||
@@ -1688,7 +1688,7 @@ class WorkflowRun(SoftDeleteModel):
|
|||||||
verbose_name=_("workflow"),
|
verbose_name=_("workflow"),
|
||||||
)
|
)
|
||||||
|
|
||||||
type = models.PositiveIntegerField(
|
type = models.PositiveSmallIntegerField(
|
||||||
_("workflow trigger type"),
|
_("workflow trigger type"),
|
||||||
choices=WorkflowTrigger.WorkflowTriggerType.choices,
|
choices=WorkflowTrigger.WorkflowTriggerType.choices,
|
||||||
null=True,
|
null=True,
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ from typing import Final
|
|||||||
from celery import states
|
from celery import states
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rich.progress import BarColumn
|
from tqdm import tqdm
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import PaperlessTask
|
from documents.models import PaperlessTask
|
||||||
@@ -96,99 +92,76 @@ def check_sanity(*, progress=False, scheduled=True) -> SanityCheckMessages:
|
|||||||
if logo_file in present_files:
|
if logo_file in present_files:
|
||||||
present_files.remove(logo_file)
|
present_files.remove(logo_file)
|
||||||
|
|
||||||
documents = Document.global_objects.all()
|
for doc in tqdm(Document.global_objects.all(), disable=not progress):
|
||||||
with Progress(
|
# Check sanity of the thumbnail
|
||||||
TextColumn("[progress.description]{task.description}"),
|
thumbnail_path: Final[Path] = Path(doc.thumbnail_path).resolve()
|
||||||
BarColumn(),
|
if not thumbnail_path.exists() or not thumbnail_path.is_file():
|
||||||
TaskProgressColumn(),
|
messages.error(doc.pk, "Thumbnail of document does not exist.")
|
||||||
TimeRemainingColumn(),
|
else:
|
||||||
disable=not progress,
|
if thumbnail_path in present_files:
|
||||||
) as progress_bar:
|
present_files.remove(thumbnail_path)
|
||||||
task = progress_bar.add_task(
|
try:
|
||||||
"Checking document sanity",
|
_ = thumbnail_path.read_bytes()
|
||||||
total=documents.count(),
|
except OSError as e:
|
||||||
)
|
messages.error(doc.pk, f"Cannot read thumbnail file of document: {e}")
|
||||||
for doc in documents:
|
|
||||||
# Check sanity of the thumbnail
|
# Check sanity of the original file
|
||||||
thumbnail_path: Final[Path] = Path(doc.thumbnail_path).resolve()
|
# TODO: extract method
|
||||||
if not thumbnail_path.exists() or not thumbnail_path.is_file():
|
source_path: Final[Path] = Path(doc.source_path).resolve()
|
||||||
messages.error(doc.pk, "Thumbnail of document does not exist.")
|
if not source_path.exists() or not source_path.is_file():
|
||||||
|
messages.error(doc.pk, "Original of document does not exist.")
|
||||||
|
else:
|
||||||
|
if source_path in present_files:
|
||||||
|
present_files.remove(source_path)
|
||||||
|
try:
|
||||||
|
checksum = hashlib.md5(source_path.read_bytes()).hexdigest()
|
||||||
|
except OSError as e:
|
||||||
|
messages.error(doc.pk, f"Cannot read original file of document: {e}")
|
||||||
else:
|
else:
|
||||||
if thumbnail_path in present_files:
|
if checksum != doc.checksum:
|
||||||
present_files.remove(thumbnail_path)
|
messages.error(
|
||||||
|
doc.pk,
|
||||||
|
"Checksum mismatch. "
|
||||||
|
f"Stored: {doc.checksum}, actual: {checksum}.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check sanity of the archive file.
|
||||||
|
if doc.archive_checksum is not None and doc.archive_filename is None:
|
||||||
|
messages.error(
|
||||||
|
doc.pk,
|
||||||
|
"Document has an archive file checksum, but no archive filename.",
|
||||||
|
)
|
||||||
|
elif doc.archive_checksum is None and doc.archive_filename is not None:
|
||||||
|
messages.error(
|
||||||
|
doc.pk,
|
||||||
|
"Document has an archive file, but its checksum is missing.",
|
||||||
|
)
|
||||||
|
elif doc.has_archive_version:
|
||||||
|
archive_path: Final[Path] = Path(doc.archive_path).resolve()
|
||||||
|
if not archive_path.exists() or not archive_path.is_file():
|
||||||
|
messages.error(doc.pk, "Archived version of document does not exist.")
|
||||||
|
else:
|
||||||
|
if archive_path in present_files:
|
||||||
|
present_files.remove(archive_path)
|
||||||
try:
|
try:
|
||||||
_ = thumbnail_path.read_bytes()
|
checksum = hashlib.md5(archive_path.read_bytes()).hexdigest()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
messages.error(
|
messages.error(
|
||||||
doc.pk,
|
doc.pk,
|
||||||
f"Cannot read thumbnail file of document: {e}",
|
f"Cannot read archive file of document : {e}",
|
||||||
)
|
|
||||||
|
|
||||||
# Check sanity of the original file
|
|
||||||
# TODO: extract method
|
|
||||||
source_path: Final[Path] = Path(doc.source_path).resolve()
|
|
||||||
if not source_path.exists() or not source_path.is_file():
|
|
||||||
messages.error(doc.pk, "Original of document does not exist.")
|
|
||||||
else:
|
|
||||||
if source_path in present_files:
|
|
||||||
present_files.remove(source_path)
|
|
||||||
try:
|
|
||||||
checksum = hashlib.md5(source_path.read_bytes()).hexdigest()
|
|
||||||
except OSError as e:
|
|
||||||
messages.error(
|
|
||||||
doc.pk,
|
|
||||||
f"Cannot read original file of document: {e}",
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if checksum != doc.checksum:
|
if checksum != doc.archive_checksum:
|
||||||
messages.error(
|
messages.error(
|
||||||
doc.pk,
|
doc.pk,
|
||||||
"Checksum mismatch. "
|
"Checksum mismatch of archived document. "
|
||||||
f"Stored: {doc.checksum}, actual: {checksum}.",
|
f"Stored: {doc.archive_checksum}, "
|
||||||
|
f"actual: {checksum}.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check sanity of the archive file.
|
# other document checks
|
||||||
if doc.archive_checksum is not None and doc.archive_filename is None:
|
if not doc.content:
|
||||||
messages.error(
|
messages.info(doc.pk, "Document contains no OCR data")
|
||||||
doc.pk,
|
|
||||||
"Document has an archive file checksum, but no archive filename.",
|
|
||||||
)
|
|
||||||
elif doc.archive_checksum is None and doc.archive_filename is not None:
|
|
||||||
messages.error(
|
|
||||||
doc.pk,
|
|
||||||
"Document has an archive file, but its checksum is missing.",
|
|
||||||
)
|
|
||||||
elif doc.has_archive_version:
|
|
||||||
archive_path: Final[Path] = Path(doc.archive_path).resolve()
|
|
||||||
if not archive_path.exists() or not archive_path.is_file():
|
|
||||||
messages.error(
|
|
||||||
doc.pk,
|
|
||||||
"Archived version of document does not exist.",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if archive_path in present_files:
|
|
||||||
present_files.remove(archive_path)
|
|
||||||
try:
|
|
||||||
checksum = hashlib.md5(archive_path.read_bytes()).hexdigest()
|
|
||||||
except OSError as e:
|
|
||||||
messages.error(
|
|
||||||
doc.pk,
|
|
||||||
f"Cannot read archive file of document : {e}",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if checksum != doc.archive_checksum:
|
|
||||||
messages.error(
|
|
||||||
doc.pk,
|
|
||||||
"Checksum mismatch of archived document. "
|
|
||||||
f"Stored: {doc.archive_checksum}, "
|
|
||||||
f"actual: {checksum}.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# other document checks
|
|
||||||
if not doc.content:
|
|
||||||
messages.info(doc.pk, "Document contains no OCR data")
|
|
||||||
|
|
||||||
progress_bar.update(task, advance=1)
|
|
||||||
|
|
||||||
for extra_file in present_files:
|
for extra_file in present_files:
|
||||||
messages.warning(None, f"Orphaned file in media dir: {extra_file}")
|
messages.warning(None, f"Orphaned file in media dir: {extra_file}")
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from pathlib import Path
|
|||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from tempfile import mkstemp
|
from tempfile import mkstemp
|
||||||
|
|
||||||
|
import tqdm
|
||||||
from celery import Task
|
from celery import Task
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from celery import states
|
from celery import states
|
||||||
@@ -18,11 +19,6 @@ from django.db import transaction
|
|||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
from whoosh.writing import AsyncWriter
|
from whoosh.writing import AsyncWriter
|
||||||
|
|
||||||
from documents import index
|
from documents import index
|
||||||
@@ -87,20 +83,9 @@ def index_reindex(*, progress_bar_disable=False) -> None:
|
|||||||
|
|
||||||
ix = index.open_index(recreate=True)
|
ix = index.open_index(recreate=True)
|
||||||
|
|
||||||
with (
|
with AsyncWriter(ix) as writer:
|
||||||
AsyncWriter(ix) as writer,
|
for document in tqdm.tqdm(documents, disable=progress_bar_disable):
|
||||||
Progress(
|
|
||||||
TextColumn("[progress.description]{task.description}"),
|
|
||||||
BarColumn(),
|
|
||||||
TaskProgressColumn(),
|
|
||||||
TimeRemainingColumn(),
|
|
||||||
disable=progress_bar_disable,
|
|
||||||
) as progress,
|
|
||||||
):
|
|
||||||
task = progress.add_task("Reindexing documents", total=documents.count())
|
|
||||||
for document in documents:
|
|
||||||
index.update_document(writer, document)
|
index.update_document(writer, document)
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Generated by Django 5.2.11 on 2026-02-09 16:37
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("paperless", "0006_applicationconfiguration_barcode_tag_split"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="applicationconfiguration",
|
||||||
|
name="barcode_dpi",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
null=True,
|
||||||
|
validators=[django.core.validators.MinValueValidator(1)],
|
||||||
|
verbose_name="Sets the barcode DPI",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="applicationconfiguration",
|
||||||
|
name="barcode_max_pages",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
null=True,
|
||||||
|
validators=[django.core.validators.MinValueValidator(1)],
|
||||||
|
verbose_name="Sets the maximum pages for barcode",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="applicationconfiguration",
|
||||||
|
name="image_dpi",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
null=True,
|
||||||
|
validators=[django.core.validators.MinValueValidator(1)],
|
||||||
|
verbose_name="Sets image DPI fallback value",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="applicationconfiguration",
|
||||||
|
name="pages",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
null=True,
|
||||||
|
validators=[django.core.validators.MinValueValidator(1)],
|
||||||
|
verbose_name="Do OCR from page 1 to this value",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -105,7 +105,7 @@ class ApplicationConfiguration(AbstractSingletonModel):
|
|||||||
Settings for the Tesseract based OCR parser
|
Settings for the Tesseract based OCR parser
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pages = models.PositiveIntegerField(
|
pages = models.PositiveSmallIntegerField(
|
||||||
verbose_name=_("Do OCR from page 1 to this value"),
|
verbose_name=_("Do OCR from page 1 to this value"),
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
@@ -134,7 +134,7 @@ class ApplicationConfiguration(AbstractSingletonModel):
|
|||||||
choices=ArchiveFileChoices.choices,
|
choices=ArchiveFileChoices.choices,
|
||||||
)
|
)
|
||||||
|
|
||||||
image_dpi = models.PositiveIntegerField(
|
image_dpi = models.PositiveSmallIntegerField(
|
||||||
verbose_name=_("Sets image DPI fallback value"),
|
verbose_name=_("Sets image DPI fallback value"),
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
@@ -254,14 +254,14 @@ class ApplicationConfiguration(AbstractSingletonModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# PAPERLESS_CONSUMER_BARCODE_DPI
|
# PAPERLESS_CONSUMER_BARCODE_DPI
|
||||||
barcode_dpi = models.PositiveIntegerField(
|
barcode_dpi = models.PositiveSmallIntegerField(
|
||||||
verbose_name=_("Sets the barcode DPI"),
|
verbose_name=_("Sets the barcode DPI"),
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
)
|
)
|
||||||
|
|
||||||
# PAPERLESS_CONSUMER_BARCODE_MAX_PAGES
|
# PAPERLESS_CONSUMER_BARCODE_MAX_PAGES
|
||||||
barcode_max_pages = models.PositiveIntegerField(
|
barcode_max_pages = models.PositiveSmallIntegerField(
|
||||||
verbose_name=_("Sets the maximum pages for barcode"),
|
verbose_name=_("Sets the maximum pages for barcode"),
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import faiss
|
import faiss
|
||||||
import llama_index.core.settings as llama_settings
|
import llama_index.core.settings as llama_settings
|
||||||
|
import tqdm
|
||||||
from celery import states
|
from celery import states
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -21,11 +22,6 @@ from llama_index.core.storage.docstore import SimpleDocumentStore
|
|||||||
from llama_index.core.storage.index_store import SimpleIndexStore
|
from llama_index.core.storage.index_store import SimpleIndexStore
|
||||||
from llama_index.core.text_splitter import TokenTextSplitter
|
from llama_index.core.text_splitter import TokenTextSplitter
|
||||||
from llama_index.vector_stores.faiss import FaissVectorStore
|
from llama_index.vector_stores.faiss import FaissVectorStore
|
||||||
from rich.progress import BarColumn
|
|
||||||
from rich.progress import Progress
|
|
||||||
from rich.progress import TaskProgressColumn
|
|
||||||
from rich.progress import TextColumn
|
|
||||||
from rich.progress import TimeRemainingColumn
|
|
||||||
|
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import PaperlessTask
|
from documents.models import PaperlessTask
|
||||||
@@ -180,18 +176,9 @@ def update_llm_index(*, progress_bar_disable=False, rebuild=False) -> str:
|
|||||||
embed_model = get_embedding_model()
|
embed_model = get_embedding_model()
|
||||||
llama_settings.Settings.embed_model = embed_model
|
llama_settings.Settings.embed_model = embed_model
|
||||||
storage_context = get_or_create_storage_context(rebuild=True)
|
storage_context = get_or_create_storage_context(rebuild=True)
|
||||||
with Progress(
|
for document in tqdm.tqdm(documents, disable=progress_bar_disable):
|
||||||
TextColumn("[progress.description]{task.description}"),
|
document_nodes = build_document_node(document)
|
||||||
BarColumn(),
|
nodes.extend(document_nodes)
|
||||||
TaskProgressColumn(),
|
|
||||||
TimeRemainingColumn(),
|
|
||||||
disable=progress_bar_disable,
|
|
||||||
) as progress:
|
|
||||||
task = progress.add_task("Building document nodes", total=documents.count())
|
|
||||||
for document in documents:
|
|
||||||
document_nodes = build_document_node(document)
|
|
||||||
nodes.extend(document_nodes)
|
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|
||||||
index = VectorStoreIndex(
|
index = VectorStoreIndex(
|
||||||
nodes=nodes,
|
nodes=nodes,
|
||||||
@@ -209,33 +196,23 @@ def update_llm_index(*, progress_bar_disable=False, rebuild=False) -> str:
|
|||||||
for node in index.docstore.get_nodes(all_node_ids)
|
for node in index.docstore.get_nodes(all_node_ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
with Progress(
|
for document in tqdm.tqdm(documents, disable=progress_bar_disable):
|
||||||
TextColumn("[progress.description]{task.description}"),
|
doc_id = str(document.id)
|
||||||
BarColumn(),
|
document_modified = document.modified.isoformat()
|
||||||
TaskProgressColumn(),
|
|
||||||
TimeRemainingColumn(),
|
|
||||||
disable=progress_bar_disable,
|
|
||||||
) as progress:
|
|
||||||
task = progress.add_task("Updating index nodes", total=documents.count())
|
|
||||||
for document in documents:
|
|
||||||
doc_id = str(document.id)
|
|
||||||
document_modified = document.modified.isoformat()
|
|
||||||
|
|
||||||
if doc_id in existing_nodes:
|
if doc_id in existing_nodes:
|
||||||
node = existing_nodes[doc_id]
|
node = existing_nodes[doc_id]
|
||||||
node_modified = node.metadata.get("modified")
|
node_modified = node.metadata.get("modified")
|
||||||
|
|
||||||
if node_modified == document_modified:
|
if node_modified == document_modified:
|
||||||
progress.update(task, advance=1)
|
continue
|
||||||
continue
|
|
||||||
|
|
||||||
# Again, delete from docstore, FAISS IndexFlatL2 are append-only
|
# Again, delete from docstore, FAISS IndexFlatL2 are append-only
|
||||||
index.docstore.delete_document(node.node_id)
|
index.docstore.delete_document(node.node_id)
|
||||||
nodes.extend(build_document_node(document))
|
nodes.extend(build_document_node(document))
|
||||||
else:
|
else:
|
||||||
# New document, add it
|
# New document, add it
|
||||||
nodes.extend(build_document_node(document))
|
nodes.extend(build_document_node(document))
|
||||||
progress.update(task, advance=1)
|
|
||||||
|
|
||||||
if nodes:
|
if nodes:
|
||||||
msg = "LLM index updated successfully."
|
msg = "LLM index updated successfully."
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ def test_update_llm_index(
|
|||||||
mock_queryset = MagicMock()
|
mock_queryset = MagicMock()
|
||||||
mock_queryset.exists.return_value = True
|
mock_queryset.exists.return_value = True
|
||||||
mock_queryset.__iter__.return_value = iter([real_document])
|
mock_queryset.__iter__.return_value = iter([real_document])
|
||||||
mock_queryset.count.return_value = 1
|
|
||||||
mock_all.return_value = mock_queryset
|
mock_all.return_value = mock_queryset
|
||||||
indexing.update_llm_index(rebuild=True)
|
indexing.update_llm_index(rebuild=True)
|
||||||
|
|
||||||
@@ -98,7 +97,6 @@ def test_update_llm_index_removes_meta(
|
|||||||
mock_queryset = MagicMock()
|
mock_queryset = MagicMock()
|
||||||
mock_queryset.exists.return_value = True
|
mock_queryset.exists.return_value = True
|
||||||
mock_queryset.__iter__.return_value = iter([real_document])
|
mock_queryset.__iter__.return_value = iter([real_document])
|
||||||
mock_queryset.count.return_value = 1
|
|
||||||
mock_all.return_value = mock_queryset
|
mock_all.return_value = mock_queryset
|
||||||
indexing.update_llm_index(rebuild=True)
|
indexing.update_llm_index(rebuild=True)
|
||||||
|
|
||||||
@@ -131,7 +129,6 @@ def test_update_llm_index_partial_update(
|
|||||||
mock_queryset = MagicMock()
|
mock_queryset = MagicMock()
|
||||||
mock_queryset.exists.return_value = True
|
mock_queryset.exists.return_value = True
|
||||||
mock_queryset.__iter__.return_value = iter([real_document, doc2])
|
mock_queryset.__iter__.return_value = iter([real_document, doc2])
|
||||||
mock_queryset.count.return_value = 2
|
|
||||||
mock_all.return_value = mock_queryset
|
mock_all.return_value = mock_queryset
|
||||||
|
|
||||||
indexing.update_llm_index(rebuild=True)
|
indexing.update_llm_index(rebuild=True)
|
||||||
@@ -152,7 +149,6 @@ def test_update_llm_index_partial_update(
|
|||||||
mock_queryset = MagicMock()
|
mock_queryset = MagicMock()
|
||||||
mock_queryset.exists.return_value = True
|
mock_queryset.exists.return_value = True
|
||||||
mock_queryset.__iter__.return_value = iter([updated_document, doc2, doc3])
|
mock_queryset.__iter__.return_value = iter([updated_document, doc2, doc3])
|
||||||
mock_queryset.count.return_value = 3
|
|
||||||
mock_all.return_value = mock_queryset
|
mock_all.return_value = mock_queryset
|
||||||
|
|
||||||
# assert logs "Updating LLM index with %d new nodes and removing %d old nodes."
|
# assert logs "Updating LLM index with %d new nodes and removing %d old nodes."
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
# Generated by Django 5.2.11 on 2026-02-09 16:37
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("paperless_mail", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailaccount",
|
||||||
|
name="account_type",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[(1, "IMAP"), (2, "Gmail OAuth"), (3, "Outlook OAuth")],
|
||||||
|
default=1,
|
||||||
|
verbose_name="account type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailaccount",
|
||||||
|
name="imap_port",
|
||||||
|
field=models.PositiveIntegerField(
|
||||||
|
blank=True,
|
||||||
|
help_text="This is usually 143 for unencrypted and STARTTLS connections, and 993 for SSL connections.",
|
||||||
|
null=True,
|
||||||
|
verbose_name="IMAP port",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailaccount",
|
||||||
|
name="imap_security",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[(1, "No encryption"), (2, "Use SSL"), (3, "Use STARTTLS")],
|
||||||
|
default=2,
|
||||||
|
verbose_name="IMAP security",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="action",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Delete"),
|
||||||
|
(2, "Move to specified folder"),
|
||||||
|
(3, "Mark as read, don't process read mails"),
|
||||||
|
(4, "Flag the mail, don't process flagged mails"),
|
||||||
|
(5, "Tag the mail with specified tag, don't process tagged mails"),
|
||||||
|
],
|
||||||
|
default=3,
|
||||||
|
verbose_name="action",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="assign_correspondent_from",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Do not assign a correspondent"),
|
||||||
|
(2, "Use mail address"),
|
||||||
|
(3, "Use name (or mail address if not available)"),
|
||||||
|
(4, "Use correspondent selected below"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="assign correspondent from",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="assign_title_from",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Use subject as title"),
|
||||||
|
(2, "Use attachment filename as title"),
|
||||||
|
(3, "Do not assign title from rule"),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="assign title from",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="attachment_type",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Only process attachments."),
|
||||||
|
(2, "Process all files, including 'inline' attachments."),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
help_text="Inline attachments include embedded images, so it's best to combine this option with a filename filter.",
|
||||||
|
verbose_name="attachment type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="consumption_scope",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Only process attachments."),
|
||||||
|
(
|
||||||
|
2,
|
||||||
|
"Process full Mail (with embedded attachments in file) as .eml",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
3,
|
||||||
|
"Process full Mail (with embedded attachments in file) as .eml + process attachments as separate documents",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
default=1,
|
||||||
|
verbose_name="consumption scope",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="maximum_age",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
default=30,
|
||||||
|
help_text="Specified in days.",
|
||||||
|
verbose_name="maximum age",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="order",
|
||||||
|
field=models.SmallIntegerField(default=0, verbose_name="order"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="pdf_layout",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[
|
||||||
|
(0, "System default"),
|
||||||
|
(1, "Text, then HTML"),
|
||||||
|
(2, "HTML, then text"),
|
||||||
|
(3, "HTML only"),
|
||||||
|
(4, "Text only"),
|
||||||
|
],
|
||||||
|
default=0,
|
||||||
|
verbose_name="pdf layout",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -24,7 +24,7 @@ class MailAccount(document_models.ModelWithOwner):
|
|||||||
|
|
||||||
imap_server = models.CharField(_("IMAP server"), max_length=256)
|
imap_server = models.CharField(_("IMAP server"), max_length=256)
|
||||||
|
|
||||||
imap_port = models.IntegerField(
|
imap_port = models.PositiveIntegerField(
|
||||||
_("IMAP port"),
|
_("IMAP port"),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@@ -34,7 +34,7 @@ class MailAccount(document_models.ModelWithOwner):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
imap_security = models.PositiveIntegerField(
|
imap_security = models.PositiveSmallIntegerField(
|
||||||
_("IMAP security"),
|
_("IMAP security"),
|
||||||
choices=ImapSecurity.choices,
|
choices=ImapSecurity.choices,
|
||||||
default=ImapSecurity.SSL,
|
default=ImapSecurity.SSL,
|
||||||
@@ -56,7 +56,7 @@ class MailAccount(document_models.ModelWithOwner):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
account_type = models.PositiveIntegerField(
|
account_type = models.PositiveSmallIntegerField(
|
||||||
_("account type"),
|
_("account type"),
|
||||||
choices=MailAccountType.choices,
|
choices=MailAccountType.choices,
|
||||||
default=MailAccountType.IMAP,
|
default=MailAccountType.IMAP,
|
||||||
@@ -142,7 +142,7 @@ class MailRule(document_models.ModelWithOwner):
|
|||||||
|
|
||||||
name = models.CharField(_("name"), max_length=256)
|
name = models.CharField(_("name"), max_length=256)
|
||||||
|
|
||||||
order = models.IntegerField(_("order"), default=0)
|
order = models.SmallIntegerField(_("order"), default=0)
|
||||||
|
|
||||||
account = models.ForeignKey(
|
account = models.ForeignKey(
|
||||||
MailAccount,
|
MailAccount,
|
||||||
@@ -215,13 +215,13 @@ class MailRule(document_models.ModelWithOwner):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
maximum_age = models.PositiveIntegerField(
|
maximum_age = models.PositiveSmallIntegerField(
|
||||||
_("maximum age"),
|
_("maximum age"),
|
||||||
default=30,
|
default=30,
|
||||||
help_text=_("Specified in days."),
|
help_text=_("Specified in days."),
|
||||||
)
|
)
|
||||||
|
|
||||||
attachment_type = models.PositiveIntegerField(
|
attachment_type = models.PositiveSmallIntegerField(
|
||||||
_("attachment type"),
|
_("attachment type"),
|
||||||
choices=AttachmentProcessing.choices,
|
choices=AttachmentProcessing.choices,
|
||||||
default=AttachmentProcessing.ATTACHMENTS_ONLY,
|
default=AttachmentProcessing.ATTACHMENTS_ONLY,
|
||||||
@@ -231,19 +231,19 @@ class MailRule(document_models.ModelWithOwner):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
consumption_scope = models.PositiveIntegerField(
|
consumption_scope = models.PositiveSmallIntegerField(
|
||||||
_("consumption scope"),
|
_("consumption scope"),
|
||||||
choices=ConsumptionScope.choices,
|
choices=ConsumptionScope.choices,
|
||||||
default=ConsumptionScope.ATTACHMENTS_ONLY,
|
default=ConsumptionScope.ATTACHMENTS_ONLY,
|
||||||
)
|
)
|
||||||
|
|
||||||
pdf_layout = models.PositiveIntegerField(
|
pdf_layout = models.PositiveSmallIntegerField(
|
||||||
_("pdf layout"),
|
_("pdf layout"),
|
||||||
choices=PdfLayout.choices,
|
choices=PdfLayout.choices,
|
||||||
default=PdfLayout.DEFAULT,
|
default=PdfLayout.DEFAULT,
|
||||||
)
|
)
|
||||||
|
|
||||||
action = models.PositiveIntegerField(
|
action = models.PositiveSmallIntegerField(
|
||||||
_("action"),
|
_("action"),
|
||||||
choices=MailAction.choices,
|
choices=MailAction.choices,
|
||||||
default=MailAction.MARK_READ,
|
default=MailAction.MARK_READ,
|
||||||
@@ -262,7 +262,7 @@ class MailRule(document_models.ModelWithOwner):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
assign_title_from = models.PositiveIntegerField(
|
assign_title_from = models.PositiveSmallIntegerField(
|
||||||
_("assign title from"),
|
_("assign title from"),
|
||||||
choices=TitleSource.choices,
|
choices=TitleSource.choices,
|
||||||
default=TitleSource.FROM_SUBJECT,
|
default=TitleSource.FROM_SUBJECT,
|
||||||
@@ -282,7 +282,7 @@ class MailRule(document_models.ModelWithOwner):
|
|||||||
verbose_name=_("assign this document type"),
|
verbose_name=_("assign this document type"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assign_correspondent_from = models.PositiveIntegerField(
|
assign_correspondent_from = models.PositiveSmallIntegerField(
|
||||||
_("assign correspondent from"),
|
_("assign correspondent from"),
|
||||||
choices=CorrespondentSource.choices,
|
choices=CorrespondentSource.choices,
|
||||||
default=CorrespondentSource.FROM_NOTHING,
|
default=CorrespondentSource.FROM_NOTHING,
|
||||||
|
|||||||
247
uv.lock
generated
247
uv.lock
generated
@@ -826,56 +826,55 @@ toml = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cryptography"
|
name = "cryptography"
|
||||||
version = "46.0.3"
|
version = "46.0.5"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "cffi", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux')" },
|
{ name = "cffi", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux')" },
|
||||||
{ name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" },
|
{ name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" },
|
{ url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" },
|
{ url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" },
|
{ url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" },
|
{ url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" },
|
{ url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" },
|
{ url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" },
|
{ url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" },
|
{ url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" },
|
{ url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" },
|
{ url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" },
|
{ url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" },
|
{ url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" },
|
{ url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" },
|
{ url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" },
|
{ url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" },
|
{ url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" },
|
{ url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" },
|
{ url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" },
|
{ url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" },
|
{ url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" },
|
{ url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" },
|
{ url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" },
|
{ url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" },
|
{ url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" },
|
{ url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" },
|
{ url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" },
|
{ url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" },
|
{ url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" },
|
{ url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" },
|
{ url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" },
|
{ url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" },
|
{ url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" },
|
{ url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" },
|
{ url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" },
|
{ url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" },
|
{ url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163, upload-time = "2025-10-15T23:18:13.821Z" },
|
{ url = "https://files.pythonhosted.org/packages/eb/dd/2d9fdb07cebdf3d51179730afb7d5e576153c6744c3ff8fded23030c204e/cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", size = 3476964, upload-time = "2026-02-10T19:18:20.687Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" },
|
{ url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" },
|
{ url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" },
|
{ url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" },
|
{ url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3083,6 +3082,7 @@ dependencies = [
|
|||||||
{ name = "tika-client", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "tika-client", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "torch", version = "2.10.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
|
{ name = "torch", version = "2.10.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
|
||||||
{ name = "torch", version = "2.10.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux'" },
|
{ name = "torch", version = "2.10.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "watchfiles", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "watchfiles", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "whitenoise", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "whitenoise", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "whoosh-reloaded", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "whoosh-reloaded", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
@@ -3165,6 +3165,7 @@ typing = [
|
|||||||
{ name = "types-pytz", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "types-pytz", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "types-redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "types-redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "types-setuptools", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "types-setuptools", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
|
{ name = "types-tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
@@ -3235,6 +3236,7 @@ requires-dist = [
|
|||||||
{ name = "setproctitle", specifier = "~=1.3.4" },
|
{ name = "setproctitle", specifier = "~=1.3.4" },
|
||||||
{ name = "tika-client", specifier = "~=0.10.0" },
|
{ name = "tika-client", specifier = "~=0.10.0" },
|
||||||
{ name = "torch", specifier = "~=2.10.0", index = "https://download.pytorch.org/whl/cpu" },
|
{ name = "torch", specifier = "~=2.10.0", index = "https://download.pytorch.org/whl/cpu" },
|
||||||
|
{ name = "tqdm", specifier = "~=4.67.1" },
|
||||||
{ name = "watchfiles", specifier = ">=1.1.1" },
|
{ name = "watchfiles", specifier = ">=1.1.1" },
|
||||||
{ name = "whitenoise", specifier = "~=6.11" },
|
{ name = "whitenoise", specifier = "~=6.11" },
|
||||||
{ name = "whoosh-reloaded", specifier = ">=2.7.5" },
|
{ name = "whoosh-reloaded", specifier = ">=2.7.5" },
|
||||||
@@ -3301,6 +3303,7 @@ typing = [
|
|||||||
{ name = "types-pytz" },
|
{ name = "types-pytz" },
|
||||||
{ name = "types-redis" },
|
{ name = "types-redis" },
|
||||||
{ name = "types-setuptools" },
|
{ name = "types-setuptools" },
|
||||||
|
{ name = "types-tqdm" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3437,78 +3440,78 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pillow"
|
name = "pillow"
|
||||||
version = "12.1.0"
|
version = "12.1.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d0/02/d52c733a2452ef1ffcc123b68e6606d07276b0e358db70eabad7e40042b7/pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9", size = 46977283, upload-time = "2026-01-02T09:13:29.892Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264, upload-time = "2026-02-11T04:23:07.146Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/fe/41/f73d92b6b883a579e79600d391f2e21cb0df767b2714ecbd2952315dfeef/pillow-12.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:fb125d860738a09d363a88daa0f59c4533529a90e564785e20fe875b200b6dbd", size = 5304089, upload-time = "2026-01-02T09:10:24.953Z" },
|
{ url = "https://files.pythonhosted.org/packages/1d/30/5bd3d794762481f8c8ae9c80e7b76ecea73b916959eb587521358ef0b2f9/pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0", size = 5304099, upload-time = "2026-02-11T04:20:06.13Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/94/55/7aca2891560188656e4a91ed9adba305e914a4496800da6b5c0a15f09edf/pillow-12.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cad302dc10fac357d3467a74a9561c90609768a6f73a1923b0fd851b6486f8b0", size = 4657815, upload-time = "2026-01-02T09:10:27.063Z" },
|
{ url = "https://files.pythonhosted.org/packages/bd/c1/aab9e8f3eeb4490180e357955e15c2ef74b31f64790ff356c06fb6cf6d84/pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713", size = 4657880, upload-time = "2026-02-11T04:20:09.291Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/e9/d2/b28221abaa7b4c40b7dba948f0f6a708bd7342c4d47ce342f0ea39643974/pillow-12.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a40905599d8079e09f25027423aed94f2823adaf2868940de991e53a449e14a8", size = 6222593, upload-time = "2026-01-02T09:10:29.115Z" },
|
{ url = "https://files.pythonhosted.org/packages/f1/0a/9879e30d56815ad529d3985aeff5af4964202425c27261a6ada10f7cbf53/pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b", size = 6222587, upload-time = "2026-02-11T04:20:10.82Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/71/b8/7a61fb234df6a9b0b479f69e66901209d89ff72a435b49933f9122f94cac/pillow-12.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:92a7fe4225365c5e3a8e598982269c6d6698d3e783b3b1ae979e7819f9cd55c1", size = 8027579, upload-time = "2026-01-02T09:10:31.182Z" },
|
{ url = "https://files.pythonhosted.org/packages/5a/5f/a1b72ff7139e4f89014e8d451442c74a774d5c43cd938fb0a9f878576b37/pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b", size = 8027678, upload-time = "2026-02-11T04:20:12.455Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ea/51/55c751a57cc524a15a0e3db20e5cde517582359508d62305a627e77fd295/pillow-12.1.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f10c98f49227ed8383d28174ee95155a675c4ed7f85e2e573b04414f7e371bda", size = 6335760, upload-time = "2026-01-02T09:10:33.02Z" },
|
{ url = "https://files.pythonhosted.org/packages/e2/c2/c7cb187dac79a3d22c3ebeae727abee01e077c8c7d930791dc592f335153/pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4", size = 6335777, upload-time = "2026-02-11T04:20:14.441Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/dc/7c/60e3e6f5e5891a1a06b4c910f742ac862377a6fe842f7184df4a274ce7bf/pillow-12.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8637e29d13f478bc4f153d8daa9ffb16455f0a6cb287da1b432fdad2bfbd66c7", size = 7027127, upload-time = "2026-01-02T09:10:35.009Z" },
|
{ url = "https://files.pythonhosted.org/packages/0c/7b/f9b09a7804ec7336effb96c26d37c29d27225783dc1501b7d62dcef6ae25/pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4", size = 7027140, upload-time = "2026-02-11T04:20:16.387Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/06/37/49d47266ba50b00c27ba63a7c898f1bb41a29627ced8c09e25f19ebec0ff/pillow-12.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:21e686a21078b0f9cb8c8a961d99e6a4ddb88e0fc5ea6e130172ddddc2e5221a", size = 6449896, upload-time = "2026-01-02T09:10:36.793Z" },
|
{ url = "https://files.pythonhosted.org/packages/98/b2/2fa3c391550bd421b10849d1a2144c44abcd966daadd2f7c12e19ea988c4/pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e", size = 6449855, upload-time = "2026-02-11T04:20:18.554Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f9/e5/67fd87d2913902462cd9b79c6211c25bfe95fcf5783d06e1367d6d9a741f/pillow-12.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2415373395a831f53933c23ce051021e79c8cd7979822d8cc478547a3f4da8ef", size = 7151345, upload-time = "2026-01-02T09:10:39.064Z" },
|
{ url = "https://files.pythonhosted.org/packages/96/ff/9caf4b5b950c669263c39e96c78c0d74a342c71c4f43fd031bb5cb7ceac9/pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff", size = 7151329, upload-time = "2026-02-11T04:20:20.646Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/43/c4/bf8328039de6cc22182c3ef007a2abfbbdab153661c0a9aa78af8d706391/pillow-12.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3", size = 5304057, upload-time = "2026-01-02T09:10:46.627Z" },
|
{ url = "https://files.pythonhosted.org/packages/2b/46/5da1ec4a5171ee7bf1a0efa064aba70ba3d6e0788ce3f5acd1375d23c8c0/pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32", size = 5304084, upload-time = "2026-02-11T04:20:27.501Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/43/06/7264c0597e676104cc22ca73ee48f752767cd4b1fe084662620b17e10120/pillow-12.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0", size = 4657811, upload-time = "2026-01-02T09:10:49.548Z" },
|
{ url = "https://files.pythonhosted.org/packages/78/93/a29e9bc02d1cf557a834da780ceccd54e02421627200696fcf805ebdc3fb/pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38", size = 4657866, upload-time = "2026-02-11T04:20:29.827Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/72/64/f9189e44474610daf83da31145fa56710b627b5c4c0b9c235e34058f6b31/pillow-12.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40a8e3b9e8773876d6e30daed22f016509e3987bab61b3b7fe309d7019a87451", size = 6232243, upload-time = "2026-01-02T09:10:51.62Z" },
|
{ url = "https://files.pythonhosted.org/packages/13/84/583a4558d492a179d31e4aae32eadce94b9acf49c0337c4ce0b70e0a01f2/pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5", size = 6232148, upload-time = "2026-02-11T04:20:31.329Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ef/30/0df458009be6a4caca4ca2c52975e6275c387d4e5c95544e34138b41dc86/pillow-12.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:800429ac32c9b72909c671aaf17ecd13110f823ddb7db4dfef412a5587c2c24e", size = 8037872, upload-time = "2026-01-02T09:10:53.446Z" },
|
{ url = "https://files.pythonhosted.org/packages/d5/e2/53c43334bbbb2d3b938978532fbda8e62bb6e0b23a26ce8592f36bcc4987/pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090", size = 8038007, upload-time = "2026-02-11T04:20:34.225Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/e4/86/95845d4eda4f4f9557e25381d70876aa213560243ac1a6d619c46caaedd9/pillow-12.1.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b022eaaf709541b391ee069f0022ee5b36c709df71986e3f7be312e46f42c84", size = 6345398, upload-time = "2026-01-02T09:10:55.426Z" },
|
{ url = "https://files.pythonhosted.org/packages/b8/a6/3d0e79c8a9d58150dd98e199d7c1c56861027f3829a3a60b3c2784190180/pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af", size = 6345418, upload-time = "2026-02-11T04:20:35.858Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5c/1f/8e66ab9be3aaf1435bc03edd1ebdf58ffcd17f7349c1d970cafe87af27d9/pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0", size = 7034667, upload-time = "2026-01-02T09:10:57.11Z" },
|
{ url = "https://files.pythonhosted.org/packages/a2/c8/46dfeac5825e600579157eea177be43e2f7ff4a99da9d0d0a49533509ac5/pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b", size = 7034590, upload-time = "2026-02-11T04:20:37.91Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f9/f6/683b83cb9b1db1fb52b87951b1c0b99bdcfceaa75febf11406c19f82cb5e/pillow-12.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d70347c8a5b7ccd803ec0c85c8709f036e6348f1e6a5bf048ecd9c64d3550b8b", size = 6458743, upload-time = "2026-01-02T09:10:59.331Z" },
|
{ url = "https://files.pythonhosted.org/packages/af/bf/e6f65d3db8a8bbfeaf9e13cc0417813f6319863a73de934f14b2229ada18/pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5", size = 6458655, upload-time = "2026-02-11T04:20:39.496Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/9a/7d/de833d63622538c1d58ce5395e7c6cb7e7dce80decdd8bde4a484e095d9f/pillow-12.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fcc52d86ce7a34fd17cb04e87cfdb164648a3662a6f20565910a99653d66c18", size = 7159342, upload-time = "2026-01-02T09:11:01.82Z" },
|
{ url = "https://files.pythonhosted.org/packages/f9/c2/66091f3f34a25894ca129362e510b956ef26f8fb67a0e6417bc5744e56f1/pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d", size = 7159286, upload-time = "2026-02-11T04:20:41.139Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/20/31/dc53fe21a2f2996e1b7d92bf671cdb157079385183ef7c1ae08b485db510/pillow-12.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b", size = 5262642, upload-time = "2026-01-02T09:11:10.138Z" },
|
{ url = "https://files.pythonhosted.org/packages/07/d3/8df65da0d4df36b094351dce696f2989bec731d4f10e743b1c5f4da4d3bf/pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052", size = 5262803, upload-time = "2026-02-11T04:20:47.653Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ab/c1/10e45ac9cc79419cedf5121b42dcca5a50ad2b601fa080f58c22fb27626e/pillow-12.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551", size = 4657464, upload-time = "2026-01-02T09:11:12.319Z" },
|
{ url = "https://files.pythonhosted.org/packages/d6/71/5026395b290ff404b836e636f51d7297e6c83beceaa87c592718747e670f/pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984", size = 4657601, upload-time = "2026-02-11T04:20:49.328Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ad/26/7b82c0ab7ef40ebede7a97c72d473bda5950f609f8e0c77b04af574a0ddb/pillow-12.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208", size = 6234878, upload-time = "2026-01-02T09:11:14.096Z" },
|
{ url = "https://files.pythonhosted.org/packages/b1/2e/1001613d941c67442f745aff0f7cc66dd8df9a9c084eb497e6a543ee6f7e/pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79", size = 6234995, upload-time = "2026-02-11T04:20:51.032Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/25/27abc9792615b5e886ca9411ba6637b675f1b77af3104710ac7353fe5605/pillow-12.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5", size = 8044868, upload-time = "2026-01-02T09:11:15.903Z" },
|
{ url = "https://files.pythonhosted.org/packages/07/26/246ab11455b2549b9233dbd44d358d033a2f780fa9007b61a913c5b2d24e/pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293", size = 8045012, upload-time = "2026-02-11T04:20:52.882Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0a/ea/f200a4c36d836100e7bc738fc48cd963d3ba6372ebc8298a889e0cfc3359/pillow-12.1.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661", size = 6349468, upload-time = "2026-01-02T09:11:17.631Z" },
|
{ url = "https://files.pythonhosted.org/packages/b2/8b/07587069c27be7535ac1fe33874e32de118fbd34e2a73b7f83436a88368c/pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397", size = 6349638, upload-time = "2026-02-11T04:20:54.444Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/11/8f/48d0b77ab2200374c66d344459b8958c86693be99526450e7aee714e03e4/pillow-12.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17", size = 7041518, upload-time = "2026-01-02T09:11:19.389Z" },
|
{ url = "https://files.pythonhosted.org/packages/ff/79/6df7b2ee763d619cda2fb4fea498e5f79d984dae304d45a8999b80d6cf5c/pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0", size = 7041540, upload-time = "2026-02-11T04:20:55.97Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1d/23/c281182eb986b5d31f0a76d2a2c8cd41722d6fb8ed07521e802f9bba52de/pillow-12.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670", size = 6462829, upload-time = "2026-01-02T09:11:21.28Z" },
|
{ url = "https://files.pythonhosted.org/packages/2c/5e/2ba19e7e7236d7529f4d873bdaf317a318896bac289abebd4bb00ef247f0/pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3", size = 6462613, upload-time = "2026-02-11T04:20:57.542Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/25/ef/7018273e0faac099d7b00982abdcc39142ae6f3bd9ceb06de09779c4a9d6/pillow-12.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616", size = 7166756, upload-time = "2026-01-02T09:11:23.559Z" },
|
{ url = "https://files.pythonhosted.org/packages/03/03/31216ec124bb5c3dacd74ce8efff4cc7f52643653bad4825f8f08c697743/pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35", size = 7166745, upload-time = "2026-02-11T04:20:59.196Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/dd/c7/2530a4aa28248623e9d7f27316b42e27c32ec410f695929696f2e0e4a778/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1", size = 4062543, upload-time = "2026-01-02T09:11:31.566Z" },
|
{ url = "https://files.pythonhosted.org/packages/d5/11/6db24d4bd7685583caeae54b7009584e38da3c3d4488ed4cd25b439de486/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e", size = 4062689, upload-time = "2026-02-11T04:21:06.804Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8f/1f/40b8eae823dc1519b87d53c30ed9ef085506b05281d313031755c1705f73/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179", size = 4138373, upload-time = "2026-01-02T09:11:33.367Z" },
|
{ url = "https://files.pythonhosted.org/packages/33/c0/ce6d3b1fe190f0021203e0d9b5b99e57843e345f15f9ef22fcd43842fd21/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9", size = 4138535, upload-time = "2026-02-11T04:21:08.452Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d4/77/6fa60634cf06e52139fd0e89e5bbf055e8166c691c42fb162818b7fda31d/pillow-12.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0", size = 3601241, upload-time = "2026-01-02T09:11:35.011Z" },
|
{ url = "https://files.pythonhosted.org/packages/a0/c6/d5eb6a4fb32a3f9c21a8c7613ec706534ea1cf9f4b3663e99f0d83f6fca8/pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6", size = 3601364, upload-time = "2026-02-11T04:21:10.194Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587", size = 5262410, upload-time = "2026-01-02T09:11:36.682Z" },
|
{ url = "https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60", size = 5262561, upload-time = "2026-02-11T04:21:11.742Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac", size = 4657312, upload-time = "2026-01-02T09:11:38.535Z" },
|
{ url = "https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2", size = 4657460, upload-time = "2026-02-11T04:21:13.786Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1d/fd/f5a0896839762885b3376ff04878f86ab2b097c2f9a9cdccf4eda8ba8dc0/pillow-12.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b", size = 6232605, upload-time = "2026-01-02T09:11:40.602Z" },
|
{ url = "https://files.pythonhosted.org/packages/9e/1b/f1a4ea9a895b5732152789326202a82464d5254759fbacae4deea3069334/pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850", size = 6232698, upload-time = "2026-02-11T04:21:15.949Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/98/aa/938a09d127ac1e70e6ed467bd03834350b33ef646b31edb7452d5de43792/pillow-12.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea", size = 8041617, upload-time = "2026-01-02T09:11:42.721Z" },
|
{ url = "https://files.pythonhosted.org/packages/95/f4/86f51b8745070daf21fd2e5b1fe0eb35d4db9ca26e6d58366562fb56a743/pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289", size = 8041706, upload-time = "2026-02-11T04:21:17.723Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/17/e8/538b24cb426ac0186e03f80f78bc8dc7246c667f58b540bdd57c71c9f79d/pillow-12.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c", size = 6346509, upload-time = "2026-01-02T09:11:44.955Z" },
|
{ url = "https://files.pythonhosted.org/packages/29/9b/d6ecd956bb1266dd1045e995cce9b8d77759e740953a1c9aad9502a0461e/pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e", size = 6346621, upload-time = "2026-02-11T04:21:19.547Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc", size = 7038117, upload-time = "2026-01-02T09:11:46.736Z" },
|
{ url = "https://files.pythonhosted.org/packages/71/24/538bff45bde96535d7d998c6fed1a751c75ac7c53c37c90dc2601b243893/pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717", size = 7038069, upload-time = "2026-02-11T04:21:21.378Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c7/a2/d40308cf86eada842ca1f3ffa45d0ca0df7e4ab33c83f81e73f5eaed136d/pillow-12.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644", size = 6460151, upload-time = "2026-01-02T09:11:48.625Z" },
|
{ url = "https://files.pythonhosted.org/packages/94/0e/58cb1a6bc48f746bc4cb3adb8cabff73e2742c92b3bf7a220b7cf69b9177/pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a", size = 6460040, upload-time = "2026-02-11T04:21:23.148Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f1/88/f5b058ad6453a085c5266660a1417bdad590199da1b32fb4efcff9d33b05/pillow-12.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c", size = 7164534, upload-time = "2026-01-02T09:11:50.445Z" },
|
{ url = "https://files.pythonhosted.org/packages/6c/57/9045cb3ff11eeb6c1adce3b2d60d7d299d7b273a2e6c8381a524abfdc474/pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029", size = 7164523, upload-time = "2026-02-11T04:21:25.01Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b5/ba/970b7d85ba01f348dee4d65412476321d40ee04dcb51cd3735b9dc94eb58/pillow-12.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d", size = 5264816, upload-time = "2026-01-02T09:11:58.227Z" },
|
{ url = "https://files.pythonhosted.org/packages/19/2a/b9d62794fc8a0dd14c1943df68347badbd5511103e0d04c035ffe5cf2255/pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da", size = 5264880, upload-time = "2026-02-11T04:21:32.865Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/10/60/650f2fb55fdba7a510d836202aa52f0baac633e50ab1cf18415d332188fb/pillow-12.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0", size = 4660472, upload-time = "2026-01-02T09:12:00.798Z" },
|
{ url = "https://files.pythonhosted.org/packages/26/9d/e03d857d1347fa5ed9247e123fcd2a97b6220e15e9cb73ca0a8d91702c6e/pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc", size = 4660616, upload-time = "2026-02-11T04:21:34.97Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2b/c0/5273a99478956a099d533c4f46cbaa19fd69d606624f4334b85e50987a08/pillow-12.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554", size = 6268974, upload-time = "2026-01-02T09:12:02.572Z" },
|
{ url = "https://files.pythonhosted.org/packages/f7/ec/8a6d22afd02570d30954e043f09c32772bfe143ba9285e2fdb11284952cd/pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c", size = 6269008, upload-time = "2026-02-11T04:21:36.623Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b4/26/0bf714bc2e73d5267887d47931d53c4ceeceea6978148ed2ab2a4e6463c4/pillow-12.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e", size = 8073070, upload-time = "2026-01-02T09:12:04.75Z" },
|
{ url = "https://files.pythonhosted.org/packages/3d/1d/6d875422c9f28a4a361f495a5f68d9de4a66941dc2c619103ca335fa6446/pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8", size = 8073226, upload-time = "2026-02-11T04:21:38.585Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/43/cf/1ea826200de111a9d65724c54f927f3111dc5ae297f294b370a670c17786/pillow-12.1.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82", size = 6380176, upload-time = "2026-01-02T09:12:06.626Z" },
|
{ url = "https://files.pythonhosted.org/packages/a1/cd/134b0b6ee5eda6dc09e25e24b40fdafe11a520bc725c1d0bbaa5e00bf95b/pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20", size = 6380136, upload-time = "2026-02-11T04:21:40.562Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/03/e0/7938dd2b2013373fd85d96e0f38d62b7a5a262af21ac274250c7ca7847c9/pillow-12.1.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4", size = 7067061, upload-time = "2026-01-02T09:12:08.624Z" },
|
{ url = "https://files.pythonhosted.org/packages/7a/a9/7628f013f18f001c1b98d8fffe3452f306a70dc6aba7d931019e0492f45e/pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13", size = 7067129, upload-time = "2026-02-11T04:21:42.521Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/86/ad/a2aa97d37272a929a98437a8c0ac37b3cf012f4f8721e1bd5154699b2518/pillow-12.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0", size = 6491824, upload-time = "2026-01-02T09:12:10.488Z" },
|
{ url = "https://files.pythonhosted.org/packages/1e/f8/66ab30a2193b277785601e82ee2d49f68ea575d9637e5e234faaa98efa4c/pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf", size = 6491807, upload-time = "2026-02-11T04:21:44.22Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a4/44/80e46611b288d51b115826f136fb3465653c28f491068a72d3da49b54cd4/pillow-12.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b", size = 7190911, upload-time = "2026-01-02T09:12:12.772Z" },
|
{ url = "https://files.pythonhosted.org/packages/da/0b/a877a6627dc8318fdb84e357c5e1a758c0941ab1ddffdafd231983788579/pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524", size = 7190954, upload-time = "2026-02-11T04:21:46.114Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8c/87/bdf971d8bbcf80a348cc3bacfcb239f5882100fe80534b0ce67a784181d8/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91", size = 4062533, upload-time = "2026-01-02T09:12:20.791Z" },
|
{ url = "https://files.pythonhosted.org/packages/03/d0/bebb3ffbf31c5a8e97241476c4cf8b9828954693ce6744b4a2326af3e16b/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af", size = 4062652, upload-time = "2026-02-11T04:21:53.19Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ff/4f/5eb37a681c68d605eb7034c004875c81f86ec9ef51f5be4a63eadd58859a/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796", size = 4138546, upload-time = "2026-01-02T09:12:23.664Z" },
|
{ url = "https://files.pythonhosted.org/packages/2d/c0/0e16fb0addda4851445c28f8350d8c512f09de27bbb0d6d0bbf8b6709605/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f", size = 4138823, upload-time = "2026-02-11T04:22:03.088Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/11/6d/19a95acb2edbace40dcd582d077b991646b7083c41b98da4ed7555b59733/pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd", size = 3601163, upload-time = "2026-01-02T09:12:26.338Z" },
|
{ url = "https://files.pythonhosted.org/packages/6b/fb/6170ec655d6f6bb6630a013dd7cf7bc218423d7b5fa9071bf63dc32175ae/pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642", size = 3601143, upload-time = "2026-02-11T04:22:04.909Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/fc/36/2b8138e51cb42e4cc39c3297713455548be855a50558c3ac2beebdc251dd/pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13", size = 5266086, upload-time = "2026-01-02T09:12:28.782Z" },
|
{ url = "https://files.pythonhosted.org/packages/59/04/dc5c3f297510ba9a6837cbb318b87dd2b8f73eb41a43cc63767f65cb599c/pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd", size = 5266254, upload-time = "2026-02-11T04:22:07.656Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/53/4b/649056e4d22e1caa90816bf99cef0884aed607ed38075bd75f091a607a38/pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e", size = 4657344, upload-time = "2026-01-02T09:12:31.117Z" },
|
{ url = "https://files.pythonhosted.org/packages/05/30/5db1236b0d6313f03ebf97f5e17cda9ca060f524b2fcc875149a8360b21c/pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202", size = 4657499, upload-time = "2026-02-11T04:22:09.613Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6c/6b/c5742cea0f1ade0cd61485dc3d81f05261fc2276f537fbdc00802de56779/pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643", size = 6232114, upload-time = "2026-01-02T09:12:32.936Z" },
|
{ url = "https://files.pythonhosted.org/packages/6f/18/008d2ca0eb612e81968e8be0bbae5051efba24d52debf930126d7eaacbba/pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f", size = 6232137, upload-time = "2026-02-11T04:22:11.434Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/bf/8f/9f521268ce22d63991601aafd3d48d5ff7280a246a1ef62d626d67b44064/pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5", size = 8042708, upload-time = "2026-01-02T09:12:34.78Z" },
|
{ url = "https://files.pythonhosted.org/packages/70/f1/f14d5b8eeb4b2cd62b9f9f847eb6605f103df89ef619ac68f92f748614ea/pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f", size = 8042721, upload-time = "2026-02-11T04:22:13.321Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1a/eb/257f38542893f021502a1bbe0c2e883c90b5cff26cc33b1584a841a06d30/pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de", size = 6347762, upload-time = "2026-01-02T09:12:36.748Z" },
|
{ url = "https://files.pythonhosted.org/packages/5a/d6/17824509146e4babbdabf04d8171491fa9d776f7061ff6e727522df9bd03/pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f", size = 6347798, upload-time = "2026-02-11T04:22:15.449Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c4/5a/8ba375025701c09b309e8d5163c5a4ce0102fa86bbf8800eb0d7ac87bc51/pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9", size = 7039265, upload-time = "2026-01-02T09:12:39.082Z" },
|
{ url = "https://files.pythonhosted.org/packages/d1/ee/c85a38a9ab92037a75615aba572c85ea51e605265036e00c5b67dfafbfe2/pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e", size = 7039315, upload-time = "2026-02-11T04:22:17.24Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/cf/dc/cf5e4cdb3db533f539e88a7bbf9f190c64ab8a08a9bc7a4ccf55067872e4/pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a", size = 6462341, upload-time = "2026-01-02T09:12:40.946Z" },
|
{ url = "https://files.pythonhosted.org/packages/ec/f3/bc8ccc6e08a148290d7523bde4d9a0d6c981db34631390dc6e6ec34cacf6/pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0", size = 6462360, upload-time = "2026-02-11T04:22:19.111Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d0/47/0291a25ac9550677e22eda48510cfc4fa4b2ef0396448b7fbdc0a6946309/pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a", size = 7165395, upload-time = "2026-01-02T09:12:42.706Z" },
|
{ url = "https://files.pythonhosted.org/packages/f6/ab/69a42656adb1d0665ab051eec58a41f169ad295cf81ad45406963105408f/pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb", size = 7165438, upload-time = "2026-02-11T04:22:21.041Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/82/54/2e1dd20c8749ff225080d6ba465a0cab4387f5db0d1c5fb1439e2d99923f/pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2", size = 5268571, upload-time = "2026-01-02T09:12:51.11Z" },
|
{ url = "https://files.pythonhosted.org/packages/6c/9d/efd18493f9de13b87ede7c47e69184b9e859e4427225ea962e32e56a49bc/pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8", size = 5268612, upload-time = "2026-02-11T04:22:29.884Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/57/61/571163a5ef86ec0cf30d265ac2a70ae6fc9e28413d1dc94fa37fae6bda89/pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61", size = 4660426, upload-time = "2026-01-02T09:12:52.865Z" },
|
{ url = "https://files.pythonhosted.org/packages/f8/f1/4f42eb2b388eb2ffc660dcb7f7b556c1015c53ebd5f7f754965ef997585b/pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9", size = 4660567, upload-time = "2026-02-11T04:22:31.799Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5e/e1/53ee5163f794aef1bf84243f755ee6897a92c708505350dd1923f4afec48/pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51", size = 6269908, upload-time = "2026-01-02T09:12:54.884Z" },
|
{ url = "https://files.pythonhosted.org/packages/01/54/df6ef130fa43e4b82e32624a7b821a2be1c5653a5fdad8469687a7db4e00/pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60", size = 6269951, upload-time = "2026-02-11T04:22:33.921Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/bc/0b/b4b4106ff0ee1afa1dc599fde6ab230417f800279745124f6c50bcffed8e/pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc", size = 8074733, upload-time = "2026-01-02T09:12:56.802Z" },
|
{ url = "https://files.pythonhosted.org/packages/a9/48/618752d06cc44bb4aae8ce0cd4e6426871929ed7b46215638088270d9b34/pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7", size = 8074769, upload-time = "2026-02-11T04:22:35.877Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/19/9f/80b411cbac4a732439e629a26ad3ef11907a8c7fc5377b7602f04f6fe4e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14", size = 6381431, upload-time = "2026-01-02T09:12:58.823Z" },
|
{ url = "https://files.pythonhosted.org/packages/c3/bd/f1d71eb39a72fa088d938655afba3e00b38018d052752f435838961127d8/pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f", size = 6381358, upload-time = "2026-02-11T04:22:37.698Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8f/b7/d65c45db463b66ecb6abc17c6ba6917a911202a07662247e1355ce1789e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8", size = 7068529, upload-time = "2026-01-02T09:13:00.885Z" },
|
{ url = "https://files.pythonhosted.org/packages/64/ef/c784e20b96674ed36a5af839305f55616f8b4f8aa8eeccf8531a6e312243/pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586", size = 7068558, upload-time = "2026-02-11T04:22:39.597Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/50/96/dfd4cd726b4a45ae6e3c669fc9e49deb2241312605d33aba50499e9d9bd1/pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924", size = 6492981, upload-time = "2026-01-02T09:13:03.314Z" },
|
{ url = "https://files.pythonhosted.org/packages/73/cb/8059688b74422ae61278202c4e1ad992e8a2e7375227be0a21c6b87ca8d5/pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce", size = 6493028, upload-time = "2026-02-11T04:22:42.73Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4d/1c/b5dc52cf713ae46033359c5ca920444f18a6359ce1020dd3e9c553ea5bc6/pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef", size = 7191878, upload-time = "2026-01-02T09:13:05.276Z" },
|
{ url = "https://files.pythonhosted.org/packages/c6/da/e3c008ed7d2dd1f905b15949325934510b9d1931e5df999bb15972756818/pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8", size = 7191940, upload-time = "2026-02-11T04:22:44.543Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8b/bc/224b1d98cffd7164b14707c91aac83c07b047fbd8f58eba4066a3e53746a/pillow-12.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ca94b6aac0d7af2a10ba08c0f888b3d5114439b6b3ef39968378723622fed377", size = 5228605, upload-time = "2026-01-02T09:13:14.084Z" },
|
{ url = "https://files.pythonhosted.org/packages/56/11/5d43209aa4cb58e0cc80127956ff1796a68b928e6324bbf06ef4db34367b/pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f", size = 5228606, upload-time = "2026-02-11T04:22:52.106Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0c/ca/49ca7769c4550107de049ed85208240ba0f330b3f2e316f24534795702ce/pillow-12.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:351889afef0f485b84078ea40fe33727a0492b9af3904661b0abbafee0355b72", size = 4622245, upload-time = "2026-01-02T09:13:15.964Z" },
|
{ url = "https://files.pythonhosted.org/packages/5f/d5/3b005b4e4fda6698b371fa6c21b097d4707585d7db99e98d9b0b87ac612a/pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9", size = 4622321, upload-time = "2026-02-11T04:22:53.827Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/73/48/fac807ce82e5955bcc2718642b94b1bd22a82a6d452aea31cbb678cddf12/pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb0984b30e973f7e2884362b7d23d0a348c7143ee559f38ef3eaab640144204c", size = 5247593, upload-time = "2026-01-02T09:13:17.913Z" },
|
{ url = "https://files.pythonhosted.org/packages/df/36/ed3ea2d594356fd8037e5a01f6156c74bc8d92dbb0fa60746cc96cabb6e8/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e", size = 5247579, upload-time = "2026-02-11T04:22:56.094Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d2/95/3e0742fe358c4664aed4fd05d5f5373dcdad0b27af52aa0972568541e3f4/pillow-12.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84cabc7095dd535ca934d57e9ce2a72ffd216e435a84acb06b2277b1de2689bd", size = 6989008, upload-time = "2026-01-02T09:13:20.083Z" },
|
{ url = "https://files.pythonhosted.org/packages/54/9a/9cc3e029683cf6d20ae5085da0dafc63148e3252c2f13328e553aaa13cfb/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9", size = 6989094, upload-time = "2026-02-11T04:22:58.288Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5a/74/fe2ac378e4e202e56d50540d92e1ef4ff34ed687f3c60f6a121bcf99437e/pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53d8b764726d3af1a138dd353116f774e3862ec7e3794e0c8781e30db0f35dfc", size = 5313824, upload-time = "2026-01-02T09:13:22.405Z" },
|
{ url = "https://files.pythonhosted.org/packages/00/98/fc53ab36da80b88df0967896b6c4b4cd948a0dc5aa40a754266aa3ae48b3/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3", size = 5313850, upload-time = "2026-02-11T04:23:00.554Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f3/77/2a60dee1adee4e2655ac328dd05c02a955c1cd683b9f1b82ec3feb44727c/pillow-12.1.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5da841d81b1a05ef940a8567da92decaa15bc4d7dedb540a8c219ad83d91808a", size = 5963278, upload-time = "2026-01-02T09:13:24.706Z" },
|
{ url = "https://files.pythonhosted.org/packages/30/02/00fa585abfd9fe9d73e5f6e554dc36cc2b842898cbfc46d70353dae227f8/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735", size = 5963343, upload-time = "2026-02-11T04:23:02.934Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5233,6 +5236,10 @@ wheels = [
|
|||||||
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:0826ac8e409551e12b2360ac18b4161a838cbd111933e694752f351191331d09" },
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:0826ac8e409551e12b2360ac18b4161a838cbd111933e694752f351191331d09" },
|
||||||
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:7fbbf409143a4fe0812a40c0b46a436030a7e1d14fe8c5234dfbe44df47f617e" },
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:7fbbf409143a4fe0812a40c0b46a436030a7e1d14fe8c5234dfbe44df47f617e" },
|
||||||
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:b39cafff7229699f9d6e172cac74d85fd71b568268e439e08d9c540e54732a3e" },
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:b39cafff7229699f9d6e172cac74d85fd71b568268e439e08d9c540e54732a3e" },
|
||||||
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:7417ef370d7c3969dd509dae8d5c7daeb945af335ab76dd38358ba30a91251c1" },
|
||||||
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:90821a3194b8806d9fa9fdaa9308c1bc73df0c26808274b14129a97c99f35794" },
|
||||||
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:358bd7125cbec6e692d60618a5eec7f55a51b29e3652a849fd42af021d818023" },
|
||||||
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:470de4176007c2700735e003a830828a88d27129032a3add07291da07e2a94e8" },
|
||||||
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2d16abfce6c92584ceeb00c3b2665d5798424dd9ed235ea69b72e045cd53ae97" },
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2d16abfce6c92584ceeb00c3b2665d5798424dd9ed235ea69b72e045cd53ae97" },
|
||||||
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:4584ab167995c0479f6821e3dceaf199c8166c811d3adbba5d8eedbbfa6764fd" },
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:4584ab167995c0479f6821e3dceaf199c8166c811d3adbba5d8eedbbfa6764fd" },
|
||||||
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:45a1c5057629444aeb1c452c18298fa7f30f2f7aeadd4dc41f9d340980294407" },
|
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:45a1c5057629444aeb1c452c18298fa7f30f2f7aeadd4dc41f9d340980294407" },
|
||||||
@@ -5580,6 +5587,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/2b/7f/016dc5cc718ec6ccaa84fb73ed409ef1c261793fd5e637cdfaa18beb40a9/types_setuptools-80.10.0.20260124-py3-none-any.whl", hash = "sha256:efed7e044f01adb9c2806c7a8e1b6aa3656b8e382379b53d5f26ee3db24d4c01", size = 64333, upload-time = "2026-01-24T03:18:38.344Z" },
|
{ url = "https://files.pythonhosted.org/packages/2b/7f/016dc5cc718ec6ccaa84fb73ed409ef1c261793fd5e637cdfaa18beb40a9/types_setuptools-80.10.0.20260124-py3-none-any.whl", hash = "sha256:efed7e044f01adb9c2806c7a8e1b6aa3656b8e382379b53d5f26ee3db24d4c01", size = 64333, upload-time = "2026-01-24T03:18:38.344Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "types-tqdm"
|
||||||
|
version = "4.67.3.20260205"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "types-requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/53/46/790b9872523a48163bdda87d47849b4466017640e5259d06eed539340afd/types_tqdm-4.67.3.20260205.tar.gz", hash = "sha256:f3023682d4aa3bbbf908c8c6bb35f35692d319460d9bbd3e646e8852f3dd9f85", size = 17597, upload-time = "2026-02-05T04:03:19.721Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/da/7f761868dbaa328392356fab30c18ab90d14cce86b269e7e63328f29d4a3/types_tqdm-4.67.3.20260205-py3-none-any.whl", hash = "sha256:85c31731e81dc3c5cecc34c6c8b2e5166fafa722468f58840c2b5ac6a8c5c173", size = 23894, upload-time = "2026-02-05T04:03:18.48Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-webencodings"
|
name = "types-webencodings"
|
||||||
version = "0.5.0.20251108"
|
version = "0.5.0.20251108"
|
||||||
|
|||||||
Reference in New Issue
Block a user