1430 lines
60 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="prev" href="../api/">
<link rel="next" href="../faq/">
<link rel="icon" href="../assets/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.5.50">
<title>Development - Paperless-ngx</title>
<link rel="stylesheet" href="../assets/stylesheets/main.a40c8224.min.css">
<link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<link rel="stylesheet" href="../assets/extra.css">
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
<link href="../assets/stylesheets/glightbox.min.css" rel="stylesheet"/><style>
html.glightbox-open { overflow: initial; height: 100%; }
.gslide-title { margin-top: 0px; user-select: text; }
.gslide-desc { color: #666; user-select: text; }
.gslide-image img { background: white; }
.gscrollbar-fixer { padding-right: 15px; }
.gdesc-inner { font-size: 0.75rem; }
body[data-md-color-scheme="slate"] .gdesc-inner { background: var(--md-default-bg-color);}
body[data-md-color-scheme="slate"] .gslide-title { color: var(--md-default-fg-color);}
body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);}</style> <script src="../assets/javascripts/glightbox.min.js"></script></head>
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#development" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href=".." title="Paperless-ngx" class="md-header__button md-logo" aria-label="Paperless-ngx" data-md-component="logo">
<img src="../assets/logo.svg" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Paperless-ngx
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Development
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="(prefers-color-scheme)" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m14.3 16-.7-2h-3.2l-.7 2H7.8L11 7h2l3.2 9zM20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12zm-9.15 3.96h2.3L12 9z"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to system preference" type="radio" name="__palette" id="__palette_2">
<label class="md-header__button md-icon" title="Switch to system preference" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://github.com/paperless-ngx/paperless-ngx" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</div>
<div class="md-source__repository">
paperless-ngx/paperless-ngx
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href=".." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../setup/" class="md-tabs__link">
Setup
</a>
</li>
<li class="md-tabs__item">
<a href="../usage/" class="md-tabs__link">
Basic Usage
</a>
</li>
<li class="md-tabs__item">
<a href="../configuration/" class="md-tabs__link">
Configuration
</a>
</li>
<li class="md-tabs__item">
<a href="../administration/" class="md-tabs__link">
Administration
</a>
</li>
<li class="md-tabs__item">
<a href="../advanced_usage/" class="md-tabs__link">
Advanced Topics
</a>
</li>
<li class="md-tabs__item">
<a href="../api/" class="md-tabs__link">
REST API
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="./" class="md-tabs__link">
Development
</a>
</li>
<li class="md-tabs__item">
<a href="../faq/" class="md-tabs__link">
FAQs
</a>
</li>
<li class="md-tabs__item">
<a href="../troubleshooting/" class="md-tabs__link">
Troubleshooting
</a>
</li>
<li class="md-tabs__item">
<a href="../changelog/" class="md-tabs__link">
Changelog
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href=".." title="Paperless-ngx" class="md-nav__button md-logo" aria-label="Paperless-ngx" data-md-component="logo">
<img src="../assets/logo.svg" alt="logo">
</a>
Paperless-ngx
</label>
<div class="md-nav__source">
<a href="https://github.com/paperless-ngx/paperless-ngx" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</div>
<div class="md-source__repository">
paperless-ngx/paperless-ngx
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href=".." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../setup/" class="md-nav__link">
<span class="md-ellipsis">
Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../usage/" class="md-nav__link">
<span class="md-ellipsis">
Basic Usage
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../configuration/" class="md-nav__link">
<span class="md-ellipsis">
Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../administration/" class="md-nav__link">
<span class="md-ellipsis">
Administration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../advanced_usage/" class="md-nav__link">
<span class="md-ellipsis">
Advanced Topics
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../api/" class="md-nav__link">
<span class="md-ellipsis">
REST API
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Development
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Development
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#contributing-to-paperless-ngx" class="md-nav__link">
<span class="md-ellipsis">
Contributing to Paperless-ngx
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#code-formatting-with-pre-commit-hooks" class="md-nav__link">
<span class="md-ellipsis">
Code formatting with pre-commit hooks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#general-setup" class="md-nav__link">
<span class="md-ellipsis">
General setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#back-end-development" class="md-nav__link">
<span class="md-ellipsis">
Back end development
</span>
</a>
<nav class="md-nav" aria-label="Back end development">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#testing" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#front-end-development" class="md-nav__link">
<span class="md-ellipsis">
Front end development
</span>
</a>
<nav class="md-nav" aria-label="Front end development">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#testing-and-code-style" class="md-nav__link">
<span class="md-ellipsis">
Testing and code style
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#building-the-frontend" class="md-nav__link">
<span class="md-ellipsis">
Building the frontend
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#localization" class="md-nav__link">
<span class="md-ellipsis">
Localization
</span>
</a>
<nav class="md-nav" aria-label="Localization">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#front-end-localization" class="md-nav__link">
<span class="md-ellipsis">
Front end localization
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#back-end-localization" class="md-nav__link">
<span class="md-ellipsis">
Back end localization
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#building-the-documentation" class="md-nav__link">
<span class="md-ellipsis">
Building the documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#building-the-docker-image" class="md-nav__link">
<span class="md-ellipsis">
Building the Docker image
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#extending-paperless-ngx" class="md-nav__link">
<span class="md-ellipsis">
Extending Paperless-ngx
</span>
</a>
<nav class="md-nav" aria-label="Extending Paperless-ngx">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#making-custom-parsers" class="md-nav__link">
<span class="md-ellipsis">
Making custom parsers
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#using-visual-studio-code-devcontainer" class="md-nav__link">
<span class="md-ellipsis">
Using Visual Studio Code devcontainer
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../faq/" class="md-nav__link">
<span class="md-ellipsis">
FAQs
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../changelog/" class="md-nav__link">
<span class="md-ellipsis">
Changelog
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="development">Development</h1>
<p>This section describes the steps you need to take to start development
on Paperless-ngx.</p>
<p>Check out the source from GitHub. The repository is organized in the
following way:</p>
<ul>
<li><code>main</code> always represents the latest release and will only see
changes when a new release is made.</li>
<li><code>dev</code> contains the code that will be in the next release.</li>
<li><code>feature-X</code> contains bigger changes that will be in some release, but
not necessarily the next one.</li>
</ul>
<p>When making functional changes to Paperless-ngx, <em>always</em> make your changes
on the <code>dev</code> branch.</p>
<p>Apart from that, the folder structure is as follows:</p>
<ul>
<li><code>docs/</code> - Documentation.</li>
<li><code>src-ui/</code> - Code of the front end.</li>
<li><code>src/</code> - Code of the back end.</li>
<li><code>scripts/</code> - Various scripts that help with different parts of
development.</li>
<li><code>docker/</code> - Files required to build the docker image.</li>
</ul>
<h2 id="contributing-to-paperless-ngx">Contributing to Paperless-ngx</h2>
<p>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!</p>
<p>Before contributing please review our <a href="https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md">code of
conduct</a>
and other important information in the <a href="https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md">contributing
guidelines</a>.</p>
<h2 id="code-formatting-with-pre-commit-hooks">Code formatting with pre-commit hooks</h2>
<p>To ensure a consistent style and formatting across the project source,
the project utilizes Git <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks"><code>pre-commit</code></a>
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.</p>
<p>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 <code>ruff</code>, will format failing
files, so all you need to do is <code>git add</code> those files again
and retry your commit.</p>
<h2 id="general-setup">General setup</h2>
<p>After you forked and cloned the code from GitHub you need to perform a
first-time setup.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Every command is executed directly from the root folder of the project unless specified otherwise.</p>
</div>
<ol>
<li>
<p>Install prerequisites + pipenv as mentioned in
<a href="../setup/#bare_metal">Bare metal route</a>.</p>
</li>
<li>
<p>Copy <code>paperless.conf.example</code> to <code>paperless.conf</code> and enable debug
mode within the file via <code>PAPERLESS_DEBUG=true</code>.</p>
</li>
<li>
<p>Create <code>consume</code> and <code>media</code> directories:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>mkdir<span class="w"> </span>-p<span class="w"> </span>consume<span class="w"> </span>media
</code></pre></div>
</li>
<li>
<p>Install the Python dependencies:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>pipenv<span class="w"> </span>install<span class="w"> </span>--dev
</code></pre></div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Using a virtual environment is highly recommended. You can spawn one via <code>pipenv shell</code>.</p>
</div>
</li>
<li>
<p>Install pre-commit hooks:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>pre-commit<span class="w"> </span>install
</code></pre></div>
</li>
<li>
<p>Apply migrations and create a superuser for your development instance:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="c1"># src/</span>
<a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>
<a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a>python3<span class="w"> </span>manage.py<span class="w"> </span>migrate
<a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a>python3<span class="w"> </span>manage.py<span class="w"> </span>createsuperuser
</code></pre></div>
</li>
<li>
<p>You can now either ...</p>
<ul>
<li>
<p>install redis or</p>
</li>
<li>
<p>use the included <code>scripts/start_services.sh</code> to use docker to fire
up a redis instance (and some other services such as tika,
gotenberg and a database server) or</p>
</li>
<li>
<p>spin up a bare redis container</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>docker run -d -p 6379:6379 --restart unless-stopped redis:latest
</code></pre></div>
</li>
</ul>
</li>
<li>
<p>Continue with either back-end or front-end development or both :-).</p>
</li>
</ol>
<h2 id="back-end-development">Back end development</h2>
<p>The back end is a <a href="https://www.djangoproject.com/">Django</a> application.
<a href="https://www.jetbrains.com/de-de/pycharm/">PyCharm</a> as well as <a href="https://code.visualstudio.com">Visual Studio Code</a>
work well for development, but you can use whatever you want.</p>
<p>Configure the IDE to use the <code>src/</code>-folder as the base source folder.
Configure the following launch configurations in your IDE:</p>
<ul>
<li><code>python3 manage.py runserver</code></li>
<li><code>python3 manage.py document_consumer</code></li>
<li><code>celery --app paperless worker -l DEBUG</code> (or any other log level)</li>
</ul>
<p>To start them all:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="c1"># src/</span>
<a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>
<a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>$<span class="w"> </span>python3<span class="w"> </span>manage.py<span class="w"> </span>runserver<span class="w"> </span><span class="p">&amp;</span><span class="w"> </span><span class="se">\</span>
<a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="w"> </span>python3<span class="w"> </span>manage.py<span class="w"> </span>document_consumer<span class="w"> </span><span class="p">&amp;</span><span class="w"> </span><span class="se">\</span>
<a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="w"> </span>celery<span class="w"> </span>--app<span class="w"> </span>paperless<span class="w"> </span>worker<span class="w"> </span>-l<span class="w"> </span>DEBUG
</code></pre></div>
<p>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 <a href="#front-end-development">Front end development</a> section for further details.
To build the front end once use this command:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="c1"># src-ui/</span>
<a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a>
<a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a>$<span class="w"> </span>npm<span class="w"> </span>install
<a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a>$<span class="w"> </span>ng<span class="w"> </span>build<span class="w"> </span>--configuration<span class="w"> </span>production
</code></pre></div>
<h3 id="testing">Testing</h3>
<ul>
<li>Run <code>pytest</code> in the <code>src/</code> directory to execute all tests. This also
generates a HTML coverage report. When runnings test, <code>paperless.conf</code>
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.</li>
</ul>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>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 <code># noqa: E501</code> to disable this check
for certain lines.</p>
</div>
<h2 id="front-end-development">Front end development</h2>
<p>The front end is built using AngularJS. In order to get started, you need Node.js (version 14.15+) and
<code>npm</code>.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The following commands are all performed in the <code>src-ui</code>-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 <a href="#back-end-development">above</a>.</p>
</div>
<ol>
<li>
<p>Install the Angular CLI. You might need sudo privileges to perform this command:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>npm<span class="w"> </span>install<span class="w"> </span>-g<span class="w"> </span>@angular/cli
</code></pre></div>
</li>
<li>
<p>Make sure that it's on your path.</p>
</li>
<li>
<p>Install all necessary modules:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a>npm<span class="w"> </span>install
</code></pre></div>
</li>
<li>
<p>You can launch a development server by running:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a>ng<span class="w"> </span>serve
</code></pre></div>
<p>This will automatically update whenever you save. However, in-place
compilation might fail on syntax errors, in which case you need to
restart it.</p>
<p>By default, the development server is available on <code>http://localhost:4200/</code> and is configured to access the API at
<code>http://localhost:8000/api/</code>, which is the default of the backend. If you enabled <code>DEBUG</code> on the back end, several security overrides for allowed hosts, CORS and X-Frame-Options are in place so that the front end behaves exactly as in production.</p>
</li>
</ol>
<h3 id="testing-and-code-style">Testing and code style</h3>
<p>The front end code (.ts, .html, .scss) use <code>prettier</code> for code
formatting via the Git <code>pre-commit</code> hooks which run automatically on
commit. See <a href="#code-formatting-with-pre-commit-hooks">above</a> for installation instructions. You can also run this via the CLI with a
command such as</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a>$<span class="w"> </span>git<span class="w"> </span>ls-files<span class="w"> </span>--<span class="w"> </span><span class="s1">&#39;*.ts&#39;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>xargs<span class="w"> </span>pre-commit<span class="w"> </span>run<span class="w"> </span>prettier<span class="w"> </span>--files
</code></pre></div>
<p>Front end testing uses Jest and Playwright. Unit tests and e2e tests,
respectively, can be run non-interactively with:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a>$<span class="w"> </span>ng<span class="w"> </span><span class="nb">test</span>
<a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>$<span class="w"> </span>npx<span class="w"> </span>playwright<span class="w"> </span><span class="nb">test</span>
</code></pre></div>
<p>Playwright also includes a UI which can be run with:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a>$<span class="w"> </span>npx<span class="w"> </span>playwright<span class="w"> </span><span class="nb">test</span><span class="w"> </span>--ui
</code></pre></div>
<h3 id="building-the-frontend">Building the frontend</h3>
<p>In order to build the front end and serve it as part of Django, execute:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a>$<span class="w"> </span>ng<span class="w"> </span>build<span class="w"> </span>--configuration<span class="w"> </span>production
</code></pre></div>
<p>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.</p>
<h2 id="localization">Localization</h2>
<p>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.</p>
<h3 id="front-end-localization">Front end localization</h3>
<ul>
<li>The AngularJS front end does localization according to the <a href="https://angular.io/guide/i18n">Angular
documentation</a>.</li>
<li>The source language of the project is "en_US".</li>
<li>The source strings end up in the file <code>src-ui/messages.xlf</code>.</li>
<li>The translated strings need to be placed in the
<code>src-ui/src/locale/</code> folder.</li>
<li>In order to extract added or changed strings from the source files,
call <code>ng extract-i18n</code>.</li>
</ul>
<p>Adding new languages requires adding the translated files in the
<code>src-ui/src/locale/</code> folder and adjusting a couple files.</p>
<ol>
<li>
<p>Adjust <code>src-ui/angular.json</code>:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="nt">&quot;i18n&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a><span class="w"> </span><span class="nt">&quot;sourceLocale&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;en-US&quot;</span><span class="p">,</span>
<a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="w"> </span><span class="nt">&quot;locales&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="w"> </span><span class="nt">&quot;de&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;src/locale/messages.de.xlf&quot;</span><span class="p">,</span>
<a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a><span class="w"> </span><span class="nt">&quot;nl-NL&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;src/locale/messages.nl_NL.xlf&quot;</span><span class="p">,</span>
<a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a><span class="w"> </span><span class="nt">&quot;fr&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;src/locale/messages.fr.xlf&quot;</span><span class="p">,</span>
<a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a><span class="w"> </span><span class="nt">&quot;en-GB&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;src/locale/messages.en_GB.xlf&quot;</span><span class="p">,</span>
<a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a><span class="w"> </span><span class="nt">&quot;pt-BR&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;src/locale/messages.pt_BR.xlf&quot;</span><span class="p">,</span>
<a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a><span class="w"> </span><span class="nt">&quot;language-code&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;language-file&quot;</span>
<a id="__codelineno-14-10" name="__codelineno-14-10" href="#__codelineno-14-10"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-14-11" name="__codelineno-14-11" href="#__codelineno-14-11"></a><span class="p">}</span>
</code></pre></div>
</li>
<li>
<p>Add the language to the <code>LANGUAGE_OPTIONS</code> array in
<code>src-ui/src/app/services/settings.service.ts</code>:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a>`dateInputFormat` is a special string that defines the behavior of
<a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a>the date input fields and absolutely needs to contain &quot;dd&quot;, &quot;mm&quot;
<a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a>and &quot;yyyy&quot;.
</code></pre></div>
</li>
<li>
<p>Import and register the Angular data for this locale in
<code>src-ui/src/app/app.module.ts</code>:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="k">import</span><span class="w"> </span><span class="nx">localeDe</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;@angular/common/locales/de&#39;</span>
<a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="nx">registerLocaleData</span><span class="p">(</span><span class="nx">localeDe</span><span class="p">)</span>
</code></pre></div>
</li>
</ol>
<h3 id="back-end-localization">Back end localization</h3>
<p>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).</p>
<ul>
<li>The django application does localization according to the <a href="https://docs.djangoproject.com/en/3.1/topics/i18n/translation/">Django
documentation</a>.</li>
<li>The source language of the project is "en_US".</li>
<li>Localization files end up in the folder <code>src/locale/</code>.</li>
<li>In order to extract strings from the application, call
<code>python3 manage.py makemessages -l en_US</code>. This is important after
making changes to translatable strings.</li>
<li>The message files need to be compiled for them to show up in the
application. Call <code>python3 manage.py compilemessages</code> 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.</li>
</ul>
<p>Adding new languages requires adding the translated files in the
<code>src/locale/</code>-folder and adjusting the file
<code>src/paperless/settings.py</code> to include the new language:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="n">LANGUAGES</span> <span class="o">=</span> <span class="p">[</span>
<a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a> <span class="p">(</span><span class="s2">&quot;en-us&quot;</span><span class="p">,</span> <span class="n">_</span><span class="p">(</span><span class="s2">&quot;English (US)&quot;</span><span class="p">)),</span>
<a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a> <span class="p">(</span><span class="s2">&quot;en-gb&quot;</span><span class="p">,</span> <span class="n">_</span><span class="p">(</span><span class="s2">&quot;English (GB)&quot;</span><span class="p">)),</span>
<a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a> <span class="p">(</span><span class="s2">&quot;de&quot;</span><span class="p">,</span> <span class="n">_</span><span class="p">(</span><span class="s2">&quot;German&quot;</span><span class="p">)),</span>
<a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a> <span class="p">(</span><span class="s2">&quot;nl-nl&quot;</span><span class="p">,</span> <span class="n">_</span><span class="p">(</span><span class="s2">&quot;Dutch&quot;</span><span class="p">)),</span>
<a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a> <span class="p">(</span><span class="s2">&quot;fr&quot;</span><span class="p">,</span> <span class="n">_</span><span class="p">(</span><span class="s2">&quot;French&quot;</span><span class="p">)),</span>
<a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a> <span class="p">(</span><span class="s2">&quot;pt-br&quot;</span><span class="p">,</span> <span class="n">_</span><span class="p">(</span><span class="s2">&quot;Portuguese (Brazil)&quot;</span><span class="p">)),</span>
<a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a> <span class="c1"># Add language here.</span>
<a id="__codelineno-17-9" name="__codelineno-17-9" href="#__codelineno-17-9"></a><span class="p">]</span>
</code></pre></div>
<h2 id="building-the-documentation">Building the documentation</h2>
<p>The documentation is built using material-mkdocs, see their <a href="https://squidfunk.github.io/mkdocs-material/reference/">documentation</a>.
If you want to build the documentation locally, this is how you do it:</p>
<ol>
<li>
<p>Have an active pipenv shell (<code>pipenv shell</code>) and install Python dependencies:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a>pipenv<span class="w"> </span>install<span class="w"> </span>--dev
</code></pre></div>
</li>
<li>
<p>Build the documentation</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a>mkdocs<span class="w"> </span>build<span class="w"> </span>--config-file<span class="w"> </span>mkdocs.yml
</code></pre></div>
<p><em>alternatively...</em></p>
</li>
<li>
<p>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.</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a>mkdocs<span class="w"> </span>serve
</code></pre></div>
</li>
</ol>
<h2 id="building-the-docker-image">Building the Docker image</h2>
<p>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.</p>
<p>Make sure you have the <code>docker-buildx</code> package installed. Building the image works as with any image:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a>docker build --file Dockerfile --tag paperless:local .
</code></pre></div>
<h2 id="extending-paperless-ngx">Extending Paperless-ngx</h2>
<p>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.</p>
<h3 id="making-custom-parsers">Making custom parsers</h3>
<p>Paperless-ngx uses parsers to add documents. A parser is
responsible for:</p>
<ul>
<li>Retrieving the content from the original</li>
<li>Creating a thumbnail</li>
<li><em>optional:</em> Retrieving a created date from the original</li>
<li><em>optional:</em> Creating an archived document from the original</li>
</ul>
<p>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.</p>
<p>The parser itself must extend <code>documents.parsers.DocumentParser</code> and
must implement the methods <code>parse</code> and <code>get_thumbnail</code>. You can provide
your own implementation to <code>get_date</code> if you don't want to rely on
Paperless-ngx' default date guessing mechanisms.</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a><span class="k">class</span><span class="w"> </span><span class="nc">MyCustomParser</span><span class="p">(</span><span class="n">DocumentParser</span><span class="p">):</span>
<a id="__codelineno-22-2" name="__codelineno-22-2" href="#__codelineno-22-2"></a>
<a id="__codelineno-22-3" name="__codelineno-22-3" href="#__codelineno-22-3"></a> <span class="k">def</span><span class="w"> </span><span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">document_path</span><span class="p">,</span> <span class="n">mime_type</span><span class="p">):</span>
<a id="__codelineno-22-4" name="__codelineno-22-4" href="#__codelineno-22-4"></a> <span class="c1"># This method does not return anything. Rather, you should assign</span>
<a id="__codelineno-22-5" name="__codelineno-22-5" href="#__codelineno-22-5"></a> <span class="c1"># whatever you got from the document to the following fields:</span>
<a id="__codelineno-22-6" name="__codelineno-22-6" href="#__codelineno-22-6"></a>
<a id="__codelineno-22-7" name="__codelineno-22-7" href="#__codelineno-22-7"></a> <span class="c1"># The content of the document.</span>
<a id="__codelineno-22-8" name="__codelineno-22-8" href="#__codelineno-22-8"></a> <span class="bp">self</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s2">&quot;content&quot;</span>
<a id="__codelineno-22-9" name="__codelineno-22-9" href="#__codelineno-22-9"></a>
<a id="__codelineno-22-10" name="__codelineno-22-10" href="#__codelineno-22-10"></a> <span class="c1"># Optional: path to a PDF document that you created from the original.</span>
<a id="__codelineno-22-11" name="__codelineno-22-11" href="#__codelineno-22-11"></a> <span class="bp">self</span><span class="o">.</span><span class="n">archive_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tempdir</span><span class="p">,</span> <span class="s2">&quot;archived.pdf&quot;</span><span class="p">)</span>
<a id="__codelineno-22-12" name="__codelineno-22-12" href="#__codelineno-22-12"></a>
<a id="__codelineno-22-13" name="__codelineno-22-13" href="#__codelineno-22-13"></a> <span class="c1"># Optional: &quot;created&quot; date of the document.</span>
<a id="__codelineno-22-14" name="__codelineno-22-14" href="#__codelineno-22-14"></a> <span class="bp">self</span><span class="o">.</span><span class="n">date</span> <span class="o">=</span> <span class="n">get_created_from_metadata</span><span class="p">(</span><span class="n">document_path</span><span class="p">)</span>
<a id="__codelineno-22-15" name="__codelineno-22-15" href="#__codelineno-22-15"></a>
<a id="__codelineno-22-16" name="__codelineno-22-16" href="#__codelineno-22-16"></a> <span class="k">def</span><span class="w"> </span><span class="nf">get_thumbnail</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">document_path</span><span class="p">,</span> <span class="n">mime_type</span><span class="p">):</span>
<a id="__codelineno-22-17" name="__codelineno-22-17" href="#__codelineno-22-17"></a> <span class="c1"># This should return the path to a thumbnail you created for this</span>
<a id="__codelineno-22-18" name="__codelineno-22-18" href="#__codelineno-22-18"></a> <span class="c1"># document.</span>
<a id="__codelineno-22-19" name="__codelineno-22-19" href="#__codelineno-22-19"></a> <span class="k">return</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tempdir</span><span class="p">,</span> <span class="s2">&quot;thumb.webp&quot;</span><span class="p">)</span>
</code></pre></div>
<p>If you encounter any issues during parsing, raise a
<code>documents.parsers.ParseError</code>.</p>
<p>The <code>self.tempdir</code> 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.</p>
<p>After that, you need to announce your parser to Paperless-ngx. You need to
connect a handler to the <code>document_consumer_declaration</code> signal. Have a
look in the file <code>src/paperless_tesseract/apps.py</code> on how that's done.
The handler is a method that returns information about your parser:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a><span class="k">def</span><span class="w"> </span><span class="nf">myparser_consumer_declaration</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<a id="__codelineno-23-2" name="__codelineno-23-2" href="#__codelineno-23-2"></a> <span class="k">return</span> <span class="p">{</span>
<a id="__codelineno-23-3" name="__codelineno-23-3" href="#__codelineno-23-3"></a> <span class="s2">&quot;parser&quot;</span><span class="p">:</span> <span class="n">MyCustomParser</span><span class="p">,</span>
<a id="__codelineno-23-4" name="__codelineno-23-4" href="#__codelineno-23-4"></a> <span class="s2">&quot;weight&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<a id="__codelineno-23-5" name="__codelineno-23-5" href="#__codelineno-23-5"></a> <span class="s2">&quot;mime_types&quot;</span><span class="p">:</span> <span class="p">{</span>
<a id="__codelineno-23-6" name="__codelineno-23-6" href="#__codelineno-23-6"></a> <span class="s2">&quot;application/pdf&quot;</span><span class="p">:</span> <span class="s2">&quot;.pdf&quot;</span><span class="p">,</span>
<a id="__codelineno-23-7" name="__codelineno-23-7" href="#__codelineno-23-7"></a> <span class="s2">&quot;image/jpeg&quot;</span><span class="p">:</span> <span class="s2">&quot;.jpg&quot;</span><span class="p">,</span>
<a id="__codelineno-23-8" name="__codelineno-23-8" href="#__codelineno-23-8"></a> <span class="p">}</span>
<a id="__codelineno-23-9" name="__codelineno-23-9" href="#__codelineno-23-9"></a> <span class="p">}</span>
</code></pre></div>
<ul>
<li><code>parser</code> is a reference to a class that extends <code>DocumentParser</code>.</li>
<li><code>weight</code> 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.</li>
<li><code>mime_types</code> 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.</li>
</ul>
<h2 id="using-visual-studio-code-devcontainer">Using Visual Studio Code devcontainer</h2>
<p>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.
<a href="https://code.visualstudio.com/docs/devcontainers/containers">Learn more about devcontainers</a>.
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).</p>
<p>To get started:</p>
<ol>
<li>
<p>Clone the repository on your machine and open the Paperless-ngx folder in VS Code.</p>
</li>
<li>
<p>VS Code will prompt you with "Reopen in container". Do so and wait for the environment to start.</p>
</li>
<li>
<p>Initialize the project by running the task <strong>Project Setup: Run all Init Tasks</strong>. 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.</p>
</li>
<li>
<p>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 <strong>Project Start: Run all Services</strong></p>
</li>
</ol>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; 2016 - 2023 Daniel Quinn, Jonas Winkler, and the Paperless-ngx team
</div>
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
<div class="md-social">
<a href="https://github.com/paperless-ngx/paperless-ngx" target="_blank" rel="noopener" title="github.com" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</a>
<a href="https://hub.docker.com/r/paperlessngx/paperless-ngx" target="_blank" rel="noopener" title="hub.docker.com" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M349.9 236.3h-66.1v-59.4h66.1zm0-204.3h-66.1v60.7h66.1zm78.2 144.8H362v59.4h66.1zm-156.3-72.1h-66.1v60.1h66.1zm78.1 0h-66.1v60.1h66.1zm276.8 100c-14.4-9.7-47.6-13.2-73.1-8.4-3.3-24-16.7-44.9-41.1-63.7l-14-9.3-9.3 14c-18.4 27.8-23.4 73.6-3.7 103.8-8.7 4.7-25.8 11.1-48.4 10.7H2.4c-8.7 50.8 5.8 116.8 44 162.1 37.1 43.9 92.7 66.2 165.4 66.2 157.4 0 273.9-72.5 328.4-204.2 21.4.4 67.6.1 91.3-45.2 1.5-2.5 6.6-13.2 8.5-17.1zm-511.1-27.9h-66v59.4h66.1v-59.4zm78.1 0h-66.1v59.4h66.1zm78.1 0h-66.1v59.4h66.1zm-78.1-72.1h-66.1v60.1h66.1z"/></svg>
</a>
<a href="https://matrix.to/#/#paperless:matrix.org" target="_blank" rel="noopener" title="matrix.to" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 3c5.5 0 10 3.58 10 8s-4.5 8-10 8c-1.24 0-2.43-.18-3.53-.5C5.55 21 2 21 2 21c2.33-2.33 2.7-3.9 2.75-4.5C3.05 15.07 2 13.13 2 11c0-4.42 4.5-8 10-8"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "..", "features": ["navigation.tabs", "navigation.top", "toc.integrate", "content.code.annotate"], "search": "../assets/javascripts/workers/search.f8cc74c7.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
<script src="../assets/javascripts/bundle.60a45f97.min.js"></script>
<script id="init-glightbox">const lightbox = GLightbox({"touchNavigation": true, "loop": false, "zoomable": true, "draggable": true, "openEffect": "zoom", "closeEffect": "zoom", "slideEffect": "slide"});
document$.subscribe(() => { lightbox.reload() });
</script></body>
</html>