Compare commits

..

19 Commits

Author SHA1 Message Date
shamoon
45c346f44f Enhancement: collapsible sidebar menus 2025-06-19 03:09:36 -07:00
Sebastian Steinbeißer
07882b918b Chore: switch from os.path to pathlib.Path (#9933)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2025-06-18 17:16:59 +00:00
shamoon
cc5ba71f06 Chore: remove spaces from run log 2025-06-17 16:02:45 -07:00
GitHub Actions
f16b8fbe2a Auto translate strings 2025-06-17 19:02:55 +00:00
shamoon
fe54d99356 Chore: bump angular to 19.12.14 (#10212) 2025-06-17 12:01:15 -07:00
GitHub Actions
a49efb07ea Auto translate strings 2025-06-17 05:46:29 +00:00
shamoon
e4fd008441 Fix: fix some API crashes (#10196) 2025-06-16 22:44:39 -07:00
robertmx
de12023311 Fix: remove duplicate base path in websocket urls (#10194) 2025-06-16 10:50:15 -07:00
shamoon
60ebdc0ad6 Fix: use hard delete for custom fields with workflow removal (#10191) 2025-06-16 07:09:22 -07:00
Michael Hobl
cbd9823ad6 Documentation: correct Gotenberg API timeout value (#10186) 2025-06-15 07:36:49 -07:00
shamoon
ce76303a32 Feature: add Persian translation (#10183) 2025-06-14 19:14:51 -07:00
Kilian
246f17c6c8 Enhancement: support import of zipped export (#10073) 2025-06-13 10:06:37 -07:00
shamoon
4313635b01 Revert "Development: fix dependabot by specifying pnpm version (#10103)"
This reverts commit 9c9a0e4496.
2025-06-10 09:19:00 -07:00
shamoon
7ca2bd0666 Fix: fix mail account test api schema (#10164) 2025-06-10 07:09:24 -07:00
shamoon
4c6075e962 Fix: correct api schema for mail_account process (#10157) 2025-06-09 14:51:58 -07:00
shamoon
8d48e99487 Fix: correct api schema for next_asn (#10151) 2025-06-09 09:32:34 -07:00
shamoon
454a2d9e9e Fix: fix email and notes endpoints api spec (#10148) 2025-06-09 07:09:28 -07:00
shamoon
b8c713d4b9 Chore: get CI running on crowdin PRs 2025-06-06 00:29:06 -07:00
github-actions[bot]
2a8cb87232 Changelog v2.16.3 - GHA (#10129)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-05 14:41:33 -07:00
35 changed files with 692 additions and 404 deletions

View File

@@ -162,7 +162,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
package_json_file: 'src-ui/package.json' version: 10
- name: Use Node.js 20 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
@@ -195,7 +195,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
package_json_file: 'src-ui/package.json' version: 10
- name: Use Node.js 20 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
@@ -245,7 +245,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
package_json_file: 'src-ui/package.json' version: 10
- name: Use Node.js 20 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
@@ -288,7 +288,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
package_json_file: 'src-ui/package.json' version: 10
- name: Use Node.js 20 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:

View File

@@ -14,6 +14,8 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
token: ${{ secrets.PNGX_BOT_PAT }}
- name: crowdin action - name: crowdin action
uses: crowdin/github-action@v2 uses: crowdin/github-action@v2
with: with:

View File

@@ -333,7 +333,7 @@ must be provided to import. If this value is lost, the export cannot be imported
The document importer takes the export produced by the [Document The document importer takes the export produced by the [Document
exporter](#exporter) and imports it into paperless. exporter](#exporter) and imports it into paperless.
The importer works just like the exporter. You point it at a directory, The importer works just like the exporter. You point it at a directory or the generated .zip file,
and the script does the rest of the work: and the script does the rest of the work:
```shell ```shell
@@ -351,9 +351,6 @@ When you use the provided docker compose script, put the export inside
the `export` folder in your paperless source directory. Specify the `export` folder in your paperless source directory. Specify
`../export` as the `source`. `../export` as the `source`.
Note that .zip files (as can be generated from the exporter) are not supported. You must unzip them into
the target directory first.
!!! note !!! note
Importing from a previous version of Paperless may work, but for best Importing from a previous version of Paperless may work, but for best

View File

@@ -1,5 +1,61 @@
# Changelog # Changelog
## paperless-ngx 2.16.3
### Features / Enhancements
- Performance: pre-filter document list in scheduled workflow checks [@shamoon](https://github.com/shamoon) ([#10031](https://github.com/paperless-ngx/paperless-ngx/pull/10031))
- Chore: refactor consumer plugin checks to a pre-flight plugin [@shamoon](https://github.com/shamoon) ([#9994](https://github.com/paperless-ngx/paperless-ngx/pull/9994))
- Enhancement: include DOCUMENT_TYPE to post consume scripts [@matthesrieke](https://github.com/matthesrieke) ([#9977](https://github.com/paperless-ngx/paperless-ngx/pull/9977))
### Bug Fixes
- Fix: handle whoosh query correction errors [@shamoon](https://github.com/shamoon) ([#10121](https://github.com/paperless-ngx/paperless-ngx/pull/10121))
- Fix: handle favicon with non-default static dir [@shamoon](https://github.com/shamoon) ([#10107](https://github.com/paperless-ngx/paperless-ngx/pull/10107))
- Fixhancement: cleanup user or group references in settings upon deletion [@shamoon](https://github.com/shamoon) ([#10049](https://github.com/paperless-ngx/paperless-ngx/pull/10049))
- Fix: Add exception to in [@Freilichtbuehne](https://github.com/Freilichtbuehne) ([#10070](https://github.com/paperless-ngx/paperless-ngx/pull/10070))
- Fix: include base href when opening global search result in new window [@shamoon](https://github.com/shamoon) ([#10066](https://github.com/paperless-ngx/paperless-ngx/pull/10066))
### Dependencies
<details>
<summary>10 changes</summary>
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10085](https://github.com/paperless-ngx/paperless-ngx/pull/10085))
- Chore(deps): Update granian[uvloop] requirement from ~=2.2.0 to ~=2.3.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10055](https://github.com/paperless-ngx/paperless-ngx/pull/10055))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 18 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10099](https://github.com/paperless-ngx/paperless-ngx/pull/10099))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10100](https://github.com/paperless-ngx/paperless-ngx/pull/10100))
- Chore(deps): Bump bootstrap from 5.3.3 to 5.3.6 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10091](https://github.com/paperless-ngx/paperless-ngx/pull/10091))
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.9.0 to 1.9.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10090](https://github.com/paperless-ngx/paperless-ngx/pull/10090))
- Chore(deps-dev): Bump @types/node from 22.15.3 to 22.15.29 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10089](https://github.com/paperless-ngx/paperless-ngx/pull/10089))
- Chore(deps): Bump zone.js from 0.15.0 to 0.15.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10088](https://github.com/paperless-ngx/paperless-ngx/pull/10088))
- docker(deps): bump astral-sh/uv from 0.7.8-python3.12-bookworm-slim to 0.7.9-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10084](https://github.com/paperless-ngx/paperless-ngx/pull/10084))
- docker(deps): Bump astral-sh/uv from 0.6.16-python3.12-bookworm-slim to 0.7.8-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10056](https://github.com/paperless-ngx/paperless-ngx/pull/10056))
</details>
### All App Changes
<details>
<summary>16 changes</summary>
- Fix: handle whoosh query correction errors [@shamoon](https://github.com/shamoon) ([#10121](https://github.com/paperless-ngx/paperless-ngx/pull/10121))
- Performance: pre-filter document list in scheduled workflow checks [@shamoon](https://github.com/shamoon) ([#10031](https://github.com/paperless-ngx/paperless-ngx/pull/10031))
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10085](https://github.com/paperless-ngx/paperless-ngx/pull/10085))
- Chore: refactor consumer plugin checks to a pre-flight plugin [@shamoon](https://github.com/shamoon) ([#9994](https://github.com/paperless-ngx/paperless-ngx/pull/9994))
- Chore(deps): Update granian[uvloop] requirement from ~=2.2.0 to ~=2.3.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10055](https://github.com/paperless-ngx/paperless-ngx/pull/10055))
- Fix: handle favicon with non-default static dir [@shamoon](https://github.com/shamoon) ([#10107](https://github.com/paperless-ngx/paperless-ngx/pull/10107))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 18 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10099](https://github.com/paperless-ngx/paperless-ngx/pull/10099))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10100](https://github.com/paperless-ngx/paperless-ngx/pull/10100))
- Chore(deps): Bump bootstrap from 5.3.3 to 5.3.6 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10091](https://github.com/paperless-ngx/paperless-ngx/pull/10091))
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.9.0 to 1.9.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10090](https://github.com/paperless-ngx/paperless-ngx/pull/10090))
- Chore(deps-dev): Bump @types/node from 22.15.3 to 22.15.29 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10089](https://github.com/paperless-ngx/paperless-ngx/pull/10089))
- Chore(deps): Bump zone.js from 0.15.0 to 0.15.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10088](https://github.com/paperless-ngx/paperless-ngx/pull/10088))
- Fixhancement: cleanup user or group references in settings upon deletion [@shamoon](https://github.com/shamoon) ([#10049](https://github.com/paperless-ngx/paperless-ngx/pull/10049))
- Enhancement: include DOCUMENT_TYPE to post consume scripts [@matthesrieke](https://github.com/matthesrieke) ([#9977](https://github.com/paperless-ngx/paperless-ngx/pull/9977))
- Fix: Add exception to in [@Freilichtbuehne](https://github.com/Freilichtbuehne) ([#10070](https://github.com/paperless-ngx/paperless-ngx/pull/10070))
- Fix: include base href when opening global search result in new window [@shamoon](https://github.com/shamoon) ([#10066](https://github.com/paperless-ngx/paperless-ngx/pull/10066))
</details>
## paperless-ngx 2.16.2 ## paperless-ngx 2.16.2
### Bug Fixes ### Bug Fixes

View File

@@ -130,7 +130,7 @@ command:
- 'gotenberg' - 'gotenberg'
- '--chromium-disable-javascript=true' - '--chromium-disable-javascript=true'
- '--chromium-allow-list=file:///tmp/.*' - '--chromium-allow-list=file:///tmp/.*'
- '--api-timeout=60' - '--api-timeout=60s'
``` ```
## Permission denied errors in the consumption directory ## Permission denied errors in the consumption directory

View File

@@ -221,22 +221,12 @@ lint.per-file-ignores."src/documents/parsers.py" = [
lint.per-file-ignores."src/documents/signals/handlers.py" = [ lint.per-file-ignores."src/documents/signals/handlers.py" = [
"PTH", "PTH",
] # TODO Enable & remove ] # TODO Enable & remove
lint.per-file-ignores."src/documents/views.py" = [
"PTH",
] # TODO Enable & remove
lint.per-file-ignores."src/paperless/checks.py" = [
"PTH",
] # TODO Enable & remove
lint.per-file-ignores."src/paperless/settings.py" = [ lint.per-file-ignores."src/paperless/settings.py" = [
"PTH", "PTH",
] # TODO Enable & remove ] # TODO Enable & remove
lint.per-file-ignores."src/paperless_mail/mail.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" = [
"PTH",
"RUF001", "RUF001",
] # TODO PTH Enable & remove ]
lint.isort.force-single-line = true lint.isort.force-single-line = true
[tool.pytest.ini_options] [tool.pytest.ini_options]

View File

@@ -27,6 +27,7 @@
"el-GR": "src/locale/messages.el_GR.xlf", "el-GR": "src/locale/messages.el_GR.xlf",
"en-GB": "src/locale/messages.en_GB.xlf", "en-GB": "src/locale/messages.en_GB.xlf",
"es-ES": "src/locale/messages.es_ES.xlf", "es-ES": "src/locale/messages.es_ES.xlf",
"fa-IR": "src/locale/messages.fa_IR.xlf",
"fi-FI": "src/locale/messages.fi_FI.xlf", "fi-FI": "src/locale/messages.fi_FI.xlf",
"fr-FR": "src/locale/messages.fr_FR.xlf", "fr-FR": "src/locale/messages.fr_FR.xlf",
"hu-HU": "src/locale/messages.hu_HU.xlf", "hu-HU": "src/locale/messages.hu_HU.xlf",

View File

@@ -8,7 +8,7 @@ module.exports = {
'abstract-paperless-service', 'abstract-paperless-service',
], ],
transformIgnorePatterns: [ transformIgnorePatterns: [
`<rootDir>/node_modules/.pnpm/(?!.*\\.mjs$|lodash-es)`, `<rootDir>/node_modules/.pnpm/(?!.*\\.mjs$|lodash-es|@angular\\+common.*locales)`,
], ],
moduleNameMapper: { moduleNameMapper: {
'^src/(.*)': '<rootDir>/src/$1', '^src/(.*)': '<rootDir>/src/$1',

View File

@@ -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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/alert/alert.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/node_modules/src/alert/alert.ts</context>
<context context-type="linenumber">51</context> <context context-type="linenumber">51</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&lt;NgbSli"/> of <x id="INTERPOLATION_1" equiv-text="EventSource = N"/> </source> <source> Slide <x id="INTERPOLATION" equiv-text="ueryList&lt;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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/carousel/carousel.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/node_modules/src/carousel/carousel.ts</context>
<context context-type="linenumber">132,136</context> <context context-type="linenumber">132,136</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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/carousel/carousel.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/node_modules/src/carousel/carousel.ts</context>
<context context-type="linenumber">158,160</context> <context context-type="linenumber">158,160</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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/carousel/carousel.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/node_modules/src/carousel/carousel.ts</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">199</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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/datepicker/datepicker-navigation.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/node_modules/src/datepicker/datepicker-navigation.ts</context>
<context context-type="linenumber">77,79</context> <context context-type="linenumber">77,79</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/datepicker/datepicker-navigation.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/node_modules/src/datepicker/datepicker-navigation.ts</context>
<context context-type="linenumber">102</context> <context context-type="linenumber">102</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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/datepicker/datepicker-navigation.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/node_modules/src/datepicker/datepicker-navigation.ts</context>
<context context-type="linenumber">102</context> <context context-type="linenumber">102</context>
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/datepicker/datepicker-navigation.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/node_modules/src/datepicker/datepicker-navigation.ts</context>
<context context-type="linenumber">102</context> <context context-type="linenumber">102</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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/ngb-config.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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@18.0.0_@angular+common@19.2.13_@angular+core@19.2.14_rxjs@7._ce5a45f3b9d5ca136f928f24177f8d04/node_modules/src/progressbar/progressbar.ts</context> <context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.14_@angular+core@19.2.14_rxjs@7._f368c1e2c12c71d00515f6f30d8f21d1/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>
@@ -9805,123 +9805,130 @@
<context context-type="linenumber">171</context> <context context-type="linenumber">171</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4977087909184008115" datatype="html">
<source>Persian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">177</context>
</context-group>
</trans-unit>
<trans-unit id="792060551707690640" datatype="html"> <trans-unit id="792060551707690640" datatype="html">
<source>Polish</source> <source>Polish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">177</context> <context context-type="linenumber">183</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9184513005098760425" datatype="html"> <trans-unit id="9184513005098760425" datatype="html">
<source>Portuguese (Brazil)</source> <source>Portuguese (Brazil)</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">189</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="153799456510623899" datatype="html"> <trans-unit id="153799456510623899" datatype="html">
<source>Portuguese</source> <source>Portuguese</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">189</context> <context context-type="linenumber">195</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8118856427047826368" datatype="html"> <trans-unit id="8118856427047826368" datatype="html">
<source>Romanian</source> <source>Romanian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">201</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7137419789978325708" datatype="html"> <trans-unit id="7137419789978325708" datatype="html">
<source>Russian</source> <source>Russian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">207</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9102963095355753902" datatype="html"> <trans-unit id="9102963095355753902" datatype="html">
<source>Slovak</source> <source>Slovak</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">207</context> <context context-type="linenumber">213</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4287008301409320881" datatype="html"> <trans-unit id="4287008301409320881" datatype="html">
<source>Slovenian</source> <source>Slovenian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">213</context> <context context-type="linenumber">219</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8608389829607915090" datatype="html"> <trans-unit id="8608389829607915090" datatype="html">
<source>Serbian</source> <source>Serbian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">219</context> <context context-type="linenumber">225</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="499386805970351976" datatype="html"> <trans-unit id="499386805970351976" datatype="html">
<source>Swedish</source> <source>Swedish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">225</context> <context context-type="linenumber">231</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5682359291233237791" datatype="html"> <trans-unit id="5682359291233237791" datatype="html">
<source>Turkish</source> <source>Turkish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">231</context> <context context-type="linenumber">237</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3578644052206125685" datatype="html"> <trans-unit id="3578644052206125685" datatype="html">
<source>Ukrainian</source> <source>Ukrainian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">237</context> <context context-type="linenumber">243</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4689443708886954687" datatype="html"> <trans-unit id="4689443708886954687" datatype="html">
<source>Chinese Simplified</source> <source>Chinese Simplified</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">243</context> <context context-type="linenumber">249</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8082606363137705994" datatype="html"> <trans-unit id="8082606363137705994" datatype="html">
<source>Chinese Traditional</source> <source>Chinese Traditional</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">249</context> <context context-type="linenumber">255</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4912706592792948707" datatype="html"> <trans-unit id="4912706592792948707" datatype="html">
<source>ISO 8601</source> <source>ISO 8601</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">257</context> <context context-type="linenumber">263</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="313643372755303297" datatype="html"> <trans-unit id="313643372755303297" datatype="html">
<source>Successfully completed one-time migratration of settings to the database!</source> <source>Successfully completed one-time migratration of settings to the database!</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">590</context> <context context-type="linenumber">596</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5558341108007064934" datatype="html"> <trans-unit id="5558341108007064934" datatype="html">
<source>Unable to migrate settings to the database, please try saving manually.</source> <source>Unable to migrate settings to the database, please try saving manually.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">591</context> <context context-type="linenumber">597</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1168781785897678748" datatype="html"> <trans-unit id="1168781785897678748" datatype="html">
<source>You can restart the tour from the settings page.</source> <source>You can restart the tour from the settings page.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">664</context> <context context-type="linenumber">670</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3852289441366561594" datatype="html"> <trans-unit id="3852289441366561594" datatype="html">

View File

@@ -12,7 +12,7 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/cdk": "^19.2.14", "@angular/cdk": "^19.2.14",
"@angular/common": "~19.2.13", "@angular/common": "~19.2.14",
"@angular/compiler": "~19.2.14", "@angular/compiler": "~19.2.14",
"@angular/core": "~19.2.14", "@angular/core": "~19.2.14",
"@angular/forms": "~19.2.14", "@angular/forms": "~19.2.14",
@@ -69,13 +69,6 @@
"ts-node": "~10.9.1", "ts-node": "~10.9.1",
"typescript": "^5.5.4" "typescript": "^5.5.4"
}, },
"packageManager": "pnpm@10.11.1",
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": "10.11.1"
}
},
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"@parcel/watcher", "@parcel/watcher",

130
src-ui/pnpm-lock.yaml generated
View File

@@ -10,10 +10,10 @@ importers:
dependencies: dependencies:
'@angular/cdk': '@angular/cdk':
specifier: ^19.2.14 specifier: ^19.2.14
version: 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) version: 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/common': '@angular/common':
specifier: ~19.2.13 specifier: ~19.2.14
version: 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) version: 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/compiler': '@angular/compiler':
specifier: ~19.2.14 specifier: ~19.2.14
version: 19.2.14 version: 19.2.14
@@ -22,28 +22,28 @@ importers:
version: 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) version: 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/forms': '@angular/forms':
specifier: ~19.2.14 specifier: ~19.2.14
version: 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) version: 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/localize': '@angular/localize':
specifier: ~19.2.14 specifier: ~19.2.14
version: 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14) version: 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)
'@angular/platform-browser': '@angular/platform-browser':
specifier: ~19.2.14 specifier: ~19.2.14
version: 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) version: 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser-dynamic': '@angular/platform-browser-dynamic':
specifier: ~19.2.14 specifier: ~19.2.14
version: 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))) version: 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))
'@angular/router': '@angular/router':
specifier: ~19.2.14 specifier: ~19.2.14
version: 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) version: 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@ng-bootstrap/ng-bootstrap': '@ng-bootstrap/ng-bootstrap':
specifier: ^18.0.0 specifier: ^18.0.0
version: 18.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2) version: 18.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2)
'@ng-select/ng-select': '@ng-select/ng-select':
specifier: ^14.9.0 specifier: ^14.9.0
version: 14.9.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)) version: 14.9.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))
'@ngneat/dirty-check-forms': '@ngneat/dirty-check-forms':
specifier: ^3.0.3 specifier: ^3.0.3
version: 3.0.3(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/router@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(lodash-es@4.17.21)(rxjs@7.8.2) version: 3.0.3(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/router@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(lodash-es@4.17.21)(rxjs@7.8.2)
'@popperjs/core': '@popperjs/core':
specifier: ^2.11.8 specifier: ^2.11.8
version: 2.11.8 version: 2.11.8
@@ -61,19 +61,19 @@ importers:
version: 10.4.0 version: 10.4.0
ngx-bootstrap-icons: ngx-bootstrap-icons:
specifier: ^1.9.3 specifier: ^1.9.3
version: 1.9.3(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) version: 1.9.3(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))
ngx-color: ngx-color:
specifier: ^10.0.0 specifier: ^10.0.0
version: 10.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) version: 10.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))
ngx-cookie-service: ngx-cookie-service:
specifier: ^19.1.2 specifier: ^19.1.2
version: 19.1.2(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) version: 19.1.2(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))
ngx-device-detector: ngx-device-detector:
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) version: 9.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))
ngx-ui-tour-ng-bootstrap: ngx-ui-tour-ng-bootstrap:
specifier: ^16.0.0 specifier: ^16.0.0
version: 16.0.0(f0e74b8bab83f30cf77f70d2cc04d4ae) version: 16.0.0(b22d6d97efbc9cb8f9e09ff61a244f2e)
rxjs: rxjs:
specifier: ^7.8.2 specifier: ^7.8.2
version: 7.8.2 version: 7.8.2
@@ -95,7 +95,7 @@ importers:
version: 19.0.1(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@types/node@22.15.29)(chokidar@4.0.3)(jest-environment-jsdom@29.7.0)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jiti@1.21.7)(typescript@5.5.4)(vite@6.2.7(@types/node@22.15.29)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.7.0))(yaml@2.7.0) version: 19.0.1(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@types/node@22.15.29)(chokidar@4.0.3)(jest-environment-jsdom@29.7.0)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jiti@1.21.7)(typescript@5.5.4)(vite@6.2.7(@types/node@22.15.29)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.7.0))(yaml@2.7.0)
'@angular-builders/jest': '@angular-builders/jest':
specifier: ^19.0.1 specifier: ^19.0.1
version: 19.0.1(a3a7a7b7cf5c06373f7925dd32bde0cb) version: 19.0.1(4b49eb59c0a92e3dbc1542019395d573)
'@angular-devkit/build-angular': '@angular-devkit/build-angular':
specifier: ^19.2.14 specifier: ^19.2.14
version: 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@types/node@22.15.29)(chokidar@4.0.3)(jest-environment-jsdom@29.7.0)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jiti@1.21.7)(typescript@5.5.4)(vite@6.2.7(@types/node@22.15.29)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.7.0))(yaml@2.7.0) version: 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@types/node@22.15.29)(chokidar@4.0.3)(jest-environment-jsdom@29.7.0)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jiti@1.21.7)(typescript@5.5.4)(vite@6.2.7(@types/node@22.15.29)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.7.0))(yaml@2.7.0)
@@ -161,7 +161,7 @@ importers:
version: 16.0.0 version: 16.0.0
jest-preset-angular: jest-preset-angular:
specifier: ^14.5.5 specifier: ^14.5.5
version: 14.5.5(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jsdom@20.0.3)(typescript@5.5.4) version: 14.5.5(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jsdom@20.0.3)(typescript@5.5.4)
jest-websocket-mock: jest-websocket-mock:
specifier: ^2.5.0 specifier: ^2.5.0
version: 2.5.0 version: 2.5.0
@@ -386,11 +386,11 @@ packages:
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
hasBin: true hasBin: true
'@angular/common@19.2.13': '@angular/common@19.2.14':
resolution: {integrity: sha512-k7I4bLH+bgI02VL81MaL0NcZPfVl153KAiARwk+ZlkmQjMnWlmsAHQ6054SWoNEXwP855ATR6YYDVqJh8TZaqw==} resolution: {integrity: sha512-NcNklcuyqaTjOVGf7aru8APX9mjsnZ01gFZrn47BxHozhaR0EMRrotYQTdi8YdVjPkeYFYanVntSLfhyobq/jg==}
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0}
peerDependencies: peerDependencies:
'@angular/core': 19.2.13 '@angular/core': 19.2.14
rxjs: ^6.5.3 || ^7.4.0 rxjs: ^6.5.3 || ^7.4.0
'@angular/compiler-cli@19.2.14': '@angular/compiler-cli@19.2.14':
@@ -6141,7 +6141,7 @@ snapshots:
- webpack-cli - webpack-cli
- yaml - yaml
'@angular-builders/jest@19.0.1(a3a7a7b7cf5c06373f7925dd32bde0cb)': '@angular-builders/jest@19.0.1(4b49eb59c0a92e3dbc1542019395d573)':
dependencies: dependencies:
'@angular-builders/common': 3.0.1(@types/node@22.15.29)(chokidar@4.0.3)(typescript@5.5.4) '@angular-builders/common': 3.0.1(@types/node@22.15.29)(chokidar@4.0.3)(typescript@5.5.4)
'@angular-devkit/architect': 0.1902.8(chokidar@4.0.3) '@angular-devkit/architect': 0.1902.8(chokidar@4.0.3)
@@ -6149,9 +6149,9 @@ snapshots:
'@angular-devkit/core': 19.2.14(chokidar@4.0.3) '@angular-devkit/core': 19.2.14(chokidar@4.0.3)
'@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4) '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser-dynamic': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))) '@angular/platform-browser-dynamic': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))
jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)) jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4))
jest-preset-angular: 14.5.4(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jsdom@20.0.3)(typescript@5.5.4) jest-preset-angular: 14.5.4(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jsdom@20.0.3)(typescript@5.5.4)
lodash: 4.17.21 lodash: 4.17.21
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
@@ -6422,9 +6422,9 @@ snapshots:
- tsx - tsx
- yaml - yaml
'@angular/cdk@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': '@angular/cdk@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)':
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
parse5: 7.3.0 parse5: 7.3.0
rxjs: 7.8.2 rxjs: 7.8.2
@@ -6454,7 +6454,7 @@ snapshots:
- chokidar - chokidar
- supports-color - supports-color
'@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': '@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)':
dependencies: dependencies:
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
rxjs: 7.8.2 rxjs: 7.8.2
@@ -6485,11 +6485,11 @@ snapshots:
tslib: 2.8.1 tslib: 2.8.1
zone.js: 0.15.1 zone.js: 0.15.1
'@angular/forms@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': '@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)':
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-browser': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))
rxjs: 7.8.2 rxjs: 7.8.2
tslib: 2.8.1 tslib: 2.8.1
@@ -6504,25 +6504,25 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))': '@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))':
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/compiler': 19.2.14 '@angular/compiler': 19.2.14
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-browser': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))
tslib: 2.8.1 tslib: 2.8.1
'@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))': '@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))':
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
tslib: 2.8.1 tslib: 2.8.1
'@angular/router@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': '@angular/router@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)':
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-browser': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))
rxjs: 7.8.2 rxjs: 7.8.2
tslib: 2.8.1 tslib: 2.8.1
@@ -8103,28 +8103,28 @@ snapshots:
'@napi-rs/nice-win32-x64-msvc': 1.0.1 '@napi-rs/nice-win32-x64-msvc': 1.0.1
optional: true optional: true
'@ng-bootstrap/ng-bootstrap@18.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2)': '@ng-bootstrap/ng-bootstrap@18.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2)':
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/forms': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/forms': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/localize': 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14) '@angular/localize': 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)
'@popperjs/core': 2.11.8 '@popperjs/core': 2.11.8
rxjs: 7.8.2 rxjs: 7.8.2
tslib: 2.8.1 tslib: 2.8.1
'@ng-select/ng-select@14.9.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))': '@ng-select/ng-select@14.9.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))':
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/forms': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/forms': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
tslib: 2.8.1 tslib: 2.8.1
'@ngneat/dirty-check-forms@3.0.3(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/router@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(lodash-es@4.17.21)(rxjs@7.8.2)': '@ngneat/dirty-check-forms@3.0.3(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/router@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(lodash-es@4.17.21)(rxjs@7.8.2)':
dependencies: dependencies:
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/forms': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/forms': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/router': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/router': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
lodash-es: 4.17.21 lodash-es: 4.17.21
rxjs: 7.8.2 rxjs: 7.8.2
tslib: 2.8.1 tslib: 2.8.1
@@ -10471,11 +10471,11 @@ snapshots:
optionalDependencies: optionalDependencies:
jest-resolve: 29.7.0 jest-resolve: 29.7.0
jest-preset-angular@14.5.4(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jsdom@20.0.3)(typescript@5.5.4): jest-preset-angular@14.5.4(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jsdom@20.0.3)(typescript@5.5.4):
dependencies: dependencies:
'@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4) '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser-dynamic': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))) '@angular/platform-browser-dynamic': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))
bs-logger: 0.2.6 bs-logger: 0.2.6
esbuild-wasm: 0.25.3 esbuild-wasm: 0.25.3
jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)) jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4))
@@ -10497,11 +10497,11 @@ snapshots:
- supports-color - supports-color
- utf-8-validate - utf-8-validate
jest-preset-angular@14.5.5(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jsdom@20.0.3)(typescript@5.5.4): jest-preset-angular@14.5.5(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)))(jsdom@20.0.3)(typescript@5.5.4):
dependencies: dependencies:
'@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4) '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser-dynamic': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))) '@angular/platform-browser-dynamic': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.14)(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))
bs-logger: 0.2.6 bs-logger: 0.2.6
esbuild-wasm: 0.25.2 esbuild-wasm: 0.25.2
jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4)) jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.5.4))
@@ -11093,46 +11093,46 @@ snapshots:
pdfjs-dist: 4.8.69 pdfjs-dist: 4.8.69
tslib: 2.8.1 tslib: 2.8.1
ngx-bootstrap-icons@1.9.3(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)): ngx-bootstrap-icons@1.9.3(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)):
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
tslib: 2.8.1 tslib: 2.8.1
ngx-color@10.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)): ngx-color@10.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)):
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@ctrl/tinycolor': 4.1.0 '@ctrl/tinycolor': 4.1.0
material-colors: 1.2.6 material-colors: 1.2.6
tslib: 2.8.1 tslib: 2.8.1
ngx-cookie-service@19.1.2(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)): ngx-cookie-service@19.1.2(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)):
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
tslib: 2.8.1 tslib: 2.8.1
ngx-device-detector@9.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)): ngx-device-detector@9.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)):
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
tslib: 2.8.1 tslib: 2.8.1
ngx-ui-tour-core@14.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2): ngx-ui-tour-core@14.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2):
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/router': 19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/router': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
rxjs: 7.8.2 rxjs: 7.8.2
tslib: 2.8.1 tslib: 2.8.1
ngx-ui-tour-ng-bootstrap@16.0.0(f0e74b8bab83f30cf77f70d2cc04d4ae): ngx-ui-tour-ng-bootstrap@16.0.0(b22d6d97efbc9cb8f9e09ff61a244f2e):
dependencies: dependencies:
'@angular/common': 19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1)
'@ng-bootstrap/ng-bootstrap': 18.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-bootstrap/ng-bootstrap': 18.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2)
ngx-ui-tour-core: 14.0.0(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.13(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2) ngx-ui-tour-core: 14.0.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2)
tslib: 2.8.1 tslib: 2.8.1
transitivePeerDependencies: transitivePeerDependencies:
- '@angular/router' - '@angular/router'

