mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 18:22:40 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			479 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			479 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Development
 | ||
| 
 | ||
| This section describes the steps you need to take to start development
 | ||
| on Paperless-ngx.
 | ||
| 
 | ||
| Check out the source from GitHub. The repository is organized in the
 | ||
| following way:
 | ||
| 
 | ||
| -   `main` always represents the latest release and will only see
 | ||
|     changes when a new release is made.
 | ||
| -   `dev` contains the code that will be in the next release.
 | ||
| -   `feature-X` contains bigger changes that will be in some release, but
 | ||
|     not necessarily the next one.
 | ||
| 
 | ||
| When making functional changes to Paperless-ngx, _always_ make your changes
 | ||
| on the `dev` branch.
 | ||
| 
 | ||
| Apart from that, the folder structure is as follows:
 | ||
| 
 | ||
| -   `docs/` - Documentation.
 | ||
| -   `src-ui/` - Code of the front end.
 | ||
| -   `src/` - Code of the back end.
 | ||
| -   `scripts/` - Various scripts that help with different parts of
 | ||
|     development.
 | ||
| -   `docker/` - Files required to build the docker image.
 | ||
| 
 | ||
| ## Contributing to Paperless-ngx
 | ||
| 
 | ||
| Maybe you've been using Paperless-ngx for a while and want to add a feature
 | ||
| or two, or maybe you've come across a bug that you have some ideas how
 | ||
| to solve. The beauty of open source software is that you can see what's
 | ||
| wrong and help to get it fixed for everyone!
 | ||
| 
 | ||
| Before contributing please review our [code of
 | ||
| conduct](https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md)
 | ||
| and other important information in the [contributing
 | ||
| guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md).
 | ||
| 
 | ||
| ## Code formatting with pre-commit hooks
 | ||
| 
 | ||
| To ensure a consistent style and formatting across the project source,
 | ||
| the project utilizes Git [`pre-commit`](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)
 | ||
| hooks to perform some formatting and linting before a commit is allowed.
 | ||
| That way, everyone uses the same style and some common issues can be caught
 | ||
| early on.
 | ||
| 
 | ||
| Once installed, hooks will run when you commit. If the formatting isn't
 | ||
| quite right or a linter catches something, the commit will be rejected.
 | ||
| You'll need to look at the output and fix the issue. Some hooks, such
 | ||
| as the Python linting and formatting tool `ruff`, will format failing
 | ||
| files, so all you need to do is `git add` those files again
 | ||
| and retry your commit.
 | ||
| 
 | ||
| ## General setup
 | ||
| 
 | ||
| After you forked and cloned the code from GitHub you need to perform a
 | ||
| first-time setup.
 | ||
| 
 | ||
| !!! note
 | ||
| 
 | ||
|       Every command is executed directly from the root folder of the project unless specified otherwise.
 | ||
| 
 | ||
