1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
{ stdenv
, lib
, fetchFromGitHub
, makeWrapper
, callPackage
, python3
, imagemagick
, ghostscript
, optipng
, tesseract
, unpaper
}:
## Usage
# ${paperless}/bin/paperless wraps manage.py
# ${paperless}/share/paperless/setup-env.sh can be sourced from a
# shell script to setup a Paperless environment
# paperless.withConfig is a convenience function to setup a
# configured Paperless instance. (See ./withConfig.nix)
# For WSGI with gunicorn, use a shell script like this:
# let
# pythonEnv = paperless.python.withPackages (ps: paperless.runtimePackages ++ [ ps.gunicorn ]);
# in
# writers.writeBash "run-gunicorn" ''
# source ${paperless}/share/paperless/setup-env.sh
# PYTHONPATH=$paperlessSrc ${pythonEnv}/bin/gunicorn paperless.wsgi
# ''
let
paperless = stdenv.mkDerivation rec {
pname = "paperless";
version = "2.7.0";
src = fetchFromGitHub {
owner = "the-paperless-project";
repo = "paperless";
rev = version;
sha256 = "0pkmyky1crjnsg7r0gfk0fadisfsgzlsq6afpz16wx4hp6yvkkf7";
};
nativeBuildInputs = [ makeWrapper ];
doCheck = true;
dontInstall = true;
pythonEnv = python.withPackages (_: runtimePackages);
pythonCheckEnv = python.withPackages (_: (runtimePackages ++ checkPackages));
unpackPhase = ''
srcDir=$out/share/paperless
mkdir -p $srcDir
cp -r --no-preserve=mode $src/src/* $src/LICENSE $srcDir
'';
postPatch = ''
# django-cors-headers 3.x requires a scheme for allowed hosts
substituteInPlace $out/share/paperless/paperless/settings.py \
--replace "localhost:8080" "http://localhost:8080"
'';
buildPhase = let
# Paperless has explicit runtime checks that expect these binaries to be in PATH
extraBin = lib.makeBinPath [ imagemagick ghostscript optipng tesseract unpaper ];
in ''
${python.interpreter} -m compileall $srcDir
makeWrapper $pythonEnv/bin/python $out/bin/paperless \
--set PATH ${extraBin} --add-flags $out/share/paperless/manage.py
# A shell snippet that can be sourced to setup a paperless env
cat > $out/share/paperless/setup-env.sh <<EOF
export PATH="$pythonEnv/bin:${extraBin}''${PATH:+:}$PATH"
export paperlessSrc=$out/share/paperless
EOF
'';
checkPhase = ''
source $out/share/paperless/setup-env.sh
tmpDir=$(realpath testsTmp)
mkdir $tmpDir
export HOME=$tmpDir
export PAPERLESS_MEDIADIR=$tmpDir
cd $paperlessSrc
# Prevent tests from writing to the derivation output
chmod -R -w $out
# Disable cache to silence a pytest warning ("could not create cache")
$pythonCheckEnv/bin/pytest -p no:cacheprovider
'';
passthru = {
withConfig = callPackage ./withConfig.nix {};
inherit python runtimePackages checkPackages tesseract;
};
meta = with lib; {
description = "Scan, index, and archive all of your paper documents";
homepage = "https://github.com/the-paperless-project/paperless";
license = licenses.gpl3;
maintainers = [ maintainers.earvstedt ];
};
};
python = python3.override {
packageOverrides = self: super: let
customPkgs = import ./python-modules super fetchFromGitHub; in
{
pyocr = pyocrWithUserTesseract super;
# Paperless is incompatible with factory_boy >= 3
factory_boy = customPkgs.factory_boy_2_12_0;
# These are pre-release versions, hence they are private to this pkg
django-filter = self.callPackage ./python-modules/django-filter.nix {};
django-crispy-forms = self.callPackage ./python-modules/django-crispy-forms.nix {};
};
};
runtimePackages = with python.pkgs; [
dateparser
dateutil
django
django-cors-headers
django-crispy-forms
django-filter
django_extensions
djangoql
djangorestframework
factory_boy
filemagic
fuzzywuzzy
langdetect
pdftotext
pillow
psycopg2
pyocr
python-dotenv
python-gnupg
pytz
termcolor
] ++ (lib.optional stdenv.isLinux inotify-simple);
checkPackages = with python.pkgs; [
pytest
pytest-django
pytest-env
pytest_xdist
];
pyocrWithUserTesseract = pyPkgs:
let
pyocr = pyPkgs.pyocr.override { inherit tesseract; };
in
if pyocr.outPath == pyPkgs.pyocr.outPath then
pyocr
else
# The user has provided a custom tesseract derivation that might be
# missing some languages that are required for PyOCR's tests. Disable them to
# avoid build errors.
pyocr.overridePythonAttrs (attrs: {
doCheck = false;
});
in
paperless
|