View File

@@ -20,6 +20,7 @@ import localeDe from '@angular/common/locales/de'
import localeEl from '@angular/common/locales/el' import localeEl from '@angular/common/locales/el'
import localeEnGb from '@angular/common/locales/en-GB' import localeEnGb from '@angular/common/locales/en-GB'
import localeEs from '@angular/common/locales/es' import localeEs from '@angular/common/locales/es'
import localeFa from '@angular/common/locales/fa'
import localeFi from '@angular/common/locales/fi' import localeFi from '@angular/common/locales/fi'
import localeFr from '@angular/common/locales/fr' import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu' import localeHu from '@angular/common/locales/hu'
@@ -53,6 +54,7 @@ registerLocaleData(localeDe)
registerLocaleData(localeEl) registerLocaleData(localeEl)
registerLocaleData(localeEnGb) registerLocaleData(localeEnGb)
registerLocaleData(localeEs) registerLocaleData(localeEs)
registerLocaleData(localeFa)
registerLocaleData(localeFi) registerLocaleData(localeFi)
registerLocaleData(localeFr) registerLocaleData(localeFr)
registerLocaleData(localeHu) registerLocaleData(localeHu)

View File

@@ -158,10 +158,13 @@
</div> </div>
<div class="nav-group mt-3 mb-1"> <div class="nav-group mt-3 mb-1">
<h6 class="sidebar-heading px-3 text-muted"> <h6 class="sidebar-heading px-3 text-muted d-flex align-items-center">
<span i18n>Manage</span> <span i18n>Manage</span>
<button class="btn btn-link p-2 py-0" (click)="manageCollapse.toggle()">
<i-bs width="0.9em" height="0.9em" [name]="isManageMenuCollapsed ? 'chevron-down' : 'chevron-up'"></i-bs>
</button>
</h6> </h6>
<ul class="nav flex-column mb-2"> <ul class="nav flex-column mb-2" #manageCollapse="ngbCollapse" [(ngbCollapse)]="isManageMenuCollapsed">
<li class="nav-item app-link" <li class="nav-item app-link"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }"> *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
<a class="nav-link" routerLink="correspondents" routerLinkActive="active" (click)="closeMenu()" <a class="nav-link" routerLink="correspondents" routerLinkActive="active" (click)="closeMenu()"
@@ -235,10 +238,14 @@
</div> </div>
<div class="nav-group mt-auto mb-1"> <div class="nav-group mt-auto mb-1">
<h6 class="sidebar-heading px-3 pt-4 text-muted"> <h6 class="sidebar-heading px-3 pt-4 text-muted d-flex align-items-center">
<span i18n>Administration</span> <span i18n>Administration</span>
<button class="btn btn-link p-2 py-0" (click)="adminCollapse.toggle()">
<i-bs width="0.9em" height="0.9em" [name]="isAdminMenuCollapsed ? 'chevron-down' : 'chevron-up'"></i-bs>
</button>
</h6> </h6>
<ul class="nav flex-column mb-2"> <div class="mb-2">
<ul class="nav flex-column" #adminCollapse="ngbCollapse" [(ngbCollapse)]="isAdminMenuCollapsed">
<li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }" <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }"
tourAnchor="tour.settings"> tourAnchor="tour.settings">
<a class="nav-link" routerLink="settings" routerLinkActive="active" (click)="closeMenu()" <a class="nav-link" routerLink="settings" routerLinkActive="active" (click)="closeMenu()"
@@ -284,6 +291,8 @@
</a> </a>
</li> </li>
} }
</ul>
<ul class="nav flex-column">
<li class="nav-item mt-2" tourAnchor="tour.outro"> <li class="nav-item mt-2" tourAnchor="tour.outro">
<a class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap text-decoration-none" <a class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap text-decoration-none"
target="_blank" rel="noopener noreferrer" href="https://docs.paperless-ngx.com" ngbPopover="Documentation" target="_blank" rel="noopener noreferrer" href="https://docs.paperless-ngx.com" ngbPopover="Documentation"
@@ -348,6 +357,7 @@
</ul> </ul>
</div> </div>
</div> </div>
</div>
</nav> </nav>
<main role="main" class="ms-sm-auto px-md-4" <main role="main" class="ms-sm-auto px-md-4"