| 1.  Install prerequisites + [uv](https://github.com/astral-sh/uv) as mentioned in
 | ||
|     [Bare metal route](setup.md#bare_metal).
 | ||
| 
 | ||
| 2.  Copy `paperless.conf.example` to `paperless.conf` and enable debug
 | ||
|     mode within the file via `PAPERLESS_DEBUG=true`.
 | ||
| 
 | ||
| 3.  Create `consume` and `media` directories:
 | ||
| 
 | ||
|     ```bash
 | ||
|     mkdir -p consume media
 | ||
|     ```
 | ||
| 
 | ||
| 4.  Install the Python dependencies:
 | ||
| 
 | ||
|     ```bash
 | ||
|     $ uv sync --group dev
 | ||
|     ```
 | ||
| 
 | ||
| 5.  Install pre-commit hooks:
 | ||
| 
 | ||
|     ```bash
 | ||
|     $ uv run pre-commit install
 | ||
|     ```
 | ||
| 
 | ||
| 6.  Apply migrations and create a superuser (also can be done via the web UI) for your development instance:
 | ||
| 
 | ||
|     ```bash
 | ||
|     # src/
 | ||
| 
 | ||
|     $ uv run manage.py migrate
 | ||
|     $ uv run manage.py createsuperuser
 | ||
|     ```
 | ||
| 
 | ||
| 7.  You can now either ...
 | ||
| 
 | ||
|     -   install redis or
 | ||
| 
 | ||
|     -   use the included `scripts/start_services.sh` to use docker to fire
 | ||
|         up a redis instance (and some other services such as tika,
 | ||
|         gotenberg and a database server) or
 | ||
| 
 | ||
|     -   spin up a bare redis container
 | ||
| 
 | ||
|         ```
 | ||
|         docker run -d -p 6379:6379 --restart unless-stopped redis:latest
 | ||
|         ```
 | ||
| 
 | ||
| 8.  Continue with either back-end or front-end development – or both :-).
 | ||
| 
 | ||
| ## Back end development
 | ||
| 
 | ||
| The back end is a [Django](https://www.djangoproject.com/) application.
 | ||
| [PyCharm](https://www.jetbrains.com/de-de/pycharm/) as well as [Visual Studio Code](https://code.visualstudio.com)
 | ||
| work well for development, but you can use whatever you want.
 | ||
| 
 | ||
| Configure the IDE to use the `src/`-folder as the base source folder.
 | ||
| Configure the following launch configurations in your IDE:
 | ||
| 
 | ||
| -   `python3 manage.py runserver`
 | ||
| -   `python3 manage.py document_consumer`
 | ||
| -   `celery --app paperless worker -l DEBUG` (or any other log level)
 | ||
| 
 | ||
| To start them all:
 | ||
| 
 | ||
| ```bash
 | ||
| # src/
 | ||
| 
 | ||
| $ python3 manage.py runserver & \
 | ||
|   python3 manage.py document_consumer & \
 | ||
|   celery --app paperless worker -l DEBUG
 | ||
| ```
 | ||
| 
 | ||
| You might need the front end to test your back end code.
 | ||
| This assumes that you have AngularJS installed on your system.
 | ||
| Go to the [Front end development](#front-end-development) section for further details.
 | ||
| To build the front end once use this command:
 | ||
| 
 | ||
| ```bash
 | ||
| # src-ui/
 | ||
| 
 | ||
| $ pnpm install
 | ||
| $ ng build --configuration production
 | ||
| ```
 | ||
| 
 | ||
| ### Testing
 | ||
| 
 | ||
| -   Run `pytest` in the `src/` directory to execute all tests. This also
 | ||
|     generates a HTML coverage report. When runnings test, `paperless.conf`
 | ||
|     is loaded as well. However, the tests rely on the default
 | ||
|     configuration. This is not ideal. But for now, make sure no settings
 | ||
|     except for DEBUG are overridden when testing.
 | ||
| 
 | ||
| !!! note
 | ||
| 
 | ||
|       The line length rule E501 is generally useful for getting multiple
 | ||
|       source files next to each other on the screen. However, in some
 | ||
|       cases, its just not possible to make some lines fit, especially
 | ||
|       complicated IF cases. Append `# noqa: E501` to disable this check
 | ||
|       for certain lines.
 | ||
| 
 | ||
| ### Package Management
 | ||
| 
 | ||
| Paperless uses `uv` to manage packages and virtual environments for both development and production.
 | ||
| To accomplish some common tasks using `uv`, follow the shortcuts below:
 | ||
| 
 | ||
| To upgrade all locked packages to the latest allowed versions: `uv lock --upgrade`
 | ||
| 
 | ||
| To upgrade a single locked package: `uv lock --upgrade-package <package>`
 | ||
| 
 | ||
| To add a new package: `uv add <package>`
 | ||
| 
 | ||
| To add a new development package `uv add --dev <package>`
 | ||
| 
 | ||
| ## Front end development
 | ||
| 
 | ||
| The front end is built using AngularJS. In order to get started, you need Node.js (version 14.15+) and
 | ||
| `pnpm`.
 | ||
| 
 | ||
| !!! note
 | ||
| 
 | ||
|     The following commands are all performed in the `src-ui`-directory. You will need a running back end (including an active session) to connect to the back end API. To spin it up refer to the commands under the section [above](#back-end-development).
 | ||
| 
 | ||
| 1.  Install the Angular CLI. You might need sudo privileges to perform this command:
 | ||
| 
 | ||
|     ```bash
 | ||
|     pnpm install -g @angular/cli
 | ||
|     ```
 | ||
| 
 | ||
| 2.  Make sure that it's on your path.
 | ||
| 
 | ||
| 3.  Install all necessary modules:
 | ||
| 
 | ||
|     ```bash
 | ||
|     pnpm install
 | ||
|     ```
 | ||
| 
 | ||
| 4.  You can launch a development server by running:
 | ||
| 
 | ||
|     ```bash
 | ||
|     ng serve
 | ||
|     ```
 | ||
| 
 | ||
|     This will automatically update whenever you save. However, in-place
 | ||
|     compilation might fail on syntax errors, in which case you need to
 | ||
|     restart it.
 | ||
| 
 | ||
|     By default, the development server is available on `http://localhost:4200/` and is configured to access the API at
 | ||
|     `http://localhost:8000/api/`, which is the default of the backend. If you enabled `DEBUG` on the back end, several security overrides for allowed hosts and CORS are in place so that the front end behaves exactly as in production.
 | ||
| 
 | ||
| ### Testing and code style
 | ||
| 
 | ||
| The front end code (.ts, .html, .scss) use `prettier` for code
 | ||
| formatting via the Git `pre-commit` hooks which run automatically on
 | ||
| commit. See [above](#code-formatting-with-pre-commit-hooks) for installation instructions. You can also run this via the CLI with a
 | ||
| command such as
 | ||
| 
 | ||
| ```bash
 | ||
| $ git ls-files -- '*.ts' | xargs pre-commit run prettier --files
 | ||
| ```
 | ||
| 
 | ||
| Front end testing uses Jest and Playwright. Unit tests and e2e tests,
 | ||
| respectively, can be run non-interactively with:
 | ||
| 
 | ||
| ```bash
 | ||
| $ ng test
 | ||
| $ npx playwright test
 | ||
| ```
 | ||
| 
 | ||
| Playwright also includes a UI which can be run with:
 | ||
| 
 | ||
| ```bash
 | ||
| $ npx playwright test --ui
 | ||
| ```
 | ||
| 
 | ||
| ### Building the frontend
 | ||
| 
 | ||
| In order to build the front end and serve it as part of Django, execute:
 | ||
| 
 | ||
| ```bash
 | ||
| $ ng build --configuration production
 | ||
| ```
 | ||
| 
 | ||
| This will build the front end and put it in a location from which the
 | ||
| Django server will serve it as static content. This way, you can verify
 | ||
| that authentication is working.
 | ||
| 
 | ||
| ## Localization
 | ||
| 
 | ||
| Paperless-ngx is available in many different languages. Since Paperless-ngx
 | ||
| consists both of a Django application and an AngularJS front end, both
 | ||
| these parts have to be translated separately.
 | ||
| 
 | ||
| ### Front end localization
 | ||
| 
 | ||
| -   The AngularJS front end does localization according to the [Angular
 | ||
|     documentation](https://angular.io/guide/i18n).
 | ||
| -   The source language of the project is "en_US".
 | ||
| -   The source strings end up in the file `src-ui/messages.xlf`.
 | ||
| -   The translated strings need to be placed in the
 | ||
|     `src-ui/src/locale/` folder.
 | ||
| -   In order to extract added or changed strings from the source files,
 | ||
|     call `ng extract-i18n`.
 | ||
| 
 | ||
| Adding new languages requires adding the translated files in the
 | ||
| `src-ui/src/locale/` folder and adjusting a couple files.
 | ||
| 
 | ||
| 1.  Adjust `src-ui/angular.json`:
 | ||
| 
 | ||
|     ```json
 | ||
|     "i18n": {
 | ||
|         "sourceLocale": "en-US",
 | ||
|         "locales": {
 | ||
|             "de": "src/locale/messages.de.xlf",
 | ||
|             "nl-NL": "src/locale/messages.nl_NL.xlf",
 | ||
|             "fr": "src/locale/messages.fr.xlf",
 | ||
|             "en-GB": "src/locale/messages.en_GB.xlf",
 | ||
|             "pt-BR": "src/locale/messages.pt_BR.xlf",
 | ||
|             "language-code": "language-file"
 | ||
|         }
 | ||
|     }
 | ||
|     ```
 | ||
| 
 | ||
| 2.  Add the language to the `LANGUAGE_OPTIONS` array in
 | ||
|     `src-ui/src/app/services/settings.service.ts`:
 | ||
| 
 | ||
|     ```
 | ||
| 
 | ||
|     `dateInputFormat` is a special string that defines the behavior of
 | ||
|     the date input fields and absolutely needs to contain "dd", "mm"
 | ||
|     and "yyyy".
 | ||
| 
 | ||
|     ```
 | ||
| 
 | ||
| 3.  Import and register the Angular data for this locale in
 | ||
|     `src-ui/src/app/app.module.ts`:
 | ||
| 
 | ||
|     ```typescript
 | ||
|     import localeDe from '@angular/common/locales/de'
 | ||
|     registerLocaleData(localeDe)
 | ||
|     ```
 | ||
| 
 | ||
| ### Back end localization
 | ||
| 
 | ||
| A majority of the strings that appear in the back end appear only when
 | ||
| the admin is used. However, some of these are still shown on the front
 | ||
| end (such as error messages).
 | ||
| 
 | ||
| -   The django application does localization according to the [Django
 | ||
|     documentation](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/).
 | ||
| -   The source language of the project is "en_US".
 | ||
| -   Localization files end up in the folder `src/locale/`.
 | ||
| -   In order to extract strings from the application, call
 | ||
|     `python3 manage.py makemessages -l en_US`. This is important after
 | ||
|     making changes to translatable strings.
 | ||
| -   The message files need to be compiled for them to show up in the
 | ||
|     application. Call `python3 manage.py compilemessages` to do this.
 | ||
|     The generated files don't get committed into git, since these are
 | ||
|     derived artifacts. The build pipeline takes care of executing this
 | ||
|     command.
 | ||
| 
 | ||
| Adding new languages requires adding the translated files in the
 | ||
| `src/locale/`-folder and adjusting the file
 | ||
| `src/paperless/settings.py` to include the new language:
 | ||
| 
 | ||
| ```python
 | ||
| LANGUAGES = [
 | ||
|     ("en-us", _("English (US)")),
 | ||
|     ("en-gb", _("English (GB)")),
 | ||
|     ("de", _("German")),
 | ||
|     ("nl-nl", _("Dutch")),
 | ||
|     ("fr", _("French")),
 | ||
|     ("pt-br", _("Portuguese (Brazil)")),
 | ||
|     # Add language here.
 | ||
| ]
 | ||
| ```
 | ||
| 
 | ||
| ## Building the documentation
 | ||
| 
 | ||
| The documentation is built using material-mkdocs, see their [documentation](https://squidfunk.github.io/mkdocs-material/reference/).
 | ||
| If you want to build the documentation locally, this is how you do it:
 | ||
| 
 | ||
| 1.  Build the documentation
 | ||
| 
 | ||
|     ```bash
 | ||
|     $ uv run mkdocs build --config-file mkdocs.yml
 | ||
|     ```
 | ||
| 
 | ||
|     _alternatively..._
 | ||
| 
 | ||
| 2.  Serve the documentation. This will spin up a
 | ||
|     copy of the documentation at http://127.0.0.1:8000
 | ||
|     that will automatically refresh every time you change
 | ||
|     something.
 | ||
| 
 | ||
|     ```bash
 | ||
|     $ uv run mkdocs serve
 | ||
|     ```
 | ||
| 
 | ||
| ## Building the Docker image
 | ||
| 
 | ||
| The docker image is primarily built by the GitHub actions workflow, but
 | ||
| it can be faster when developing to build and tag an image locally.
 | ||
| 
 | ||
| Make sure you have the `docker-buildx` package installed. Building the image works as with any image:
 | ||
| 
 | ||
| ```
 | ||
| docker build --file Dockerfile --tag paperless:local .
 | ||
| ```
 | ||
| 
 | ||
| ## Extending Paperless-ngx
 | ||
| 
 | ||
| Paperless-ngx does not have any fancy plugin systems and will probably never
 | ||
| have. However, some parts of the application have been designed to allow
 | ||
| easy integration of additional features without any modification to the
 | ||
| base code.
 | ||
| 
 | ||
| ### Making custom parsers
 | ||
| 
 | ||
| Paperless-ngx uses parsers to add documents. A parser is
 | ||
| responsible for:
 | ||
| 
 | ||
| -   Retrieving the content from the original
 | ||
| -   Creating a thumbnail
 | ||
| -   _optional:_ Retrieving a created date from the original
 | ||
| -   _optional:_ Creating an archived document from the original
 | ||
| 
 | ||
| Custom parsers can be added to Paperless-ngx to support more file types. In
 | ||
| order to do that, you need to write the parser itself and announce its
 | ||
| existence to Paperless-ngx.
 | ||
| 
 | ||
| The parser itself must extend `documents.parsers.DocumentParser` and
 | ||
| must implement the methods `parse` and `get_thumbnail`. You can provide
 | ||
| your own implementation to `get_date` if you don't want to rely on
 | ||
| Paperless-ngx' default date guessing mechanisms.
 | ||
| 
 | ||
| ```python
 | ||
| class MyCustomParser(DocumentParser):
 | ||
| 
 | ||
|     def parse(self, document_path, mime_type):
 | ||
|         # This method does not return anything. Rather, you should assign
 | ||
|         # whatever you got from the document to the following fields:
 | ||
| 
 | ||
|         # The content of the document.
 | ||
|         self.text = "content"
 | ||
| 
 | ||
|         # Optional: path to a PDF document that you created from the original.
 | ||
|         self.archive_path = os.path.join(self.tempdir, "archived.pdf")
 | ||
| 
 | ||
|         # Optional: "created" date of the document.
 | ||
|         self.date = get_created_from_metadata(document_path)
 | ||
| 
 | ||
|     def get_thumbnail(self, document_path, mime_type):
 | ||
|         # This should return the path to a thumbnail you created for this
 | ||
|         # document.
 | ||
|         return os.path.join(self.tempdir, "thumb.webp")
 | ||
| ```
 | ||
| 
 | ||
| If you encounter any issues during parsing, raise a
 | ||
| `documents.parsers.ParseError`.
 | ||
| 
 | ||
| The `self.tempdir` directory is a temporary directory that is guaranteed
 | ||
| to be empty and removed after consumption finished. You can use that
 | ||
| directory to store any intermediate files and also use it to store the
 | ||
| thumbnail / archived document.
 | ||
| 
 | ||
| After that, you need to announce your parser to Paperless-ngx. You need to
 | ||
| connect a handler to the `document_consumer_declaration` signal. Have a
 | ||
| look in the file `src/paperless_tesseract/apps.py` on how that's done.
 | ||
| The handler is a method that returns information about your parser:
 | ||
| 
 | ||
| ```python
 | ||
| def myparser_consumer_declaration(sender, **kwargs):
 | ||
|     return {
 | ||
|         "parser": MyCustomParser,
 | ||
|         "weight": 0,
 | ||
|         "mime_types": {
 | ||
|             "application/pdf": ".pdf",
 | ||
|             "image/jpeg": ".jpg",
 | ||
|         }
 | ||
|     }
 | ||
| ```
 | ||
| 
 | ||
| -   `parser` is a reference to a class that extends `DocumentParser`.
 | ||
| -   `weight` is used whenever two or more parsers are able to parse a
 | ||
|     file: The parser with the higher weight wins. This can be used to
 | ||
|     override the parsers provided by Paperless-ngx.
 | ||
| -   `mime_types` is a dictionary. The keys are the mime types your
 | ||
|     parser supports and the value is the default file extension that
 | ||
|     Paperless-ngx should use when storing files and serving them for
 | ||
|     download. We could guess that from the file extensions, but some
 | ||
|     mime types have many extensions associated with them and the Python
 | ||
|     methods responsible for guessing the extension do not always return
 | ||
|     the same value.
 | ||
| 
 | ||
| ## Using Visual Studio Code devcontainer
 | ||
| 
 | ||
| Another easy way to get started with development is to use Visual Studio
 | ||
| Code devcontainers. This approach will create a preconfigured development
 | ||
| environment with all of the required tools and dependencies.
 | ||
| [Learn more about devcontainers](https://code.visualstudio.com/docs/devcontainers/containers).
 | ||
| The .devcontainer/vscode/tasks.json and .devcontainer/vscode/launch.json files
 | ||
| contain more information about the specific tasks and launch configurations (see the
 | ||
| non-standard "description" field).
 | ||
| 
 | ||
| To get started:
 | ||
| 
 | ||
| 1. Clone the repository on your machine and open the Paperless-ngx folder in VS Code.
 | ||
| 
 | ||
| 2. VS Code will prompt you with "Reopen in container". Do so and wait for the environment to start.
 | ||
| 
 | ||
| 3. Initialize the project by running the task **Project Setup: Run all Init Tasks**. This
 | ||
|    will initialize the database tables and create a superuser. Then you can compile the front end
 | ||
|    for production or run the frontend in debug mode.
 | ||
| 
 | ||
| 4. The project is ready for debugging, start either run the fullstack debug or individual debug
 | ||
|    processes. Yo spin up the project without debugging run the task **Project Start: Run all Services**
 |