We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] Auto-detected date is day before receipt date
**Describe the bug**
When the system automatically detects the date on the receipt, the "Date Created" field is set to the day before.

**To Reproduce**
Steps to reproduce the behavior:

1. Have system timezone set to UTC-6
2. Scan document that has approved date type
3. See that date is day before date in receipt.

**Expected behavior**
Date should be exact date on receipt.

**Webserver logs**
N/A

**Relevant information**
 - Host OS of the machine running paperless: Docker/Ubuntu 18.04
 - Browser: Chrome
 - Version 1.5.0
 - Installation method: docker
 - Any configuration changes:
```
PAPERLESS_TIME_ZONE=America/Chicago
PAPERLESS_DATE_ORDER=MDY
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/documents/parsers.py`
Content:
```
1 import logging
2 import mimetypes
3 import os
4 import re
5 import shutil
6 import subprocess
7 import tempfile
8 
9 import magic
10 from django.conf import settings
11 from django.utils import timezone
12 
13 # This regular expression will try to find dates in the document at
14 # hand and will match the following formats:
15 # - XX.YY.ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
16 # - XX/YY/ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
17 # - XX-YY-ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
18 # - ZZZZ.XX.YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
19 # - ZZZZ/XX/YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
20 # - ZZZZ-XX-YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits
21 # - XX. MONTH ZZZZ with XX being 1 or 2 and ZZZZ being 2 or 4 digits
22 # - MONTH ZZZZ, with ZZZZ being 4 digits
23 # - MONTH XX, ZZZZ with XX being 1 or 2 and ZZZZ being 4 digits
24 from documents.loggers import LoggingMixin
25 from documents.signals import document_consumer_declaration
26 
27 # TODO: isnt there a date parsing library for this?
28 
29 DATE_REGEX = re.compile(
30     r"(\b|(?!=([_-])))([0-9]{1,2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{4}|[0-9]{2})(\b|(?=([_-])))|"  # NOQA: E501
31     r"(\b|(?!=([_-])))([0-9]{4}|[0-9]{2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{1,2})(\b|(?=([_-])))|"  # NOQA: E501
32     r"(\b|(?!=([_-])))([0-9]{1,2}[\. ]+[^ ]{3,9} ([0-9]{4}|[0-9]{2}))(\b|(?=([_-])))|"  # NOQA: E501
33     r"(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{1,2}, ([0-9]{4}))(\b|(?=([_-])))|"
34     r"(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{4})(\b|(?=([_-])))"
35 )
36 
37 
38 logger = logging.getLogger("paperless.parsing")
39 
40 
41 def is_mime_type_supported(mime_type):
42     return get_parser_class_for_mime_type(mime_type) is not None
43 
44 
45 def get_default_file_extension(mime_type):
46     for response in document_consumer_declaration.send(None):
47         parser_declaration = response[1]
48         supported_mime_types = parser_declaration["mime_types"]
49 
50         if mime_type in supported_mime_types:
51             return supported_mime_types[mime_type]
52 
53     ext = mimetypes.guess_extension(mime_type)
54     if ext:
55         return ext
56     else:
57         return ""
58 
59 
60 def is_file_ext_supported(ext):
61     if ext:
62         return ext.lower() in get_supported_file_extensions()
63     else:
64         return False
65 
66 
67 def get_supported_file_extensions():
68     extensions = set()
69     for response in document_consumer_declaration.send(None):
70         parser_declaration = response[1]
71         supported_mime_types = parser_declaration["mime_types"]
72 
73         for mime_type in supported_mime_types:
74             extensions.update(mimetypes.guess_all_extensions(mime_type))
75 
76     return extensions
77 
78 
79 def get_parser_class_for_mime_type(mime_type):
80 
81     options = []
82 
83     # Sein letzter Befehl war: KOMMT! Und sie kamen. Alle. Sogar die Parser.
84 
85     for response in document_consumer_declaration.send(None):
86         parser_declaration = response[1]
87         supported_mime_types = parser_declaration["mime_types"]
88 
89         if mime_type in supported_mime_types:
90             options.append(parser_declaration)
91 
92     if not options:
93         return None
94 
95     # Return the parser with the highest weight.
96     return sorted(options, key=lambda _: _["weight"], reverse=True)[0]["parser"]
97 
98 
99 def get_parser_class(path):
100     """
101     Determine the appropriate parser class based on the file
102     """
103 
104     mime_type = magic.from_file(path, mime=True)
105 
106     return get_parser_class_for_mime_type(mime_type)
107 
108 
109 def run_convert(
110     input_file,
111     output_file,
112     density=None,
113     scale=None,
114     alpha=None,
115     strip=False,
116     trim=False,
117     type=None,
118     depth=None,
119     auto_orient=False,
120     extra=None,
121     logging_group=None,
122 ):
123 
124     environment = os.environ.copy()
125     if settings.CONVERT_MEMORY_LIMIT:
126         environment["MAGICK_MEMORY_LIMIT"] = settings.CONVERT_MEMORY_LIMIT
127     if settings.CONVERT_TMPDIR:
128         environment["MAGICK_TMPDIR"] = settings.CONVERT_TMPDIR
129 
130     args = [settings.CONVERT_BINARY]
131     args += ["-density", str(density)] if density else []
132     args += ["-scale", str(scale)] if scale else []
133     args += ["-alpha", str(alpha)] if alpha else []
134     args += ["-strip"] if strip else []
135     args += ["-trim"] if trim else []
136     args += ["-type", str(type)] if type else []
137     args += ["-depth", str(depth)] if depth else []
138     args += ["-auto-orient"] if auto_orient else []
139     args += [input_file, output_file]
140 
141     logger.debug("Execute: " + " ".join(args), extra={"group": logging_group})
142 
143     if not subprocess.Popen(args, env=environment).wait() == 0:
144         raise ParseError("Convert failed at {}".format(args))
145 
146 
147 def get_default_thumbnail():
148     return os.path.join(os.path.dirname(__file__), "resources", "document.png")
149 
150 
151 def make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None):
152     out_path = os.path.join(temp_dir, "convert_gs.png")
153 
154     # if convert fails, fall back to extracting
155     # the first PDF page as a PNG using Ghostscript
156     logger.warning(
157         "Thumbnail generation with ImageMagick failed, falling back "
158         "to ghostscript. Check your /etc/ImageMagick-x/policy.xml!",
159         extra={"group": logging_group},
160     )
161     gs_out_path = os.path.join(temp_dir, "gs_out.png")
162     cmd = [settings.GS_BINARY, "-q", "-sDEVICE=pngalpha", "-o", gs_out_path, in_path]
163     try:
164         if not subprocess.Popen(cmd).wait() == 0:
165             raise ParseError("Thumbnail (gs) failed at {}".format(cmd))
166         # then run convert on the output from gs
167         run_convert(
168             density=300,
169             scale="500x5000>",
170             alpha="remove",
171             strip=True,
172             trim=False,
173             auto_orient=True,
174             input_file=gs_out_path,
175             output_file=out_path,
176             logging_group=logging_group,
177         )
178 
179         return out_path
180 
181     except ParseError:
182         return get_default_thumbnail()
183 
184 
185 def make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None):
186     """
187     The thumbnail of a PDF is just a 500px wide image of the first page.
188     """
189     out_path = os.path.join(temp_dir, "convert.png")
190 
191     # Run convert to get a decent thumbnail
192     try:
193         run_convert(
194             density=300,
195             scale="500x5000>",
196             alpha="remove",
197             strip=True,
198             trim=False,
199             auto_orient=True,
200             input_file="{}[0]".format(in_path),
201             output_file=out_path,
202             logging_group=logging_group,
203         )
204     except ParseError:
205         out_path = make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group)
206 
207     return out_path
208 
209 
210 def parse_date(filename, text):
211     """
212     Returns the date of the document.
213     """
214 
215     def __parser(ds, date_order):
216         """
217         Call dateparser.parse with a particular date ordering
218         """
219         import dateparser
220 
221         return dateparser.parse(
222             ds,
223             settings={
224                 "DATE_ORDER": date_order,
225                 "PREFER_DAY_OF_MONTH": "first",
226                 "RETURN_AS_TIMEZONE_AWARE": True,
227             },
228         )
229 
230     def __filter(date):
231         if (
232             date
233             and date.year > 1900
234             and date <= timezone.now()
235             and date.date() not in settings.IGNORE_DATES
236         ):
237             return date
238         return None
239 
240     date = None
241 
242     # if filename date parsing is enabled, search there first:
243     if settings.FILENAME_DATE_ORDER:
244         for m in re.finditer(DATE_REGEX, filename):
245             date_string = m.group(0)
246 
247             try:
248                 date = __parser(date_string, settings.FILENAME_DATE_ORDER)
249             except (TypeError, ValueError):
250                 # Skip all matches that do not parse to a proper date
251                 continue
252 
253             date = __filter(date)
254             if date is not None:
255                 return date
256 
257     # Iterate through all regex matches in text and try to parse the date
258     for m in re.finditer(DATE_REGEX, text):
259         date_string = m.group(0)
260 
261         try:
262             date = __parser(date_string, settings.DATE_ORDER)
263         except (TypeError, ValueError):
264             # Skip all matches that do not parse to a proper date
265             continue
266 
267         date = __filter(date)
268         if date is not None:
269             break
270 
271     return date
272 
273 
274 class ParseError(Exception):
275     pass
276 
277 
278 class DocumentParser(LoggingMixin):
279     """
280     Subclass this to make your own parser.  Have a look at
281     `paperless_tesseract.parsers` for inspiration.
282     """
283 
284     logging_name = "paperless.parsing"
285 
286     def __init__(self, logging_group, progress_callback=None):
287         super().__init__()
288         self.logging_group = logging_group
289         os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
290         self.tempdir = tempfile.mkdtemp(prefix="paperless-", dir=settings.SCRATCH_DIR)
291 
292         self.archive_path = None
293         self.text = None
294         self.date = None
295         self.progress_callback = progress_callback
296 
297     def progress(self, current_progress, max_progress):
298         if self.progress_callback:
299             self.progress_callback(current_progress, max_progress)
300 
301     def extract_metadata(self, document_path, mime_type):
302         return []
303 
304     def parse(self, document_path, mime_type, file_name=None):
305         raise NotImplementedError()
306 
307     def get_archive_path(self):
308         return self.archive_path
309 
310     def get_thumbnail(self, document_path, mime_type, file_name=None):
311         """
312         Returns the path to a file we can use as a thumbnail for this document.
313         """
314         raise NotImplementedError()
315 
316     def get_optimised_thumbnail(self, document_path, mime_type, file_name=None):
317         thumbnail = self.get_thumbnail(document_path, mime_type, file_name)
318         if settings.OPTIMIZE_THUMBNAILS:
319             out_path = os.path.join(self.tempdir, "thumb_optipng.png")
320 
321             args = (
322                 settings.OPTIPNG_BINARY,
323                 "-silent",
324                 "-o5",
325                 thumbnail,
326                 "-out",
327                 out_path,
328             )
329 
330             self.log("debug", f"Execute: {' '.join(args)}")
331 
332             if not subprocess.Popen(args).wait() == 0:
333                 raise ParseError("Optipng failed at {}".format(args))
334 
335             return out_path
336         else:
337             return thumbnail
338 
339     def get_text(self):
340         return self.text
341 
342     def get_date(self):
343         return self.date
344 
345     def cleanup(self):
346         self.log("debug", f"Deleting directory {self.tempdir}")
347         shutil.rmtree(self.tempdir)
348
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/documents/parsers.py b/src/documents/parsers.py
--- a/src/documents/parsers.py
+++ b/src/documents/parsers.py
@@ -224,6 +224,7 @@
                 "DATE_ORDER": date_order,
                 "PREFER_DAY_OF_MONTH": "first",
                 "RETURN_AS_TIMEZONE_AWARE": True,
+                "TIMEZONE": settings.TIME_ZONE,
             },
         )

{"golden_diff": "diff --git a/src/documents/parsers.py b/src/documents/parsers.py\n--- a/src/documents/parsers.py\n+++ b/src/documents/parsers.py\n@@ -224,6 +224,7 @@\n                 \"DATE_ORDER\": date_order,\n                 \"PREFER_DAY_OF_MONTH\": \"first\",\n                 \"RETURN_AS_TIMEZONE_AWARE\": True,\n+                \"TIMEZONE\": settings.TIME_ZONE,\n             },\n         )\n", "issue": "[BUG] Auto-detected date is day before receipt date\n**Describe the bug**\r\nWhen the system automatically detects the date on the receipt, the \"Date Created\" field is set to the day before.\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n\r\n1. Have system timezone set to UTC-6\r\n2. Scan document that has approved date type\r\n3. See that date is day before date in receipt.\r\n\r\n**Expected behavior**\r\nDate should be exact date on receipt.\r\n\r\n**Webserver logs**\r\nN/A\r\n\r\n**Relevant information**\r\n - Host OS of the machine running paperless: Docker/Ubuntu 18.04\r\n - Browser: Chrome\r\n - Version 1.5.0\r\n - Installation method: docker\r\n - Any configuration changes:\r\n```\r\nPAPERLESS_TIME_ZONE=America/Chicago\r\nPAPERLESS_DATE_ORDER=MDY\r\n```\n", "before_files": [{"content": "import logging\nimport mimetypes\nimport os\nimport re\nimport shutil\nimport subprocess\nimport tempfile\n\nimport magic\nfrom django.conf import settings\nfrom django.utils import timezone\n\n# This regular expression will try to find dates in the document at\n# hand and will match the following formats:\n# - XX.YY.ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - XX/YY/ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - XX-YY-ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - ZZZZ.XX.YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - ZZZZ/XX/YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - ZZZZ-XX-YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - XX. MONTH ZZZZ with XX being 1 or 2 and ZZZZ being 2 or 4 digits\n# - MONTH ZZZZ, with ZZZZ being 4 digits\n# - MONTH XX, ZZZZ with XX being 1 or 2 and ZZZZ being 4 digits\nfrom documents.loggers import LoggingMixin\nfrom documents.signals import document_consumer_declaration\n\n# TODO: isnt there a date parsing library for this?\n\nDATE_REGEX = re.compile(\n    r\"(\\b|(?!=([_-])))([0-9]{1,2})[\\.\\/-]([0-9]{1,2})[\\.\\/-]([0-9]{4}|[0-9]{2})(\\b|(?=([_-])))|\"  # NOQA: E501\n    r\"(\\b|(?!=([_-])))([0-9]{4}|[0-9]{2})[\\.\\/-]([0-9]{1,2})[\\.\\/-]([0-9]{1,2})(\\b|(?=([_-])))|\"  # NOQA: E501\n    r\"(\\b|(?!=([_-])))([0-9]{1,2}[\\. ]+[^ ]{3,9} ([0-9]{4}|[0-9]{2}))(\\b|(?=([_-])))|\"  # NOQA: E501\n    r\"(\\b|(?!=([_-])))([^\\W\\d_]{3,9} [0-9]{1,2}, ([0-9]{4}))(\\b|(?=([_-])))|\"\n    r\"(\\b|(?!=([_-])))([^\\W\\d_]{3,9} [0-9]{4})(\\b|(?=([_-])))\"\n)\n\n\nlogger = logging.getLogger(\"paperless.parsing\")\n\n\ndef is_mime_type_supported(mime_type):\n    return get_parser_class_for_mime_type(mime_type) is not None\n\n\ndef get_default_file_extension(mime_type):\n    for response in document_consumer_declaration.send(None):\n        parser_declaration = response[1]\n        supported_mime_types = parser_declaration[\"mime_types\"]\n\n        if mime_type in supported_mime_types:\n            return supported_mime_types[mime_type]\n\n    ext = mimetypes.guess_extension(mime_type)\n    if ext:\n        return ext\n    else:\n        return \"\"\n\n\ndef is_file_ext_supported(ext):\n    if ext:\n        return ext.lower() in get_supported_file_extensions()\n    else:\n        return False\n\n\ndef get_supported_file_extensions():\n    extensions = set()\n    for response in document_consumer_declaration.send(None):\n        parser_declaration = response[1]\n        supported_mime_types = parser_declaration[\"mime_types\"]\n\n        for mime_type in supported_mime_types:\n            extensions.update(mimetypes.guess_all_extensions(mime_type))\n\n    return extensions\n\n\ndef get_parser_class_for_mime_type(mime_type):\n\n    options = []\n\n    # Sein letzter Befehl war: KOMMT! Und sie kamen. Alle. Sogar die Parser.\n\n    for response in document_consumer_declaration.send(None):\n        parser_declaration = response[1]\n        supported_mime_types = parser_declaration[\"mime_types\"]\n\n        if mime_type in supported_mime_types:\n            options.append(parser_declaration)\n\n    if not options:\n        return None\n\n    # Return the parser with the highest weight.\n    return sorted(options, key=lambda _: _[\"weight\"], reverse=True)[0][\"parser\"]\n\n\ndef get_parser_class(path):\n    \"\"\"\n    Determine the appropriate parser class based on the file\n    \"\"\"\n\n    mime_type = magic.from_file(path, mime=True)\n\n    return get_parser_class_for_mime_type(mime_type)\n\n\ndef run_convert(\n    input_file,\n    output_file,\n    density=None,\n    scale=None,\n    alpha=None,\n    strip=False,\n    trim=False,\n    type=None,\n    depth=None,\n    auto_orient=False,\n    extra=None,\n    logging_group=None,\n):\n\n    environment = os.environ.copy()\n    if settings.CONVERT_MEMORY_LIMIT:\n        environment[\"MAGICK_MEMORY_LIMIT\"] = settings.CONVERT_MEMORY_LIMIT\n    if settings.CONVERT_TMPDIR:\n        environment[\"MAGICK_TMPDIR\"] = settings.CONVERT_TMPDIR\n\n    args = [settings.CONVERT_BINARY]\n    args += [\"-density\", str(density)] if density else []\n    args += [\"-scale\", str(scale)] if scale else []\n    args += [\"-alpha\", str(alpha)] if alpha else []\n    args += [\"-strip\"] if strip else []\n    args += [\"-trim\"] if trim else []\n    args += [\"-type\", str(type)] if type else []\n    args += [\"-depth\", str(depth)] if depth else []\n    args += [\"-auto-orient\"] if auto_orient else []\n    args += [input_file, output_file]\n\n    logger.debug(\"Execute: \" + \" \".join(args), extra={\"group\": logging_group})\n\n    if not subprocess.Popen(args, env=environment).wait() == 0:\n        raise ParseError(\"Convert failed at {}\".format(args))\n\n\ndef get_default_thumbnail():\n    return os.path.join(os.path.dirname(__file__), \"resources\", \"document.png\")\n\n\ndef make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None):\n    out_path = os.path.join(temp_dir, \"convert_gs.png\")\n\n    # if convert fails, fall back to extracting\n    # the first PDF page as a PNG using Ghostscript\n    logger.warning(\n        \"Thumbnail generation with ImageMagick failed, falling back \"\n        \"to ghostscript. Check your /etc/ImageMagick-x/policy.xml!\",\n        extra={\"group\": logging_group},\n    )\n    gs_out_path = os.path.join(temp_dir, \"gs_out.png\")\n    cmd = [settings.GS_BINARY, \"-q\", \"-sDEVICE=pngalpha\", \"-o\", gs_out_path, in_path]\n    try:\n        if not subprocess.Popen(cmd).wait() == 0:\n            raise ParseError(\"Thumbnail (gs) failed at {}\".format(cmd))\n        # then run convert on the output from gs\n        run_convert(\n            density=300,\n            scale=\"500x5000>\",\n            alpha=\"remove\",\n            strip=True,\n            trim=False,\n            auto_orient=True,\n            input_file=gs_out_path,\n            output_file=out_path,\n            logging_group=logging_group,\n        )\n\n        return out_path\n\n    except ParseError:\n        return get_default_thumbnail()\n\n\ndef make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None):\n    \"\"\"\n    The thumbnail of a PDF is just a 500px wide image of the first page.\n    \"\"\"\n    out_path = os.path.join(temp_dir, \"convert.png\")\n\n    # Run convert to get a decent thumbnail\n    try:\n        run_convert(\n            density=300,\n            scale=\"500x5000>\",\n            alpha=\"remove\",\n            strip=True,\n            trim=False,\n            auto_orient=True,\n            input_file=\"{}[0]\".format(in_path),\n            output_file=out_path,\n            logging_group=logging_group,\n        )\n    except ParseError:\n        out_path = make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group)\n\n    return out_path\n\n\ndef parse_date(filename, text):\n    \"\"\"\n    Returns the date of the document.\n    \"\"\"\n\n    def __parser(ds, date_order):\n        \"\"\"\n        Call dateparser.parse with a particular date ordering\n        \"\"\"\n        import dateparser\n\n        return dateparser.parse(\n            ds,\n            settings={\n                \"DATE_ORDER\": date_order,\n                \"PREFER_DAY_OF_MONTH\": \"first\",\n                \"RETURN_AS_TIMEZONE_AWARE\": True,\n            },\n        )\n\n    def __filter(date):\n        if (\n            date\n            and date.year > 1900\n            and date <= timezone.now()\n            and date.date() not in settings.IGNORE_DATES\n        ):\n            return date\n        return None\n\n    date = None\n\n    # if filename date parsing is enabled, search there first:\n    if settings.FILENAME_DATE_ORDER:\n        for m in re.finditer(DATE_REGEX, filename):\n            date_string = m.group(0)\n\n            try:\n                date = __parser(date_string, settings.FILENAME_DATE_ORDER)\n            except (TypeError, ValueError):\n                # Skip all matches that do not parse to a proper date\n                continue\n\n            date = __filter(date)\n            if date is not None:\n                return date\n\n    # Iterate through all regex matches in text and try to parse the date\n    for m in re.finditer(DATE_REGEX, text):\n        date_string = m.group(0)\n\n        try:\n            date = __parser(date_string, settings.DATE_ORDER)\n        except (TypeError, ValueError):\n            # Skip all matches that do not parse to a proper date\n            continue\n\n        date = __filter(date)\n        if date is not None:\n            break\n\n    return date\n\n\nclass ParseError(Exception):\n    pass\n\n\nclass DocumentParser(LoggingMixin):\n    \"\"\"\n    Subclass this to make your own parser.  Have a look at\n    `paperless_tesseract.parsers` for inspiration.\n    \"\"\"\n\n    logging_name = \"paperless.parsing\"\n\n    def __init__(self, logging_group, progress_callback=None):\n        super().__init__()\n        self.logging_group = logging_group\n        os.makedirs(settings.SCRATCH_DIR, exist_ok=True)\n        self.tempdir = tempfile.mkdtemp(prefix=\"paperless-\", dir=settings.SCRATCH_DIR)\n\n        self.archive_path = None\n        self.text = None\n        self.date = None\n        self.progress_callback = progress_callback\n\n    def progress(self, current_progress, max_progress):\n        if self.progress_callback:\n            self.progress_callback(current_progress, max_progress)\n\n    def extract_metadata(self, document_path, mime_type):\n        return []\n\n    def parse(self, document_path, mime_type, file_name=None):\n        raise NotImplementedError()\n\n    def get_archive_path(self):\n        return self.archive_path\n\n    def get_thumbnail(self, document_path, mime_type, file_name=None):\n        \"\"\"\n        Returns the path to a file we can use as a thumbnail for this document.\n        \"\"\"\n        raise NotImplementedError()\n\n    def get_optimised_thumbnail(self, document_path, mime_type, file_name=None):\n        thumbnail = self.get_thumbnail(document_path, mime_type, file_name)\n        if settings.OPTIMIZE_THUMBNAILS:\n            out_path = os.path.join(self.tempdir, \"thumb_optipng.png\")\n\n            args = (\n                settings.OPTIPNG_BINARY,\n                \"-silent\",\n                \"-o5\",\n                thumbnail,\n                \"-out\",\n                out_path,\n            )\n\n            self.log(\"debug\", f\"Execute: {' '.join(args)}\")\n\n            if not subprocess.Popen(args).wait() == 0:\n                raise ParseError(\"Optipng failed at {}\".format(args))\n\n            return out_path\n        else:\n            return thumbnail\n\n    def get_text(self):\n        return self.text\n\n    def get_date(self):\n        return self.date\n\n    def cleanup(self):\n        self.log(\"debug\", f\"Deleting directory {self.tempdir}\")\n        shutil.rmtree(self.tempdir)\n", "path": "src/documents/parsers.py"}], "after_files": [{"content": "import logging\nimport mimetypes\nimport os\nimport re\nimport shutil\nimport subprocess\nimport tempfile\n\nimport magic\nfrom django.conf import settings\nfrom django.utils import timezone\n\n# This regular expression will try to find dates in the document at\n# hand and will match the following formats:\n# - XX.YY.ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - XX/YY/ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - XX-YY-ZZZZ with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - ZZZZ.XX.YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - ZZZZ/XX/YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - ZZZZ-XX-YY with XX + YY being 1 or 2 and ZZZZ being 2 or 4 digits\n# - XX. MONTH ZZZZ with XX being 1 or 2 and ZZZZ being 2 or 4 digits\n# - MONTH ZZZZ, with ZZZZ being 4 digits\n# - MONTH XX, ZZZZ with XX being 1 or 2 and ZZZZ being 4 digits\nfrom documents.loggers import LoggingMixin\nfrom documents.signals import document_consumer_declaration\n\n# TODO: isnt there a date parsing library for this?\n\nDATE_REGEX = re.compile(\n    r\"(\\b|(?!=([_-])))([0-9]{1,2})[\\.\\/-]([0-9]{1,2})[\\.\\/-]([0-9]{4}|[0-9]{2})(\\b|(?=([_-])))|\"  # NOQA: E501\n    r\"(\\b|(?!=([_-])))([0-9]{4}|[0-9]{2})[\\.\\/-]([0-9]{1,2})[\\.\\/-]([0-9]{1,2})(\\b|(?=([_-])))|\"  # NOQA: E501\n    r\"(\\b|(?!=([_-])))([0-9]{1,2}[\\. ]+[^ ]{3,9} ([0-9]{4}|[0-9]{2}))(\\b|(?=([_-])))|\"  # NOQA: E501\n    r\"(\\b|(?!=([_-])))([^\\W\\d_]{3,9} [0-9]{1,2}, ([0-9]{4}))(\\b|(?=([_-])))|\"\n    r\"(\\b|(?!=([_-])))([^\\W\\d_]{3,9} [0-9]{4})(\\b|(?=([_-])))\"\n)\n\n\nlogger = logging.getLogger(\"paperless.parsing\")\n\n\ndef is_mime_type_supported(mime_type):\n    return get_parser_class_for_mime_type(mime_type) is not None\n\n\ndef get_default_file_extension(mime_type):\n    for response in document_consumer_declaration.send(None):\n        parser_declaration = response[1]\n        supported_mime_types = parser_declaration[\"mime_types\"]\n\n        if mime_type in supported_mime_types:\n            return supported_mime_types[mime_type]\n\n    ext = mimetypes.guess_extension(mime_type)\n    if ext:\n        return ext\n    else:\n        return \"\"\n\n\ndef is_file_ext_supported(ext):\n    if ext:\n        return ext.lower() in get_supported_file_extensions()\n    else:\n        return False\n\n\ndef get_supported_file_extensions():\n    extensions = set()\n    for response in document_consumer_declaration.send(None):\n        parser_declaration = response[1]\n        supported_mime_types = parser_declaration[\"mime_types\"]\n\n        for mime_type in supported_mime_types:\n            extensions.update(mimetypes.guess_all_extensions(mime_type))\n\n    return extensions\n\n\ndef get_parser_class_for_mime_type(mime_type):\n\n    options = []\n\n    # Sein letzter Befehl war: KOMMT! Und sie kamen. Alle. Sogar die Parser.\n\n    for response in document_consumer_declaration.send(None):\n        parser_declaration = response[1]\n        supported_mime_types = parser_declaration[\"mime_types\"]\n\n        if mime_type in supported_mime_types:\n            options.append(parser_declaration)\n\n    if not options:\n        return None\n\n    # Return the parser with the highest weight.\n    return sorted(options, key=lambda _: _[\"weight\"], reverse=True)[0][\"parser\"]\n\n\ndef get_parser_class(path):\n    \"\"\"\n    Determine the appropriate parser class based on the file\n    \"\"\"\n\n    mime_type = magic.from_file(path, mime=True)\n\n    return get_parser_class_for_mime_type(mime_type)\n\n\ndef run_convert(\n    input_file,\n    output_file,\n    density=None,\n    scale=None,\n    alpha=None,\n    strip=False,\n    trim=False,\n    type=None,\n    depth=None,\n    auto_orient=False,\n    extra=None,\n    logging_group=None,\n):\n\n    environment = os.environ.copy()\n    if settings.CONVERT_MEMORY_LIMIT:\n        environment[\"MAGICK_MEMORY_LIMIT\"] = settings.CONVERT_MEMORY_LIMIT\n    if settings.CONVERT_TMPDIR:\n        environment[\"MAGICK_TMPDIR\"] = settings.CONVERT_TMPDIR\n\n    args = [settings.CONVERT_BINARY]\n    args += [\"-density\", str(density)] if density else []\n    args += [\"-scale\", str(scale)] if scale else []\n    args += [\"-alpha\", str(alpha)] if alpha else []\n    args += [\"-strip\"] if strip else []\n    args += [\"-trim\"] if trim else []\n    args += [\"-type\", str(type)] if type else []\n    args += [\"-depth\", str(depth)] if depth else []\n    args += [\"-auto-orient\"] if auto_orient else []\n    args += [input_file, output_file]\n\n    logger.debug(\"Execute: \" + \" \".join(args), extra={\"group\": logging_group})\n\n    if not subprocess.Popen(args, env=environment).wait() == 0:\n        raise ParseError(\"Convert failed at {}\".format(args))\n\n\ndef get_default_thumbnail():\n    return os.path.join(os.path.dirname(__file__), \"resources\", \"document.png\")\n\n\ndef make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None):\n    out_path = os.path.join(temp_dir, \"convert_gs.png\")\n\n    # if convert fails, fall back to extracting\n    # the first PDF page as a PNG using Ghostscript\n    logger.warning(\n        \"Thumbnail generation with ImageMagick failed, falling back \"\n        \"to ghostscript. Check your /etc/ImageMagick-x/policy.xml!\",\n        extra={\"group\": logging_group},\n    )\n    gs_out_path = os.path.join(temp_dir, \"gs_out.png\")\n    cmd = [settings.GS_BINARY, \"-q\", \"-sDEVICE=pngalpha\", \"-o\", gs_out_path, in_path]\n    try:\n        if not subprocess.Popen(cmd).wait() == 0:\n            raise ParseError(\"Thumbnail (gs) failed at {}\".format(cmd))\n        # then run convert on the output from gs\n        run_convert(\n            density=300,\n            scale=\"500x5000>\",\n            alpha=\"remove\",\n            strip=True,\n            trim=False,\n            auto_orient=True,\n            input_file=gs_out_path,\n            output_file=out_path,\n            logging_group=logging_group,\n        )\n\n        return out_path\n\n    except ParseError:\n        return get_default_thumbnail()\n\n\ndef make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None):\n    \"\"\"\n    The thumbnail of a PDF is just a 500px wide image of the first page.\n    \"\"\"\n    out_path = os.path.join(temp_dir, \"convert.png\")\n\n    # Run convert to get a decent thumbnail\n    try:\n        run_convert(\n            density=300,\n            scale=\"500x5000>\",\n            alpha=\"remove\",\n            strip=True,\n            trim=False,\n            auto_orient=True,\n            input_file=\"{}[0]\".format(in_path),\n            output_file=out_path,\n            logging_group=logging_group,\n        )\n    except ParseError:\n        out_path = make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group)\n\n    return out_path\n\n\ndef parse_date(filename, text):\n    \"\"\"\n    Returns the date of the document.\n    \"\"\"\n\n    def __parser(ds, date_order):\n        \"\"\"\n        Call dateparser.parse with a particular date ordering\n        \"\"\"\n        import dateparser\n\n        return dateparser.parse(\n            ds,\n            settings={\n                \"DATE_ORDER\": date_order,\n                \"PREFER_DAY_OF_MONTH\": \"first\",\n                \"RETURN_AS_TIMEZONE_AWARE\": True,\n                \"TIMEZONE\": settings.TIME_ZONE,\n            },\n        )\n\n    def __filter(date):\n        if (\n            date\n            and date.year > 1900\n            and date <= timezone.now()\n            and date.date() not in settings.IGNORE_DATES\n        ):\n            return date\n        return None\n\n    date = None\n\n    # if filename date parsing is enabled, search there first:\n    if settings.FILENAME_DATE_ORDER:\n        for m in re.finditer(DATE_REGEX, filename):\n            date_string = m.group(0)\n\n            try:\n                date = __parser(date_string, settings.FILENAME_DATE_ORDER)\n            except (TypeError, ValueError):\n                # Skip all matches that do not parse to a proper date\n                continue\n\n            date = __filter(date)\n            if date is not None:\n                return date\n\n    # Iterate through all regex matches in text and try to parse the date\n    for m in re.finditer(DATE_REGEX, text):\n        date_string = m.group(0)\n\n        try:\n            date = __parser(date_string, settings.DATE_ORDER)\n        except (TypeError, ValueError):\n            # Skip all matches that do not parse to a proper date\n            continue\n\n        date = __filter(date)\n        if date is not None:\n            break\n\n    return date\n\n\nclass ParseError(Exception):\n    pass\n\n\nclass DocumentParser(LoggingMixin):\n    \"\"\"\n    Subclass this to make your own parser.  Have a look at\n    `paperless_tesseract.parsers` for inspiration.\n    \"\"\"\n\n    logging_name = \"paperless.parsing\"\n\n    def __init__(self, logging_group, progress_callback=None):\n        super().__init__()\n        self.logging_group = logging_group\n        os.makedirs(settings.SCRATCH_DIR, exist_ok=True)\n        self.tempdir = tempfile.mkdtemp(prefix=\"paperless-\", dir=settings.SCRATCH_DIR)\n\n        self.archive_path = None\n        self.text = None\n        self.date = None\n        self.progress_callback = progress_callback\n\n    def progress(self, current_progress, max_progress):\n        if self.progress_callback:\n            self.progress_callback(current_progress, max_progress)\n\n    def extract_metadata(self, document_path, mime_type):\n        return []\n\n    def parse(self, document_path, mime_type, file_name=None):\n        raise NotImplementedError()\n\n    def get_archive_path(self):\n        return self.archive_path\n\n    def get_thumbnail(self, document_path, mime_type, file_name=None):\n        \"\"\"\n        Returns the path to a file we can use as a thumbnail for this document.\n        \"\"\"\n        raise NotImplementedError()\n\n    def get_optimised_thumbnail(self, document_path, mime_type, file_name=None):\n        thumbnail = self.get_thumbnail(document_path, mime_type, file_name)\n        if settings.OPTIMIZE_THUMBNAILS:\n            out_path = os.path.join(self.tempdir, \"thumb_optipng.png\")\n\n            args = (\n                settings.OPTIPNG_BINARY,\n                \"-silent\",\n                \"-o5\",\n                thumbnail,\n                \"-out\",\n                out_path,\n            )\n\n            self.log(\"debug\", f\"Execute: {' '.join(args)}\")\n\n            if not subprocess.Popen(args).wait() == 0:\n                raise ParseError(\"Optipng failed at {}\".format(args))\n\n            return out_path\n        else:\n            return thumbnail\n\n    def get_text(self):\n        return self.text\n\n    def get_date(self):\n        return self.date\n\n    def cleanup(self):\n        self.log(\"debug\", f\"Deleting directory {self.tempdir}\")\n        shutil.rmtree(self.tempdir)\n", "path": "src/documents/parsers.py"}]}