Merge branch 'dev' into feature-pnpm

This commit is contained in:
shamoon 2025-03-11 00:07:52 -07:00
commit 556bfb3b6f
No known key found for this signature in database
22 changed files with 587 additions and 589 deletions

View File

@ -1,18 +1,18 @@
codecov: codecov:
require_ci_to_pass: true require_ci_to_pass: true
# https://docs.codecov.com/docs/flags#recommended-automatic-flag-management # https://docs.codecov.com/docs/components
# Require each flag to have 1 upload before notification component_management:
flag_management: individual_components:
individual_flags: - component_id: backend
- name: backend
paths: paths:
- src/ - src/**
- name: frontend - component_id: frontend
paths: paths:
- src-ui/ - src-ui/**
# https://docs.codecov.com/docs/pull-request-comments # https://docs.codecov.com/docs/pull-request-comments
# codecov will only comment if coverage changes # codecov will only comment if coverage changes
comment: comment:
layout: "header, diff, components, flags, files"
require_changes: true require_changes: true
# https://docs.codecov.com/docs/javascript-bundle-analysis # https://docs.codecov.com/docs/javascript-bundle-analysis
require_bundle_changes: true require_bundle_changes: true

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: [shamoon, stumpylog]

View File

@ -89,3 +89,50 @@ updates:
- "major" - "major"
- "minor" - "minor"
- "patch" - "patch"
# Update Dockerfile in root directory
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
reviewers:
- "paperless-ngx/ci-cd"
labels:
- "ci-cd"
- "dependencies"
commit-message:
prefix: "docker"
include: "scope"
# Update Docker Compose files in docker/compose directory
- package-ecosystem: "docker-compose"
directory: "/docker/compose/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
reviewers:
- "paperless-ngx/ci-cd"
labels:
- "ci-cd"
- "dependencies"
commit-message:
prefix: "docker-compose"
include: "scope"
groups:
# Individual groups for each image
gotenberg:
patterns:
- "docker.io/gotenberg/gotenberg*"
tika:
patterns:
- "docker.io/apache/tika*"
redis:
patterns:
- "docker.io/library/redis*"
mariadb:
patterns:
- "docker.io/library/mariadb*"
postgres:
patterns:
- "docker.io/library/postgres*"

View File

@ -162,16 +162,20 @@ jobs:
--frozen \ --frozen \
pytest pytest
- -
name: Upload coverage name: Upload backend test results to Codecov
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }} if: always()
uses: actions/upload-artifact@v4 uses: codecov/test-results-action@v1
with: with:
name: backend-coverage-report token: ${{ secrets.CODECOV_TOKEN }}
path: | flags: backend-python-${{ matrix.python-version }}
coverage.xml files: junit.xml
junit.xml -
retention-days: 7 name: Upload backend coverage to Codecov
if-no-files-found: error uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: backend-python-${{ matrix.python-version }}
files: coverage.xml
- -
name: Stop containers name: Stop containers
if: always() if: always()
@ -179,7 +183,7 @@ jobs:
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down
install-frontend-depedendencies: install-frontend-dependencies:
name: "Install Frontend Dependencies" name: "Install Frontend Dependencies"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
@ -218,7 +222,7 @@ jobs:
name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})" name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- install-frontend-depedendencies - install-frontend-dependencies
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -253,111 +257,33 @@ jobs:
run: cd src-ui && pnpm run lint run: cd src-ui && pnpm run lint
- -
name: Run Jest unit tests name: Run Jest unit tests
env:
JEST_JUNIT_OUTPUT_FILE: junit-report-${{ matrix.shard-index }}.xml
run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }} run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
-
name: Upload Jest coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: jest-coverage-report-${{ matrix.shard-index }}
path: |
src-ui/coverage/coverage-final.json
src-ui/coverage/lcov.info
src-ui/coverage/clover.xml
retention-days: 7
if-no-files-found: error
- -
name: Run Playwright e2e tests name: Run Playwright e2e tests
run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }} run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
- -
name: Upload Playwright test results name: Upload frontend test results to Codecov
uses: codecov/test-results-action@v1
if: always() if: always()
uses: actions/upload-artifact@v4
with: with:
name: playwright-report-${{ matrix.shard-index }} token: ${{ secrets.CODECOV_TOKEN }}
path: src-ui/playwright-report flags: frontend-node-${{ matrix.node-version }}
retention-days: 7 directory: src-ui/
-
name: Upload frontend test results
if: always()
uses: actions/upload-artifact@v4
with:
name: junit-report-${{ matrix.shard-index }}
path: src-ui/junit-report-${{ matrix.shard-index }}.xml
retention-days: 7
tests-coverage-upload:
name: "Upload to Codecov"
runs-on: ubuntu-24.04
needs:
- tests-backend
- tests-frontend
steps:
-
uses: actions/checkout@v4
-
name: Download frontend jest coverage
uses: actions/download-artifact@v4
with:
path: src-ui/coverage/
pattern: jest-coverage-report-*
-
name: Download frontend playwright coverage
uses: actions/download-artifact@v4
with:
path: src-ui/coverage/
pattern: playwright-report-*
merge-multiple: true
-
name: Download frontend test results
uses: actions/download-artifact@v4
with:
path: src-ui/junit/
pattern: junit-report-*
merge-multiple: true
- -
name: Upload frontend coverage to Codecov name: Upload frontend coverage to Codecov
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5
with: with:
# not required for public repos, but intermittently fails otherwise
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
flags: frontend flags: frontend-node-${{ matrix.node-version }}
directory: src-ui/coverage/ directory: src-ui/coverage/
# dont include backend coverage files here
files: '!coverage.xml' frontend-bundle-analysis:
- name: "Frontend Bundle Analysis"
name: Upload frontend test results to Codecov runs-on: ubuntu-24.04
if: ${{ !cancelled() }} needs:
uses: codecov/test-results-action@v1 - tests-frontend
with: steps:
token: ${{ secrets.CODECOV_TOKEN }} - uses: actions/checkout@v4
flags: frontend
directory: src-ui/junit/
-
name: Download backend coverage
uses: actions/download-artifact@v4
with:
name: backend-coverage-report
path: src/
-
name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
# not required for public repos, but intermittently fails otherwise
token: ${{ secrets.CODECOV_TOKEN }}
# future expansion
flags: backend
directory: src/
-
name: Upload backend test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: backend
directory: src/
- -
name: Install pnpm name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4

View File

@ -32,7 +32,7 @@ RUN set -eux \
# Purpose: Installs s6-overlay and rootfs # Purpose: Installs s6-overlay and rootfs
# Comments: # Comments:
# - Don't leave anything extra in here either # - Don't leave anything extra in here either
FROM ghcr.io/astral-sh/uv:0.6.3-python3.12-bookworm-slim AS s6-overlay-base FROM ghcr.io/astral-sh/uv:0.6.5-python3.12-bookworm-slim AS s6-overlay-base
WORKDIR /usr/src/s6 WORKDIR /usr/src/s6

View File

@ -38,7 +38,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:16 image: docker.io/library/postgres:17
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data

View File

@ -38,7 +38,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:16 image: docker.io/library/postgres:17
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data

View File

@ -34,7 +34,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:16 image: docker.io/library/postgres:17
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data

View File

@ -37,7 +37,7 @@ dependencies = [
"djangorestframework~=3.15", "djangorestframework~=3.15",
"djangorestframework-guardian~=0.3.0", "djangorestframework-guardian~=0.3.0",
"drf-spectacular~=0.28", "drf-spectacular~=0.28",
"drf-spectacular-sidecar~=2025.2.1", "drf-spectacular-sidecar~=2025.3.1",
"drf-writable-nested~=0.7.1", "drf-writable-nested~=0.7.1",
"filelock~=3.17.0", "filelock~=3.17.0",
"flower~=2.0.1", "flower~=2.0.1",
@ -48,7 +48,7 @@ dependencies = [
"jinja2~=3.1.5", "jinja2~=3.1.5",
"langdetect~=1.0.9", "langdetect~=1.0.9",
"nltk~=3.9.1", "nltk~=3.9.1",
"ocrmypdf~=16.9.0", "ocrmypdf~=16.10.0",
"pathvalidate~=3.2.3", "pathvalidate~=3.2.3",
"pdf2image~=1.17.0", "pdf2image~=1.17.0",
"python-dateutil~=2.9.0", "python-dateutil~=2.9.0",
@ -73,9 +73,9 @@ optional-dependencies.mariadb = [
"mysqlclient~=2.2.7", "mysqlclient~=2.2.7",
] ]
optional-dependencies.postgres = [ optional-dependencies.postgres = [
"psycopg[c]==3.2.4", "psycopg[c]==3.2.5",
# Direct dependency for proper resolution of the pre-built wheels # Direct dependency for proper resolution of the pre-built wheels
"psycopg-c==3.2.4", "psycopg-c==3.2.5",
] ]
optional-dependencies.webserver = [ optional-dependencies.webserver = [
"granian~=1.7.6", "granian~=1.7.6",
@ -343,8 +343,8 @@ environments = [
[tool.uv.sources] [tool.uv.sources]
# Markers are chosen to select these almost exclusively when building the Docker image # Markers are chosen to select these almost exclusively when building the Docker image
psycopg-c = [ psycopg-c = [
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" }, { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" }, { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
] ]
zxing-cpp = [ zxing-cpp = [
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" }, { url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },

View File

@ -36,7 +36,13 @@ export const routes: Routes = [
component: AppFrameComponent, component: AppFrameComponent,
canDeactivate: [DirtyDocGuard], canDeactivate: [DirtyDocGuard],
children: [ children: [
{ path: 'dashboard', component: DashboardComponent }, {
path: 'dashboard',
component: DashboardComponent,
data: {
componentName: 'AppFrameComponent',
},
},
{ {
path: 'documents', path: 'documents',
component: DocumentListComponent, component: DocumentListComponent,
@ -47,6 +53,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'DocumentListComponent',
}, },
}, },
{ {
@ -59,6 +66,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.SavedView, type: PermissionType.SavedView,
}, },
componentName: 'DocumentListComponent',
}, },
}, },
{ {
@ -70,6 +78,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'DocumentDetailComponent',
}, },
}, },
{ {
@ -81,6 +90,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'DocumentDetailComponent',
}, },
}, },
{ {
@ -92,6 +102,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'DocumentAsnComponent',
}, },
}, },
{ {
@ -103,6 +114,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Tag, type: PermissionType.Tag,
}, },
componentName: 'TagListComponent',
}, },
}, },
{ {
@ -114,6 +126,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.DocumentType, type: PermissionType.DocumentType,
}, },
componentName: 'DocumentTypeListComponent',
}, },
}, },
{ {
@ -125,6 +138,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Correspondent, type: PermissionType.Correspondent,
}, },
componentName: 'CorrespondentListComponent',
}, },
}, },
{ {
@ -136,6 +150,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.StoragePath, type: PermissionType.StoragePath,
}, },
componentName: 'StoragePathListComponent',
}, },
}, },
{ {
@ -144,6 +159,7 @@ export const routes: Routes = [
canActivate: [PermissionsGuard], canActivate: [PermissionsGuard],
data: { data: {
requireAdmin: true, requireAdmin: true,
componentName: 'LogsComponent',
}, },
}, },
{ {
@ -155,6 +171,7 @@ export const routes: Routes = [
action: PermissionAction.Delete, action: PermissionAction.Delete,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'TrashComponent',
}, },
}, },
// redirect old paths // redirect old paths
@ -180,6 +197,7 @@ export const routes: Routes = [
action: PermissionAction.Change, action: PermissionAction.Change,
type: PermissionType.UISettings, type: PermissionType.UISettings,
}, },
componentName: 'SettingsComponent',
}, },
}, },
{ {
@ -192,6 +210,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.UISettings, type: PermissionType.UISettings,
}, },
componentName: 'SettingsComponent',
}, },
}, },
{ {
@ -203,6 +222,7 @@ export const routes: Routes = [
action: PermissionAction.Change, action: PermissionAction.Change,
type: PermissionType.AppConfig, type: PermissionType.AppConfig,
}, },
componentName: 'ConfigComponent',
}, },
}, },
{ {
@ -214,6 +234,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.PaperlessTask, type: PermissionType.PaperlessTask,
}, },
componentName: 'TasksComponent',
}, },
}, },
{ {
@ -225,6 +246,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.CustomField, type: PermissionType.CustomField,
}, },
componentName: 'CustomFieldsComponent',
}, },
}, },
{ {
@ -236,6 +258,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Workflow, type: PermissionType.Workflow,
}, },
componentName: 'WorkflowsComponent',
}, },
}, },
{ {
@ -247,6 +270,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.MailAccount, type: PermissionType.MailAccount,
}, },
componentName: 'MailComponent',
}, },
}, },
{ {
@ -258,6 +282,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.User, type: PermissionType.User,
}, },
componentName: 'UsersAndGroupsComponent',
}, },
}, },
{ {
@ -269,6 +294,7 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.SavedView, type: PermissionType.SavedView,
}, },
componentName: 'SavedViewsComponent',
}, },
}, },
], ],

View File

@ -29,7 +29,7 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url', url: 'test-url',
component: { name: 'TestComponent' }, data: { componentName: 'TestComponent' },
} as any) } as any)
) )
@ -41,13 +41,13 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-1', url: 'test-url-1',
component: { name: 'TestComponent' }, data: { componentName: 'TestComponent' },
} as any) } as any)
) )
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-2', url: 'test-url-2',
component: { name: 'TestComponent' }, data: { componentName: 'TestComponent' },
} as any) } as any)
) )
@ -59,13 +59,13 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-1', url: 'test-url-1',
component: { name: 'TestComponent1' }, data: { componentName: 'TestComponent1' },
} as any) } as any)
) )
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-2', url: 'test-url-2',
component: { name: 'TestComponent2' }, data: { componentName: 'TestComponent2' },
} as any) } as any)
) )
@ -76,13 +76,13 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-1', url: 'test-url-1',
component: { name: 'TestComponent' }, data: { componentName: 'TestComponent' },
} as any) } as any)
) )
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-2', url: 'test-url-2',
component: { name: 'TestComponent' }, data: { componentName: 'TestComponent' },
} as any) } as any)
) )
@ -93,7 +93,7 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url', url: 'test-url',
component: { name: 'TestComponent' }, data: { componentName: 'TestComponent' },
} as any) } as any)
) )

View File

@ -17,11 +17,11 @@ export class ComponentRouterService {
.subscribe((event: ActivationStart) => { .subscribe((event: ActivationStart) => {
if ( if (
this.componentHistory[this.componentHistory.length - 1] !== this.componentHistory[this.componentHistory.length - 1] !==
event.snapshot.component.name && event.snapshot.data.componentName &&
!EXCLUDE_COMPONENTS.includes(event.snapshot.component.name) !EXCLUDE_COMPONENTS.includes(event.snapshot.data.componentName)
) { ) {
this.history.push(event.snapshot.url.toString()) this.history.push(event.snapshot.url.toString())
this.componentHistory.push(event.snapshot.component.name) this.componentHistory.push(event.snapshot.data.componentName)
} else { } else {
// Update the URL of the current component in case the same component was loaded via a different URL // Update the URL of the current component in case the same component was loaded via a different URL
this.history[this.history.length - 1] = event.snapshot.url.toString() this.history[this.history.length - 1] = event.snapshot.url.toString()

View File

@ -61,7 +61,7 @@ def move_documents_and_create_thumbnails(apps, schema_editor):
), ),
) )
Path(settings.SCRATCH_DIR).mkdir(parents=True, exists_ok=True) Path(settings.SCRATCH_DIR).mkdir(parents=True, exist_ok=True)
for f in sorted(documents): for f in sorted(documents):
if not f.endswith("gpg"): if not f.endswith("gpg"):

View File

@ -43,6 +43,7 @@ from documents.models import CustomFieldInstance
from documents.models import Document from documents.models import Document
from documents.models import DocumentType from documents.models import DocumentType
from documents.models import MatchingModel from documents.models import MatchingModel
from documents.models import Note
from documents.models import PaperlessTask from documents.models import PaperlessTask
from documents.models import SavedView from documents.models import SavedView
from documents.models import SavedViewFilterRule from documents.models import SavedViewFilterRule
@ -861,6 +862,22 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer):
] ]
class BasicUserSerializer(serializers.ModelSerializer):
# Different than paperless.serializers.UserSerializer
class Meta:
model = User
fields = ["id", "username", "first_name", "last_name"]
class NotesSerializer(serializers.ModelSerializer):
user = BasicUserSerializer()
class Meta:
model = Note
fields = ["id", "note", "created", "user"]
ordering = ["-created"]
class DocumentSerializer( class DocumentSerializer(
OwnedObjectSerializer, OwnedObjectSerializer,
NestedUpdateMixin, NestedUpdateMixin,
@ -876,6 +893,8 @@ class DocumentSerializer(
created_date = serializers.DateField(required=False) created_date = serializers.DateField(required=False)
page_count = SerializerMethodField() page_count = SerializerMethodField()
notes = NotesSerializer(many=True, required=False)
custom_fields = CustomFieldInstanceSerializer( custom_fields = CustomFieldInstanceSerializer(
many=True, many=True,
allow_null=False, allow_null=False,

View File

@ -0,0 +1,18 @@
{% extends "paperless-ngx/base.html" %}
{% load i18n %}
{% block head_title %}
{% trans "Paperless-ngx account inactive" %}
{% endblock head_title %}
{% block form_top_content %}
<h4>{% translate "Account inactive." %}</h4>
{% endblock form_top_content %}
{% block form_content %}
{% url 'account_login' as login_url %}
<p>{% translate "This account is inactive." %}</p>
<div class="d-grid mt-3">
<a class="btn btn-lg btn-primary" href="{{ login_url }}">{% translate "Return to login" %}</a>
</div>
{% endblock form_content %}

View File

@ -2170,8 +2170,10 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
GIVEN: GIVEN:
- A document with a single note - A document with a single note
WHEN: WHEN:
- API request for document
- API request for document notes is made - API request for document notes is made
THEN: THEN:
- Note is included in the document response
- The associated note is returned - The associated note is returned
""" """
doc = Document.objects.create( doc = Document.objects.create(
@ -2185,6 +2187,18 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
user=self.user, user=self.user,
) )
response = self.client.get(
f"/api/documents/{doc.pk}/",
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
resp_data = response.json()
self.assertEqual(len(resp_data["notes"]), 1)
self.assertEqual(resp_data["notes"][0]["note"], note.note)
self.assertEqual(resp_data["notes"][0]["user"]["username"], self.user.username)
response = self.client.get( response = self.client.get(
f"/api/documents/{doc.pk}/notes/", f"/api/documents/{doc.pk}/notes/",
format="json", format="json",

View File

@ -4,6 +4,7 @@ from pathlib import Path
from django.conf import settings from django.conf import settings
from documents.tests.utils import DirectoriesMixin
from documents.tests.utils import TestMigrations from documents.tests.utils import TestMigrations
@ -14,7 +15,7 @@ def source_path_before(self):
return os.path.join(settings.ORIGINALS_DIR, fname) return os.path.join(settings.ORIGINALS_DIR, fname)
class TestMigrateDocumentPageCount(TestMigrations): class TestMigrateDocumentPageCount(DirectoriesMixin, TestMigrations):
migrate_from = "1052_document_transaction_id" migrate_from = "1052_document_transaction_id"
migrate_to = "1053_document_page_count" migrate_to = "1053_document_page_count"

View File

@ -803,33 +803,6 @@ class DocumentViewSet(
except (FileNotFoundError, Document.DoesNotExist): except (FileNotFoundError, Document.DoesNotExist):
raise Http404 raise Http404
def getNotes(self, doc):
return [
{
"id": c.pk,
"note": c.note,
"created": c.created,
"user": {
"id": c.user.id,
"username": c.user.username,
"first_name": c.user.first_name,
"last_name": c.user.last_name,
},
}
for c in Note.objects.select_related("user")
.only(
"pk",
"note",
"created",
"user__id",
"user__username",
"user__first_name",
"user__last_name",
)
.filter(document=doc)
.order_by("-created")
]
@action( @action(
methods=["get", "post", "delete"], methods=["get", "post", "delete"],
detail=True, detail=True,
@ -854,9 +827,11 @@ class DocumentViewSet(
except Document.DoesNotExist: except Document.DoesNotExist:
raise Http404 raise Http404
serializer = self.get_serializer(doc)
if request.method == "GET": if request.method == "GET":
try: try:
notes = self.getNotes(doc) notes = serializer.to_representation(doc).get("notes")
return Response(notes) return Response(notes)
except Exception as e: except Exception as e:
logger.warning(f"An error occurred retrieving notes: {e!s}") logger.warning(f"An error occurred retrieving notes: {e!s}")
@ -897,7 +872,7 @@ class DocumentViewSet(
index.add_or_update_document(doc) index.add_or_update_document(doc)
notes = self.getNotes(doc) notes = serializer.to_representation(doc).get("notes")
return Response(notes) return Response(notes)
except Exception as e: except Exception as e:
@ -934,7 +909,9 @@ class DocumentViewSet(
index.add_or_update_document(doc) index.add_or_update_document(doc)
return Response(self.getNotes(doc)) notes = serializer.to_representation(doc).get("notes")
return Response(notes)
return Response( return Response(
{ {

File diff suppressed because it is too large Load Diff

View File

@ -278,10 +278,15 @@ urlpatterns = [
include( include(
[ [
# see allauth/account/urls.py # see allauth/account/urls.py
# login, logout, signup # login, logout, signup, account_inactive
path("login/", allauth_account_views.login, name="account_login"), path("login/", allauth_account_views.login, name="account_login"),
path("logout/", allauth_account_views.logout, name="account_logout"), path("logout/", allauth_account_views.logout, name="account_logout"),
path("signup/", allauth_account_views.signup, name="account_signup"), path("signup/", allauth_account_views.signup, name="account_signup"),
path(
"account_inactive/",
allauth_account_views.account_inactive,
name="account_inactive",
),
# password reset # password reset
path( path(
"password/", "password/",

View File

@ -32,6 +32,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from documents.index import DelayedQuery
from documents.permissions import PaperlessObjectPermissions from documents.permissions import PaperlessObjectPermissions
from paperless.filters import GroupFilterSet from paperless.filters import GroupFilterSet
from paperless.filters import UserFilterSet from paperless.filters import UserFilterSet
@ -66,17 +67,17 @@ class StandardPagination(PageNumberPagination):
) )
def get_all_result_ids(self): def get_all_result_ids(self):
ids = [] query = self.page.paginator.object_list
if hasattr(self.page.paginator.object_list, "saved_results"): if isinstance(query, DelayedQuery):
results_page = self.page.paginator.object_list.saved_results[0] try:
if results_page is not None: ids = [
for i in range(len(results_page.results.docs())): query.searcher.ixreader.stored_fields(
try: doc_num,
fields = results_page.results.fields(i) )["id"]
if "id" in fields: for doc_num in query.saved_results.get(0).results.docs()
ids.append(fields["id"]) ]
except Exception: except Exception:
pass pass
else: else:
ids = self.page.paginator.object_list.values_list("pk", flat=True) ids = self.page.paginator.object_list.values_list("pk", flat=True)
return ids return ids

58
uv.lock generated
View File

@ -880,14 +880,14 @@ wheels = [
[[package]] [[package]]
name = "drf-spectacular-sidecar" name = "drf-spectacular-sidecar"
version = "2025.2.1" version = "2025.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/7e/4c/6c945509d578f04d19eee2ffeb25bc37e6aeb9713688a87fc4f3b02bc32b/drf_spectacular_sidecar-2025.2.1.tar.gz", hash = "sha256:ca9507c5fe708680d6b8eda96928f3cf3ffc07e8d67678778bcd2e80f9f2baae", size = 2390256 } sdist = { url = "https://files.pythonhosted.org/packages/80/5c/85671ba50957e0ab45e2828ca713cd7b5d0d237c398936c1b45bd0286b72/drf_spectacular_sidecar-2025.3.1.tar.gz", hash = "sha256:7425940a409fb68a46c9b024eb504098fab5b4d73d12573efcaef048445d678f", size = 2401986 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/eb/8a/b66f58e92c5752bbc241bb04ce76d2bf92c676af4cb8b94edac3a0e0c701/drf_spectacular_sidecar-2025.2.1-py3-none-any.whl", hash = "sha256:674e1336810c7cf117442300b5c213c4fee1e984ba48689502c947a6ecd25d0c", size = 2409274 }, { url = "https://files.pythonhosted.org/packages/5e/09/7382eb93a09f33fac72a8af1fc986f43681d15dd5fda750b238a8aace0e8/drf_spectacular_sidecar-2025.3.1-py3-none-any.whl", hash = "sha256:6b9c96204c8e45a06c39928dad704b18536d3253b61591c478540b63db6bdcea", size = 2422301 },
] ]
[[package]] [[package]]
@ -1803,7 +1803,7 @@ wheels = [
[[package]] [[package]]
name = "ocrmypdf" name = "ocrmypdf"
version = "16.9.0" version = "16.10.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "deprecation", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "deprecation", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
@ -1816,9 +1816,9 @@ dependencies = [
{ name = "pluggy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "pluggy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/a8/07/d8810415b98718a39b1161720acd925b6ef15743227012809f6da9d3b7bc/ocrmypdf-16.9.0.tar.gz", hash = "sha256:d000a2294cd1478d4bbfe15df5172327f77f4139bb5307404bc53be9bd81f039", size = 6804849 } sdist = { url = "https://files.pythonhosted.org/packages/07/cf/d207aea8442a8e5a63b16faae89af2b9e3474d8d5925a5ea8c4f10f73fa9/ocrmypdf-16.10.0.tar.gz", hash = "sha256:d5b907a7b92951f1f3617f0f5ca002d866143d94fd168546a70e51756bf6412e", size = 6809110 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/12/69/e40431c5410eaf0c55588d0d256e27ee32af7622cb7116ac99a2e868bd2f/ocrmypdf-16.9.0-py3-none-any.whl", hash = "sha256:33fec95450727b0d9482ee3851e45dd0219ff8d52a14fd45a8d3d0c71875584e", size = 161863 }, { url = "https://files.pythonhosted.org/packages/33/47/10058f54f593f5e618a6796fc3e8dc3e19536128f832e2d3d6e4943e9834/ocrmypdf-16.10.0-py3-none-any.whl", hash = "sha256:5093b9b058e7278b17c0b0978eb5175063b7a5511e3b9068257ece063d91ce8f", size = 162336 },
] ]
[[package]] [[package]]
@ -1904,9 +1904,9 @@ mariadb = [
] ]
postgres = [ postgres = [
{ name = "psycopg", extra = ["c"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "psycopg", extra = ["c"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "psycopg-c", version = "3.2.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version != '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version != '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or sys_platform == 'darwin'" }, { name = "psycopg-c", version = "3.2.5", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version != '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version != '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or sys_platform == 'darwin'" },
{ name = "psycopg-c", version = "3.2.4", source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl" }, marker = "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'" }, { name = "psycopg-c", version = "3.2.5", source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl" }, marker = "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'" },
{ name = "psycopg-c", version = "3.2.4", source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl" }, marker = "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "psycopg-c", version = "3.2.5", source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl" }, marker = "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'" },
] ]
webserver = [ webserver = [
{ name = "granian", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "granian", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
@ -1994,7 +1994,7 @@ requires-dist = [
{ name = "djangorestframework", specifier = "~=3.15" }, { name = "djangorestframework", specifier = "~=3.15" },
{ name = "djangorestframework-guardian", specifier = "~=0.3.0" }, { name = "djangorestframework-guardian", specifier = "~=0.3.0" },
{ name = "drf-spectacular", specifier = "~=0.28" }, { name = "drf-spectacular", specifier = "~=0.28" },
{ name = "drf-spectacular-sidecar", specifier = "~=2025.2.1" }, { name = "drf-spectacular-sidecar", specifier = "~=2025.3.1" },
{ name = "drf-writable-nested", specifier = "~=0.7.1" }, { name = "drf-writable-nested", specifier = "~=0.7.1" },
{ name = "filelock", specifier = "~=3.17.0" }, { name = "filelock", specifier = "~=3.17.0" },
{ name = "flower", specifier = "~=2.0.1" }, { name = "flower", specifier = "~=2.0.1" },
@ -2007,13 +2007,13 @@ requires-dist = [
{ name = "langdetect", specifier = "~=1.0.9" }, { name = "langdetect", specifier = "~=1.0.9" },
{ name = "mysqlclient", marker = "extra == 'mariadb'", specifier = "~=2.2.7" }, { name = "mysqlclient", marker = "extra == 'mariadb'", specifier = "~=2.2.7" },
{ name = "nltk", specifier = "~=3.9.1" }, { name = "nltk", specifier = "~=3.9.1" },
{ name = "ocrmypdf", specifier = "~=16.9.0" }, { name = "ocrmypdf", specifier = "~=16.10.0" },
{ name = "pathvalidate", specifier = "~=3.2.3" }, { name = "pathvalidate", specifier = "~=3.2.3" },
{ name = "pdf2image", specifier = "~=1.17.0" }, { name = "pdf2image", specifier = "~=1.17.0" },
{ name = "psycopg", extras = ["c"], marker = "extra == 'postgres'", specifier = "==3.2.4" }, { name = "psycopg", extras = ["c"], marker = "extra == 'postgres'", specifier = "==3.2.5" },
{ name = "psycopg-c", marker = "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'postgres'", url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl" }, { name = "psycopg-c", marker = "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'postgres'", url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl" },
{ name = "psycopg-c", marker = "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'postgres'", url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl" }, { name = "psycopg-c", marker = "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'postgres'", url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl" },
{ name = "psycopg-c", marker = "(python_full_version != '3.12.*' and platform_machine == 'aarch64' and extra == 'postgres') or (python_full_version != '3.12.*' and platform_machine == 'x86_64' and extra == 'postgres') or (platform_machine != 'aarch64' and platform_machine != 'x86_64' and extra == 'postgres') or (sys_platform != 'linux' and extra == 'postgres')", specifier = "==3.2.4" }, { name = "psycopg-c", marker = "(python_full_version != '3.12.*' and platform_machine == 'aarch64' and extra == 'postgres') or (python_full_version != '3.12.*' and platform_machine == 'x86_64' and extra == 'postgres') or (platform_machine != 'aarch64' and platform_machine != 'x86_64' and extra == 'postgres') or (sys_platform != 'linux' and extra == 'postgres')", specifier = "==3.2.5" },
{ name = "python-dateutil", specifier = "~=2.9.0" }, { name = "python-dateutil", specifier = "~=2.9.0" },
{ name = "python-dotenv", specifier = "~=1.0.1" }, { name = "python-dotenv", specifier = "~=1.0.1" },
{ name = "python-gnupg", specifier = "~=0.5.4" }, { name = "python-gnupg", specifier = "~=0.5.4" },
@ -2348,53 +2348,53 @@ wheels = [
[[package]] [[package]]
name = "psycopg" name = "psycopg"
version = "3.2.4" version = "3.2.5"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux')" }, { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux')" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/e0/f2/954b1467b3e2ca5945b83b5e320268be1f4df486c3e8ffc90f4e4b707979/psycopg-3.2.4.tar.gz", hash = "sha256:f26f1346d6bf1ef5f5ef1714dd405c67fb365cfd1c6cea07de1792747b167b92", size = 156109 } sdist = { url = "https://files.pythonhosted.org/packages/0e/cf/dc1a4d45e3c6222fe272a245c5cea9a969a7157639da606ac7f2ab5de3a1/psycopg-3.2.5.tar.gz", hash = "sha256:f5f750611c67cb200e85b408882f29265c66d1de7f813add4f8125978bfd70e8", size = 156158 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/40/49/15114d5f7ee68983f4e1a24d47e75334568960352a07c6f0e796e912685d/psycopg-3.2.4-py3-none-any.whl", hash = "sha256:43665368ccd48180744cab26b74332f46b63b7e06e8ce0775547a3533883d381", size = 198716 }, { url = "https://files.pythonhosted.org/packages/18/f3/14a1370b1449ca875d5e353ef02cb9db6b70bd46ec361c236176837c0be1/psycopg-3.2.5-py3-none-any.whl", hash = "sha256:b782130983e5b3de30b4c529623d3687033b4dafa05bb661fc6bf45837ca5879", size = 198749 },
] ]
[package.optional-dependencies] [package.optional-dependencies]
c = [ c = [
{ name = "psycopg-c", version = "3.2.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version != '3.12.*' and implementation_name != 'pypy' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version != '3.12.*' and implementation_name != 'pypy' and platform_machine == 'x86_64' and sys_platform == 'linux') or (implementation_name != 'pypy' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (implementation_name != 'pypy' and sys_platform == 'darwin')" }, { name = "psycopg-c", version = "3.2.5", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version != '3.12.*' and implementation_name != 'pypy' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version != '3.12.*' and implementation_name != 'pypy' and platform_machine == 'x86_64' and sys_platform == 'linux') or (implementation_name != 'pypy' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (implementation_name != 'pypy' and sys_platform == 'darwin')" },
{ name = "psycopg-c", version = "3.2.4", source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl" }, marker = "python_full_version == '3.12.*' and implementation_name != 'pypy' and platform_machine == 'aarch64' and sys_platform == 'linux'" }, { name = "psycopg-c", version = "3.2.5", source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl" }, marker = "python_full_version == '3.12.*' and implementation_name != 'pypy' and platform_machine == 'aarch64' and sys_platform == 'linux'" },
{ name = "psycopg-c", version = "3.2.4", source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl" }, marker = "python_full_version == '3.12.*' and implementation_name != 'pypy' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "psycopg-c", version = "3.2.5", source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl" }, marker = "python_full_version == '3.12.*' and implementation_name != 'pypy' and platform_machine == 'x86_64' and sys_platform == 'linux'" },
] ]
[[package]] [[package]]
name = "psycopg-c" name = "psycopg-c"
version = "3.2.4" version = "3.2.5"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
resolution-markers = [ resolution-markers = [
"sys_platform == 'darwin'", "sys_platform == 'darwin'",
"(python_full_version != '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version != '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux')", "(python_full_version != '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version != '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux')",
] ]
sdist = { url = "https://files.pythonhosted.org/packages/17/76/dbdadd9b93b8ad38cff31402c73a6bb9a23c88a4466fa09655d3c6db4d11/psycopg_c-3.2.4.tar.gz", hash = "sha256:22097a04263efb2efd2cc8b00a51fa90e23f9cd4a2e09903fe4d9c6923dac17a", size = 601853 } sdist = { url = "https://files.pythonhosted.org/packages/cf/cb/468dcca82f08b47af59af4681ef39473cf5c0ef2e09775c701ccdf7284d6/psycopg_c-3.2.5.tar.gz", hash = "sha256:57ad4cfd28de278c424aaceb1f2ad5c7910466e315dfe84e403f3c7a0a2ce81b", size = 609318 }
[[package]] [[package]]
name = "psycopg-c" name = "psycopg-c"
version = "3.2.4" version = "3.2.5"
source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl" } source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl" }
resolution-markers = [ resolution-markers = [
"python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
] ]
wheels = [ wheels = [
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl", hash = "sha256:7a3b1152b676afe4a9113f784257221cf85147812116de86970272553a846f40" }, { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl", hash = "sha256:39012b8df2ef34e172d43ab5976017d054f2c2fc549854927a62e73f5253eacc" },
] ]
[[package]] [[package]]
name = "psycopg-c" name = "psycopg-c"
version = "3.2.4" version = "3.2.5"
source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl" } source = { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl" }
resolution-markers = [ resolution-markers = [
"python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
] ]
wheels = [ wheels = [
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl", hash = "sha256:d0c0c87699846bd7f8c9259db0d17f0c9d062f3281d07b32be00ca28cdcbd5f3" }, { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl", hash = "sha256:a0667a62595e355c2d3b6ac05336403c998fbfb31cf6922d73e19018016df1bc" },
] ]
[[package]] [[package]]