mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Compare commits
	
		
			14 Commits
		
	
	
		
			955ff32dcd
			...
			f4413e0a08
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | f4413e0a08 | ||
|   | 169aa8c8bd | ||
|   | 94556a2607 | ||
|   | dcd50d5359 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 376823598e | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 54bcbfa546 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 5421e54cb0 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 032bada221 | ||
|   | 670ee6c5b0 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 309fb199f2 | ||
|   | 79328b1cec | ||
|   | 5570d20625 | ||
|   | ba2cb1dec8 | ||
|   | 4d15544a3e | 
							
								
								
									
										47
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| @@ -89,3 +89,50 @@ updates: | ||||
|           - "major" | ||||
|           - "minor" | ||||
|           - "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*" | ||||
|   | ||||
| @@ -30,7 +30,7 @@ RUN set -eux \ | ||||
| # Purpose: Installs s6-overlay and rootfs | ||||
| # Comments: | ||||
| #  - 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 | ||||
|  | ||||
|   | ||||
| @@ -38,7 +38,7 @@ services: | ||||
|       - redisdata:/data | ||||
|  | ||||
|   db: | ||||
|     image: docker.io/library/postgres:16 | ||||
|     image: docker.io/library/postgres:17 | ||||
|     restart: unless-stopped | ||||
|     volumes: | ||||
|       - pgdata:/var/lib/postgresql/data | ||||
|   | ||||
| @@ -38,7 +38,7 @@ services: | ||||
|       - redisdata:/data | ||||
|  | ||||
|   db: | ||||
|     image: docker.io/library/postgres:16 | ||||
|     image: docker.io/library/postgres:17 | ||||
|     restart: unless-stopped | ||||
|     volumes: | ||||
|       - pgdata:/var/lib/postgresql/data | ||||
|   | ||||
| @@ -34,7 +34,7 @@ services: | ||||
|       - redisdata:/data | ||||
|  | ||||
|   db: | ||||
|     image: docker.io/library/postgres:16 | ||||
|     image: docker.io/library/postgres:17 | ||||
|     restart: unless-stopped | ||||
|     volumes: | ||||
|       - pgdata:/var/lib/postgresql/data | ||||
|   | ||||
| @@ -37,7 +37,7 @@ dependencies = [ | ||||
|   "djangorestframework~=3.15", | ||||
|   "djangorestframework-guardian~=0.3.0", | ||||
|   "drf-spectacular~=0.28", | ||||
|   "drf-spectacular-sidecar~=2025.2.1", | ||||
|   "drf-spectacular-sidecar~=2025.3.1", | ||||
|   "drf-writable-nested~=0.7.1", | ||||
|   "filelock~=3.17.0", | ||||
|   "flower~=2.0.1", | ||||
| @@ -48,7 +48,7 @@ dependencies = [ | ||||
|   "jinja2~=3.1.5", | ||||
|   "langdetect~=1.0.9", | ||||
|   "nltk~=3.9.1", | ||||
|   "ocrmypdf~=16.9.0", | ||||
|   "ocrmypdf~=16.10.0", | ||||
|   "pathvalidate~=3.2.3", | ||||
|   "pdf2image~=1.17.0", | ||||
|   "python-dateutil~=2.9.0", | ||||
| @@ -73,9 +73,9 @@ optional-dependencies.mariadb = [ | ||||
|   "mysqlclient~=2.2.7", | ||||
| ] | ||||
| optional-dependencies.postgres = [ | ||||
|   "psycopg[c]==3.2.4", | ||||
|   "psycopg[c]==3.2.5", | ||||
|   # Direct dependency for proper resolution of the pre-built wheels | ||||
|   "psycopg-c==3.2.4", | ||||
|   "psycopg-c==3.2.5", | ||||
| ] | ||||
| optional-dependencies.webserver = [ | ||||
|   "granian~=1.7.6", | ||||
| @@ -343,8 +343,8 @@ environments = [ | ||||
| [tool.uv.sources] | ||||
| # Markers are chosen to select these almost exclusively when building the Docker image | ||||
| 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.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_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_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" }, | ||||
| ] | ||||
| 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'" }, | ||||
|   | ||||
| @@ -6185,32 +6185,39 @@ | ||||
|           <context context-type="linenumber">43</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4524189418954028091" datatype="html"> | ||||
|         <source>Hint: saved views can be created from the <x id="START_LINK" ctype="x-a" equiv-text="<a routerLink="/documents">"/>documents list<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.html</context> | ||||
|           <context context-type="linenumber">42</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6581372518205328477" datatype="html"> | ||||
|         <source>Hello <x id="PH" equiv-text="this.settingsService.displayName"/>, welcome to <x id="PH_1" equiv-text="environment.appTitle"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> | ||||
|           <context context-type="linenumber">57</context> | ||||
|           <context context-type="linenumber">61</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2901300640157872718" datatype="html"> | ||||
|         <source>Welcome to <x id="PH" equiv-text="environment.appTitle"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> | ||||
|           <context context-type="linenumber">59</context> | ||||
|           <context context-type="linenumber">63</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1325877348738783391" datatype="html"> | ||||
|         <source>Dashboard updated</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> | ||||
|           <context context-type="linenumber">90</context> | ||||
|           <context context-type="linenumber">94</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3214475953924351473" datatype="html"> | ||||
|         <source>Error updating dashboard</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> | ||||
|           <context context-type="linenumber">93</context> | ||||
|           <context context-type="linenumber">97</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2946624699882754313" datatype="html"> | ||||
|   | ||||
| @@ -34,6 +34,17 @@ | ||||
|       } | ||||
|  | ||||
|       <ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }"> | ||||
|         @if (!settingsService.offerTour() && savedViewService.allViews.length === 0) { | ||||
|           <div class="col"> | ||||
|             <div class="card shadow-sm bg-light opacity-50"> | ||||
|               <div class="card-body"> | ||||
|                 <div class="text-center"> | ||||
|                   <p class="mb-0 fst-italic"><i-bs name="info-circle" class="me-2"></i-bs><ng-container i18n>Hint: saved views can be created from the <a routerLink="/documents">documents list</a></ng-container></p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         } | ||||
|         @for (v of dashboardViews; track v.id) { | ||||
|           <div class="col"> | ||||
|             <pngx-saved-view-widget | ||||
|   | ||||
| @@ -105,6 +105,7 @@ describe('DashboardComponent', () => { | ||||
|                 results: saved_views, | ||||
|               }), | ||||
|             dashboardViews: saved_views.filter((v) => v.show_on_dashboard), | ||||
|             allViews: saved_views, | ||||
|           }, | ||||
|         }, | ||||
|         provideHttpClient(withInterceptorsFromDi()), | ||||
|   | ||||
| @@ -6,6 +6,8 @@ import { | ||||
|   moveItemInArray, | ||||
| } from '@angular/cdk/drag-drop' | ||||
| import { Component } from '@angular/core' | ||||
| import { RouterModule } from '@angular/router' | ||||
| import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' | ||||
| import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap' | ||||
| import { SavedView } from 'src/app/data/saved-view' | ||||
| import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' | ||||
| @@ -35,6 +37,8 @@ import { WelcomeWidgetComponent } from './widgets/welcome-widget/welcome-widget. | ||||
|     IfPermissionsDirective, | ||||
|     DragDropModule, | ||||
|     TourNgBootstrapModule, | ||||
|     NgxBootstrapIconsModule, | ||||
|     RouterModule, | ||||
|   ], | ||||
| }) | ||||
| export class DashboardComponent extends ComponentWithPermissions { | ||||
|   | ||||
| @@ -45,7 +45,8 @@ describe('CustomDatePipe', () => { | ||||
|     if (now.getMonth() === 0) { | ||||
|       notNow.setFullYear(now.getFullYear() - 1) | ||||
|     } | ||||
|     expect(['Last month', '4 weeks ago']).toContain( | ||||
|     // weird options are for february... | ||||
|     expect(['Last month', '4 weeks ago', '3 weeks ago']).toContain( | ||||
|       datePipe.transform(notNow, 'relative') | ||||
|     ) | ||||
|     expect(datePipe.transform(now, 'relative')).toEqual('Just now') | ||||
|   | ||||
| @@ -294,9 +294,9 @@ class Command(BaseCommand): | ||||
|         inotify = INotify() | ||||
|         inotify_flags = flags.CLOSE_WRITE | flags.MOVED_TO | flags.MODIFY | ||||
|         if recursive: | ||||
|             descriptor = inotify.add_watch_recursive(directory, inotify_flags) | ||||
|             inotify.add_watch_recursive(directory, inotify_flags) | ||||
|         else: | ||||
|             descriptor = inotify.add_watch(directory, inotify_flags) | ||||
|             inotify.add_watch(directory, inotify_flags) | ||||
|  | ||||
|         inotify_debounce_secs: Final[float] = settings.CONSUMER_INOTIFY_DELAY | ||||
|         inotify_debounce_ms: Final[int] = inotify_debounce_secs * 1000 | ||||
| @@ -305,55 +305,55 @@ class Command(BaseCommand): | ||||
|  | ||||
|         notified_files = {} | ||||
|  | ||||
|         while not finished: | ||||
|             try: | ||||
|                 for event in inotify.read(timeout=timeout_ms): | ||||
|                     path = inotify.get_path(event.wd) if recursive else directory | ||||
|                     filepath = os.path.join(path, event.name) | ||||
|                     if flags.MODIFY in flags.from_mask(event.mask): | ||||
|                         notified_files.pop(filepath, None) | ||||
|         try: | ||||
|             while not finished: | ||||
|                 try: | ||||
|                     for event in inotify.read(timeout=timeout_ms): | ||||
|                         path = inotify.get_path(event.wd) if recursive else directory | ||||
|                         filepath = os.path.join(path, event.name) | ||||
|                         if flags.MODIFY in flags.from_mask(event.mask): | ||||
|                             notified_files.pop(filepath, None) | ||||
|                         else: | ||||
|                             notified_files[filepath] = monotonic() | ||||
|  | ||||
|                     # Check the files against the timeout | ||||
|                     still_waiting = {} | ||||
|                     # last_event_time is time of the last inotify event for this file | ||||
|                     for filepath, last_event_time in notified_files.items(): | ||||
|                         # Current time - last time over the configured timeout | ||||
|                         waited_long_enough = ( | ||||
|                             monotonic() - last_event_time | ||||
|                         ) > inotify_debounce_secs | ||||
|  | ||||
|                         # Also make sure the file exists still, some scanners might write a | ||||
|                         # temporary file first | ||||
|                         file_still_exists = os.path.exists(filepath) and os.path.isfile( | ||||
|                             filepath, | ||||
|                         ) | ||||
|  | ||||
|                         if waited_long_enough and file_still_exists: | ||||
|                             _consume(filepath) | ||||
|                         elif file_still_exists: | ||||
|                             still_waiting[filepath] = last_event_time | ||||
|  | ||||
|                     # These files are still waiting to hit the timeout | ||||
|                     notified_files = still_waiting | ||||
|  | ||||
|                     # If files are waiting, need to exit read() to check them | ||||
|                     # Otherwise, go back to infinite sleep time, but only if not testing | ||||
|                     if len(notified_files) > 0: | ||||
|                         timeout_ms = inotify_debounce_ms | ||||
|                     elif is_testing: | ||||
|                         timeout_ms = self.testing_timeout_ms | ||||
|                     else: | ||||
|                         notified_files[filepath] = monotonic() | ||||
|                         timeout_ms = None | ||||
|  | ||||
|                 # Check the files against the timeout | ||||
|                 still_waiting = {} | ||||
|                 # last_event_time is time of the last inotify event for this file | ||||
|                 for filepath, last_event_time in notified_files.items(): | ||||
|                     # Current time - last time over the configured timeout | ||||
|                     waited_long_enough = ( | ||||
|                         monotonic() - last_event_time | ||||
|                     ) > inotify_debounce_secs | ||||
|                     if self.stop_flag.is_set(): | ||||
|                         logger.debug("Finishing because event is set") | ||||
|                         finished = True | ||||
|  | ||||
|                     # Also make sure the file exists still, some scanners might write a | ||||
|                     # temporary file first | ||||
|                     file_still_exists = os.path.exists(filepath) and os.path.isfile( | ||||
|                         filepath, | ||||
|                     ) | ||||
|  | ||||
|                     if waited_long_enough and file_still_exists: | ||||
|                         _consume(filepath) | ||||
|                     elif file_still_exists: | ||||
|                         still_waiting[filepath] = last_event_time | ||||
|  | ||||
|                 # These files are still waiting to hit the timeout | ||||
|                 notified_files = still_waiting | ||||
|  | ||||
|                 # If files are waiting, need to exit read() to check them | ||||
|                 # Otherwise, go back to infinite sleep time, but only if not testing | ||||
|                 if len(notified_files) > 0: | ||||
|                     timeout_ms = inotify_debounce_ms | ||||
|                 elif is_testing: | ||||
|                     timeout_ms = self.testing_timeout_ms | ||||
|                 else: | ||||
|                     timeout_ms = None | ||||
|  | ||||
|                 if self.stop_flag.is_set(): | ||||
|                     logger.debug("Finishing because event is set") | ||||
|                 except KeyboardInterrupt: | ||||
|                     logger.info("Received SIGINT, stopping inotify") | ||||
|                     finished = True | ||||
|  | ||||
|             except KeyboardInterrupt: | ||||
|                 logger.info("Received SIGINT, stopping inotify") | ||||
|                 finished = True | ||||
|  | ||||
|         inotify.rm_watch(descriptor) | ||||
|         inotify.close() | ||||
|         finally: | ||||
|             inotify.close() | ||||
|   | ||||
| @@ -43,6 +43,7 @@ from documents.models import CustomFieldInstance | ||||
| from documents.models import Document | ||||
| from documents.models import DocumentType | ||||
| from documents.models import MatchingModel | ||||
| from documents.models import Note | ||||
| from documents.models import PaperlessTask | ||||
| from documents.models import SavedView | ||||
| 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( | ||||
|     OwnedObjectSerializer, | ||||
|     NestedUpdateMixin, | ||||
| @@ -876,6 +893,8 @@ class DocumentSerializer( | ||||
|     created_date = serializers.DateField(required=False) | ||||
|     page_count = SerializerMethodField() | ||||
|  | ||||
|     notes = NotesSerializer(many=True, required=False) | ||||
|  | ||||
|     custom_fields = CustomFieldInstanceSerializer( | ||||
|         many=True, | ||||
|         allow_null=False, | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| {% endblock head_title %} | ||||
|  | ||||
| {% block form_top_content %} | ||||
|     <h4>{% translate "Account inactve." %}</h4> | ||||
|     <h4>{% translate "Account inactive." %}</h4> | ||||
| {% endblock form_top_content %} | ||||
|  | ||||
| {% block form_content %} | ||||
|   | ||||
| @@ -2170,8 +2170,10 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): | ||||
|         GIVEN: | ||||
|             - A document with a single note | ||||
|         WHEN: | ||||
|             - API request for document | ||||
|             - API request for document notes is made | ||||
|         THEN: | ||||
|             - Note is included in the document response | ||||
|             - The associated note is returned | ||||
|         """ | ||||
|         doc = Document.objects.create( | ||||
| @@ -2185,6 +2187,18 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): | ||||
|             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( | ||||
|             f"/api/documents/{doc.pk}/notes/", | ||||
|             format="json", | ||||
|   | ||||
| @@ -4,6 +4,7 @@ from pathlib import Path | ||||
|  | ||||
| from django.conf import settings | ||||
|  | ||||
| from documents.tests.utils import DirectoriesMixin | ||||
| from documents.tests.utils import TestMigrations | ||||
|  | ||||
|  | ||||
| @@ -14,7 +15,7 @@ def source_path_before(self): | ||||
|     return os.path.join(settings.ORIGINALS_DIR, fname) | ||||
|  | ||||
|  | ||||
| class TestMigrateDocumentPageCount(TestMigrations): | ||||
| class TestMigrateDocumentPageCount(DirectoriesMixin, TestMigrations): | ||||
|     migrate_from = "1052_document_transaction_id" | ||||
|     migrate_to = "1053_document_page_count" | ||||
|  | ||||
|   | ||||
| @@ -803,33 +803,6 @@ class DocumentViewSet( | ||||
|         except (FileNotFoundError, Document.DoesNotExist): | ||||
|             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( | ||||
|         methods=["get", "post", "delete"], | ||||
|         detail=True, | ||||
| @@ -854,9 +827,11 @@ class DocumentViewSet( | ||||
|         except Document.DoesNotExist: | ||||
|             raise Http404 | ||||
|  | ||||
|         serializer = self.get_serializer(doc) | ||||
|  | ||||
|         if request.method == "GET": | ||||
|             try: | ||||
|                 notes = self.getNotes(doc) | ||||
|                 notes = serializer.to_representation(doc).get("notes") | ||||
|                 return Response(notes) | ||||
|             except Exception as e: | ||||
|                 logger.warning(f"An error occurred retrieving notes: {e!s}") | ||||
| @@ -897,7 +872,7 @@ class DocumentViewSet( | ||||
|  | ||||
|                 index.add_or_update_document(doc) | ||||
|  | ||||
|                 notes = self.getNotes(doc) | ||||
|                 notes = serializer.to_representation(doc).get("notes") | ||||
|  | ||||
|                 return Response(notes) | ||||
|             except Exception as e: | ||||
| @@ -934,7 +909,9 @@ class DocumentViewSet( | ||||
|  | ||||
|             index.add_or_update_document(doc) | ||||
|  | ||||
|             return Response(self.getNotes(doc)) | ||||
|             notes = serializer.to_representation(doc).get("notes") | ||||
|  | ||||
|             return Response(notes) | ||||
|  | ||||
|         return Response( | ||||
|             { | ||||
|   | ||||
| @@ -1199,7 +1199,7 @@ msgid "Paperless-ngx account inactive" | ||||
| msgstr "" | ||||
|  | ||||
| #: documents/templates/account/account_inactive.html:9 | ||||
| msgid "Account inactve." | ||||
| msgid "Account inactive." | ||||
| msgstr "" | ||||
|  | ||||
| #: documents/templates/account/account_inactive.html:14 | ||||
|   | ||||
							
								
								
									
										58
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										58
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							| @@ -880,14 +880,14 @@ wheels = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "drf-spectacular-sidecar" | ||||
| version = "2025.2.1" | ||||
| version = "2025.3.1" | ||||
| source = { registry = "https://pypi.org/simple" } | ||||
| dependencies = [ | ||||
|     { 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 = [ | ||||
|     { 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]] | ||||
| @@ -1803,7 +1803,7 @@ wheels = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "ocrmypdf" | ||||
| version = "16.9.0" | ||||
| version = "16.10.0" | ||||
| source = { registry = "https://pypi.org/simple" } | ||||
| dependencies = [ | ||||
|     { 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 = "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 = [ | ||||
|     { 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]] | ||||
| @@ -1904,9 +1904,9 @@ mariadb = [ | ||||
| ] | ||||
| postgres = [ | ||||
|     { 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.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.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 = { 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 = { 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.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 = [ | ||||
|     { name = "granian", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, | ||||
| @@ -1994,7 +1994,7 @@ requires-dist = [ | ||||
|     { name = "djangorestframework", specifier = "~=3.15" }, | ||||
|     { name = "djangorestframework-guardian", specifier = "~=0.3.0" }, | ||||
|     { 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 = "filelock", specifier = "~=3.17.0" }, | ||||
|     { name = "flower", specifier = "~=2.0.1" }, | ||||
| @@ -2007,13 +2007,13 @@ requires-dist = [ | ||||
|     { name = "langdetect", specifier = "~=1.0.9" }, | ||||
|     { name = "mysqlclient", marker = "extra == 'mariadb'", specifier = "~=2.2.7" }, | ||||
|     { 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 = "pdf2image", specifier = "~=1.17.0" }, | ||||
|     { name = "psycopg", extras = ["c"], marker = "extra == 'postgres'", specifier = "==3.2.4" }, | ||||
|     { 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 == '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 == '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", 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.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.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.5" }, | ||||
|     { name = "python-dateutil", specifier = "~=2.9.0" }, | ||||
|     { name = "python-dotenv", specifier = "~=1.0.1" }, | ||||
|     { name = "python-gnupg", specifier = "~=0.5.4" }, | ||||
| @@ -2348,53 +2348,53 @@ wheels = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "psycopg" | ||||
| version = "3.2.4" | ||||
| version = "3.2.5" | ||||
| source = { registry = "https://pypi.org/simple" } | ||||
| dependencies = [ | ||||
|     { 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 = [ | ||||
|     { 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] | ||||
| 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.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.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 = { 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 = { 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.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]] | ||||
| name = "psycopg-c" | ||||
| version = "3.2.4" | ||||
| version = "3.2.5" | ||||
| source = { registry = "https://pypi.org/simple" } | ||||
| resolution-markers = [ | ||||
|     "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')", | ||||
| ] | ||||
| 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]] | ||||
| 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" } | ||||
| 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" } | ||||
| resolution-markers = [ | ||||
|     "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", | ||||
| ] | ||||
| 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]] | ||||
| 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" } | ||||
| 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" } | ||||
| resolution-markers = [ | ||||
|     "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux'", | ||||
| ] | ||||
| 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]] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user