From 972f6b7b8e1d50a45ac76375eacc54fbfae3c7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Ml=C3=A1dek?= Date: Sat, 2 Jan 2021 13:09:03 +0100 Subject: [PATCH] autogenerated opengraph images for documents --- poetry.lock | 428 +++++++++--------- pyproject.toml | 2 + .../pile/migrations/0014_document_image.py | 19 + sdbs_pile/pile/models.py | 9 + sdbs_pile/pile/templates/front_base.html | 2 +- .../pile/templates/front_doc_detail.html | 1 + .../pile/templates/front_doc_listing.html | 1 + sdbs_pile/pile/urls.py | 1 + sdbs_pile/pile/views.py | 27 ++ 9 files changed, 286 insertions(+), 204 deletions(-) create mode 100644 sdbs_pile/pile/migrations/0014_document_image.py diff --git a/poetry.lock b/poetry.lock index b592a50..8ae1cf5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,67 +1,71 @@ [[package]] -category = "dev" -description = "Disable App Nap on OS X 10.9" -marker = "sys_platform == \"darwin\"" name = "appnope" +version = "0.1.0" +description = "Disable App Nap on OS X 10.9" +category = "dev" optional = false python-versions = "*" -version = "0.1.0" +marker = "sys_platform == \"darwin\"" [[package]] -category = "main" -description = "ASGI specs, helper code, and adapters" name = "asgiref" +version = "3.2.5" +description = "ASGI specs, helper code, and adapters" +category = "main" optional = false python-versions = ">=3.5" -version = "3.2.5" [package.extras] tests = ["pytest (>=4.3.0,<4.4.0)", "pytest-asyncio (>=0.10.0,<0.11.0)"] [[package]] -category = "dev" -description = "Specifications for callback functions passed in to an API" name = "backcall" +version = "0.1.0" +description = "Specifications for callback functions passed in to an API" +category = "dev" optional = false python-versions = "*" -version = "0.1.0" [[package]] -category = "main" -description = "An easy safelist-based HTML-sanitizing tool." name = "bleach" +version = "3.1.4" +description = "An easy safelist-based HTML-sanitizing tool." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "3.1.4" [package.dependencies] six = ">=1.9.0" webencodings = "*" [[package]] -category = "main" -description = "cffi-based cairo bindings for Python" name = "cairocffi" +version = "1.1.0" +description = "cffi-based cairo bindings for Python" +category = "main" optional = false python-versions = ">= 3.5" -version = "1.1.0" - -[package.dependencies] -cffi = ">=1.1.0" -setuptools = ">=39.2.0" [package.extras] doc = ["sphinx", "sphinx-rtd-theme"] test = ["pytest-runner", "pytest-cov", "pytest-flake8", "pytest-isort"] xcb = ["xcffib (>=0.3.2)"] +[package.dependencies] +cffi = ">=1.1.0" +setuptools = ">=39.2.0" + [[package]] -category = "main" -description = "A Simple SVG Converter based on Cairo" name = "cairosvg" +version = "2.4.2" +description = "A Simple SVG Converter based on Cairo" +category = "main" optional = false python-versions = ">= 3.5" -version = "2.4.2" + +[package.extras] +doc = ["sphinx", "sphinx-rtd-theme"] +test = ["pytest-runner", "pytest-cov", "pytest-flake8", "pytest-isort"] [package.dependencies] cairocffi = "*" @@ -70,109 +74,102 @@ defusedxml = "*" pillow = "*" tinycss2 = "*" -[package.extras] -doc = ["sphinx", "sphinx-rtd-theme"] -test = ["pytest-runner", "pytest-cov", "pytest-flake8", "pytest-isort"] - [[package]] -category = "main" -description = "Foreign Function Interface for Python calling C code." name = "cffi" +version = "1.14.0" +description = "Foreign Function Interface for Python calling C code." +category = "main" optional = false python-versions = "*" -version = "1.14.0" [package.dependencies] pycparser = "*" [[package]] -category = "dev" -description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" name = "colorama" +version = "0.4.3" +description = "Cross-platform colored terminal text." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.3" +marker = "sys_platform == \"win32\"" [[package]] -category = "main" -description = "CSS selectors for Python ElementTree" name = "cssselect2" +version = "0.3.0" +description = "CSS selectors for Python ElementTree" +category = "main" optional = false python-versions = ">=3.5" -version = "0.3.0" + +[package.extras] +doc = ["sphinx", "sphinx-rtd-theme"] +test = ["pytest-runner", "pytest-cov", "pytest-flake8", "pytest-isort"] [package.dependencies] setuptools = ">=39.2.0" tinycss2 = "*" webencodings = "*" -[package.extras] -doc = ["sphinx", "sphinx-rtd-theme"] -test = ["pytest-runner", "pytest-cov", "pytest-flake8", "pytest-isort"] - [[package]] -category = "dev" -description = "Decorators for Humans" name = "decorator" +version = "4.4.2" +description = "Decorators for Humans" +category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.4.2" [[package]] -category = "main" -description = "XML bomb protection for Python stdlib modules" name = "defusedxml" +version = "0.6.0" +description = "XML bomb protection for Python stdlib modules" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.6.0" [[package]] -category = "main" -description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." name = "django" +version = "3.0.4" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +category = "main" optional = false python-versions = ">=3.6" -version = "3.0.4" + +[package.extras] +argon2 = ["argon2-cffi (>=16.1.0)"] +bcrypt = ["bcrypt"] [package.dependencies] asgiref = ">=3.2,<4.0" pytz = "*" sqlparse = ">=0.2.2" -[package.extras] -argon2 = ["argon2-cffi (>=16.1.0)"] -bcrypt = ["bcrypt"] - [[package]] -category = "main" -description = "Django model mixins and utilities" name = "django-model-utils" +version = "4.0.0" +description = "Django model mixins and utilities" +category = "main" optional = false python-versions = "*" -version = "4.0.0" [package.dependencies] Django = ">=2.0.1" [[package]] -category = "main" -description = "Allows Django models to be ordered and provides a simple admin interface for reordering them." name = "django-ordered-model" +version = "3.4.1" +description = "Allows Django models to be ordered and provides a simple admin interface for reordering them." +category = "main" optional = false python-versions = "*" -version = "3.4.1" [[package]] -category = "dev" -description = "WSGI HTTP Server for UNIX" name = "gunicorn" +version = "20.0.4" +description = "WSGI HTTP Server for UNIX" +category = "dev" optional = false python-versions = ">=3.4" -version = "20.0.4" - -[package.dependencies] -setuptools = ">=3.0" [package.extras] eventlet = ["eventlet (>=0.9.7)"] @@ -180,17 +177,16 @@ gevent = ["gevent (>=0.13)"] setproctitle = ["setproctitle"] tornado = ["tornado (>=0.2)"] +[package.dependencies] +setuptools = ">=3.0" + [[package]] -category = "main" -description = "HTML parser based on the WHATWG HTML specification" name = "html5lib" +version = "1.0.1" +description = "HTML parser based on the WHATWG HTML specification" +category = "main" optional = false python-versions = "*" -version = "1.0.1" - -[package.dependencies] -six = ">=1.9" -webencodings = "*" [package.extras] all = ["genshi", "chardet (>=2.2)", "datrie", "lxml"] @@ -199,13 +195,28 @@ datrie = ["datrie"] genshi = ["genshi"] lxml = ["lxml"] +[package.dependencies] +six = ">=1.9" +webencodings = "*" + [[package]] -category = "dev" -description = "IPython: Productive Interactive Computing" name = "ipython" +version = "7.13.0" +description = "IPython: Productive Interactive Computing" +category = "dev" optional = false python-versions = ">=3.6" -version = "7.13.0" + +[package.extras] +all = ["numpy (>=1.14)", "testpath", "notebook", "nose (>=0.10.1)", "nbconvert", "requests", "ipywidgets", "qtconsole", "ipyparallel", "Sphinx (>=1.3)", "pygments", "nbformat", "ipykernel"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["notebook", "ipywidgets"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] [package.dependencies] appnope = "*" @@ -220,236 +231,237 @@ pygments = "*" setuptools = ">=18.5" traitlets = ">=4.2" -[package.extras] -all = ["numpy (>=1.14)", "testpath", "notebook", "nose (>=0.10.1)", "nbconvert", "requests", "ipywidgets", "qtconsole", "ipyparallel", "Sphinx (>=1.3)", "pygments", "nbformat", "ipykernel"] -doc = ["Sphinx (>=1.3)"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["notebook", "ipywidgets"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] - [[package]] -category = "dev" -description = "Vestigial utilities from IPython" name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "dev" -description = "An autocompletion tool for Python that can be used for text editors." name = "jedi" +version = "0.16.0" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.16.0" - -[package.dependencies] -parso = ">=0.5.2" [package.extras] qa = ["flake8 (3.7.9)"] testing = ["colorama (0.4.1)", "docopt", "pytest (>=3.9.0,<5.0.0)"] -[[package]] -category = "main" -description = "A fast and complete Python implementation of Markdown" -name = "markdown2" -optional = false -python-versions = "*" -version = "2.3.8" +[package.dependencies] +parso = ">=0.5.2" + +[[package]] +name = "markdown2" +version = "2.3.8" +description = "A fast and complete Python implementation of Markdown" +category = "main" +optional = false +python-versions = "*" [[package]] -category = "dev" -description = "A Python Parser" name = "parso" +version = "0.6.2" +description = "A Python Parser" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.6.2" [package.extras] testing = ["docopt", "pytest (>=3.0.7)"] [[package]] -category = "dev" -description = "Pexpect allows easy control of interactive console applications." -marker = "sys_platform != \"win32\"" -name = "pexpect" +name = "pdf2image" +version = "1.14.0" +description = "A wrapper around the pdftoppm and pdftocairo command line tools to convert PDF to a PIL Image list." +category = "main" optional = false python-versions = "*" + +[package.dependencies] +pillow = "*" + +[[package]] +name = "pexpect" version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "dev" +optional = false +python-versions = "*" +marker = "sys_platform != \"win32\"" [package.dependencies] ptyprocess = ">=0.5" [[package]] -category = "dev" -description = "Tiny 'shelve'-like database with concurrency support" name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "dev" optional = false python-versions = "*" -version = "0.7.5" [[package]] -category = "main" -description = "Python Imaging Library (Fork)" name = "pillow" +version = "8.0.1" +description = "Python Imaging Library (Fork)" +category = "main" optional = false -python-versions = ">=3.5" -version = "7.0.0" +python-versions = ">=3.6" [[package]] -category = "dev" -description = "Library for building powerful interactive command lines in Python" name = "prompt-toolkit" +version = "3.0.4" +description = "Library for building powerful interactive command lines in Python" +category = "dev" optional = false python-versions = ">=3.6.1" -version = "3.0.4" [package.dependencies] wcwidth = "*" [[package]] -category = "dev" -description = "Run a subprocess in a pseudo terminal" -marker = "sys_platform != \"win32\"" name = "ptyprocess" +version = "0.6.0" +description = "Run a subprocess in a pseudo terminal" +category = "dev" optional = false python-versions = "*" -version = "0.6.0" +marker = "sys_platform != \"win32\"" [[package]] -category = "main" -description = "C parser in Python" name = "pycparser" +version = "2.20" +description = "C parser in Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.20" [[package]] -category = "dev" -description = "Pygments is a syntax highlighting package written in Python." name = "pygments" +version = "2.6.1" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" optional = false python-versions = ">=3.5" -version = "2.6.1" [[package]] -category = "main" -description = "PDF toolkit" name = "pypdf2" -optional = false -python-versions = "*" version = "1.26.0" +description = "PDF toolkit" +category = "main" +optional = false +python-versions = "*" [[package]] -category = "main" -description = "Pure Python module to hyphenate text" name = "pyphen" -optional = false -python-versions = "*" version = "0.9.5" - -[[package]] +description = "Pure Python module to hyphenate text" category = "main" -description = "World timezone definitions, modern and historical" -name = "pytz" optional = false python-versions = "*" -version = "2019.3" [[package]] +name = "pytz" +version = "2019.3" +description = "World timezone definitions, modern and historical" category = "main" -description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "*" + +[[package]] name = "six" +version = "1.14.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.14.0" [[package]] -category = "main" -description = "Non-validating SQL parser" name = "sqlparse" +version = "0.3.1" +description = "Non-validating SQL parser" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.3.1" [[package]] -category = "main" -description = "Low-level CSS parser for Python" name = "tinycss2" +version = "1.0.2" +description = "Low-level CSS parser for Python" +category = "main" optional = false python-versions = ">= 3.5" -version = "1.0.2" - -[package.dependencies] -setuptools = ">=39.2.0" -webencodings = ">=0.4" [package.extras] doc = ["sphinx", "sphinx-rtd-theme"] test = ["pytest-runner", "pytest-cov", "pytest-flake8", "pytest-isort"] +[package.dependencies] +setuptools = ">=39.2.0" +webencodings = ">=0.4" + [[package]] -category = "dev" -description = "Traitlets Python config system" name = "traitlets" +version = "4.3.3" +description = "Traitlets Python config system" +category = "dev" optional = false python-versions = "*" -version = "4.3.3" + +[package.extras] +test = ["pytest", "mock"] [package.dependencies] decorator = "*" ipython-genutils = "*" six = "*" -[package.extras] -test = ["pytest", "mock"] - [[package]] -category = "dev" -description = "Measures number of Terminal column cells of wide-character codes" name = "wcwidth" +version = "0.1.8" +description = "Measures number of Terminal column cells of wide-character codes" +category = "dev" optional = false python-versions = "*" -version = "0.1.8" [[package]] -category = "main" -description = "The Awesome Document Factory" name = "weasyprint" +version = "51" +description = "The Awesome Document Factory" +category = "main" optional = false python-versions = ">=3.5" -version = "51" - -[package.dependencies] -CairoSVG = ">=2.4.0" -Pyphen = ">=0.9.1" -cairocffi = ">=0.9.0" -cffi = ">=0.6" -cssselect2 = ">=0.1" -html5lib = ">=0.999999999" -setuptools = ">=39.2.0" -tinycss2 = ">=1.0.0" [package.extras] doc = ["sphinx", "sphinx-rtd-theme"] test = ["pytest-runner", "pytest-cov", "pytest-flake8", "pytest-isort"] +[package.dependencies] +cairocffi = ">=0.9.0" +CairoSVG = ">=2.4.0" +cffi = ">=0.6" +cssselect2 = ">=0.1" +html5lib = ">=0.999999999" +Pyphen = ">=0.9.1" +setuptools = ">=39.2.0" +tinycss2 = ">=1.0.0" + [[package]] -category = "main" -description = "Character encoding aliases for legacy web content" name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "main" optional = false python-versions = "*" -version = "0.5.1" [metadata] -content-hash = "c4c5d2ad677b97810bb47fc430207910d648ddc8ebc22de10715c58d8bb36f32" +lock-version = "1.0" python-versions = "^3.8" +content-hash = "6d4369e2d21c34afe8c416e78d96e7de712a26b80781513d0296f32c5a556f26" [metadata.files] appnope = [ @@ -561,6 +573,10 @@ parso = [ {file = "parso-0.6.2-py2.py3-none-any.whl", hash = "sha256:8515fc12cfca6ee3aa59138741fc5624d62340c97e401c74875769948d4f2995"}, {file = "parso-0.6.2.tar.gz", hash = "sha256:0c5659e0c6eba20636f99a04f469798dca8da279645ce5c387315b2c23912157"}, ] +pdf2image = [ + {file = "pdf2image-1.14.0-py3-none-any.whl", hash = "sha256:cf1b2dc77cf3e050cb06078cd7373469c8dca93d4cea1af2a2e1bbe0ed4a8800"}, + {file = "pdf2image-1.14.0.tar.gz", hash = "sha256:066527e1bf954762fb4369c677ae3bc15f2ce8707eee830cccef8471fde736d7"}, +] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, @@ -570,28 +586,34 @@ pickleshare = [ {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] pillow = [ - {file = "Pillow-7.0.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00"}, - {file = "Pillow-7.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff"}, - {file = "Pillow-7.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865"}, - {file = "Pillow-7.0.0-cp35-cp35m-win32.whl", hash = "sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386"}, - {file = "Pillow-7.0.0-cp35-cp35m-win_amd64.whl", hash = "sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435"}, - {file = "Pillow-7.0.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2"}, - {file = "Pillow-7.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317"}, - {file = "Pillow-7.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2"}, - {file = "Pillow-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313"}, - {file = "Pillow-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0"}, - {file = "Pillow-7.0.0-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f"}, - {file = "Pillow-7.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636"}, - {file = "Pillow-7.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9"}, - {file = "Pillow-7.0.0-cp37-cp37m-win32.whl", hash = "sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837"}, - {file = "Pillow-7.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda"}, - {file = "Pillow-7.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be"}, - {file = "Pillow-7.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533"}, - {file = "Pillow-7.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614"}, - {file = "Pillow-7.0.0-cp38-cp38-win32.whl", hash = "sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a"}, - {file = "Pillow-7.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d"}, - {file = "Pillow-7.0.0-pp373-pypy36_pp73-win32.whl", hash = "sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358"}, - {file = "Pillow-7.0.0.tar.gz", hash = "sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946"}, + {file = "Pillow-8.0.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3"}, + {file = "Pillow-8.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302"}, + {file = "Pillow-8.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c"}, + {file = "Pillow-8.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11"}, + {file = "Pillow-8.0.1-cp36-cp36m-win32.whl", hash = "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e"}, + {file = "Pillow-8.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3"}, + {file = "Pillow-8.0.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09"}, + {file = "Pillow-8.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae"}, + {file = "Pillow-8.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a"}, + {file = "Pillow-8.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8"}, + {file = "Pillow-8.0.1-cp37-cp37m-win32.whl", hash = "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0"}, + {file = "Pillow-8.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039"}, + {file = "Pillow-8.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11"}, + {file = "Pillow-8.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72"}, + {file = "Pillow-8.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792"}, + {file = "Pillow-8.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015"}, + {file = "Pillow-8.0.1-cp38-cp38-win32.whl", hash = "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271"}, + {file = "Pillow-8.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7"}, + {file = "Pillow-8.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5"}, + {file = "Pillow-8.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce"}, + {file = "Pillow-8.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3"}, + {file = "Pillow-8.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544"}, + {file = "Pillow-8.0.1-cp39-cp39-win32.whl", hash = "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140"}, + {file = "Pillow-8.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021"}, + {file = "Pillow-8.0.1-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6"}, + {file = "Pillow-8.0.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb"}, + {file = "Pillow-8.0.1-pp37-pypy37_pp73-win32.whl", hash = "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8"}, + {file = "Pillow-8.0.1.tar.gz", hash = "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e"}, ] prompt-toolkit = [ {file = "prompt_toolkit-3.0.4-py3-none-any.whl", hash = "sha256:859e1b205b6cf6a51fa57fa34202e45365cf58f8338f0ee9f4e84a4165b37d5b"}, diff --git a/pyproject.toml b/pyproject.toml index 6db0a85..97d6962 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,8 @@ pypdf2 = "^1.26.0" markdown2 = "^2.3.8" bleach = "^3.1.4" django-ordered-model = "^3.4.1" +pillow = "^8.0.1" +pdf2image = "^1.14.0" [tool.poetry.dev-dependencies] ipython = "^7.13.0" diff --git a/sdbs_pile/pile/migrations/0014_document_image.py b/sdbs_pile/pile/migrations/0014_document_image.py new file mode 100644 index 0000000..3e8049a --- /dev/null +++ b/sdbs_pile/pile/migrations/0014_document_image.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.4 on 2021-01-02 11:34 + +import django.core.files.storage +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pile', '0013_auto_20200727_1533'), + ] + + operations = [ + migrations.AddField( + model_name='document', + name='image', + field=models.ImageField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location='docs/images'), upload_to=''), + ), + ] diff --git a/sdbs_pile/pile/models.py b/sdbs_pile/pile/models.py index 52cf66f..0238245 100644 --- a/sdbs_pile/pile/models.py +++ b/sdbs_pile/pile/models.py @@ -6,6 +6,7 @@ from django.db.models import Count, Q from model_utils.managers import SoftDeletableManager, SoftDeletableQuerySet from model_utils.models import SoftDeletableModel from ordered_model.models import OrderedModel +from pdf2image import convert_from_path BLEACH_ALLOWED_TAGS = bleach.sanitizer.ALLOWED_TAGS + ['p', 'br', 'h1', 'h2', 'h3', 'hr'] @@ -59,6 +60,7 @@ class Document(SoftDeletableModel): published = models.CharField(max_length=128, null=False, blank=True) description = models.TextField(max_length=2048, null=False, blank=True) file = models.FileField(null=True, blank=True, storage=FileSystemStorage(location='docs')) + image = models.ImageField(null=True, blank=True, storage=FileSystemStorage(location='docs/images')) public = models.BooleanField(default=True, null=False, blank=False) media_type = models.CharField(null=False, blank=False, max_length=1, choices=DocumentType.choices, default=DocumentType.TEXT) @@ -88,6 +90,13 @@ class Document(SoftDeletableModel): def is_local_pdf(self): return self.file.name is not None and self.file.name.endswith(".pdf") + @property + def image_first_page(self): + if self.is_local_pdf: + images = convert_from_path(self.file.path, last_page=1) + if images: + return images[0] + class Meta: ordering = ['-id'] permissions = [ diff --git a/sdbs_pile/pile/templates/front_base.html b/sdbs_pile/pile/templates/front_base.html index e86e82f..4d0cfb2 100644 --- a/sdbs_pile/pile/templates/front_base.html +++ b/sdbs_pile/pile/templates/front_base.html @@ -13,11 +13,11 @@ + {% endblock %} - diff --git a/sdbs_pile/pile/templates/front_doc_detail.html b/sdbs_pile/pile/templates/front_doc_detail.html index 594dd79..218ba1d 100644 --- a/sdbs_pile/pile/templates/front_doc_detail.html +++ b/sdbs_pile/pile/templates/front_doc_detail.html @@ -123,4 +123,5 @@ + {% endblock %} \ No newline at end of file diff --git a/sdbs_pile/pile/templates/front_doc_listing.html b/sdbs_pile/pile/templates/front_doc_listing.html index 2872f7d..231ba73 100644 --- a/sdbs_pile/pile/templates/front_doc_listing.html +++ b/sdbs_pile/pile/templates/front_doc_listing.html @@ -46,5 +46,6 @@ + {% endif %} {% endblock %} diff --git a/sdbs_pile/pile/urls.py b/sdbs_pile/pile/urls.py index 936f3f7..32d2a19 100644 --- a/sdbs_pile/pile/urls.py +++ b/sdbs_pile/pile/urls.py @@ -10,6 +10,7 @@ urlpatterns = [ path('item/', views.DocumentView.as_view(), name='document'), path('label/', views.LabelView.as_view(), name='label'), path('retrieve/', views.DocumentWithLabelView.as_view(), name='retrieve'), + path('image/', views.BrandedImageView.as_view(), name='image'), path('feed', views.RecentlyUploadedFeed()), path('api/external_links', ExternalLinkView), path('api/ipfs_cids', IPFSView) diff --git a/sdbs_pile/pile/views.py b/sdbs_pile/pile/views.py index 60dd4c4..4a0d46b 100644 --- a/sdbs_pile/pile/views.py +++ b/sdbs_pile/pile/views.py @@ -7,7 +7,9 @@ from operator import itemgetter from random import choice import weasyprint +from PIL import Image from PyPDF2 import PdfFileWriter, PdfFileReader +from django.contrib.staticfiles import finders from django.contrib.syndication.views import Feed from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.http import Http404, FileResponse, HttpRequest, HttpResponse @@ -157,6 +159,31 @@ class DocumentWithLabelView(View): return redirect(document.url) +class BrandedImageView(View): + def get(self, request: HttpRequest, document_id: int): + try: + document = Document.objects.get(pk=document_id) + except ObjectDoesNotExist: + raise Http404 + + margin = 32 + pile_image = Image.open(finders.find('pile_300dpi.png')).resize((256 - margin, 256 - margin)) + image = Image.open(document.image) if document.image else document.image_first_page + image.thumbnail((256, 256)) + + result = Image.new('RGBA', (256, 256), (0, 0, 0, 0)) + result.paste(image, ((256 - image.size[0]) // 2, (256 - image.size[1]) // 2)) + result.paste(pile_image, (margin//2, margin//2), pile_image) + + result = result.crop(result.getbbox()) + + img_byte_arr = io.BytesIO() + result.save(img_byte_arr, format='PNG') + img_byte_arr = img_byte_arr.getvalue() + + return HttpResponse(img_byte_arr, content_type="image/png") + + class RecentlyUploadedFeed(Feed): title = "The /-\\ pile" link = "https://pile.sbds.cz"