View File

@@ -78,6 +78,8 @@ export class AppFrameComponent
appRemoteVersion: AppRemoteVersion appRemoteVersion: AppRemoteVersion
isMenuCollapsed: boolean = true isMenuCollapsed: boolean = true
isManageMenuCollapsed: boolean = false
isAdminMenuCollapsed: boolean = false
slimSidebarAnimating: boolean = false slimSidebarAnimating: boolean = false

View File

@@ -172,6 +172,12 @@ const LANGUAGE_OPTIONS = [
englishName: 'Norwegian', englishName: 'Norwegian',
dateInputFormat: 'dd.mm.yyyy', dateInputFormat: 'dd.mm.yyyy',
}, },
{
code: 'fa-ir',
name: $localize`Persian`,
englishName: 'Persian',
dateInputFormat: 'yyyy-mm-dd',
},
{ {
code: 'pl-pl', code: 'pl-pl',
name: $localize`Polish`, name: $localize`Polish`,

View File

@@ -55,6 +55,8 @@ import {
checkLg, checkLg,
chevronDoubleLeft, chevronDoubleLeft,
chevronDoubleRight, chevronDoubleRight,
chevronDown,
chevronUp,
clipboard, clipboard,
clipboardCheck, clipboardCheck,
clipboardCheckFill, clipboardCheckFill,
@@ -162,6 +164,7 @@ import localeDe from '@angular/common/locales/de'
import localeEl from '@angular/common/locales/el' import localeEl from '@angular/common/locales/el'
import localeEnGb from '@angular/common/locales/en-GB' import localeEnGb from '@angular/common/locales/en-GB'
import localeEs from '@angular/common/locales/es' import localeEs from '@angular/common/locales/es'
import localeFa from '@angular/common/locales/fa'
import localeFi from '@angular/common/locales/fi' import localeFi from '@angular/common/locales/fi'
import localeFr from '@angular/common/locales/fr' import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu' import localeHu from '@angular/common/locales/hu'
@@ -198,6 +201,7 @@ registerLocaleData(localeDe)
registerLocaleData(localeEl) registerLocaleData(localeEl)
registerLocaleData(localeEnGb) registerLocaleData(localeEnGb)
registerLocaleData(localeEs) registerLocaleData(localeEs)
registerLocaleData(localeFa)
registerLocaleData(localeFi) registerLocaleData(localeFi)
registerLocaleData(localeFr) registerLocaleData(localeFr)
registerLocaleData(localeHu) registerLocaleData(localeHu)
@@ -258,6 +262,8 @@ const icons = {
checkAll, checkAll,
checkCircleFill, checkCircleFill,
checkLg, checkLg,
chevronDown,
chevronUp,
chevronDoubleLeft, chevronDoubleLeft,
chevronDoubleRight, chevronDoubleRight,
clipboard, clipboard,

View File

@@ -1,9 +1,12 @@
import json import json
import logging import logging
import os import os
import tempfile
from collections.abc import Generator from collections.abc import Generator
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from zipfile import ZipFile
from zipfile import is_zipfile
import tqdm import tqdm
from django.conf import settings from django.conf import settings
@@ -234,14 +237,19 @@ class Command(CryptMixin, BaseCommand):
self.manifest_paths = [] self.manifest_paths = []
self.manifest = [] self.manifest = []
# Create a temporary directory for extracting a zip file into it, even if supplied source is no zip file to keep code cleaner.
with tempfile.TemporaryDirectory() as tmp_dir:
if is_zipfile(self.source):
with ZipFile(self.source) as zf:
zf.extractall(tmp_dir)
self.source = Path(tmp_dir)
self._run_import()
def _run_import(self):
self.pre_check() self.pre_check()
self.load_metadata() self.load_metadata()
self.load_manifest_files() self.load_manifest_files()
self.check_manifest_validity() self.check_manifest_validity()
self.decrypt_secret_fields() self.decrypt_secret_fields()
# see /src/documents/signals/handlers.py # see /src/documents/signals/handlers.py

View File

@@ -1189,7 +1189,6 @@ class SavedViewSerializer(OwnedObjectSerializer):
"owner", "owner",
"permissions", "permissions",
"user_can_change", "user_can_change",
"set_permissions",
] ]
def validate(self, attrs): def validate(self, attrs):
@@ -1754,6 +1753,8 @@ class StoragePathSerializer(MatchingModelSerializer, OwnedObjectSerializer):
class UiSettingsViewSerializer(serializers.ModelSerializer): class UiSettingsViewSerializer(serializers.ModelSerializer):
settings = serializers.DictField(required=False, allow_null=True)
class Meta: class Meta:
model = UiSettings model = UiSettings
depth = 1 depth = 1
@@ -2020,8 +2021,9 @@ class WorkflowTriggerSerializer(serializers.ModelSerializer):
): ):
attrs["filter_path"] = None attrs["filter_path"] = None
trigger_type = attrs.get("type", getattr(self.instance, "type", None))
if ( if (
attrs["type"] == WorkflowTrigger.WorkflowTriggerType.CONSUMPTION trigger_type == WorkflowTrigger.WorkflowTriggerType.CONSUMPTION
and "filter_mailrule" not in attrs and "filter_mailrule" not in attrs
and ("filter_filename" not in attrs or attrs["filter_filename"] is None) and ("filter_filename" not in attrs or attrs["filter_filename"] is None)
and ("filter_path" not in attrs or attrs["filter_path"] is None) and ("filter_path" not in attrs or attrs["filter_path"] is None)

View File

@@ -1021,7 +1021,7 @@ def run_workflows(
if action.remove_all_custom_fields: if action.remove_all_custom_fields:
if not use_overrides: if not use_overrides:
CustomFieldInstance.objects.filter(document=document).delete() CustomFieldInstance.objects.filter(document=document).hard_delete()
else: else:
overrides.custom_fields = None overrides.custom_fields = None
elif action.remove_custom_fields.exists(): elif action.remove_custom_fields.exists():
@@ -1029,7 +1029,7 @@ def run_workflows(
CustomFieldInstance.objects.filter( CustomFieldInstance.objects.filter(
field__in=action.remove_custom_fields.all(), field__in=action.remove_custom_fields.all(),
document=document, document=document,
).delete() ).hard_delete()
elif overrides.custom_fields: elif overrides.custom_fields:
for field in action.remove_custom_fields.filter( for field in action.remove_custom_fields.filter(
pk__in=overrides.custom_fields.keys(), pk__in=overrides.custom_fields.keys(),

View File

@@ -167,3 +167,25 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
}, },
) )
self.assertFalse(Path(old_logo.path).exists()) self.assertFalse(Path(old_logo.path).exists())
def test_create_not_allowed(self):
"""
GIVEN:
- API request to create a new app config
WHEN:
- API is called
THEN:
- Correct HTTP response
- No new config is created
"""
response = self.client.post(
self.ENDPOINT,
json.dumps(
{
"output_type": "pdf",
},
),
content_type="application/json",
)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
self.assertEqual(ApplicationConfiguration.objects.count(), 1)

View File

@@ -117,6 +117,30 @@ class TestApiUiSettings(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_settings_must_be_dict(self):
"""
GIVEN:
- API request to update ui_settings with settings not being a dict
WHEN:
- API is called
THEN:
- Correct HTTP 400 response
"""
response = self.client.post(
self.ENDPOINT,
json.dumps(
{
"settings": "not a dict",
},
),
content_type="application/json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Expected a dictionary",
str(response.data["settings"]),
)
@override_settings( @override_settings(
OAUTH_CALLBACK_BASE_URL="http://localhost:8000", OAUTH_CALLBACK_BASE_URL="http://localhost:8000",
GMAIL_OAUTH_CLIENT_ID="abc123", GMAIL_OAUTH_CLIENT_ID="abc123",

View File

@@ -2,6 +2,7 @@ import json
import tempfile import tempfile
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
from zipfile import ZipFile
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management import call_command from django.core.management import call_command
@@ -335,3 +336,42 @@ class TestCommandImport(
self.assertIn("Version mismatch:", stdout_str) self.assertIn("Version mismatch:", stdout_str)
self.assertIn("importing 2.8.1", stdout_str) self.assertIn("importing 2.8.1", stdout_str)
def test_import_zipped_export(self):
"""
GIVEN:
- A zip file with correct content (manifest.json and version.json inside)
WHEN:
- An import is attempted using the zip file as the source
THEN:
- The command reads from the zip without warnings or errors
"""
stdout = StringIO()
zip_path = self.dirs.scratch_dir / "export.zip"
# Create manifest.json and version.json in a temp dir
with tempfile.TemporaryDirectory() as temp_dir:
temp_dir_path = Path(temp_dir)
(temp_dir_path / "manifest.json").touch()
(temp_dir_path / "version.json").touch()
# Create the zip file
with ZipFile(zip_path, "w") as zf:
zf.write(temp_dir_path / "manifest.json", arcname="manifest.json")
zf.write(temp_dir_path / "version.json", arcname="version.json")
# Try to import from the zip file
with self.assertRaises(json.decoder.JSONDecodeError):
call_command(
"document_importer",
"--no-progress-bar",
str(zip_path),
stdout=stdout,
)
stdout.seek(0)
stdout_str = str(stdout.read())
# There should be no error or warnings. Therefore the output should be empty.
self.assertEqual(stdout_str, "")

View File

@@ -145,6 +145,7 @@ from documents.serialisers import CustomFieldSerializer
from documents.serialisers import DocumentListSerializer from documents.serialisers import DocumentListSerializer
from documents.serialisers import DocumentSerializer from documents.serialisers import DocumentSerializer
from documents.serialisers import DocumentTypeSerializer from documents.serialisers import DocumentTypeSerializer
from documents.serialisers import NotesSerializer
from documents.serialisers import PostDocumentSerializer from documents.serialisers import PostDocumentSerializer
from documents.serialisers import RunTaskViewSerializer from documents.serialisers import RunTaskViewSerializer
from documents.serialisers import SavedViewSerializer from documents.serialisers import SavedViewSerializer
@@ -433,27 +434,24 @@ class DocumentTypeViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin):
), ),
notes=extend_schema( notes=extend_schema(
description="View, add, or delete notes for the document", description="View, add, or delete notes for the document",
methods=["GET", "POST", "DELETE"],
request=inline_serializer(
name="NoteCreateRequest",
fields={
"note": serializers.CharField(),
},
),
parameters=[
OpenApiParameter(
name="id",
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
required=False,
description="Note ID to delete (used only for DELETE requests)",
),
],
responses={ responses={
200: { 200: NotesSerializer(many=True),
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"note": {"type": "string"},
"created": {"type": "string", "format": "date-time"},
"user": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"username": {"type": "string"},
"first_name": {"type": "string"},
"last_name": {"type": "string"},
},
},
},
},
},
400: None, 400: None,
403: None, 403: None,
404: None, 404: None,
@@ -519,6 +517,28 @@ class DocumentTypeViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin):
404: None, 404: None,
}, },
), ),
email=extend_schema(
description="Email the document to one or more recipients as an attachment.",
request=inline_serializer(
name="EmailRequest",
fields={
"addresses": serializers.CharField(),
"subject": serializers.CharField(),
"message": serializers.CharField(),
"use_archive_version": serializers.BooleanField(default=True),
},
),
responses={
200: inline_serializer(
name="EmailResponse",
fields={"message": serializers.CharField()},
),
400: None,
403: None,
404: None,
500: None,
},
),
) )
class DocumentViewSet( class DocumentViewSet(
PassUserMixin, PassUserMixin,
@@ -630,7 +650,7 @@ class DocumentViewSet(
) )
def get_metadata(self, file, mime_type): def get_metadata(self, file, mime_type):
if not os.path.isfile(file): if not Path(file).is_file():
return None return None
parser_class = get_parser_class_for_mime_type(mime_type) parser_class = get_parser_class_for_mime_type(mime_type)
@@ -648,8 +668,8 @@ class DocumentViewSet(
return [] return []
def get_filesize(self, filename): def get_filesize(self, filename):
if os.path.isfile(filename): if Path(filename).is_file():
return os.stat(filename).st_size return Path(filename).stat().st_size
else: else:
return None return None
@@ -1079,6 +1099,12 @@ class DocumentViewSet(
200: DocumentSerializer(many=True, all_fields=True), 200: DocumentSerializer(many=True, all_fields=True),
}, },
), ),
next_asn=extend_schema(
description="Get the next available Archive Serial Number (ASN) for a new document",
responses={
200: OpenApiTypes.INT,
},
),
) )
class UnifiedSearchViewSet(DocumentViewSet): class UnifiedSearchViewSet(DocumentViewSet):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -1189,31 +1215,37 @@ class UnifiedSearchViewSet(DocumentViewSet):
class LogViewSet(ViewSet): class LogViewSet(ViewSet):
permission_classes = (IsAuthenticated, PaperlessAdminPermissions) permission_classes = (IsAuthenticated, PaperlessAdminPermissions)
log_files = ["paperless", "mail", "celery"] ALLOWED_LOG_FILES = {
"paperless": "paperless.log",
"mail": "mail.log",
"celery": "celery.log",
}
def get_log_filename(self, log): def get_log_file(self, log_key: str) -> Path:
return os.path.join(settings.LOGGING_DIR, f"{log}.log") return Path(settings.LOGGING_DIR) / self.ALLOWED_LOG_FILES[log_key]
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
log_file = kwargs.get("pk") log_key = kwargs.get("pk")
if log_file not in self.log_files: if log_key not in self.ALLOWED_LOG_FILES:
raise Http404 raise Http404
filename = self.get_log_filename(log_file) log_file = self.get_log_file(log_key)
if not os.path.isfile(filename): if not log_file.is_file():
raise Http404 raise Http404
with open(filename) as f: with log_file.open() as f:
lines = [line.rstrip() for line in f.readlines()] lines = [line.rstrip() for line in f.readlines()]
return Response(lines) return Response(lines)
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
exist = [ existing_logs = [
log for log in self.log_files if os.path.isfile(self.get_log_filename(log)) log_key
for log_key in self.ALLOWED_LOG_FILES
if self.get_log_file(log_key).is_file()
] ]
return Response(exist) return Response(existing_logs)
class SavedViewViewSet(ModelViewSet, PassUserMixin): class SavedViewViewSet(ModelViewSet, PassUserMixin):
@@ -2047,7 +2079,7 @@ class BulkDownloadView(GenericAPIView):
strategy.add_document(document) strategy.add_document(document)
# TODO(stumpylog): Investigate using FileResponse here # TODO(stumpylog): Investigate using FileResponse here
with open(temp.name, "rb") as f: with Path(temp.name).open("rb") as f:
response = HttpResponse(f, content_type="application/zip") response = HttpResponse(f, content_type="application/zip")
response["Content-Disposition"] = '{}; filename="{}"'.format( response["Content-Disposition"] = '{}; filename="{}"'.format(
"attachment", "attachment",

View File

@@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-24 17:14+0000\n" "POT-Creation-Date: 2025-06-17 05:45+0000\n"
"PO-Revision-Date: 2022-02-17 04:17\n" "PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: English\n" "Language-Team: English\n"
@@ -1185,12 +1185,12 @@ msgstr ""
msgid "Invalid color." msgid "Invalid color."
msgstr "" msgstr ""
#: documents/serialisers.py:1646 #: documents/serialisers.py:1645
#, python-format #, python-format
msgid "File type %(type)s not supported" msgid "File type %(type)s not supported"
msgstr "" msgstr ""
#: documents/serialisers.py:1740 #: documents/serialisers.py:1739
msgid "Invalid variable detected." msgid "Invalid variable detected."
msgstr "" msgstr ""
@@ -1694,90 +1694,94 @@ msgid "Spanish"
msgstr "" msgstr ""
#: paperless/settings.py:766 #: paperless/settings.py:766
msgid "Finnish" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings.py:767 #: paperless/settings.py:767
msgid "French" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings.py:768 #: paperless/settings.py:768
msgid "Hungarian" msgid "French"
msgstr "" msgstr ""
#: paperless/settings.py:769 #: paperless/settings.py:769
msgid "Italian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings.py:770 #: paperless/settings.py:770
msgid "Japanese" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings.py:771 #: paperless/settings.py:771
msgid "Korean" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings.py:772 #: paperless/settings.py:772
msgid "Luxembourgish" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings.py:773 #: paperless/settings.py:773
msgid "Norwegian" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings.py:774 #: paperless/settings.py:774
msgid "Dutch" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings.py:775 #: paperless/settings.py:775
msgid "Polish" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings.py:776 #: paperless/settings.py:776
msgid "Portuguese (Brazil)" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings.py:777 #: paperless/settings.py:777
msgid "Portuguese" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings.py:778 #: paperless/settings.py:778
msgid "Romanian" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings.py:779 #: paperless/settings.py:779
msgid "Russian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings.py:780 #: paperless/settings.py:780
msgid "Slovak" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings.py:781 #: paperless/settings.py:781
msgid "Slovenian" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings.py:782 #: paperless/settings.py:782
msgid "Serbian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings.py:783 #: paperless/settings.py:783
msgid "Swedish" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings.py:784 #: paperless/settings.py:784
msgid "Turkish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings.py:785 #: paperless/settings.py:785
msgid "Ukrainian" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings.py:786 #: paperless/settings.py:786
msgid "Chinese Simplified" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings.py:787 #: paperless/settings.py:787
msgid "Chinese Simplified"
msgstr ""
#: paperless/settings.py:788
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""

View File

@@ -3,6 +3,7 @@ import os
import pwd import pwd
import shutil import shutil
import stat import stat
from pathlib import Path
from django.conf import settings from django.conf import settings
from django.core.checks import Error from django.core.checks import Error
@@ -19,26 +20,23 @@ writeable_hint = (
) )
def path_check(var, directory): def path_check(var, directory: Path) -> list[Error]:
messages = [] messages: list[Error] = []
if directory: if directory:
if not os.path.isdir(directory): if not directory.is_dir():
messages.append( messages.append(
Error(exists_message.format(var), exists_hint.format(directory)), Error(exists_message.format(var), exists_hint.format(directory)),
) )
else: else:
test_file = os.path.join( test_file: Path = directory / f"__paperless_write_test_{os.getpid()}__"
directory,
f"__paperless_write_test_{os.getpid()}__",
)
try: try:
with open(test_file, "w"): with test_file.open("w"):
pass pass
except PermissionError: except PermissionError:
dir_stat = os.stat(directory) dir_stat: os.stat_result = Path(directory).stat()
dir_mode = stat.filemode(dir_stat.st_mode) dir_mode: str = stat.filemode(dir_stat.st_mode)
dir_owner = pwd.getpwuid(dir_stat.st_uid).pw_name dir_owner: str = pwd.getpwuid(dir_stat.st_uid).pw_name
dir_group = grp.getgrgid(dir_stat.st_gid).gr_name dir_group: str = grp.getgrgid(dir_stat.st_gid).gr_name
messages.append( messages.append(
Error( Error(
writeable_message.format(var), writeable_message.format(var),
@@ -48,14 +46,18 @@ def path_check(var, directory):
), ),
) )
finally: finally:
if os.path.isfile(test_file): try:
os.remove(test_file) if test_file.is_file():
test_file.unlink()
except (PermissionError, OSError):
# Skip cleanup if we can't access the file — expected in permission tests
pass
return messages return messages
@register() @register()
def paths_check(app_configs, **kwargs): def paths_check(app_configs, **kwargs) -> list[Error]:
""" """
Check the various paths for existence, readability and writeability Check the various paths for existence, readability and writeability
""" """

View File

@@ -763,6 +763,7 @@ LANGUAGES = [
("el-gr", _("Greek")), ("el-gr", _("Greek")),
("en-gb", _("English (GB)")), ("en-gb", _("English (GB)")),
("es-es", _("Spanish")), ("es-es", _("Spanish")),
("fa-ir", _("Persian")),
("fi-fi", _("Finnish")), ("fi-fi", _("Finnish")),
("fr-fr", _("French")), ("fr-fr", _("French")),
("hu-hu", _("Hungarian")), ("hu-hu", _("Hungarian")),

View File

@@ -27,9 +27,9 @@ class TestChecks(DirectoriesMixin, TestCase):
self.assertEqual(paths_check(None), []) self.assertEqual(paths_check(None), [])
@override_settings( @override_settings(
MEDIA_ROOT="uuh", MEDIA_ROOT=Path("uuh"),
DATA_DIR="whatever", DATA_DIR=Path("whatever"),
CONSUMPTION_DIR="idontcare", CONSUMPTION_DIR=Path("idontcare"),
) )
def test_paths_check_dont_exist(self): def test_paths_check_dont_exist(self):
msgs = paths_check(None) msgs = paths_check(None)

View File

@@ -363,7 +363,7 @@ urlpatterns = [
websocket_urlpatterns = [ websocket_urlpatterns = [
path(settings.BASE_URL.lstrip("/") + "ws/status/", StatusConsumer.as_asgi()), path("ws/status/", StatusConsumer.as_asgi()),
] ]
# Text in each page's <h1> (and above login form). # Text in each page's <h1> (and above login form).

View File

@@ -342,6 +342,10 @@ class ApplicationConfigurationViewSet(ModelViewSet):
serializer_class = ApplicationConfigurationSerializer serializer_class = ApplicationConfigurationSerializer
permission_classes = (IsAuthenticated, DjangoModelPermissions) permission_classes = (IsAuthenticated, DjangoModelPermissions)
@extend_schema(exclude=True)
def create(self, request, *args, **kwargs):
return Response(status=405) # Not Allowed
@extend_schema_view( @extend_schema_view(
post=extend_schema( post=extend_schema(

View File

@@ -1,7 +1,6 @@
import datetime import datetime
import itertools import itertools
import logging import logging
import os
import ssl import ssl
import tempfile import tempfile
import traceback import traceback
@@ -484,7 +483,7 @@ class MailAccountHandler(LoggingMixin):
return message.subject return message.subject
elif rule.assign_title_from == MailRule.TitleSource.FROM_FILENAME: elif rule.assign_title_from == MailRule.TitleSource.FROM_FILENAME:
return os.path.splitext(os.path.basename(att.filename))[0] return Path(att.filename).stem
elif rule.assign_title_from == MailRule.TitleSource.NONE: elif rule.assign_title_from == MailRule.TitleSource.NONE:
return None return None
@@ -908,7 +907,7 @@ class MailAccountHandler(LoggingMixin):
dir=settings.SCRATCH_DIR, dir=settings.SCRATCH_DIR,
suffix=".eml", suffix=".eml",
) )
with open(temp_filename, "wb") as f: with Path(temp_filename).open("wb") as f:
# Move "From"-header to beginning of file # Move "From"-header to beginning of file
# TODO: This ugly workaround is needed because the parser is # TODO: This ugly workaround is needed because the parser is
# chosen only by the mime_type detected via magic # chosen only by the mime_type detected via magic

View File

@@ -108,18 +108,20 @@ class MailRuleSerializer(OwnedObjectSerializer):
return instance return instance
def create(self, validated_data): def create(self, validated_data):
if "assign_tags" in validated_data: assign_tags = validated_data.pop("assign_tags", [])
assign_tags = validated_data.pop("assign_tags")
mail_rule = super().create(validated_data) mail_rule = super().create(validated_data)
if assign_tags: if assign_tags:
mail_rule.assign_tags.set(assign_tags) mail_rule.assign_tags.set(assign_tags)
return mail_rule return mail_rule
def validate(self, attrs): def validate(self, attrs):
action = attrs.get("action")
action_parameter = attrs.get("action_parameter")
if ( if (
attrs["action"] == MailRule.MailAction.TAG action in [MailRule.MailAction.TAG, MailRule.MailAction.MOVE]
or attrs["action"] == MailRule.MailAction.MOVE and not action_parameter
) and attrs["action_parameter"] is None: ):
raise serializers.ValidationError("An action parameter is required.") raise serializers.ValidationError("An action parameter is required.")
return attrs return attrs

View File

@@ -1822,3 +1822,66 @@ class TestMailAccountProcess(APITestCase):
response = self.client.post(self.url) response = self.client.post(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
m.assert_called_once() m.assert_called_once()
class TestMailRuleAPI(APITestCase):
def setUp(self):
self.user = User.objects.create_superuser(
username="testuser",
password="testpassword",
)
self.client.force_authenticate(user=self.user)
self.account = MailAccount.objects.create(
imap_server="imap.example.com",
imap_port=993,
imap_security=MailAccount.ImapSecurity.SSL,
username="admin",
password="secret",
account_type=MailAccount.MailAccountType.IMAP,
owner=self.user,
)
self.url = "/api/mail_rules/"
def test_create_mail_rule(self):
"""
GIVEN:
- Valid data for creating a mail rule
WHEN:
- A POST request is made to the mail rules endpoint
THEN:
- The rule should be created successfully
- The response should contain the created rule's details
"""
data = {
"name": "Test Rule",
"account": self.account.pk,
"action": MailRule.MailAction.MOVE,
"action_parameter": "inbox",
}
response = self.client.post(self.url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(MailRule.objects.count(), 1)
rule = MailRule.objects.first()
self.assertEqual(rule.name, "Test Rule")
def test_mail_rule_action_parameter_required_for_tag_or_move(self):
"""
GIVEN:
- Valid data for creating a mail rule without action_parameter
WHEN:
- A POST request is made to the mail rules endpoint
THEN:
- The request should fail with a 400 Bad Request status
- The response should indicate that action_parameter is required
"""
data = {
"name": "Test Rule",
"account": self.account.pk,
"action": MailRule.MailAction.MOVE,
}
response = self.client.post(self.url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"action parameter is required",
str(response.data["non_field_errors"]),
)

View File

@@ -35,6 +35,7 @@ from paperless_mail.tasks import process_mail_accounts
@extend_schema_view( @extend_schema_view(
test=extend_schema( test=extend_schema(
operation_id="mail_account_test", operation_id="mail_account_test",
request=MailAccountSerializer,
description="Test a mail account", description="Test a mail account",
responses={ responses={
200: inline_serializer( 200: inline_serializer(
@@ -44,6 +45,17 @@ from paperless_mail.tasks import process_mail_accounts
400: OpenApiTypes.STR, 400: OpenApiTypes.STR,
}, },
), ),
process=extend_schema(
operation_id="mail_account_process",
description="Manually process the selected mail account for new messages.",
responses={
200: inline_serializer(
name="MailAccountProcessResponse",
fields={"result": serializers.CharField(default="OK")},
),
404: None,
},
),
) )
class MailAccountViewSet(ModelViewSet, PassUserMixin): class MailAccountViewSet(ModelViewSet, PassUserMixin):
model = MailAccount model = MailAccount

View File

@@ -1,4 +1,3 @@
import os
import shutil import shutil
import tempfile import tempfile
import uuid import uuid
@@ -70,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(
os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"), (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(),
"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(
os.path.join(self.SAMPLE_FILES, "multi-page-mixed.pdf"), (self.SAMPLE_FILES / "multi-page-mixed.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertEqual(page_count, 6) self.assertEqual(page_count, 6)
@@ -93,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(
os.path.join(self.SAMPLE_FILES, "password-protected.pdf"), (self.SAMPLE_FILES / "password-protected.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertEqual(page_count, None) self.assertEqual(page_count, None)
@@ -102,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(
os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"), (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(thumb) self.assertIsFile(thumb)
@@ -119,7 +118,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
parser = RasterisedDocumentParser(uuid.uuid4()) parser = RasterisedDocumentParser(uuid.uuid4())
thumb = parser.get_thumbnail( thumb = parser.get_thumbnail(
os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"), (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(thumb) self.assertIsFile(thumb)
@@ -127,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(
os.path.join(self.SAMPLE_FILES, "encrypted.pdf"), (self.SAMPLE_FILES / "encrypted.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(thumb) self.assertIsFile(thumb)
@@ -135,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(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png")) dpi = parser.get_dpi((self.SAMPLE_FILES / "simple-no-dpi.png").as_posix())
self.assertEqual(dpi, None) self.assertEqual(dpi, None)
dpi = parser.get_dpi(os.path.join(self.SAMPLE_FILES, "simple.png")) dpi = parser.get_dpi((self.SAMPLE_FILES / "simple.png").as_posix())
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(
os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"), (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
@@ -157,7 +156,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "with-form.pdf"), (self.SAMPLE_FILES / "with-form.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
@@ -173,7 +172,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "with-form.pdf"), (self.SAMPLE_FILES / "with-form.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
@@ -187,7 +186,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
def test_signed(self): def test_signed(self):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse(os.path.join(self.SAMPLE_FILES, "signed.pdf"), "application/pdf") parser.parse((self.SAMPLE_FILES / "signed.pdf").as_posix(), "application/pdf")
self.assertIsNone(parser.archive_path) self.assertIsNone(parser.archive_path)
self.assertContainsStrings( self.assertContainsStrings(
@@ -203,7 +202,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "encrypted.pdf"), (self.SAMPLE_FILES / "encrypted.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
@@ -214,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(
os.path.join(self.SAMPLE_FILES, "with-form.pdf"), (self.SAMPLE_FILES / "with-form.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
@@ -228,7 +227,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "with-form.pdf"), (self.SAMPLE_FILES / "with-form.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
@@ -240,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(os.path.join(self.SAMPLE_FILES, "simple.png"), "image/png") parser.parse((self.SAMPLE_FILES / "simple.png").as_posix(), "image/png")
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -252,11 +251,11 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
with tempfile.TemporaryDirectory() as tempdir: with tempfile.TemporaryDirectory() as tempdir:
# Copy sample file to temp directory, as the parsing changes the file # Copy sample file to temp directory, as the parsing changes the file
# and this makes it modified to Git # and this makes it modified to Git
sample_file = os.path.join(self.SAMPLE_FILES, "simple-alpha.png") sample_file = self.SAMPLE_FILES / "simple-alpha.png"
dest_file = os.path.join(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, "image/png") parser.parse(dest_file.as_posix(), "image/png")
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -266,7 +265,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
dpi = parser.calculate_a4_dpi( dpi = parser.calculate_a4_dpi(
os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), (self.SAMPLE_FILES / "simple-no-dpi.png").as_posix(),
) )
self.assertEqual(dpi, 62) self.assertEqual(dpi, 62)
@@ -278,7 +277,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
def f(): def f():
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), (self.SAMPLE_FILES / "simple-no-dpi.png").as_posix(),
"image/png", "image/png",
) )
@@ -288,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(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), "image/png") parser.parse((self.SAMPLE_FILES / "simple-no-dpi.png").as_posix(), "image/png")
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -300,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(
os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -313,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(
os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -326,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(
os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -339,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(
os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -352,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(
os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -376,7 +375,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -398,7 +397,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -420,7 +419,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNone(parser.archive_path) self.assertIsNone(parser.archive_path)
@@ -443,7 +442,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
@@ -468,7 +467,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNotNone(parser.archive_path) self.assertIsNotNone(parser.archive_path)
@@ -491,7 +490,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNotNone(parser.archive_path) self.assertIsNotNone(parser.archive_path)
@@ -514,7 +513,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNone(parser.archive_path) self.assertIsNone(parser.archive_path)
@@ -537,7 +536,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNotNone(parser.archive_path) self.assertIsNotNone(parser.archive_path)
@@ -560,7 +559,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), (self.SAMPLE_FILES / "multi-page-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNone(parser.archive_path) self.assertIsNone(parser.archive_path)
@@ -583,7 +582,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), (self.SAMPLE_FILES / "multi-page-images.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNone(parser.archive_path) self.assertIsNone(parser.archive_path)
@@ -606,7 +605,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-mixed.pdf"), (self.SAMPLE_FILES / "multi-page-mixed.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNotNone(parser.archive_path) self.assertIsNotNone(parser.archive_path)
@@ -616,7 +615,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
["page 1", "page 2", "page 3", "page 4", "page 5", "page 6"], ["page 1", "page 2", "page 3", "page 4", "page 5", "page 6"],
) )
with open(os.path.join(parser.tempdir, "sidecar.txt")) as f: with (parser.tempdir / "sidecar.txt").open() as f:
sidecar = f.read() sidecar = f.read()
self.assertIn("[OCR skipped on page(s) 4-6]", sidecar) self.assertIn("[OCR skipped on page(s) 4-6]", sidecar)
@@ -637,7 +636,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "single-page-mixed.pdf"), (self.SAMPLE_FILES / "single-page-mixed.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNotNone(parser.archive_path) self.assertIsNotNone(parser.archive_path)
@@ -651,7 +650,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
], ],
) )
with open(os.path.join(parser.tempdir, "sidecar.txt")) as f: with (parser.tempdir / "sidecar.txt").open() as f:
sidecar = f.read().lower() sidecar = f.read().lower()
self.assertIn("this is some text, but in an image, also on page 1.", sidecar) self.assertIn("this is some text, but in an image, also on page 1.", sidecar)
@@ -674,7 +673,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-mixed.pdf"), (self.SAMPLE_FILES / "multi-page-mixed.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
self.assertIsNone(parser.archive_path) self.assertIsNone(parser.archive_path)
@@ -686,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(os.path.join(self.SAMPLE_FILES, "rotated.pdf"), "application/pdf") parser.parse((self.SAMPLE_FILES / "rotated.pdf").as_posix(), "application/pdf")
self.assertContainsStrings( self.assertContainsStrings(
parser.get_text(), parser.get_text(),
[ [
@@ -708,7 +707,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "multi-page-images.tiff"), (self.SAMPLE_FILES / "multi-page-images.tiff").as_posix(),
"image/tiff", "image/tiff",
) )
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
@@ -728,7 +727,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
- Text from all pages extracted - Text from all pages extracted
""" """
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
sample_file = os.path.join(self.SAMPLE_FILES, "multi-page-images-alpha.tiff") sample_file = self.SAMPLE_FILES / "multi-page-images-alpha.tiff"
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(
@@ -753,10 +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 = os.path.join( sample_file = (
self.SAMPLE_FILES, self.SAMPLE_FILES / "multi-page-images-alpha-rgb.tiff"
"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(
@@ -845,7 +843,7 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse( parser.parse(
os.path.join(self.SAMPLE_FILES, "rtl-test.pdf"), (self.SAMPLE_FILES / "rtl-test.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
@@ -860,49 +858,52 @@ class TestParser(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
self.assertRaises( self.assertRaises(
ParseError, ParseError,
parser.parse, parser.parse,
os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"), (self.SAMPLE_FILES / "simple-digital.pdf").as_posix(),
"application/pdf", "application/pdf",
) )
class TestParserFileTypes(DirectoriesMixin, FileSystemAssertsMixin, TestCase): class TestParserFileTypes(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples") SAMPLE_FILES = Path(__file__).parent / "samples"
def test_bmp(self): def test_bmp(self):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse(os.path.join(self.SAMPLE_FILES, "simple.bmp"), "image/bmp") parser.parse((self.SAMPLE_FILES / "simple.bmp").as_posix(), "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(os.path.join(self.SAMPLE_FILES, "simple.jpg"), "image/jpeg") parser.parse((self.SAMPLE_FILES / "simple.jpg").as_posix(), "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(os.path.join(self.SAMPLE_FILES, "simple.heic"), "image/heic") parser.parse((self.SAMPLE_FILES / "simple.heic").as_posix(), "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(os.path.join(self.SAMPLE_FILES, "simple.gif"), "image/gif") parser.parse((self.SAMPLE_FILES / "simple.gif").as_posix(), "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(os.path.join(self.SAMPLE_FILES, "simple.tif"), "image/tiff") parser.parse((self.SAMPLE_FILES / "simple.tif").as_posix(), "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())
@override_settings(OCR_IMAGE_DPI=72) @override_settings(OCR_IMAGE_DPI=72)
def test_webp(self): def test_webp(self):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse(os.path.join(self.SAMPLE_FILES, "document.webp"), "image/webp") parser.parse(
(self.SAMPLE_FILES / "document.webp").as_posix(),
"image/webp",
)
self.assertIsFile(parser.archive_path) self.assertIsFile(parser.archive_path)
# Older tesseracts consistently mangle the space between "a webp", # Older tesseracts consistently mangle the space between "a webp",
# tesseract 5.3.0 seems to do a better job, so we're accepting both # tesseract 5.3.0 seems to do a better job, so we're accepting both