mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Compare commits
	
		
			17 Commits
		
	
	
		
			b9e34bd793
			...
			3825023337
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3825023337 | ||
|   | b2703b4605 | ||
|   | 852eb0ef36 | ||
|   | 0870d42eae | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | e2cf95f8af | ||
|   | a79c8dc51c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 4b95c2f0e5 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | e1c8cd779b | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | cc7c7f31ba | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 1d30ce2afa | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 5aa86f8755 | ||
|   | de2ddad5ee | ||
|   | d2064a2535 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | cc621cf729 | ||
|   | fc4134e15c | ||
|   | ac1b420966 | ||
|   | 80595899c1 | 
| @@ -3,7 +3,7 @@ | |||||||
|     "dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml", |     "dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml", | ||||||
|     "service": "paperless-development", |     "service": "paperless-development", | ||||||
|     "workspaceFolder": "/usr/src/paperless/paperless-ngx", |     "workspaceFolder": "/usr/src/paperless/paperless-ngx", | ||||||
|     "postCreateCommand": "/bin/bash -c 'uv sync --group dev && uv run pre-commit install'", |     "postCreateCommand": "/bin/bash -c 'rm -rf .venv/.*  && uv sync --group dev && uv run pre-commit install'", | ||||||
|     "customizations": { |     "customizations": { | ||||||
|         "vscode": { |         "vscode": { | ||||||
|           "extensions": [ |           "extensions": [ | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -26,7 +26,7 @@ jobs: | |||||||
|     runs-on: ubuntu-24.04 |     runs-on: ubuntu-24.04 | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout repository |       - name: Checkout repository | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|       - name: Install python |       - name: Install python | ||||||
|         uses: actions/setup-python@v5 |         uses: actions/setup-python@v5 | ||||||
|         with: |         with: | ||||||
| @@ -40,7 +40,7 @@ jobs: | |||||||
|       - pre-commit |       - pre-commit | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|       - name: Set up Python |       - name: Set up Python | ||||||
|         id: setup-python |         id: setup-python | ||||||
|         uses: actions/setup-python@v5 |         uses: actions/setup-python@v5 | ||||||
| @@ -90,7 +90,7 @@ jobs: | |||||||
|       fail-fast: false |       fail-fast: false | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|       - name: Start containers |       - name: Start containers | ||||||
|         run: | |         run: | | ||||||
|           docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet |           docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet | ||||||
| @@ -162,7 +162,7 @@ jobs: | |||||||
|     needs: |     needs: | ||||||
|       - pre-commit |       - pre-commit | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - uses: actions/checkout@v5 | ||||||
|       - name: Install pnpm |       - name: Install pnpm | ||||||
|         uses: pnpm/action-setup@v4 |         uses: pnpm/action-setup@v4 | ||||||
|         with: |         with: | ||||||
| @@ -195,7 +195,7 @@ jobs: | |||||||
|         shard-index: [1, 2, 3, 4] |         shard-index: [1, 2, 3, 4] | ||||||
|         shard-count: [4] |         shard-count: [4] | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - uses: actions/checkout@v5 | ||||||
|       - name: Install pnpm |       - name: Install pnpm | ||||||
|         uses: pnpm/action-setup@v4 |         uses: pnpm/action-setup@v4 | ||||||
|         with: |         with: | ||||||
| @@ -245,7 +245,7 @@ jobs: | |||||||
|         shard-index: [1, 2] |         shard-index: [1, 2] | ||||||
|         shard-count: [2] |         shard-count: [2] | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - uses: actions/checkout@v5 | ||||||
|       - name: Install pnpm |       - name: Install pnpm | ||||||
|         uses: pnpm/action-setup@v4 |         uses: pnpm/action-setup@v4 | ||||||
|         with: |         with: | ||||||
| @@ -288,7 +288,7 @@ jobs: | |||||||
|       - tests-frontend |       - tests-frontend | ||||||
|       - tests-frontend-e2e |       - tests-frontend-e2e | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - uses: actions/checkout@v5 | ||||||
|       - name: Install pnpm |       - name: Install pnpm | ||||||
|         uses: pnpm/action-setup@v4 |         uses: pnpm/action-setup@v4 | ||||||
|         with: |         with: | ||||||
| @@ -363,7 +363,7 @@ jobs: | |||||||
|             type=semver,pattern={{version}} |             type=semver,pattern={{version}} | ||||||
|             type=semver,pattern={{major}}.{{minor}} |             type=semver,pattern={{major}}.{{minor}} | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|       # If https://github.com/docker/buildx/issues/1044 is resolved, |       # If https://github.com/docker/buildx/issues/1044 is resolved, | ||||||
|       # the append input with a native arm64 arch could be used to |       # the append input with a native arm64 arch could be used to | ||||||
|       # significantly speed up building |       # significantly speed up building | ||||||
| @@ -433,7 +433,7 @@ jobs: | |||||||
|     runs-on: ubuntu-24.04 |     runs-on: ubuntu-24.04 | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|       - name: Set up Python |       - name: Set up Python | ||||||
|         id: setup-python |         id: setup-python | ||||||
|         uses: actions/setup-python@v5 |         uses: actions/setup-python@v5 | ||||||
| @@ -453,12 +453,12 @@ jobs: | |||||||
|           sudo apt-get update -qq |           sudo apt-get update -qq | ||||||
|           sudo apt-get install -qq --no-install-recommends gettext liblept5 |           sudo apt-get install -qq --no-install-recommends gettext liblept5 | ||||||
|       - name: Download frontend artifact |       - name: Download frontend artifact | ||||||
|         uses: actions/download-artifact@v4 |         uses: actions/download-artifact@v5 | ||||||
|         with: |         with: | ||||||
|           name: frontend-compiled |           name: frontend-compiled | ||||||
|           path: src/documents/static/frontend/ |           path: src/documents/static/frontend/ | ||||||
|       - name: Download documentation artifact |       - name: Download documentation artifact | ||||||
|         uses: actions/download-artifact@v4 |         uses: actions/download-artifact@v5 | ||||||
|         with: |         with: | ||||||
|           name: documentation |           name: documentation | ||||||
|           path: docs/_build/html/ |           path: docs/_build/html/ | ||||||
| @@ -538,7 +538,7 @@ jobs: | |||||||
|     if: github.ref_type == 'tag' && (startsWith(github.ref_name, 'v') || contains(github.ref_name, '-beta.rc')) |     if: github.ref_type == 'tag' && (startsWith(github.ref_name, 'v') || contains(github.ref_name, '-beta.rc')) | ||||||
|     steps: |     steps: | ||||||
|       - name: Download release artifact |       - name: Download release artifact | ||||||
|         uses: actions/download-artifact@v4 |         uses: actions/download-artifact@v5 | ||||||
|         with: |         with: | ||||||
|           name: release |           name: release | ||||||
|           path: ./ |           path: ./ | ||||||
| @@ -579,7 +579,7 @@ jobs: | |||||||
|     if: needs.publish-release.outputs.prerelease == 'false' |     if: needs.publish-release.outputs.prerelease == 'false' | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|         with: |         with: | ||||||
|           ref: main |           ref: main | ||||||
|       - name: Set up Python |       - name: Set up Python | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.github/workflows/cleanup-tags.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/cleanup-tags.yml
									
									
									
									
										vendored
									
									
								
							| @@ -28,7 +28,7 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|       - name: Clean temporary images |       - name: Clean temporary images | ||||||
|         if: "${{ env.TOKEN != '' }}" |         if: "${{ env.TOKEN != '' }}" | ||||||
|         uses: stumpylog/image-cleaner-action/ephemeral@v0.10.0 |         uses: stumpylog/image-cleaner-action/ephemeral@v0.11.0 | ||||||
|         with: |         with: | ||||||
|           token: "${{ env.TOKEN }}" |           token: "${{ env.TOKEN }}" | ||||||
|           owner: "${{ github.repository_owner }}" |           owner: "${{ github.repository_owner }}" | ||||||
| @@ -54,7 +54,7 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|       - name: Clean untagged images |       - name: Clean untagged images | ||||||
|         if: "${{ env.TOKEN != '' }}" |         if: "${{ env.TOKEN != '' }}" | ||||||
|         uses: stumpylog/image-cleaner-action/untagged@v0.10.0 |         uses: stumpylog/image-cleaner-action/untagged@v0.11.0 | ||||||
|         with: |         with: | ||||||
|           token: "${{ env.TOKEN }}" |           token: "${{ env.TOKEN }}" | ||||||
|           owner: "${{ github.repository_owner }}" |           owner: "${{ github.repository_owner }}" | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							| @@ -34,7 +34,7 @@ jobs: | |||||||
|         # Learn more about CodeQL language support at https://git.io/codeql-language-support |         # Learn more about CodeQL language support at https://git.io/codeql-language-support | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout repository |       - name: Checkout repository | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|       # Initializes the CodeQL tools for scanning. |       # Initializes the CodeQL tools for scanning. | ||||||
|       - name: Initialize CodeQL |       - name: Initialize CodeQL | ||||||
|         uses: github/codeql-action/init@v3 |         uses: github/codeql-action/init@v3 | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/crowdin.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/crowdin.yml
									
									
									
									
										vendored
									
									
								
							| @@ -13,7 +13,7 @@ jobs: | |||||||
|     runs-on: ubuntu-24.04 |     runs-on: ubuntu-24.04 | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|         with: |         with: | ||||||
|           token: ${{ secrets.PNGX_BOT_PAT }} |           token: ${{ secrets.PNGX_BOT_PAT }} | ||||||
|       - name: crowdin action |       - name: crowdin action | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/pr-bot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/pr-bot.yml
									
									
									
									
										vendored
									
									
								
							| @@ -37,7 +37,7 @@ jobs: | |||||||
|               labels.push('bug'); |               labels.push('bug'); | ||||||
|             } else if (/^feature/i.test(title)) { |             } else if (/^feature/i.test(title)) { | ||||||
|               labels.push('enhancement'); |               labels.push('enhancement'); | ||||||
|             } else if (!/^(dependabot)/i.test(title)) { |             } else if (!/^(dependabot)/i.test(title) && !/^(chore)/i.test(title)) { | ||||||
|               labels.push('enhancement'); // Default fallback |               labels.push('enhancement'); // Default fallback | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/translate-strings.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/translate-strings.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,7 +11,7 @@ jobs: | |||||||
|       contents: write |       contents: write | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout code |       - name: Checkout code | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|         with: |         with: | ||||||
|           token: ${{ secrets.PNGX_BOT_PAT }} |           token: ${{ secrets.PNGX_BOT_PAT }} | ||||||
|           ref: ${{ github.head_ref }} |           ref: ${{ github.head_ref }} | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ repos: | |||||||
|         exclude_types: |         exclude_types: | ||||||
|           - svg |           - svg | ||||||
|           - pofile |           - pofile | ||||||
|         exclude: "(^LICENSE$)" |         exclude: "(^LICENSE$|^src/documents/static/bootstrap.min.css$)" | ||||||
|       - id: mixed-line-ending |       - id: mixed-line-ending | ||||||
|         args: |         args: | ||||||
|           - "--fix=lf" |           - "--fix=lf" | ||||||
| @@ -51,7 +51,7 @@ repos: | |||||||
|   - repo: https://github.com/astral-sh/ruff-pre-commit |   - repo: https://github.com/astral-sh/ruff-pre-commit | ||||||
|     rev: v0.12.2 |     rev: v0.12.2 | ||||||
|     hooks: |     hooks: | ||||||
|       - id: ruff |       - id: ruff-check | ||||||
|       - id: ruff-format |       - id: ruff-format | ||||||
|   - repo: https://github.com/tox-dev/pyproject-fmt |   - repo: https://github.com/tox-dev/pyproject-fmt | ||||||
|     rev: "v2.6.0" |     rev: "v2.6.0" | ||||||
|   | |||||||
| @@ -214,18 +214,9 @@ lint.per-file-ignores."docker/wait-for-redis.py" = [ | |||||||
|   "INP001", |   "INP001", | ||||||
|   "T201", |   "T201", | ||||||
| ] | ] | ||||||
| lint.per-file-ignores."src/documents/management/commands/document_consumer.py" = [ |  | ||||||
|   "PTH", |  | ||||||
| ] # TODO Enable & remove |  | ||||||
| lint.per-file-ignores."src/documents/migrations/1012_fix_archive_files.py" = [ |  | ||||||
|   "PTH", |  | ||||||
| ] # TODO Enable & remove |  | ||||||
| lint.per-file-ignores."src/documents/models.py" = [ | lint.per-file-ignores."src/documents/models.py" = [ | ||||||
|   "SIM115", |   "SIM115", | ||||||
| ] | ] | ||||||
| lint.per-file-ignores."src/documents/parsers.py" = [ |  | ||||||
|   "PTH", |  | ||||||
| ] # TODO Enable & remove |  | ||||||
| lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [ | lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [ | ||||||
|   "RUF001", |   "RUF001", | ||||||
| ] | ] | ||||||
|   | |||||||
| @@ -5,14 +5,14 @@ | |||||||
|       <trans-unit id="ngb.alert.close" datatype="html"> |       <trans-unit id="ngb.alert.close" datatype="html"> | ||||||
|         <source>Close</source> |         <source>Close</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/alert/alert.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/alert/alert.ts</context> | ||||||
|           <context context-type="linenumber">50</context> |           <context context-type="linenumber">50</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.carousel.slide-number" datatype="html"> |       <trans-unit id="ngb.carousel.slide-number" datatype="html"> | ||||||
|         <source> Slide <x id="INTERPOLATION" equiv-text="ueryList<NgbSli"/> of <x id="INTERPOLATION_1" equiv-text="EventSource = N"/> </source> |         <source> Slide <x id="INTERPOLATION" equiv-text="ueryList<NgbSli"/> of <x id="INTERPOLATION_1" equiv-text="EventSource = N"/> </source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/carousel/carousel.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/carousel/carousel.ts</context> | ||||||
|           <context context-type="linenumber">131,135</context> |           <context context-type="linenumber">131,135</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|         <note priority="1" from="description">Currently selected slide number read by screen reader</note> |         <note priority="1" from="description">Currently selected slide number read by screen reader</note> | ||||||
| @@ -20,212 +20,212 @@ | |||||||
|       <trans-unit id="ngb.carousel.previous" datatype="html"> |       <trans-unit id="ngb.carousel.previous" datatype="html"> | ||||||
|         <source>Previous</source> |         <source>Previous</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/carousel/carousel.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/carousel/carousel.ts</context> | ||||||
|           <context context-type="linenumber">157,159</context> |           <context context-type="linenumber">157,159</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.carousel.next" datatype="html"> |       <trans-unit id="ngb.carousel.next" datatype="html"> | ||||||
|         <source>Next</source> |         <source>Next</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/carousel/carousel.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/carousel/carousel.ts</context> | ||||||
|           <context context-type="linenumber">198</context> |           <context context-type="linenumber">198</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.datepicker.previous-month" datatype="html"> |       <trans-unit id="ngb.datepicker.previous-month" datatype="html"> | ||||||
|         <source>Previous month</source> |         <source>Previous month</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/datepicker/datepicker-navigation.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/datepicker/datepicker-navigation.ts</context> | ||||||
|           <context context-type="linenumber">83,85</context> |           <context context-type="linenumber">83,85</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/datepicker/datepicker-navigation.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/datepicker/datepicker-navigation.ts</context> | ||||||
|           <context context-type="linenumber">112</context> |           <context context-type="linenumber">112</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.datepicker.next-month" datatype="html"> |       <trans-unit id="ngb.datepicker.next-month" datatype="html"> | ||||||
|         <source>Next month</source> |         <source>Next month</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/datepicker/datepicker-navigation.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/datepicker/datepicker-navigation.ts</context> | ||||||
|           <context context-type="linenumber">112</context> |           <context context-type="linenumber">112</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/datepicker/datepicker-navigation.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/datepicker/datepicker-navigation.ts</context> | ||||||
|           <context context-type="linenumber">112</context> |           <context context-type="linenumber">112</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.HH" datatype="html"> |       <trans-unit id="ngb.timepicker.HH" datatype="html"> | ||||||
|         <source>HH</source> |         <source>HH</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.toast.close-aria" datatype="html"> |       <trans-unit id="ngb.toast.close-aria" datatype="html"> | ||||||
|         <source>Close</source> |         <source>Close</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.datepicker.select-month" datatype="html"> |       <trans-unit id="ngb.datepicker.select-month" datatype="html"> | ||||||
|         <source>Select month</source> |         <source>Select month</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.pagination.first" datatype="html"> |       <trans-unit id="ngb.pagination.first" datatype="html"> | ||||||
|         <source>««</source> |         <source>««</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.hours" datatype="html"> |       <trans-unit id="ngb.timepicker.hours" datatype="html"> | ||||||
|         <source>Hours</source> |         <source>Hours</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.pagination.previous" datatype="html"> |       <trans-unit id="ngb.pagination.previous" datatype="html"> | ||||||
|         <source>«</source> |         <source>«</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.MM" datatype="html"> |       <trans-unit id="ngb.timepicker.MM" datatype="html"> | ||||||
|         <source>MM</source> |         <source>MM</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.pagination.next" datatype="html"> |       <trans-unit id="ngb.pagination.next" datatype="html"> | ||||||
|         <source>»</source> |         <source>»</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.datepicker.select-year" datatype="html"> |       <trans-unit id="ngb.datepicker.select-year" datatype="html"> | ||||||
|         <source>Select year</source> |         <source>Select year</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.minutes" datatype="html"> |       <trans-unit id="ngb.timepicker.minutes" datatype="html"> | ||||||
|         <source>Minutes</source> |         <source>Minutes</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.pagination.last" datatype="html"> |       <trans-unit id="ngb.pagination.last" datatype="html"> | ||||||
|         <source>»»</source> |         <source>»»</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.pagination.first-aria" datatype="html"> |       <trans-unit id="ngb.pagination.first-aria" datatype="html"> | ||||||
|         <source>First</source> |         <source>First</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.increment-hours" datatype="html"> |       <trans-unit id="ngb.timepicker.increment-hours" datatype="html"> | ||||||
|         <source>Increment hours</source> |         <source>Increment hours</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.pagination.previous-aria" datatype="html"> |       <trans-unit id="ngb.pagination.previous-aria" datatype="html"> | ||||||
|         <source>Previous</source> |         <source>Previous</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.decrement-hours" datatype="html"> |       <trans-unit id="ngb.timepicker.decrement-hours" datatype="html"> | ||||||
|         <source>Decrement hours</source> |         <source>Decrement hours</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.pagination.next-aria" datatype="html"> |       <trans-unit id="ngb.pagination.next-aria" datatype="html"> | ||||||
|         <source>Next</source> |         <source>Next</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.increment-minutes" datatype="html"> |       <trans-unit id="ngb.timepicker.increment-minutes" datatype="html"> | ||||||
|         <source>Increment minutes</source> |         <source>Increment minutes</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.pagination.last-aria" datatype="html"> |       <trans-unit id="ngb.pagination.last-aria" datatype="html"> | ||||||
|         <source>Last</source> |         <source>Last</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.decrement-minutes" datatype="html"> |       <trans-unit id="ngb.timepicker.decrement-minutes" datatype="html"> | ||||||
|         <source>Decrement minutes</source> |         <source>Decrement minutes</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.SS" datatype="html"> |       <trans-unit id="ngb.timepicker.SS" datatype="html"> | ||||||
|         <source>SS</source> |         <source>SS</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.seconds" datatype="html"> |       <trans-unit id="ngb.timepicker.seconds" datatype="html"> | ||||||
|         <source>Seconds</source> |         <source>Seconds</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.increment-seconds" datatype="html"> |       <trans-unit id="ngb.timepicker.increment-seconds" datatype="html"> | ||||||
|         <source>Increment seconds</source> |         <source>Increment seconds</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.decrement-seconds" datatype="html"> |       <trans-unit id="ngb.timepicker.decrement-seconds" datatype="html"> | ||||||
|         <source>Decrement seconds</source> |         <source>Decrement seconds</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="ngb.timepicker.PM" datatype="html"> |       <trans-unit id="ngb.timepicker.PM" datatype="html"> | ||||||
|         <source><x id="INTERPOLATION"/></source> |         <source><x id="INTERPOLATION"/></source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/ngb-config.ts</context> | ||||||
|           <context context-type="linenumber">13</context> |           <context context-type="linenumber">13</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
| @@ -233,7 +233,7 @@ | |||||||
|         <source><x id="INTERPOLATION" equiv-text="barConfig); |         <source><x id="INTERPOLATION" equiv-text="barConfig); | ||||||
| 	pu"/></source> | 	pu"/></source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/progressbar/progressbar.ts</context> |           <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.2.4_@angular+core@20.2.4_@angular+_db9461b4835bfc9061e01150e14e6256/node_modules/src/progressbar/progressbar.ts</context> | ||||||
|           <context context-type="linenumber">41,42</context> |           <context context-type="linenumber">41,42</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
| @@ -553,7 +553,7 @@ | |||||||
|         </context-group> |         </context-group> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> | ||||||
|           <context context-type="linenumber">45</context> |           <context context-type="linenumber">55</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.html</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.html</context> | ||||||
| @@ -1444,7 +1444,7 @@ | |||||||
|         </context-group> |         </context-group> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> | ||||||
|           <context context-type="linenumber">44</context> |           <context context-type="linenumber">54</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.html</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.html</context> | ||||||
| @@ -3673,42 +3673,42 @@ | |||||||
|         <source>Warning: existing instances of this field will retain their current value index (e.g. option #1, #2, #3) after editing the options here</source> |         <source>Warning: existing instances of this field will retain their current value index (e.g. option #1, #2, #3) after editing the options here</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> | ||||||
|           <context context-type="linenumber">32</context> |           <context context-type="linenumber">42</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="2739003406164860877" datatype="html"> |       <trans-unit id="2739003406164860877" datatype="html"> | ||||||
|         <source>Default Currency</source> |         <source>Default Currency</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> | ||||||
|           <context context-type="linenumber">37</context> |           <context context-type="linenumber">47</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="7615210738790237590" datatype="html"> |       <trans-unit id="7615210738790237590" datatype="html"> | ||||||
|         <source>3-character currency code</source> |         <source>3-character currency code</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> | ||||||
|           <context context-type="linenumber">37</context> |           <context context-type="linenumber">47</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="607636736207886379" datatype="html"> |       <trans-unit id="607636736207886379" datatype="html"> | ||||||
|         <source>Use locale</source> |         <source>Use locale</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html</context> | ||||||
|           <context context-type="linenumber">37</context> |           <context context-type="linenumber">47</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="528950215505228201" datatype="html"> |       <trans-unit id="528950215505228201" datatype="html"> | ||||||
|         <source>Create new custom field</source> |         <source>Create new custom field</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts</context> | ||||||
|           <context context-type="linenumber">93</context> |           <context context-type="linenumber">118</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="8751213029607178010" datatype="html"> |       <trans-unit id="8751213029607178010" datatype="html"> | ||||||
|         <source>Edit custom field</source> |         <source>Edit custom field</source> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts</context> |           <context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts</context> | ||||||
|           <context context-type="linenumber">97</context> |           <context context-type="linenumber">122</context> | ||||||
|         </context-group> |         </context-group> | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="6672809941092516947" datatype="html"> |       <trans-unit id="6672809941092516947" datatype="html"> | ||||||
|   | |||||||
| @@ -11,27 +11,27 @@ | |||||||
|   }, |   }, | ||||||
|   "private": true, |   "private": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@angular/cdk": "^20.1.4", |     "@angular/cdk": "^20.2.2", | ||||||
|     "@angular/common": "~20.1.4", |     "@angular/common": "~20.2.4", | ||||||
|     "@angular/compiler": "~20.1.4", |     "@angular/compiler": "~20.2.4", | ||||||
|     "@angular/core": "~20.1.4", |     "@angular/core": "~20.2.4", | ||||||
|     "@angular/forms": "~20.1.4", |     "@angular/forms": "~20.2.4", | ||||||
|     "@angular/localize": "~20.1.4", |     "@angular/localize": "~20.2.4", | ||||||
|     "@angular/platform-browser": "~20.1.4", |     "@angular/platform-browser": "~20.2.4", | ||||||
|     "@angular/platform-browser-dynamic": "~20.1.4", |     "@angular/platform-browser-dynamic": "~20.2.4", | ||||||
|     "@angular/router": "~20.1.4", |     "@angular/router": "~20.2.4", | ||||||
|     "@ng-bootstrap/ng-bootstrap": "^19.0.1", |     "@ng-bootstrap/ng-bootstrap": "^19.0.1", | ||||||
|     "@ng-select/ng-select": "^20.0.1", |     "@ng-select/ng-select": "^20.1.3", | ||||||
|     "@ngneat/dirty-check-forms": "^3.0.3", |     "@ngneat/dirty-check-forms": "^3.0.3", | ||||||
|     "@popperjs/core": "^2.11.8", |     "@popperjs/core": "^2.11.8", | ||||||
|     "bootstrap": "^5.3.7", |     "bootstrap": "^5.3.8", | ||||||
|     "file-saver": "^2.0.5", |     "file-saver": "^2.0.5", | ||||||
|     "mime-names": "^1.0.0", |     "mime-names": "^1.0.0", | ||||||
|     "ng2-pdf-viewer": "^10.4.0", |     "ng2-pdf-viewer": "^10.4.0", | ||||||
|     "ngx-bootstrap-icons": "^1.9.3", |     "ngx-bootstrap-icons": "^1.9.3", | ||||||
|     "ngx-color": "^10.0.0", |     "ngx-color": "^10.0.0", | ||||||
|     "ngx-cookie-service": "^20.0.1", |     "ngx-cookie-service": "^20.1.0", | ||||||
|     "ngx-device-detector": "^10.0.2", |     "ngx-device-detector": "^10.1.0", | ||||||
|     "ngx-ui-tour-ng-bootstrap": "^17.0.1", |     "ngx-ui-tour-ng-bootstrap": "^17.0.1", | ||||||
|     "rxjs": "^7.8.2", |     "rxjs": "^7.8.2", | ||||||
|     "tslib": "^2.8.1", |     "tslib": "^2.8.1", | ||||||
| @@ -42,33 +42,33 @@ | |||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@angular-builders/custom-webpack": "^20.0.0", |     "@angular-builders/custom-webpack": "^20.0.0", | ||||||
|     "@angular-builders/jest": "^20.0.0", |     "@angular-builders/jest": "^20.0.0", | ||||||
|     "@angular-devkit/core": "^20.1.4", |     "@angular-devkit/core": "^20.2.2", | ||||||
|     "@angular-devkit/schematics": "^20.1.4", |     "@angular-devkit/schematics": "^20.2.2", | ||||||
|     "@angular-eslint/builder": "20.1.1", |     "@angular-eslint/builder": "20.2.0", | ||||||
|     "@angular-eslint/eslint-plugin": "20.1.1", |     "@angular-eslint/eslint-plugin": "20.2.0", | ||||||
|     "@angular-eslint/eslint-plugin-template": "20.1.1", |     "@angular-eslint/eslint-plugin-template": "20.2.0", | ||||||
|     "@angular-eslint/schematics": "20.1.1", |     "@angular-eslint/schematics": "20.2.0", | ||||||
|     "@angular-eslint/template-parser": "20.1.1", |     "@angular-eslint/template-parser": "20.2.0", | ||||||
|     "@angular/build": "^20.1.4", |     "@angular/build": "^20.2.2", | ||||||
|     "@angular/cli": "~20.1.4", |     "@angular/cli": "~20.2.2", | ||||||
|     "@angular/compiler-cli": "~20.1.4", |     "@angular/compiler-cli": "~20.2.4", | ||||||
|     "@codecov/webpack-plugin": "^1.9.1", |     "@codecov/webpack-plugin": "^1.9.1", | ||||||
|     "@playwright/test": "^1.54.2", |     "@playwright/test": "^1.55.0", | ||||||
|     "@types/jest": "^30.0.0", |     "@types/jest": "^30.0.0", | ||||||
|     "@types/node": "^24.1.0", |     "@types/node": "^24.3.0", | ||||||
|     "@typescript-eslint/eslint-plugin": "^8.38.0", |     "@typescript-eslint/eslint-plugin": "^8.41.0", | ||||||
|     "@typescript-eslint/parser": "^8.38.0", |     "@typescript-eslint/parser": "^8.41.0", | ||||||
|     "@typescript-eslint/utils": "^8.38.0", |     "@typescript-eslint/utils": "^8.41.0", | ||||||
|     "eslint": "^9.32.0", |     "eslint": "^9.34.0", | ||||||
|     "jest": "30.0.5", |     "jest": "30.1.3", | ||||||
|     "jest-environment-jsdom": "^30.0.5", |     "jest-environment-jsdom": "^30.1.2", | ||||||
|     "jest-junit": "^16.0.0", |     "jest-junit": "^16.0.0", | ||||||
|     "jest-preset-angular": "^15.0.0", |     "jest-preset-angular": "^15.0.0", | ||||||
|     "jest-websocket-mock": "^2.5.0", |     "jest-websocket-mock": "^2.5.0", | ||||||
|     "prettier-plugin-organize-imports": "^4.2.0", |     "prettier-plugin-organize-imports": "^4.2.0", | ||||||
|     "ts-node": "~10.9.1", |     "ts-node": "~10.9.1", | ||||||
|     "typescript": "^5.8.3", |     "typescript": "^5.8.3", | ||||||
|     "webpack": "^5.101.0" |     "webpack": "^5.101.3" | ||||||
|   }, |   }, | ||||||
|   "pnpm": { |   "pnpm": { | ||||||
|     "onlyBuiltDependencies": [ |     "onlyBuiltDependencies": [ | ||||||
|   | |||||||
							
								
								
									
										3635
									
								
								src-ui/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3635
									
								
								src-ui/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -28,6 +28,16 @@ | |||||||
|               </div> |               </div> | ||||||
|             } |             } | ||||||
|           </div> |           </div> | ||||||
|  |           @if (allSelectOptions.length > SELECT_OPTION_PAGE_SIZE) { | ||||||
|  |             <ngb-pagination | ||||||
|  |               class="d-flex justify-content-end" | ||||||
|  |               [pageSize]="SELECT_OPTION_PAGE_SIZE" | ||||||
|  |               [collectionSize]="allSelectOptions.length" | ||||||
|  |               [(page)]="selectOptionsPage" | ||||||
|  |               [maxSize]="5" | ||||||
|  |               size="sm" | ||||||
|  |             ></ngb-pagination> | ||||||
|  |           } | ||||||
|           @if (object?.id) { |           @if (object?.id) { | ||||||
|             <small class="d-block mt-2" i18n>Warning: existing instances of this field will retain their current value index (e.g. option #1, #2, #3) after editing the options here</small> |             <small class="d-block mt-2" i18n>Warning: existing instances of this field will retain their current value index (e.g. option #1, #2, #3) after editing the options here</small> | ||||||
|           } |           } | ||||||
|   | |||||||
| @@ -125,4 +125,42 @@ describe('CustomFieldEditDialogComponent', () => { | |||||||
|     fixture.detectChanges() |     fixture.detectChanges() | ||||||
|     expect(document.activeElement).toBe(selectOptionInputs.last.nativeElement) |     expect(document.activeElement).toBe(selectOptionInputs.last.nativeElement) | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|  |   it('should send all select options including those changed in form on save', () => { | ||||||
|  |     component.dialogMode = EditDialogMode.EDIT | ||||||
|  |     component.object = { | ||||||
|  |       id: 1, | ||||||
|  |       name: 'Field 1', | ||||||
|  |       data_type: CustomFieldDataType.Select, | ||||||
|  |       extra_data: { | ||||||
|  |         select_options: Array.from({ length: 50 }, (_, i) => ({ | ||||||
|  |           label: `Option ${i + 1}`, | ||||||
|  |           id: `${i + 1}-xyz`, | ||||||
|  |         })), | ||||||
|  |       }, | ||||||
|  |     } | ||||||
|  |     fixture.detectChanges() | ||||||
|  |     component.ngOnInit() | ||||||
|  |     component.selectOptionsPage = 2 | ||||||
|  |     fixture.detectChanges() | ||||||
|  |     component.objectForm | ||||||
|  |       .get('extra_data') | ||||||
|  |       .get('select_options') | ||||||
|  |       .get('0') | ||||||
|  |       .get('label') | ||||||
|  |       .setValue('Updated Option 9') | ||||||
|  |     const formValues = (component as any).getFormValues() | ||||||
|  |     // first item unchanged | ||||||
|  |     expect(formValues.extra_data.select_options[0]).toEqual({ | ||||||
|  |       label: 'Option 1', | ||||||
|  |       id: '1-xyz', | ||||||
|  |     }) | ||||||
|  |     // page 2 first item updated | ||||||
|  |     expect( | ||||||
|  |       formValues.extra_data.select_options[component.SELECT_OPTION_PAGE_SIZE] | ||||||
|  |     ).toEqual({ | ||||||
|  |       label: 'Updated Option 9', | ||||||
|  |       id: '9-xyz', | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
| }) | }) | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import { | |||||||
|   FormsModule, |   FormsModule, | ||||||
|   ReactiveFormsModule, |   ReactiveFormsModule, | ||||||
| } from '@angular/forms' | } from '@angular/forms' | ||||||
|  | import { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap' | ||||||
| import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' | import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' | ||||||
| import { takeUntil } from 'rxjs' | import { takeUntil } from 'rxjs' | ||||||
| import { | import { | ||||||
| @@ -28,6 +29,8 @@ import { SelectComponent } from '../../input/select/select.component' | |||||||
| import { TextComponent } from '../../input/text/text.component' | import { TextComponent } from '../../input/text/text.component' | ||||||
| import { EditDialogComponent, EditDialogMode } from '../edit-dialog.component' | import { EditDialogComponent, EditDialogMode } from '../edit-dialog.component' | ||||||
|  |  | ||||||
|  | const SELECT_OPTION_PAGE_SIZE = 8 | ||||||
|  |  | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'pngx-custom-field-edit-dialog', |   selector: 'pngx-custom-field-edit-dialog', | ||||||
|   templateUrl: './custom-field-edit-dialog.component.html', |   templateUrl: './custom-field-edit-dialog.component.html', | ||||||
| @@ -37,6 +40,7 @@ import { EditDialogComponent, EditDialogMode } from '../edit-dialog.component' | |||||||
|     TextComponent, |     TextComponent, | ||||||
|     FormsModule, |     FormsModule, | ||||||
|     ReactiveFormsModule, |     ReactiveFormsModule, | ||||||
|  |     NgbPaginationModule, | ||||||
|     NgxBootstrapIconsModule, |     NgxBootstrapIconsModule, | ||||||
|   ], |   ], | ||||||
| }) | }) | ||||||
| @@ -45,6 +49,21 @@ export class CustomFieldEditDialogComponent | |||||||
|   implements OnInit, AfterViewInit |   implements OnInit, AfterViewInit | ||||||
| { | { | ||||||
|   CustomFieldDataType = CustomFieldDataType |   CustomFieldDataType = CustomFieldDataType | ||||||
|  |   SELECT_OPTION_PAGE_SIZE = SELECT_OPTION_PAGE_SIZE | ||||||
|  |  | ||||||
|  |   private _allSelectOptions: any[] = [] | ||||||
|  |   public get allSelectOptions(): any[] { | ||||||
|  |     return this._allSelectOptions | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private _selectOptionsPage: number | ||||||
|  |   public get selectOptionsPage(): number { | ||||||
|  |     return this._selectOptionsPage | ||||||
|  |   } | ||||||
|  |   public set selectOptionsPage(v: number) { | ||||||
|  |     this._selectOptionsPage = v | ||||||
|  |     this.updateSelectOptions() | ||||||
|  |   } | ||||||
|  |  | ||||||
|   @ViewChildren('selectOption') |   @ViewChildren('selectOption') | ||||||
|   private selectOptionInputs: QueryList<ElementRef> |   private selectOptionInputs: QueryList<ElementRef> | ||||||
| @@ -67,17 +86,10 @@ export class CustomFieldEditDialogComponent | |||||||
|       this.objectForm.get('data_type').disable() |       this.objectForm.get('data_type').disable() | ||||||
|     } |     } | ||||||
|     if (this.object?.data_type === CustomFieldDataType.Select) { |     if (this.object?.data_type === CustomFieldDataType.Select) { | ||||||
|       this.selectOptions.clear() |       this._allSelectOptions = [ | ||||||
|       this.object.extra_data.select_options |         ...(this.object.extra_data.select_options ?? []), | ||||||
|         .filter((option) => option) |       ] | ||||||
|         .forEach((option) => |       this.selectOptionsPage = 1 | ||||||
|           this.selectOptions.push( |  | ||||||
|             new FormGroup({ |  | ||||||
|               label: new FormControl(option.label), |  | ||||||
|               id: new FormControl(option.id), |  | ||||||
|             }) |  | ||||||
|           ) |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -87,6 +99,19 @@ export class CustomFieldEditDialogComponent | |||||||
|       .subscribe(() => { |       .subscribe(() => { | ||||||
|         this.selectOptionInputs.last?.nativeElement.focus() |         this.selectOptionInputs.last?.nativeElement.focus() | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|  |     this.objectForm.valueChanges | ||||||
|  |       .pipe(takeUntil(this.unsubscribeNotifier)) | ||||||
|  |       .subscribe((change) => { | ||||||
|  |         // Update the relevant select options values if changed in the form, which is only a page of the entire list | ||||||
|  |         this.objectForm | ||||||
|  |           .get('extra_data.select_options') | ||||||
|  |           ?.value.forEach((option, index) => { | ||||||
|  |             this._allSelectOptions[ | ||||||
|  |               index + (this.selectOptionsPage - 1) * SELECT_OPTION_PAGE_SIZE | ||||||
|  |             ] = option | ||||||
|  |           }) | ||||||
|  |       }) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getCreateTitle() { |   getCreateTitle() { | ||||||
| @@ -108,6 +133,17 @@ export class CustomFieldEditDialogComponent | |||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   protected getFormValues() { | ||||||
|  |     const formValues = super.getFormValues() | ||||||
|  |     if ( | ||||||
|  |       this.objectForm.get('data_type')?.value === CustomFieldDataType.Select | ||||||
|  |     ) { | ||||||
|  |       // Make sure we send all select options, with updated values | ||||||
|  |       formValues.extra_data.select_options = this._allSelectOptions | ||||||
|  |     } | ||||||
|  |     return formValues | ||||||
|  |   } | ||||||
|  |  | ||||||
|   getDataTypes() { |   getDataTypes() { | ||||||
|     return DATA_TYPE_LABELS |     return DATA_TYPE_LABELS | ||||||
|   } |   } | ||||||
| @@ -116,13 +152,35 @@ export class CustomFieldEditDialogComponent | |||||||
|     return this.dialogMode === EditDialogMode.EDIT |     return this.dialogMode === EditDialogMode.EDIT | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public addSelectOption() { |   private updateSelectOptions() { | ||||||
|  |     this.selectOptions.clear() | ||||||
|  |     this._allSelectOptions | ||||||
|  |       .slice( | ||||||
|  |         (this.selectOptionsPage - 1) * SELECT_OPTION_PAGE_SIZE, | ||||||
|  |         this.selectOptionsPage * SELECT_OPTION_PAGE_SIZE | ||||||
|  |       ) | ||||||
|  |       .forEach((option) => | ||||||
|         this.selectOptions.push( |         this.selectOptions.push( | ||||||
|       new FormGroup({ label: new FormControl(null), id: new FormControl(null) }) |           new FormGroup({ | ||||||
|  |             label: new FormControl(option.label), | ||||||
|  |             id: new FormControl(option.id), | ||||||
|  |           }) | ||||||
|  |         ) | ||||||
|  |       ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public addSelectOption() { | ||||||
|  |     this._allSelectOptions.push({ label: null, id: null }) | ||||||
|  |     this.selectOptionsPage = Math.ceil( | ||||||
|  |       this.allSelectOptions.length / SELECT_OPTION_PAGE_SIZE | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public removeSelectOption(index: number) { |   public removeSelectOption(index: number) { | ||||||
|     this.selectOptions.removeAt(index) |     this.selectOptions.removeAt(index) | ||||||
|  |     this._allSelectOptions.splice( | ||||||
|  |       index + (this.selectOptionsPage - 1) * SELECT_OPTION_PAGE_SIZE, | ||||||
|  |       1 | ||||||
|  |     ) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -147,9 +147,13 @@ export abstract class EditDialogComponent< | |||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   protected getFormValues(): any { | ||||||
|  |     return Object.assign({}, this.objectForm.value) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   save() { |   save() { | ||||||
|     this.error = null |     this.error = null | ||||||
|     const formValues = Object.assign({}, this.objectForm.value) |     const formValues = this.getFormValues() | ||||||
|     const permissionsObject: PermissionsFormObject = |     const permissionsObject: PermissionsFormObject = | ||||||
|       this.objectForm.get('permissions_form')?.value |       this.objectForm.get('permissions_form')?.value | ||||||
|     if (permissionsObject) { |     if (permissionsObject) { | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ except ImportError:  # pragma: no cover | |||||||
| logger = logging.getLogger("paperless.management.consumer") | logger = logging.getLogger("paperless.management.consumer") | ||||||
|  |  | ||||||
|  |  | ||||||
| def _tags_from_path(filepath) -> list[int]: | def _tags_from_path(filepath: Path) -> list[int]: | ||||||
|     """ |     """ | ||||||
|     Walk up the directory tree from filepath to CONSUMPTION_DIR |     Walk up the directory tree from filepath to CONSUMPTION_DIR | ||||||
|     and get or create Tag IDs for every directory. |     and get or create Tag IDs for every directory. | ||||||
| @@ -41,7 +41,7 @@ def _tags_from_path(filepath) -> list[int]: | |||||||
|     """ |     """ | ||||||
|     db.close_old_connections() |     db.close_old_connections() | ||||||
|     tag_ids = set() |     tag_ids = set() | ||||||
|     path_parts = Path(filepath).relative_to(settings.CONSUMPTION_DIR).parent.parts |     path_parts = filepath.relative_to(settings.CONSUMPTION_DIR).parent.parts | ||||||
|     for part in path_parts: |     for part in path_parts: | ||||||
|         tag_ids.add( |         tag_ids.add( | ||||||
|             Tag.objects.get_or_create(name__iexact=part, defaults={"name": part})[0].pk, |             Tag.objects.get_or_create(name__iexact=part, defaults={"name": part})[0].pk, | ||||||
| @@ -50,17 +50,13 @@ def _tags_from_path(filepath) -> list[int]: | |||||||
|     return list(tag_ids) |     return list(tag_ids) | ||||||
|  |  | ||||||
|  |  | ||||||
| def _is_ignored(filepath: str) -> bool: | def _is_ignored(filepath: Path) -> bool: | ||||||
|     """ |     """ | ||||||
|     Checks if the given file should be ignored, based on configured |     Checks if the given file should be ignored, based on configured | ||||||
|     patterns. |     patterns. | ||||||
|  |  | ||||||
|     Returns True if the file is ignored, False otherwise |     Returns True if the file is ignored, False otherwise | ||||||
|     """ |     """ | ||||||
|     filepath = os.path.abspath( |  | ||||||
|         os.path.normpath(filepath), |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     # Trim out the consume directory, leaving only filename and it's |     # Trim out the consume directory, leaving only filename and it's | ||||||
|     # path relative to the consume directory |     # path relative to the consume directory | ||||||
|     filepath_relative = PurePath(filepath).relative_to(settings.CONSUMPTION_DIR) |     filepath_relative = PurePath(filepath).relative_to(settings.CONSUMPTION_DIR) | ||||||
| @@ -85,15 +81,15 @@ def _is_ignored(filepath: str) -> bool: | |||||||
|     return False |     return False | ||||||
|  |  | ||||||
|  |  | ||||||
| def _consume(filepath: str) -> None: | def _consume(filepath: Path) -> None: | ||||||
|     if os.path.isdir(filepath) or _is_ignored(filepath): |     if filepath.is_dir() or _is_ignored(filepath): | ||||||
|         return |         return | ||||||
|  |  | ||||||
|     if not os.path.isfile(filepath): |     if not filepath.is_file(): | ||||||
|         logger.debug(f"Not consuming file {filepath}: File has moved.") |         logger.debug(f"Not consuming file {filepath}: File has moved.") | ||||||
|         return |         return | ||||||
|  |  | ||||||
|     if not is_file_ext_supported(os.path.splitext(filepath)[1]): |     if not is_file_ext_supported(filepath.suffix): | ||||||
|         logger.warning(f"Not consuming file {filepath}: Unknown file extension.") |         logger.warning(f"Not consuming file {filepath}: Unknown file extension.") | ||||||
|         return |         return | ||||||
|  |  | ||||||
| @@ -107,7 +103,7 @@ def _consume(filepath: str) -> None: | |||||||
|  |  | ||||||
|     while (read_try_count < os_error_retry_count) and not file_open_ok: |     while (read_try_count < os_error_retry_count) and not file_open_ok: | ||||||
|         try: |         try: | ||||||
|             with open(filepath, "rb"): |             with filepath.open("rb"): | ||||||
|                 file_open_ok = True |                 file_open_ok = True | ||||||
|         except OSError as e: |         except OSError as e: | ||||||
|             read_try_count += 1 |             read_try_count += 1 | ||||||
| @@ -141,7 +137,7 @@ def _consume(filepath: str) -> None: | |||||||
|         logger.exception("Error while consuming document") |         logger.exception("Error while consuming document") | ||||||
|  |  | ||||||
|  |  | ||||||
| def _consume_wait_unmodified(file: str) -> None: | def _consume_wait_unmodified(file: Path) -> None: | ||||||
|     """ |     """ | ||||||
|     Waits for the given file to appear unmodified based on file size |     Waits for the given file to appear unmodified based on file size | ||||||
|     and modification time.  Will wait a configured number of seconds |     and modification time.  Will wait a configured number of seconds | ||||||
| @@ -157,7 +153,7 @@ def _consume_wait_unmodified(file: str) -> None: | |||||||
|     current_try = 0 |     current_try = 0 | ||||||
|     while current_try < settings.CONSUMER_POLLING_RETRY_COUNT: |     while current_try < settings.CONSUMER_POLLING_RETRY_COUNT: | ||||||
|         try: |         try: | ||||||
|             stat_data = os.stat(file) |             stat_data = file.stat() | ||||||
|             new_mtime = stat_data.st_mtime |             new_mtime = stat_data.st_mtime | ||||||
|             new_size = stat_data.st_size |             new_size = stat_data.st_size | ||||||
|         except FileNotFoundError: |         except FileNotFoundError: | ||||||
| @@ -182,10 +178,10 @@ class Handler(FileSystemEventHandler): | |||||||
|         self._pool = pool |         self._pool = pool | ||||||
|  |  | ||||||
|     def on_created(self, event): |     def on_created(self, event): | ||||||
|         self._pool.submit(_consume_wait_unmodified, event.src_path) |         self._pool.submit(_consume_wait_unmodified, Path(event.src_path)) | ||||||
|  |  | ||||||
|     def on_moved(self, event): |     def on_moved(self, event): | ||||||
|         self._pool.submit(_consume_wait_unmodified, event.dest_path) |         self._pool.submit(_consume_wait_unmodified, Path(event.dest_path)) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||||
| @@ -227,9 +223,9 @@ class Command(BaseCommand): | |||||||
|         if not directory: |         if not directory: | ||||||
|             raise CommandError("CONSUMPTION_DIR does not appear to be set.") |             raise CommandError("CONSUMPTION_DIR does not appear to be set.") | ||||||
|  |  | ||||||
|         directory = os.path.abspath(directory) |         directory = Path(directory).resolve() | ||||||
|  |  | ||||||
|         if not os.path.isdir(directory): |         if not directory.is_dir(): | ||||||
|             raise CommandError(f"Consumption directory {directory} does not exist") |             raise CommandError(f"Consumption directory {directory} does not exist") | ||||||
|  |  | ||||||
|         # Consumer will need this |         # Consumer will need this | ||||||
| @@ -238,11 +234,11 @@ class Command(BaseCommand): | |||||||
|         if recursive: |         if recursive: | ||||||
|             for dirpath, _, filenames in os.walk(directory): |             for dirpath, _, filenames in os.walk(directory): | ||||||
|                 for filename in filenames: |                 for filename in filenames: | ||||||
|                     filepath = os.path.join(dirpath, filename) |                     filepath = Path(dirpath) / filename | ||||||
|                     _consume(filepath) |                     _consume(filepath) | ||||||
|         else: |         else: | ||||||
|             for entry in os.scandir(directory): |             for filepath in directory.iterdir(): | ||||||
|                 _consume(entry.path) |                 _consume(filepath) | ||||||
|  |  | ||||||
|         if options["oneshot"]: |         if options["oneshot"]: | ||||||
|             return |             return | ||||||
| @@ -310,7 +306,7 @@ class Command(BaseCommand): | |||||||
|                 try: |                 try: | ||||||
|                     for event in inotify.read(timeout=timeout_ms): |                     for event in inotify.read(timeout=timeout_ms): | ||||||
|                         path = inotify.get_path(event.wd) if recursive else directory |                         path = inotify.get_path(event.wd) if recursive else directory | ||||||
|                         filepath = os.path.join(path, event.name) |                         filepath = Path(path) / event.name | ||||||
|                         if flags.MODIFY in flags.from_mask(event.mask): |                         if flags.MODIFY in flags.from_mask(event.mask): | ||||||
|                             notified_files.pop(filepath, None) |                             notified_files.pop(filepath, None) | ||||||
|                         else: |                         else: | ||||||
| @@ -327,9 +323,7 @@ class Command(BaseCommand): | |||||||
|  |  | ||||||
|                         # Also make sure the file exists still, some scanners might write a |                         # Also make sure the file exists still, some scanners might write a | ||||||
|                         # temporary file first |                         # temporary file first | ||||||
|                         file_still_exists = os.path.exists(filepath) and os.path.isfile( |                         file_still_exists = filepath.exists() and filepath.is_file() | ||||||
|                             filepath, |  | ||||||
|                         ) |  | ||||||
|  |  | ||||||
|                         if waited_long_enough and file_still_exists: |                         if waited_long_enough and file_still_exists: | ||||||
|                             _consume(filepath) |                             _consume(filepath) | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import logging | |||||||
| import os | import os | ||||||
| import shutil | import shutil | ||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
|  | from pathlib import Path | ||||||
| from time import sleep | from time import sleep | ||||||
|  |  | ||||||
| import pathvalidate | import pathvalidate | ||||||
| @@ -50,38 +51,38 @@ def many_to_dictionary(field):  # pragma: no cover | |||||||
|     return mydictionary |     return mydictionary | ||||||
|  |  | ||||||
|  |  | ||||||
| def archive_name_from_filename(filename): | def archive_name_from_filename(filename: Path) -> Path: | ||||||
|     return os.path.splitext(filename)[0] + ".pdf" |     return Path(filename.stem + ".pdf") | ||||||
|  |  | ||||||
|  |  | ||||||
| def archive_path_old(doc): | def archive_path_old(doc) -> Path: | ||||||
|     if doc.filename: |     if doc.filename: | ||||||
|         fname = archive_name_from_filename(doc.filename) |         fname = archive_name_from_filename(Path(doc.filename)) | ||||||
|     else: |     else: | ||||||
|         fname = f"{doc.pk:07}.pdf" |         fname = Path(f"{doc.pk:07}.pdf") | ||||||
|  |  | ||||||
|     return os.path.join(settings.ARCHIVE_DIR, fname) |     return settings.ARCHIVE_DIR / fname | ||||||
|  |  | ||||||
|  |  | ||||||
| STORAGE_TYPE_GPG = "gpg" | STORAGE_TYPE_GPG = "gpg" | ||||||
|  |  | ||||||
|  |  | ||||||
| def archive_path_new(doc): | def archive_path_new(doc) -> Path | None: | ||||||
|     if doc.archive_filename is not None: |     if doc.archive_filename is not None: | ||||||
|         return os.path.join(settings.ARCHIVE_DIR, str(doc.archive_filename)) |         return settings.ARCHIVE_DIR / doc.archive_filename | ||||||
|     else: |     else: | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|  |  | ||||||
| def source_path(doc): | def source_path(doc) -> Path: | ||||||
|     if doc.filename: |     if doc.filename: | ||||||
|         fname = str(doc.filename) |         fname = doc.filename | ||||||
|     else: |     else: | ||||||
|         fname = f"{doc.pk:07}{doc.file_type}" |         fname = f"{doc.pk:07}{doc.file_type}" | ||||||
|         if doc.storage_type == STORAGE_TYPE_GPG: |         if doc.storage_type == STORAGE_TYPE_GPG: | ||||||
|             fname += ".gpg"  # pragma: no cover |             fname = Path(str(fname) + ".gpg")  # pragma: no cover | ||||||
|  |  | ||||||
|     return os.path.join(settings.ORIGINALS_DIR, fname) |     return settings.ORIGINALS_DIR / fname | ||||||
|  |  | ||||||
|  |  | ||||||
| def generate_unique_filename(doc, *, archive_filename=False): | def generate_unique_filename(doc, *, archive_filename=False): | ||||||
| @@ -104,7 +105,7 @@ def generate_unique_filename(doc, *, archive_filename=False): | |||||||
|             # still the same as before. |             # still the same as before. | ||||||
|             return new_filename |             return new_filename | ||||||
|  |  | ||||||
|         if os.path.exists(os.path.join(root, new_filename)): |         if (root / new_filename).exists(): | ||||||
|             counter += 1 |             counter += 1 | ||||||
|         else: |         else: | ||||||
|             return new_filename |             return new_filename | ||||||
| @@ -202,18 +203,18 @@ def create_archive_version(doc, retry_count=3): | |||||||
|                 parser, |                 parser, | ||||||
|                 source_path(doc), |                 source_path(doc), | ||||||
|                 doc.mime_type, |                 doc.mime_type, | ||||||
|                 os.path.basename(doc.filename), |                 Path(doc.filename).name, | ||||||
|             ) |             ) | ||||||
|             doc.content = parser.get_text() |             doc.content = parser.get_text() | ||||||
|  |  | ||||||
|             if parser.get_archive_path() and os.path.isfile(parser.get_archive_path()): |             if parser.get_archive_path() and Path(parser.get_archive_path()).is_file(): | ||||||
|                 doc.archive_filename = generate_unique_filename( |                 doc.archive_filename = generate_unique_filename( | ||||||
|                     doc, |                     doc, | ||||||
|                     archive_filename=True, |                     archive_filename=True, | ||||||
|                 ) |                 ) | ||||||
|                 with open(parser.get_archive_path(), "rb") as f: |                 with Path(parser.get_archive_path()).open("rb") as f: | ||||||
|                     doc.archive_checksum = hashlib.md5(f.read()).hexdigest() |                     doc.archive_checksum = hashlib.md5(f.read()).hexdigest() | ||||||
|                 os.makedirs(os.path.dirname(archive_path_new(doc)), exist_ok=True) |                 archive_path_new(doc).parent.mkdir(parents=True, exist_ok=True) | ||||||
|                 shutil.copy2(parser.get_archive_path(), archive_path_new(doc)) |                 shutil.copy2(parser.get_archive_path(), archive_path_new(doc)) | ||||||
|             else: |             else: | ||||||
|                 doc.archive_checksum = None |                 doc.archive_checksum = None | ||||||
| @@ -264,7 +265,7 @@ def move_old_to_new_locations(apps, schema_editor): | |||||||
|     # check that archive files of all unaffected documents are in place |     # check that archive files of all unaffected documents are in place | ||||||
|     for doc in Document.objects.filter(archive_checksum__isnull=False): |     for doc in Document.objects.filter(archive_checksum__isnull=False): | ||||||
|         old_path = archive_path_old(doc) |         old_path = archive_path_old(doc) | ||||||
|         if doc.id not in affected_document_ids and not os.path.isfile(old_path): |         if doc.id not in affected_document_ids and not old_path.is_file(): | ||||||
|             raise ValueError( |             raise ValueError( | ||||||
|                 f"Archived document ID:{doc.id} does not exist at: {old_path}", |                 f"Archived document ID:{doc.id} does not exist at: {old_path}", | ||||||
|             ) |             ) | ||||||
| @@ -285,12 +286,12 @@ def move_old_to_new_locations(apps, schema_editor): | |||||||
|         if doc.id in affected_document_ids: |         if doc.id in affected_document_ids: | ||||||
|             old_path = archive_path_old(doc) |             old_path = archive_path_old(doc) | ||||||
|             # remove affected archive versions |             # remove affected archive versions | ||||||
|             if os.path.isfile(old_path): |             if old_path.is_file(): | ||||||
|                 logger.debug(f"Removing {old_path}") |                 logger.debug(f"Removing {old_path}") | ||||||
|                 os.unlink(old_path) |                 old_path.unlink() | ||||||
|         else: |         else: | ||||||
|             # Set archive path for unaffected files |             # Set archive path for unaffected files | ||||||
|             doc.archive_filename = archive_name_from_filename(doc.filename) |             doc.archive_filename = archive_name_from_filename(Path(doc.filename)) | ||||||
|             Document.objects.filter(id=doc.id).update( |             Document.objects.filter(id=doc.id).update( | ||||||
|                 archive_filename=doc.archive_filename, |                 archive_filename=doc.archive_filename, | ||||||
|             ) |             ) | ||||||
| @@ -316,7 +317,7 @@ def move_new_to_old_locations(apps, schema_editor): | |||||||
|                 f"filename.", |                 f"filename.", | ||||||
|             ) |             ) | ||||||
|         old_archive_paths.add(old_archive_path) |         old_archive_paths.add(old_archive_path) | ||||||
|         if new_archive_path != old_archive_path and os.path.isfile(old_archive_path): |         if new_archive_path != old_archive_path and old_archive_path.is_file(): | ||||||
|             raise ValueError( |             raise ValueError( | ||||||
|                 f"Cannot migrate: Cannot move {new_archive_path} to " |                 f"Cannot migrate: Cannot move {new_archive_path} to " | ||||||
|                 f"{old_archive_path}: file already exists.", |                 f"{old_archive_path}: file already exists.", | ||||||
|   | |||||||
| @@ -169,7 +169,7 @@ def run_convert( | |||||||
|     args += ["-depth", str(depth)] if depth else [] |     args += ["-depth", str(depth)] if depth else [] | ||||||
|     args += ["-auto-orient"] if auto_orient else [] |     args += ["-auto-orient"] if auto_orient else [] | ||||||
|     args += ["-define", "pdf:use-cropbox=true"] if use_cropbox else [] |     args += ["-define", "pdf:use-cropbox=true"] if use_cropbox else [] | ||||||
|     args += [input_file, output_file] |     args += [str(input_file), str(output_file)] | ||||||
|  |  | ||||||
|     logger.debug("Execute: " + " ".join(args), extra={"group": logging_group}) |     logger.debug("Execute: " + " ".join(args), extra={"group": logging_group}) | ||||||
|  |  | ||||||
| @@ -188,8 +188,8 @@ def get_default_thumbnail() -> Path: | |||||||
|     return (Path(__file__).parent / "resources" / "document.webp").resolve() |     return (Path(__file__).parent / "resources" / "document.webp").resolve() | ||||||
|  |  | ||||||
|  |  | ||||||
| def make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None) -> str: | def make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None) -> Path: | ||||||
|     out_path = os.path.join(temp_dir, "convert_gs.webp") |     out_path: Path = Path(temp_dir) / "convert_gs.webp" | ||||||
|  |  | ||||||
|     # if convert fails, fall back to extracting |     # if convert fails, fall back to extracting | ||||||
|     # the first PDF page as a PNG using Ghostscript |     # the first PDF page as a PNG using Ghostscript | ||||||
| @@ -199,7 +199,7 @@ def make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None) - | |||||||
|         extra={"group": logging_group}, |         extra={"group": logging_group}, | ||||||
|     ) |     ) | ||||||
|     # Ghostscript doesn't handle WebP outputs |     # Ghostscript doesn't handle WebP outputs | ||||||
|     gs_out_path = os.path.join(temp_dir, "gs_out.png") |     gs_out_path: Path = Path(temp_dir) / "gs_out.png" | ||||||
|     cmd = [settings.GS_BINARY, "-q", "-sDEVICE=pngalpha", "-o", gs_out_path, in_path] |     cmd = [settings.GS_BINARY, "-q", "-sDEVICE=pngalpha", "-o", gs_out_path, in_path] | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
| @@ -227,16 +227,16 @@ def make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None) - | |||||||
|         # The caller might expect a generated thumbnail that can be moved, |         # The caller might expect a generated thumbnail that can be moved, | ||||||
|         # so we need to copy it before it gets moved. |         # so we need to copy it before it gets moved. | ||||||
|         # https://github.com/paperless-ngx/paperless-ngx/issues/3631 |         # https://github.com/paperless-ngx/paperless-ngx/issues/3631 | ||||||
|         default_thumbnail_path = os.path.join(temp_dir, "document.webp") |         default_thumbnail_path: Path = Path(temp_dir) / "document.webp" | ||||||
|         copy_file_with_basic_stats(get_default_thumbnail(), default_thumbnail_path) |         copy_file_with_basic_stats(get_default_thumbnail(), default_thumbnail_path) | ||||||
|         return default_thumbnail_path |         return default_thumbnail_path | ||||||
|  |  | ||||||
|  |  | ||||||
| def make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None) -> Path: | def make_thumbnail_from_pdf(in_path: Path, temp_dir: Path, logging_group=None) -> Path: | ||||||
|     """ |     """ | ||||||
|     The thumbnail of a PDF is just a 500px wide image of the first page. |     The thumbnail of a PDF is just a 500px wide image of the first page. | ||||||
|     """ |     """ | ||||||
|     out_path = temp_dir / "convert.webp" |     out_path: Path = temp_dir / "convert.webp" | ||||||
|  |  | ||||||
|     # Run convert to get a decent thumbnail |     # Run convert to get a decent thumbnail | ||||||
|     try: |     try: | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								src/documents/static/bootstrap.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								src/documents/static/bootstrap.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -654,7 +654,7 @@ class TestClassifier(DirectoriesMixin, TestCase): | |||||||
|         }, |         }, | ||||||
|     ) |     ) | ||||||
|     @override_settings( |     @override_settings( | ||||||
|         MODEL_FILE=(Path(__file__).parent / "data" / "model.pickle").as_posix(), |         MODEL_FILE=str(Path(__file__).parent / "data" / "model.pickle"), | ||||||
|     ) |     ) | ||||||
|     @pytest.mark.skip( |     @pytest.mark.skip( | ||||||
|         reason="Disabled caching due to high memory usage - need to investigate.", |         reason="Disabled caching due to high memory usage - need to investigate.", | ||||||
|   | |||||||
| @@ -254,7 +254,7 @@ class TestConsumer( | |||||||
|         # https://github.com/jonaswinkler/paperless-ng/discussions/1037 |         # https://github.com/jonaswinkler/paperless-ng/discussions/1037 | ||||||
|  |  | ||||||
|         filename = self.get_test_file() |         filename = self.get_test_file() | ||||||
|         shadow_file = Path(self.dirs.scratch_dir / "._sample.pdf") |         shadow_file = Path(self.dirs.scratch_dir) / "._sample.pdf" | ||||||
|  |  | ||||||
|         shutil.copy(filename, shadow_file) |         shutil.copy(filename, shadow_file) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -258,66 +258,66 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase): | |||||||
|     def test_is_ignored(self): |     def test_is_ignored(self): | ||||||
|         test_paths = [ |         test_paths = [ | ||||||
|             { |             { | ||||||
|                 "path": (Path(self.dirs.consumption_dir) / "foo.pdf").as_posix(), |                 "path": str(Path(self.dirs.consumption_dir) / "foo.pdf"), | ||||||
|                 "ignore": False, |                 "ignore": False, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": ( |                 "path": str( | ||||||
|                     Path(self.dirs.consumption_dir) / "foo" / "bar.pdf" |                     Path(self.dirs.consumption_dir) / "foo" / "bar.pdf", | ||||||
|                 ).as_posix(), |                 ), | ||||||
|                 "ignore": False, |                 "ignore": False, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": (Path(self.dirs.consumption_dir) / ".DS_STORE").as_posix(), |                 "path": str(Path(self.dirs.consumption_dir) / ".DS_STORE"), | ||||||
|                 "ignore": True, |                 "ignore": True, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": (Path(self.dirs.consumption_dir) / ".DS_Store").as_posix(), |                 "path": str(Path(self.dirs.consumption_dir) / ".DS_Store"), | ||||||
|                 "ignore": True, |                 "ignore": True, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": ( |                 "path": str( | ||||||
|                     Path(self.dirs.consumption_dir) / ".stfolder" / "foo.pdf" |                     Path(self.dirs.consumption_dir) / ".stfolder" / "foo.pdf", | ||||||
|                 ).as_posix(), |                 ), | ||||||
|                 "ignore": True, |                 "ignore": True, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": (Path(self.dirs.consumption_dir) / ".stfolder.pdf").as_posix(), |                 "path": str(Path(self.dirs.consumption_dir) / ".stfolder.pdf"), | ||||||
|                 "ignore": False, |                 "ignore": False, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": ( |                 "path": str( | ||||||
|                     Path(self.dirs.consumption_dir) / ".stversions" / "foo.pdf" |                     Path(self.dirs.consumption_dir) / ".stversions" / "foo.pdf", | ||||||
|                 ).as_posix(), |                 ), | ||||||
|                 "ignore": True, |                 "ignore": True, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": ( |                 "path": str( | ||||||
|                     Path(self.dirs.consumption_dir) / ".stversions.pdf" |                     Path(self.dirs.consumption_dir) / ".stversions.pdf", | ||||||
|                 ).as_posix(), |                 ), | ||||||
|                 "ignore": False, |                 "ignore": False, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": (Path(self.dirs.consumption_dir) / "._foo.pdf").as_posix(), |                 "path": str(Path(self.dirs.consumption_dir) / "._foo.pdf"), | ||||||
|                 "ignore": True, |                 "ignore": True, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": (Path(self.dirs.consumption_dir) / "my_foo.pdf").as_posix(), |                 "path": str(Path(self.dirs.consumption_dir) / "my_foo.pdf"), | ||||||
|                 "ignore": False, |                 "ignore": False, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": ( |                 "path": str( | ||||||
|                     Path(self.dirs.consumption_dir) / "._foo" / "bar.pdf" |                     Path(self.dirs.consumption_dir) / "._foo" / "bar.pdf", | ||||||
|                 ).as_posix(), |                 ), | ||||||
|                 "ignore": True, |                 "ignore": True, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "path": ( |                 "path": str( | ||||||
|                     Path(self.dirs.consumption_dir) |                     Path(self.dirs.consumption_dir) | ||||||
|                     / "@eaDir" |                     / "@eaDir" | ||||||
|                     / "SYNO@.fileindexdb" |                     / "SYNO@.fileindexdb" | ||||||
|                     / "_1jk.fnm" |                     / "_1jk.fnm", | ||||||
|                 ).as_posix(), |                 ), | ||||||
|                 "ignore": True, |                 "ignore": True, | ||||||
|             }, |             }, | ||||||
|         ] |         ] | ||||||
| @@ -330,7 +330,7 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase): | |||||||
|                 f'_is_ignored("{filepath}") != {expected_ignored_result}', |                 f'_is_ignored("{filepath}") != {expected_ignored_result}', | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|     @mock.patch("documents.management.commands.document_consumer.open") |     @mock.patch("documents.management.commands.document_consumer.Path.open") | ||||||
|     def test_consume_file_busy(self, open_mock): |     def test_consume_file_busy(self, open_mock): | ||||||
|         # Calling this mock always raises this |         # Calling this mock always raises this | ||||||
|         open_mock.side_effect = OSError |         open_mock.side_effect = OSError | ||||||
|   | |||||||
| @@ -230,9 +230,9 @@ class TestExportImport( | |||||||
|  |  | ||||||
|         for element in manifest: |         for element in manifest: | ||||||
|             if element["model"] == "documents.document": |             if element["model"] == "documents.document": | ||||||
|                 fname = ( |                 fname = str( | ||||||
|                     self.target / element[document_exporter.EXPORTER_FILE_NAME] |                     self.target / element[document_exporter.EXPORTER_FILE_NAME], | ||||||
|                 ).as_posix() |                 ) | ||||||
|                 self.assertIsFile(fname) |                 self.assertIsFile(fname) | ||||||
|                 self.assertIsFile( |                 self.assertIsFile( | ||||||
|                     self.target / element[document_exporter.EXPORTER_THUMBNAIL_NAME], |                     self.target / element[document_exporter.EXPORTER_THUMBNAIL_NAME], | ||||||
| @@ -462,9 +462,9 @@ class TestExportImport( | |||||||
|  |  | ||||||
|         call_command(*args) |         call_command(*args) | ||||||
|  |  | ||||||
|         expected_file = ( |         expected_file = str( | ||||||
|             self.target / f"export-{timezone.localdate().isoformat()}.zip" |             self.target / f"export-{timezone.localdate().isoformat()}.zip", | ||||||
|         ).as_posix() |         ) | ||||||
|  |  | ||||||
|         self.assertIsFile(expected_file) |         self.assertIsFile(expected_file) | ||||||
|  |  | ||||||
| @@ -498,9 +498,9 @@ class TestExportImport( | |||||||
|         ): |         ): | ||||||
|             call_command(*args) |             call_command(*args) | ||||||
|  |  | ||||||
|         expected_file = ( |         expected_file = str( | ||||||
|             self.target / f"export-{timezone.localdate().isoformat()}.zip" |             self.target / f"export-{timezone.localdate().isoformat()}.zip", | ||||||
|         ).as_posix() |         ) | ||||||
|  |  | ||||||
|         self.assertIsFile(expected_file) |         self.assertIsFile(expected_file) | ||||||
|  |  | ||||||
| @@ -544,9 +544,9 @@ class TestExportImport( | |||||||
|  |  | ||||||
|         call_command(*args) |         call_command(*args) | ||||||
|  |  | ||||||
|         expected_file = ( |         expected_file = str( | ||||||
|             self.target / f"export-{timezone.localdate().isoformat()}.zip" |             self.target / f"export-{timezone.localdate().isoformat()}.zip", | ||||||
|         ).as_posix() |         ) | ||||||
|  |  | ||||||
|         self.assertIsFile(expected_file) |         self.assertIsFile(expected_file) | ||||||
|         self.assertIsNotFile(existing_file) |         self.assertIsNotFile(existing_file) | ||||||
|   | |||||||
| @@ -19,15 +19,15 @@ migration_1012_obj = importlib.import_module( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def archive_name_from_filename(filename): | def archive_name_from_filename(filename: Path) -> Path: | ||||||
|     return Path(filename).stem + ".pdf" |     return Path(filename.stem + ".pdf") | ||||||
|  |  | ||||||
|  |  | ||||||
| def archive_path_old(self): | def archive_path_old(self) -> Path: | ||||||
|     if self.filename: |     if self.filename: | ||||||
|         fname = archive_name_from_filename(self.filename) |         fname = archive_name_from_filename(Path(self.filename)) | ||||||
|     else: |     else: | ||||||
|         fname = f"{self.pk:07}.pdf" |         fname = Path(f"{self.pk:07}.pdf") | ||||||
|  |  | ||||||
|     return Path(settings.ARCHIVE_DIR) / fname |     return Path(settings.ARCHIVE_DIR) / fname | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2899,7 +2899,7 @@ class SystemStatusView(PassUserMixin): | |||||||
|             install_type = "docker" |             install_type = "docker" | ||||||
|  |  | ||||||
|         db_conn = connections["default"] |         db_conn = connections["default"] | ||||||
|         db_url = db_conn.settings_dict["NAME"] |         db_url = str(db_conn.settings_dict["NAME"]) | ||||||
|         db_error = None |         db_error = None | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|   | |||||||
| @@ -711,7 +711,7 @@ def _parse_db_settings() -> dict: | |||||||
|     databases = { |     databases = { | ||||||
|         "default": { |         "default": { | ||||||
|             "ENGINE": "django.db.backends.sqlite3", |             "ENGINE": "django.db.backends.sqlite3", | ||||||
|             "NAME": str(DATA_DIR / "db.sqlite3"), |             "NAME": DATA_DIR / "db.sqlite3", | ||||||
|             "OPTIONS": {}, |             "OPTIONS": {}, | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
| @@ -839,7 +839,7 @@ LANGUAGES = [ | |||||||
|     ("zh-tw", _("Chinese Traditional")), |     ("zh-tw", _("Chinese Traditional")), | ||||||
| ] | ] | ||||||
|  |  | ||||||
| LOCALE_PATHS = [str(BASE_DIR / "locale")] | LOCALE_PATHS = [BASE_DIR / "locale"] | ||||||
|  |  | ||||||
| TIME_ZONE = os.getenv("PAPERLESS_TIME_ZONE", "UTC") | TIME_ZONE = os.getenv("PAPERLESS_TIME_ZONE", "UTC") | ||||||
|  |  | ||||||
| @@ -880,21 +880,21 @@ LOGGING = { | |||||||
|         "file_paperless": { |         "file_paperless": { | ||||||
|             "class": "concurrent_log_handler.ConcurrentRotatingFileHandler", |             "class": "concurrent_log_handler.ConcurrentRotatingFileHandler", | ||||||
|             "formatter": "verbose", |             "formatter": "verbose", | ||||||
|             "filename": str(LOGGING_DIR / "paperless.log"), |             "filename": LOGGING_DIR / "paperless.log", | ||||||
|             "maxBytes": LOGROTATE_MAX_SIZE, |             "maxBytes": LOGROTATE_MAX_SIZE, | ||||||
|             "backupCount": LOGROTATE_MAX_BACKUPS, |             "backupCount": LOGROTATE_MAX_BACKUPS, | ||||||
|         }, |         }, | ||||||
|         "file_mail": { |         "file_mail": { | ||||||
|             "class": "concurrent_log_handler.ConcurrentRotatingFileHandler", |             "class": "concurrent_log_handler.ConcurrentRotatingFileHandler", | ||||||
|             "formatter": "verbose", |             "formatter": "verbose", | ||||||
|             "filename": str(LOGGING_DIR / "mail.log"), |             "filename": LOGGING_DIR / "mail.log", | ||||||
|             "maxBytes": LOGROTATE_MAX_SIZE, |             "maxBytes": LOGROTATE_MAX_SIZE, | ||||||
|             "backupCount": LOGROTATE_MAX_BACKUPS, |             "backupCount": LOGROTATE_MAX_BACKUPS, | ||||||
|         }, |         }, | ||||||
|         "file_celery": { |         "file_celery": { | ||||||
|             "class": "concurrent_log_handler.ConcurrentRotatingFileHandler", |             "class": "concurrent_log_handler.ConcurrentRotatingFileHandler", | ||||||
|             "formatter": "verbose", |             "formatter": "verbose", | ||||||
|             "filename": str(LOGGING_DIR / "celery.log"), |             "filename": LOGGING_DIR / "celery.log", | ||||||
|             "maxBytes": LOGROTATE_MAX_SIZE, |             "maxBytes": LOGROTATE_MAX_SIZE, | ||||||
|             "backupCount": LOGROTATE_MAX_BACKUPS, |             "backupCount": LOGROTATE_MAX_BACKUPS, | ||||||
|         }, |         }, | ||||||
| @@ -954,7 +954,7 @@ CELERY_ACCEPT_CONTENT = ["application/json", "application/x-python-serialize"] | |||||||
| CELERY_BEAT_SCHEDULE = _parse_beat_schedule() | CELERY_BEAT_SCHEDULE = _parse_beat_schedule() | ||||||
|  |  | ||||||
| # https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-schedule-filename | # https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-schedule-filename | ||||||
| CELERY_BEAT_SCHEDULE_FILENAME = str(DATA_DIR / "celerybeat-schedule.db") | CELERY_BEAT_SCHEDULE_FILENAME = DATA_DIR / "celerybeat-schedule.db" | ||||||
|  |  | ||||||
|  |  | ||||||
| # Cachalot: Database read cache. | # Cachalot: Database read cache. | ||||||
|   | |||||||
| @@ -69,13 +69,13 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(uuid.uuid4()) |         parser = RasterisedDocumentParser(uuid.uuid4()) | ||||||
|         page_count = parser.get_page_count( |         page_count = parser.get_page_count( | ||||||
|             (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "simple-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(page_count, 1) |         self.assertEqual(page_count, 1) | ||||||
|  |  | ||||||
|         page_count = parser.get_page_count( |         page_count = parser.get_page_count( | ||||||
|             (self.SAMPLE_FILES / "multi-page-mixed.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-mixed.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(page_count, 6) |         self.assertEqual(page_count, 6) | ||||||
| @@ -92,7 +92,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         parser = RasterisedDocumentParser(uuid.uuid4()) |         parser = RasterisedDocumentParser(uuid.uuid4()) | ||||||
|         with self.assertLogs("paperless.parsing.tesseract", level="WARNING") as cm: |         with self.assertLogs("paperless.parsing.tesseract", level="WARNING") as cm: | ||||||
|             page_count = parser.get_page_count( |             page_count = parser.get_page_count( | ||||||
|                 (self.SAMPLE_FILES / "password-protected.pdf").as_posix(), |                 str(self.SAMPLE_FILES / "password-protected.pdf"), | ||||||
|                 "application/pdf", |                 "application/pdf", | ||||||
|             ) |             ) | ||||||
|             self.assertEqual(page_count, None) |             self.assertEqual(page_count, None) | ||||||
| @@ -101,7 +101,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_thumbnail(self): |     def test_thumbnail(self): | ||||||
|         parser = RasterisedDocumentParser(uuid.uuid4()) |         parser = RasterisedDocumentParser(uuid.uuid4()) | ||||||
|         thumb = parser.get_thumbnail( |         thumb = parser.get_thumbnail( | ||||||
|             (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "simple-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(thumb) |         self.assertIsFile(thumb) | ||||||
| @@ -109,7 +109,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     @mock.patch("documents.parsers.run_convert") |     @mock.patch("documents.parsers.run_convert") | ||||||
|     def test_thumbnail_fallback(self, m): |     def test_thumbnail_fallback(self, m): | ||||||
|         def call_convert(input_file, output_file, **kwargs): |         def call_convert(input_file, output_file, **kwargs): | ||||||
|             if ".pdf" in input_file: |             if ".pdf" in str(input_file): | ||||||
|                 raise ParseError("Does not compute.") |                 raise ParseError("Does not compute.") | ||||||
|             else: |             else: | ||||||
|                 run_convert(input_file=input_file, output_file=output_file, **kwargs) |                 run_convert(input_file=input_file, output_file=output_file, **kwargs) | ||||||
| @@ -118,7 +118,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|  |  | ||||||
|         parser = RasterisedDocumentParser(uuid.uuid4()) |         parser = RasterisedDocumentParser(uuid.uuid4()) | ||||||
|         thumb = parser.get_thumbnail( |         thumb = parser.get_thumbnail( | ||||||
|             (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "simple-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(thumb) |         self.assertIsFile(thumb) | ||||||
| @@ -126,7 +126,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_thumbnail_encrypted(self): |     def test_thumbnail_encrypted(self): | ||||||
|         parser = RasterisedDocumentParser(uuid.uuid4()) |         parser = RasterisedDocumentParser(uuid.uuid4()) | ||||||
|         thumb = parser.get_thumbnail( |         thumb = parser.get_thumbnail( | ||||||
|             (self.SAMPLE_FILES / "encrypted.pdf").as_posix(), |             str(self.SAMPLE_FILES / "encrypted.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(thumb) |         self.assertIsFile(thumb) | ||||||
| @@ -134,17 +134,17 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_get_dpi(self): |     def test_get_dpi(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         dpi = parser.get_dpi((self.SAMPLE_FILES / "simple-no-dpi.png").as_posix()) |         dpi = parser.get_dpi(str(self.SAMPLE_FILES / "simple-no-dpi.png")) | ||||||
|         self.assertEqual(dpi, None) |         self.assertEqual(dpi, None) | ||||||
|  |  | ||||||
|         dpi = parser.get_dpi((self.SAMPLE_FILES / "simple.png").as_posix()) |         dpi = parser.get_dpi(str(self.SAMPLE_FILES / "simple.png")) | ||||||
|         self.assertEqual(dpi, 72) |         self.assertEqual(dpi, 72) | ||||||
|  |  | ||||||
|     def test_simple_digital(self): |     def test_simple_digital(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "simple-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -156,7 +156,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "with-form.pdf").as_posix(), |             str(self.SAMPLE_FILES / "with-form.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -172,7 +172,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "with-form.pdf").as_posix(), |             str(self.SAMPLE_FILES / "with-form.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -186,7 +186,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_signed(self): |     def test_signed(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse((self.SAMPLE_FILES / "signed.pdf").as_posix(), "application/pdf") |         parser.parse(str(self.SAMPLE_FILES / "signed.pdf"), "application/pdf") | ||||||
|  |  | ||||||
|         self.assertIsNone(parser.archive_path) |         self.assertIsNone(parser.archive_path) | ||||||
|         self.assertContainsStrings( |         self.assertContainsStrings( | ||||||
| @@ -202,7 +202,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "encrypted.pdf").as_posix(), |             str(self.SAMPLE_FILES / "encrypted.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -213,7 +213,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_with_form_error_notext(self): |     def test_with_form_error_notext(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "with-form.pdf").as_posix(), |             str(self.SAMPLE_FILES / "with-form.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -227,7 +227,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "with-form.pdf").as_posix(), |             str(self.SAMPLE_FILES / "with-form.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -239,7 +239,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_image_simple(self): |     def test_image_simple(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse((self.SAMPLE_FILES / "simple.png").as_posix(), "image/png") |         parser.parse(str(self.SAMPLE_FILES / "simple.png"), "image/png") | ||||||
|  |  | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
|  |  | ||||||
| @@ -255,7 +255,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|             dest_file = Path(tempdir) / "simple-alpha.png" |             dest_file = Path(tempdir) / "simple-alpha.png" | ||||||
|             shutil.copy(sample_file, dest_file) |             shutil.copy(sample_file, dest_file) | ||||||
|  |  | ||||||
|             parser.parse(dest_file.as_posix(), "image/png") |             parser.parse(str(dest_file), "image/png") | ||||||
|  |  | ||||||
|             self.assertIsFile(parser.archive_path) |             self.assertIsFile(parser.archive_path) | ||||||
|  |  | ||||||
| @@ -265,7 +265,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         dpi = parser.calculate_a4_dpi( |         dpi = parser.calculate_a4_dpi( | ||||||
|             (self.SAMPLE_FILES / "simple-no-dpi.png").as_posix(), |             str(self.SAMPLE_FILES / "simple-no-dpi.png"), | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         self.assertEqual(dpi, 62) |         self.assertEqual(dpi, 62) | ||||||
| @@ -277,7 +277,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|  |  | ||||||
|         def f(): |         def f(): | ||||||
|             parser.parse( |             parser.parse( | ||||||
|                 (self.SAMPLE_FILES / "simple-no-dpi.png").as_posix(), |                 str(self.SAMPLE_FILES / "simple-no-dpi.png"), | ||||||
|                 "image/png", |                 "image/png", | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
| @@ -287,7 +287,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_image_no_dpi_default(self): |     def test_image_no_dpi_default(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse((self.SAMPLE_FILES / "simple-no-dpi.png").as_posix(), "image/png") |         parser.parse(str(self.SAMPLE_FILES / "simple-no-dpi.png"), "image/png") | ||||||
|  |  | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
|  |  | ||||||
| @@ -299,7 +299,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_multi_page(self): |     def test_multi_page(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
| @@ -312,7 +312,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_multi_page_pages_skip(self): |     def test_multi_page_pages_skip(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
| @@ -325,7 +325,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_multi_page_pages_redo(self): |     def test_multi_page_pages_redo(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
| @@ -338,7 +338,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_multi_page_pages_force(self): |     def test_multi_page_pages_force(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
| @@ -351,7 +351,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_multi_page_analog_pages_skip(self): |     def test_multi_page_analog_pages_skip(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-images.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
| @@ -375,7 +375,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-images.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
| @@ -397,7 +397,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-images.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
| @@ -419,7 +419,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNone(parser.archive_path) |         self.assertIsNone(parser.archive_path) | ||||||
| @@ -442,7 +442,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-images.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -467,7 +467,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNotNone(parser.archive_path) |         self.assertIsNotNone(parser.archive_path) | ||||||
| @@ -490,7 +490,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-images.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNotNone(parser.archive_path) |         self.assertIsNotNone(parser.archive_path) | ||||||
| @@ -513,7 +513,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNone(parser.archive_path) |         self.assertIsNone(parser.archive_path) | ||||||
| @@ -536,7 +536,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-images.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNotNone(parser.archive_path) |         self.assertIsNotNone(parser.archive_path) | ||||||
| @@ -559,7 +559,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNone(parser.archive_path) |         self.assertIsNone(parser.archive_path) | ||||||
| @@ -582,7 +582,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-images.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNone(parser.archive_path) |         self.assertIsNone(parser.archive_path) | ||||||
| @@ -605,7 +605,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-mixed.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-mixed.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNotNone(parser.archive_path) |         self.assertIsNotNone(parser.archive_path) | ||||||
| @@ -636,7 +636,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "single-page-mixed.pdf").as_posix(), |             str(self.SAMPLE_FILES / "single-page-mixed.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNotNone(parser.archive_path) |         self.assertIsNotNone(parser.archive_path) | ||||||
| @@ -673,7 +673,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-mixed.pdf").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-mixed.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|         self.assertIsNone(parser.archive_path) |         self.assertIsNone(parser.archive_path) | ||||||
| @@ -685,7 +685,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     @override_settings(OCR_MODE="skip", OCR_ROTATE_PAGES=True) |     @override_settings(OCR_MODE="skip", OCR_ROTATE_PAGES=True) | ||||||
|     def test_rotate(self): |     def test_rotate(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse((self.SAMPLE_FILES / "rotated.pdf").as_posix(), "application/pdf") |         parser.parse(str(self.SAMPLE_FILES / "rotated.pdf"), "application/pdf") | ||||||
|         self.assertContainsStrings( |         self.assertContainsStrings( | ||||||
|             parser.get_text(), |             parser.get_text(), | ||||||
|             [ |             [ | ||||||
| @@ -707,7 +707,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "multi-page-images.tiff").as_posix(), |             str(self.SAMPLE_FILES / "multi-page-images.tiff"), | ||||||
|             "image/tiff", |             "image/tiff", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
| @@ -752,9 +752,9 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|             - Text from all pages extracted |             - Text from all pages extracted | ||||||
|         """ |         """ | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         sample_file = ( |         sample_file = str( | ||||||
|             self.SAMPLE_FILES / "multi-page-images-alpha-rgb.tiff" |             self.SAMPLE_FILES / "multi-page-images-alpha-rgb.tiff", | ||||||
|         ).as_posix() |         ) | ||||||
|         with tempfile.NamedTemporaryFile() as tmp_file: |         with tempfile.NamedTemporaryFile() as tmp_file: | ||||||
|             shutil.copy(sample_file, tmp_file.name) |             shutil.copy(sample_file, tmp_file.name) | ||||||
|             parser.parse( |             parser.parse( | ||||||
| @@ -843,7 +843,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|  |  | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "rtl-test.pdf").as_posix(), |             str(self.SAMPLE_FILES / "rtl-test.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -858,7 +858,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|         self.assertRaises( |         self.assertRaises( | ||||||
|             ParseError, |             ParseError, | ||||||
|             parser.parse, |             parser.parse, | ||||||
|             (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(), |             str(self.SAMPLE_FILES / "simple-digital.pdf"), | ||||||
|             "application/pdf", |             "application/pdf", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -868,32 +868,32 @@ class TestParserFileTypes(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|  |  | ||||||
|     def test_bmp(self): |     def test_bmp(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse((self.SAMPLE_FILES / "simple.bmp").as_posix(), "image/bmp") |         parser.parse(str(self.SAMPLE_FILES / "simple.bmp"), "image/bmp") | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
|         self.assertIn("this is a test document", parser.get_text().lower()) |         self.assertIn("this is a test document", parser.get_text().lower()) | ||||||
|  |  | ||||||
|     def test_jpg(self): |     def test_jpg(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse((self.SAMPLE_FILES / "simple.jpg").as_posix(), "image/jpeg") |         parser.parse(str(self.SAMPLE_FILES / "simple.jpg"), "image/jpeg") | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
|         self.assertIn("this is a test document", parser.get_text().lower()) |         self.assertIn("this is a test document", parser.get_text().lower()) | ||||||
|  |  | ||||||
|     def test_heic(self): |     def test_heic(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse((self.SAMPLE_FILES / "simple.heic").as_posix(), "image/heic") |         parser.parse(str(self.SAMPLE_FILES / "simple.heic"), "image/heic") | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
|         self.assertIn("pizza", parser.get_text().lower()) |         self.assertIn("pizza", parser.get_text().lower()) | ||||||
|  |  | ||||||
|     @override_settings(OCR_IMAGE_DPI=200) |     @override_settings(OCR_IMAGE_DPI=200) | ||||||
|     def test_gif(self): |     def test_gif(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse((self.SAMPLE_FILES / "simple.gif").as_posix(), "image/gif") |         parser.parse(str(self.SAMPLE_FILES / "simple.gif"), "image/gif") | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
|         self.assertIn("this is a test document", parser.get_text().lower()) |         self.assertIn("this is a test document", parser.get_text().lower()) | ||||||
|  |  | ||||||
|     def test_tiff(self): |     def test_tiff(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse((self.SAMPLE_FILES / "simple.tif").as_posix(), "image/tiff") |         parser.parse(str(self.SAMPLE_FILES / "simple.tif"), "image/tiff") | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
|         self.assertIn("this is a test document", parser.get_text().lower()) |         self.assertIn("this is a test document", parser.get_text().lower()) | ||||||
|  |  | ||||||
| @@ -901,7 +901,7 @@ class TestParserFileTypes(DirectoriesMixin, FileSystemAssertsMixin, TestCase): | |||||||
|     def test_webp(self): |     def test_webp(self): | ||||||
|         parser = RasterisedDocumentParser(None) |         parser = RasterisedDocumentParser(None) | ||||||
|         parser.parse( |         parser.parse( | ||||||
|             (self.SAMPLE_FILES / "document.webp").as_posix(), |             str(self.SAMPLE_FILES / "document.webp"), | ||||||
|             "image/webp", |             "image/webp", | ||||||
|         ) |         ) | ||||||
|         self.assertIsFile(parser.archive_path) |         self.assertIsFile(parser.archive_path) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user