From 8a6aaf4e2d05021a14adc681c66dff6a815aa2a0 Mon Sep 17 00:00:00 2001 From: Trenton Holmes Date: Tue, 12 Apr 2022 19:52:56 -0700 Subject: [PATCH] Adds additional testing for both date parsing and consumed document created date --- src/documents/consumer.py | 47 ++++--- src/documents/models.py | 9 +- src/documents/parsers.py | 43 +++--- .../samples/documents/originals/0000005.pdf | Bin 0 -> 20299 bytes .../samples/documents/originals/0000006.pdf | Bin 0 -> 21044 bytes src/documents/tests/test_consumer.py | 126 +++++++++++++++++- src/documents/tests/test_date_parsing.py | 100 +++++++++++++- src/paperless/settings.py | 17 ++- src/paperless/tests/test_settings.py | 45 +++++++ 9 files changed, 345 insertions(+), 42 deletions(-) create mode 100755 src/documents/tests/samples/documents/originals/0000005.pdf create mode 100755 src/documents/tests/samples/documents/originals/0000006.pdf create mode 100644 src/paperless/tests/test_settings.py diff --git a/src/documents/consumer.py b/src/documents/consumer.py index 4fe6b02ed..44fb1cb97 100644 --- a/src/documents/consumer.py +++ b/src/documents/consumer.py @@ -3,6 +3,8 @@ import hashlib import os import uuid from subprocess import Popen +from typing import Optional +from typing import Type import magic from asgiref.sync import async_to_sync @@ -23,6 +25,7 @@ from .models import Document from .models import DocumentType from .models import FileInfo from .models import Tag +from .parsers import DocumentParser from .parsers import get_parser_class_for_mime_type from .parsers import parse_date from .parsers import ParseError @@ -186,7 +189,7 @@ class Consumer(LoggingMixin): override_document_type_id=None, override_tag_ids=None, task_id=None, - ): + ) -> Document: """ Return the document object if it was successfully created. """ @@ -220,7 +223,10 @@ class Consumer(LoggingMixin): self.log("debug", f"Detected mime type: {mime_type}") - parser_class = get_parser_class_for_mime_type(mime_type) + # Based on the mime type, get the parser for that type + parser_class: Optional[Type[DocumentParser]] = get_parser_class_for_mime_type( + mime_type, + ) if not parser_class: self._fail(MESSAGE_UNSUPPORTED_TYPE, f"Unsupported mime type {mime_type}") @@ -241,7 +247,10 @@ class Consumer(LoggingMixin): # This doesn't parse the document yet, but gives us a parser. - document_parser = parser_class(self.logging_group, progress_callback) + document_parser: DocumentParser = parser_class( + self.logging_group, + progress_callback, + ) self.log("debug", f"Parser: {type(document_parser).__name__}") @@ -270,7 +279,7 @@ class Consumer(LoggingMixin): text = document_parser.get_text() date = document_parser.get_date() - if not date: + if date is None: self._send_progress(90, 100, "WORKING", MESSAGE_PARSE_DATE) date = parse_date(self.filename, text) archive_path = document_parser.get_archive_path() @@ -342,7 +351,7 @@ class Consumer(LoggingMixin): ).hexdigest() # Don't save with the lock active. Saving will cause the file - # renaming logic to aquire the lock as well. + # renaming logic to acquire the lock as well. document.save() # Delete the file only if it was successfully consumed @@ -362,7 +371,8 @@ class Consumer(LoggingMixin): except Exception as e: self._fail( str(e), - f"The following error occured while consuming " f"{self.filename}: {e}", + f"The following error occurred while consuming " + f"{self.filename}: {e}", exc_info=True, ) finally: @@ -376,21 +386,26 @@ class Consumer(LoggingMixin): return document - def _store(self, text, date, mime_type): + def _store(self, text, date, mime_type) -> Document: # If someone gave us the original filename, use it instead of doc. file_info = FileInfo.from_filename(self.filename) - stats = os.stat(self.path) - self.log("debug", "Saving record to database") - created = ( - file_info.created - or date - or timezone.make_aware(datetime.datetime.fromtimestamp(stats.st_mtime)) - ) + if file_info.created is not None: + create_date = file_info.created + self.log("debug", f"Creation date from FileInfo: {create_date}") + elif date is not None: + create_date = date + self.log("debug", f"Creation date from parse_date: {create_date}") + else: + stats = os.stat(self.path) + create_date = timezone.make_aware( + datetime.datetime.fromtimestamp(stats.st_mtime), + ) + self.log("debug", "Creation date from st_mtime: {create_date}") storage_type = Document.STORAGE_TYPE_UNENCRYPTED @@ -400,8 +415,8 @@ class Consumer(LoggingMixin): content=text, mime_type=mime_type, checksum=hashlib.md5(f.read()).hexdigest(), - created=created, - modified=created, + created=create_date, + modified=create_date, storage_type=storage_type, ) diff --git a/src/documents/models.py b/src/documents/models.py index 8f531e401..c7bd3767e 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -380,6 +380,10 @@ class SavedViewFilterRule(models.Model): # TODO: why is this in the models file? +# TODO: how about, what is this and where is it documented? +# It appears to parsing JSON from an environment variable to get a title and date from +# the filename, if possible, as a higher priority than either document filename or +# content parsing class FileInfo: REGEXES = OrderedDict( @@ -387,8 +391,7 @@ class FileInfo: ( "created-title", re.compile( - r"^(?P\d\d\d\d\d\d\d\d(\d\d\d\d\d\d)?Z) - " - r"(?P.*)$", + r"^(?P<created>\d{8}(\d{6})?Z) - " r"(?P<title>.*)$", flags=re.IGNORECASE, ), ), @@ -428,7 +431,7 @@ class FileInfo: properties[name] = getattr(cls, "_get_{}".format(name))(properties[name]) @classmethod - def from_filename(cls, filename): + def from_filename(cls, filename) -> "FileInfo": # Mutate filename in-place before parsing its components # by applying at most one of the configured transformations. for (pattern, repl) in settings.FILENAME_PARSE_TRANSFORMS: diff --git a/src/documents/parsers.py b/src/documents/parsers.py index 269fddbb4..8335d433e 100644 --- a/src/documents/parsers.py +++ b/src/documents/parsers.py @@ -1,3 +1,4 @@ +import datetime import logging import mimetypes import os @@ -5,6 +6,8 @@ import re import shutil import subprocess import tempfile +from typing import Optional +from typing import Set import magic from django.conf import settings @@ -40,11 +43,11 @@ DATE_REGEX = re.compile( logger = logging.getLogger("paperless.parsing") -def is_mime_type_supported(mime_type): +def is_mime_type_supported(mime_type) -> bool: return get_parser_class_for_mime_type(mime_type) is not None -def get_default_file_extension(mime_type): +def get_default_file_extension(mime_type) -> str: for response in document_consumer_declaration.send(None): parser_declaration = response[1] supported_mime_types = parser_declaration["mime_types"] @@ -59,14 +62,14 @@ def get_default_file_extension(mime_type): return "" -def is_file_ext_supported(ext): +def is_file_ext_supported(ext) -> bool: if ext: return ext.lower() in get_supported_file_extensions() else: return False -def get_supported_file_extensions(): +def get_supported_file_extensions() -> Set[str]: extensions = set() for response in document_consumer_declaration.send(None): parser_declaration = response[1] @@ -121,7 +124,7 @@ def run_convert( auto_orient=False, extra=None, logging_group=None, -): +) -> None: environment = os.environ.copy() if settings.CONVERT_MEMORY_LIMIT: @@ -146,11 +149,11 @@ def run_convert( raise ParseError("Convert failed at {}".format(args)) -def get_default_thumbnail(): +def get_default_thumbnail() -> str: return os.path.join(os.path.dirname(__file__), "resources", "document.png") -def make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None): +def make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None) -> str: out_path = os.path.join(temp_dir, "convert_gs.png") # if convert fails, fall back to extracting @@ -184,7 +187,7 @@ def make_thumbnail_from_pdf_gs_fallback(in_path, temp_dir, logging_group=None): return get_default_thumbnail() -def make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None): +def make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None) -> str: """ The thumbnail of a PDF is just a 500px wide image of the first page. """ @@ -209,12 +212,12 @@ def make_thumbnail_from_pdf(in_path, temp_dir, logging_group=None): return out_path -def parse_date(filename, text): +def parse_date(filename, text) -> Optional[datetime.datetime]: """ Returns the date of the document. """ - def __parser(ds, date_order): + def __parser(ds: str, date_order: str) -> datetime.datetime: """ Call dateparser.parse with a particular date ordering """ @@ -230,9 +233,9 @@ def parse_date(filename, text): }, ) - def __filter(date): + def __filter(date: datetime.datetime) -> Optional[datetime.datetime]: if ( - date + date is not None and date.year > 1900 and date <= timezone.now() and date.date() not in settings.IGNORE_DATES @@ -244,8 +247,10 @@ def parse_date(filename, text): # if filename date parsing is enabled, search there first: if settings.FILENAME_DATE_ORDER: + logger.info("Attempting parsing from filename") for m in re.finditer(DATE_REGEX, filename): date_string = m.group(0) + logger.info(f"Found potential date: {date_string}") try: date = __parser(date_string, settings.FILENAME_DATE_ORDER) @@ -255,11 +260,16 @@ def parse_date(filename, text): date = __filter(date) if date is not None: + logger.info(f"Found date: {date}") return date + else: + logger.info("Filtered date out") + logger.info("Attempting parsing from content") # Iterate through all regex matches in text and try to parse the date for m in re.finditer(DATE_REGEX, text): date_string = m.group(0) + logger.info(f"Found potential date: {date_string}") try: date = __parser(date_string, settings.DATE_ORDER) @@ -269,7 +279,10 @@ def parse_date(filename, text): date = __filter(date) if date is not None: - break + logger.info(f"Found date: {date}") + return date + else: + logger.info("Filtered date out") return date @@ -294,7 +307,7 @@ class DocumentParser(LoggingMixin): self.archive_path = None self.text = None - self.date = None + self.date: Optional[datetime.datetime] = None self.progress_callback = progress_callback def progress(self, current_progress, max_progress): @@ -342,7 +355,7 @@ class DocumentParser(LoggingMixin): def get_text(self): return self.text - def get_date(self): + def get_date(self) -> Optional[datetime.datetime]: return self.date def cleanup(self): diff --git a/src/documents/tests/samples/documents/originals/0000005.pdf b/src/documents/tests/samples/documents/originals/0000005.pdf new file mode 100755 index 0000000000000000000000000000000000000000..cc78528f5b1004a3107eef12dfbafde59d3e1279 GIT binary patch literal 20299 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 DS@I*& literal 0 HcmV?d00001 diff --git a/src/documents/tests/samples/documents/originals/0000006.pdf b/src/documents/tests/samples/documents/originals/0000006.pdf new file mode 100755 index 0000000000000000000000000000000000000000..c66896b4e7a26da0089179d4c44d781f6aa29cc3 GIT binary patch literal 21044 zcmaHSV~}NC(`DJVx@_C%E_T_r?OV2O+v>7yTV1wod+K?<cjk?m9}}_ToIDx1bKT6d zR>Y38_a&1P7NujNXN4h~KRelhVIpKCv^B7R;pJseG6z^06H+SM0RAzI2#stFove** z08}sxat^jePKL$~gp`Vw=K2iZDG05Z7?}yhY;8@y=Lp*xIua@v+ZY);7&}n$^TT|b z|0HGkZ*oI@LM9jnL1{t;8CwTyeXIWlvHdql)Z7a24Z<L5r4KL`Ha4_1GKOK0GPW@V zm=UruvGVg1IszPw^{ruCGcQ!vWY!sxyq;8_AuV|Kuw&@*B-$IU*QtGgz@fuJ=Dt$W ztrY6=JP09FRv}T$Inq&cT8*YSi3r~AU>^ijR&)hgw&+tYE51HGNM3f{-42E$hIw3^ zlioK7IJSf8N=jD*J{_7bhgl%v32Oz+>{lFR;MS(Hw8wl8b{MxdxYG+@3>ssLPTL_l zmKF5sNz0xuhjNBf&&#hawV{={Sm2|@em0^0lt8PT=ov&f4r5LDdk<!|35<b)xtm5+ zoTV+u`lr|#jvTnR58WKhf^i-%C<h21R*=aInLVvw7e*qe|4(JwI2nk9WpLZCu<bV7 za7;GPM+E#lX1zJi;Bh}grig<dWs>!df<P5eFn_Ybp@0|#psq3a?>b!#H>$&Aw2B63 z-CNEh&|$ASoe|1o=b5x<|FmD_1Myr^gLi?;=h4cQ&o_6HQg>$Ko#;;|`f8I8Ga*WN z+kAa_59Ij$P})s$Mf!TWEZT^AYo$(e{9IpOw6U@K+W<+HacB==dV<r)5MeXvmW$q^ zG8y1Ww52?s5_=cagYgrru}^7L3S5;G?B|hrP%Ihv$&q<JA{sNStEPvL2YdJ4b|t>k zYssgKl+Q)JdVM3ls`tp#_~|@~0rmXwu~?byg608F#x-d-ifqI5nkI}hh1QMAD4v@A zES%M$%oi-?Rl(dp6#EYu{zEqA|ME!5&CZyRK~CTFn?4H0j<!w?hQ^MB-xUAmv7w?d zfKc;a3Nt7fy8;Lq#I3&r1^;sw`p;cl8-_u|6(FVv_@?4NQ^i;a|Dm(OH*pz6ZEXPm z_(WL=S^vlPk5}5*$Xs90)|F6`@!QGH#7xM+#r&N{=sWsbMvjF4FkJzLK@s5KWC)Pc zclg)I_^qygO+xnnr2kJn|2+P0W&WRf9REq@^o{;4CP{N6M?%ei%2v?+ubepk-)Tbn z0DUW4(|=%0zq|1Npm6>-{C|YTM99L##l`+z<o`#EOq|S|O#hc0JKbEBL>>h^|6E>p zcwBuC8Hx?@5}sHDu=pA<Vsk=Z*7FQ#6zGieIxu8(vQj8w81B7Gs7so;KwscAqJqe1 zSWp)3snCMl{CpOuJ;DOBxvR3a4Qc<^x2da`C$}88oU6{V>B<Y01wN;#GDS&HBp^G& z&>2ft<p;)Bst+LFs)1$n{^97fnnQx84;WNaV5FyO=!9S_9}-((ACP&Es5CjPy15Sn zv^x)`b;ec;rHkeELm$wYPV}E22NNRmDWM^N4dB2MCa<jt^l_mWu+Li;ATMNJ)|+rL zM~qG<=?zezMRGV@E>rhClxu(B@#>!UvCN<)E&GbfbIP8cIG_MjM^ictw|m=%)Garh zp4=QBkJk#rDb-qD2e`YZtO(>s1tI?6+Nf9om)YAS7XgD#+;N-!mLOQn>CDfr6SPH< z8!t0$R<8q;4I9{C=w!q^ni7We3-jZst!m96du2%s4tiOPKE8A9<SwI?bFw%Wu?xtN zPmG>>+(^`)h-gZLqol9cbPNI2c_7~4%tJJCa0zkl(}p@fg2A^{>wJ@5P%*n05Kpn- zh0r;hKh_&zVon^ihYTZUP;EEvjpt2&njNx@V+fi})vdr1vbsD*8Y5|Ndc7neaqE&F zdm^gYo)~ACls}8+6dldRcq_<_WZOpnXn9+IeB^>Tp2T7`vOo)bFk)%asP_1&8a`-b zF1r(B2i|z*^p`X&QJc6#u*C9j>Z~DQ^n>ku=LS=B;#k0M$4q3(u*A?)9fL$+8^ecG z#cD<v3u|;vbaYX9J73({2o7L@O)!wysMHt^KadV+z;MP-I9re+SJdMW4nJzFFiwae z5*dGu0V-GB-;+6AeT-NT3Cg5YTBM8NjcpIEKBaGRI@Go&o@a=)Yj!-FU(E00Ev;wP z)Z!aFK9@~WwL0c+MYpN14V}IA368av%<UTx)3BRIp6x3a1URWl7vp(ctoN!iWKnZd zXeC^6;Zjxknb{}?vV}hh-amn#!Ic@|M0?ayq3-mUw7^ySRhEZdf$koBb$E5%QatDN ziD)H{B)xA1)>#r*zJ%Nb!%ahal#n=^G0fM;UJ|{)b(Wd^bzL`QxnFMDF(-6_-FOWD zXeGzwqoG`aywid5A|Upgw1BJh5gsFwxuQm*UsrnWrz*TZM#M2-iIt8@hhkD}LDE2l z!UQiEu^SbP%T5JRdm;#NfNYEy!A1@rk$TDunbPr>=z05fmpOZmZ{@y!yeqwvABWg0 zGG_TElpNl*JaHKtS%E$lhjJqx3Mr1s#%j<gc!yu4XWYV4s%|KF*DB)wkO*NrNSO&4 zL-BCHznYa_u20Zs560!9V4R$R<SOF0?Ey#`GezigX@N>IszROALG2ysZPFlPR|j$@ zVF-q$N8PCA`EU<8!)HxD!5z}};<)eq=);&eF#c+3%EQtK_70wB(y`Q+)j8@@^17=D zZcSZG{nHueGYK=GKBIU#5ZLf8us<w=>4gEY=i!m2YZWMd9GzeOlY{F=io1+#2-8<~ z4vj1a*Gl$Hcd*vT=1br>6|BIgk41z{U9y1Jjz{OGZ8zka3v`>N9Nf^ZBUT%ZRW)di z&`c#sb?+-c=YETn5B5xrx9tyrcg~2eOE>K5!0=tVtVy!1b6e<Wi4UBgDcl`9KBs`E zH~Yt3JcSy!#~i~=&|)I!`=hlHzE!}-H8WR~t+m)`W6xD@02@D8`F2VTLaQ^((0cd> z4}NPajJLQarRLx@l*0pw9nzf+42K>=YZ%81ctvny-`ew{M$J!rhcUJN^tI8EedSX9 z=x~laK#VZGhG2Sp<0wzO3mHKOg<j0H$2m5t#F$hEB6;`R2iptB{$-gR$2y|esCKY6 zrFKd_+$FMi(EEe097Ftm=02CzFM5l?080CUZ9?+`CR#6cPniy*Bt|qzH`=t{_&r*{ zrDFVEK#%eKMx2ui($diq_ybH8kQRC56$RMNdmAv_!6X9}JXxN+ZaAKDfhf&5CpVCD z<ba0ilTxDrQEDf#3r{BRCEIN_PN}~AW<c#!1<mZB(-QgCFTO?Gms<r_n63B7_l!;7 zCyO0rdo>5Bu`i951bJL<NCIR5DssaSqtz_>Vno<+IY|urvdka{Cz|z;BA?#|MjUMO zLVbq2j=N6^&cn5Z=pLjni1|P6G!1$g;NwIcNWKLo7&2#m|Le8(TJi?_M)nr)emX91 zxQ;O0+Z~p5Pq8;*Y(yWOeKwM{@9hwaM^169#~nwXTN2pXr?9iMi_}388nr9krU_^k zr{K2WK>)nh?rn|6@6$0Nyy(L>ecB`jV(cdfUE^<x?HJ~hkQ)s02s@0DI{et=h+P{k z*f+n0Gw;<vNQfE5+_$`if630DD@2*hZ)HWiV$>F`F77TwoE35<DE`Xbg2pG34Tbg! zgvpapi>%b3up076xsE%=L<|!zbPT)1-@w?IZ#x1ooXJR<yomB0Z@uo+KK4B#^5hzN z4fFk&{6RMw(-Y)%EJh7Bdx{&vZNPnclVrBV=jZv!&O~zYUKk5UOcKXDKjK-JSRpL2 zDTPlcd)?Ii^N}d&Q&2`fgO0UlZ9U;JPv{t{gQ2AUM4>HhQ$+K){XUZrmb{m|B~Vbm zMUG(tBN=uH>In*Zhi(US$8e|O8gx^9mJ(u+mx+34U}u;0hWbYT#_`5+Lun(UO_r>n z;dh&eMbanLQxfE;;eowFXNUMEU3Y=)&r45m=iZ2{2WT$x3gkaQ+IEn1=wA1T_lP6Y z^LojQ;lw3#C8O@{eCByCZAo)Kxnm=P=MB)zKd3hZr|10oC>4ardpuV)X`~&(r*{hk zJ;7zd->%m_eaWg5C-x!SC76X+cDOU&Wsb>)0y%a<?Gi_JNv^xhNbw!w`N<+h=${)o zf2+c__6c44*Cc8owwtk~V{V;s5EI0Zmrm;uQxR>!n8nS{j);5&%F2b%<@y9AP!6B% z=rBuWg1Y>qQU}lJPoPBKa8i{BijU}!ISRuq4QK#D&(Yt59;NV~g62qY-vK^B^FkDB z6Us;CN6Q{wB*<rgDj1k=yD_4d=y@=+rj@SpvjjBm=8WJWjTnW74$HMyqDTyAc~*&5 zGNU`K+9Y0fmO?_n7BCJ)WU`>V7@$~$LSA<vJ_Ls1u_7A3EBrO^@x?GFwhrRrrukJF zfb%&SePbre3;jfK)RQ#Yo(p*z;54eNwmW}KW#wNox*C0ECpGzN^d&a(FML4=pg*4P z7~&N_>*v+UYWMF8sGGhN*KS|GUuHSBNM9hRxy-FhFQ)sKHl_MkA=q}%c*E82FqT+E z4ZqBX&Duz~q}2SkJkW;7awH(akPpc*^CvqIvtykYwWIY3C`NLwJ*op%0G<x5jO~W_ zbNZ+XMA3iSU>>F`Z}Jkz5u#NQ+DZ+!eQ!TMUK5j&?=O|t>&4_NYCM|#%}50@Q*dDp zjUemZ`KyO79JtT_7}RS_kQe-7kmQ!n1q=Vv-;X00W+o&U87vlg+}pQvg2mLSC@e%A zDBs_9C?1I8+9bD>X+PmM2+p;qG3825Auot7H(Nld^aFtmK_y72(rBzj<9<sAy$Y60 z#^H<da%<uP<NoMy7dFr0YW<I#KWpG0)P9wT4Y>`upur19?2zD)DfUN7{~w~L?$XDS zu6>fuNYjLUe18@B^dL3bF|3sO2&(?bJohPAfI|~0@Oh~ioVTo={;61BZ^IXXW(JtJ zih}eBp)_Glg9y2?L7STi9xenP)>iwi3G#%mvRhdx>xl|Y@Y3*FzW!YeWbBMy$Q!EJ z{1bSzy0>}vi*}vA!CCw0FRi{%ql-=`IER%Qri9fG)io}*6n*U<$$nRJBQ?SB{WYgi zzX#Hxi7KM1D$Au~8`X?My`bNyFWyW-%GIU%-{ty{@KLhc^}RuzqkM!whIA9w&Yy;# zNMroExf}EitT{R4)6lCjbkLV~0^#?nS56{AJhc_w(JthaqH1_5ni7YtPR`S^#P|5K zvtBHQOerPRBKj~%gh&NhgqGu18NC&KAnM=xgVR2kaCMx(wj?I{@Y7Nl;jEW);q-3K zKLY9V1BGzAfcJf`M|}WMY1!d%#A6&i9g(uk6~QOwKExQ3Xc){GaUljd2Dp$nb~*SW zG#cid9m3E%preq6aZU4Q5NISXj*jY(TZ=d$*jT?=qskZhh*IMg;BI!+wEfv@5}N?w zmMD_u5+z1DwooJ#&30#NNi$+BKJ0@RrP(}$zt+D(_E$9ACj;#!Tc{fIPT}FHRu`qt zQRe2}Oy?B30u9Y7V?`AC)5)%tjK<2+CTQg>WkQkP6F3#8Es`Eal6ZpygUJ$uk#QEW z9UZ^=Q#2AvRy5!3KO2=;P(cl}`$5|S+gYbOe)T}?$*Hs0j|IaI(VLH0Xar%F;E&ue z*0OW3vH~Z_F_SzmrCXg%YiX+xN0Z`?F%y27%a`e;M5<Hy{LqQy@j!zY5^2`&udy>* z4i1K&*G!OR=_4ZR&`0!2>*~-KcM?js*GTZN9hZPxQwwQGmxRcblf$Ybky?ydL#8;R zkSg0K018}`t*|Z*M6kwEXeJ?;w|EK!+T~5IQ^ObqA;R#*@h4c3df@e0%;x-wDvRz0 zhB$3S*0Xni!U@8nS%y(kxJNk@rlFNNTpMwcnulu1<A{hk+J^7hvuBw^UhwJfL?aIb z&9Lo4Ei!F#jMA3)67P@}(McE<FFdO@v)4{2;U9?V2vk3My?1Ne;k#9amR6}eVl~L3 ztzAq{r=4u19;@!$8+hd(N|e1}!0g{77*C~puH;QU*z<Xxb_*{+uiI7QDNnOy!lW(N zB?HE%({puqkbx9gMmwbR;~Oa#^TA!E3w#UwNANW|(4fJm{R_muIH;N=thbm7y;TRe zaP9%WSZfaTUV>!~$}wOw#GKklwM%ZDzRX`-Pv9VQ_`6`^by%;h5C&iOZlzs;yA`RH zA!ru${G=G`56h4zj9Al16OhL-(MghNu$@G+CyOXCuhVJ$uo<?S6RBo`^`wn!b=11B zagCCtD!B3u`be=Y`&H$jG9O`Ge=!7r|I;3D$3IXO)DiK2NF3EGOv*)?G@K?Hnhh-P zO;*P0O<S{Y1aeA>@U8v@cWw$V^Cw9XkzR;vwPG)yBww?7ZjHy?0uNBO-CL>mx`Z7Q z^HaG7@h(wdEP<u5|3#i!CizA<SWM|I(mS2x6}c;#p|i6hq`nF33Jks)jd{(J<j=+T z430kn)}CDbC;ShNG7pWwcyx}b0-SR;AIJC;LE@Fk>ewio3U%rWMx`gurl?2B?Sj%v zoE{Lf!ze>Nvey5!C75<18$3<#Q1kf!z@;TqFg`ejxZAGRgsaW#q3?YRqEnyV8}Kil z^+@lEJ%W)x)+xY^mp*2A_eRe#^5vOcEz1Pa$WUV142#*JpCJSM-Swst$cLING;lz{ zkqC9)$%wvtYwt<(!&}80Z3{eOc=u>UvQ30x+js#6_}@1>krq>QC`3MTHlpR{%Yak^ z0rK2>X!A^8%5Mo&b1>5^=0eYZ#Ly1!@Nb7|wn1ncftY(Iny+anC}=2#lM|wHEKlO1 zj2cW=>f9cIJ0s|0(L+Fp{wKG3=p<F36yqJ;J4{fW5D^)g!NJpzOK)6dAdn`4(6}W1 zwsA8k@xRUB%zf-TKT(!o?{dC+mm!VE4C5q5e%I3E>Vm!G6$2~j0b)z~`2O8FurN3O zysH{`uRMx2DI|A*cS#MShT1VTt?-m8&5ic8h<4Bnq#`*S0RWf*l6MA`q04EJC20;Z z7epctg!vcc*}Keib`YP3yT_w2pD{u7m0<47JSv32*kAbt%xi}k#3I)l>(V);F%cl4 zT!C+hwhH-_<VRY%NopDq?GwD9E5VwQZZrtPv5GLZ?+G?QuY2I{GDUFUSbjy(zEVgK zPTnOxU-6$G^=}VigFD%wSAsU;xdL#*iF}Om$4xA!@c5e)fDpsu(wR=K<R9xKXizJY zpaC~3L>=>&)V1)JM1eDp)Q4DtT9Gz9Z66?OF6rd^H9vR;7LL<rti_rMHuM3dEy(H? zP#XpS&6yvDRDw-NW(eR(z2N?cTMv13rl`DjFxn_tK)(hI0ERQtLe0<{7yy*%VFQc; z%_tjzws~GMde`(T>cJ+Dblbg{tUJu)>umrXXJa;`i;hMKpIFp<wlb#W3WV8;dy4ri zUx?YXKk<cmOd@M-$*jU0`!Ze24avsx^*7J(fpCdR37cT(f9DpM!CElc_ja)eXw(^Z z6M`U@{?x+&ORxqpZz?4Vh+-9&&09pAt3DQ|3(8(MSC(vo>k9OSTbsQQqnL@g`3&_2 zD?ecpbsUiRF6u(e1!{u|fWjC2B;O%WNZ=pk4K#R6J^Nh?_=Y#2WM=dpR5<7}YI2^F z*@ui<;w!jx=IN$>Q><Z@<d{b=MsI%8=2BIsF2uuNkV=*@{?@fl0l&SyFzK`GrTub5 zUjE#CzceL~*JxUSsG4hzXPMeHgXR!i^Mry&sCIj?xAT}M_UEwFiA&9)3t^xQZ5sqY zaz^~v1JmnD5VdXcxG7&USBPFnX(s3x_jptAiT+*?Q;Y*6D;wWt$U@^J@9;QwTaV)d z)C+z=C>Fo4`)IoPxcvQv{NsmDSM)TVXV))X>nZ3yJ=9{*u7YjFUQ?#xvtwJA!?l3X zUh0jQ23TT@f^uOFhz(Xi#*8>XVA3)hy}^)(p%>RI9n74?vo}Y!ufRQY8gZyVC=OT% zE+3R$kf1MlCYdry?5dR@050EUJ%}G-HiIKS=GXly+p!^%b_<9XTz>swLCi0}80$S# zXmnaAf1t|ib;JvJueTva?2I06Cr%lFP2t3%aBhKcu6zZ4E3CpAksT9*co08yxD68` z<1xP2tI(X++&z(Xl3bc2wwSH3%%4?TCf%Tr>SwycZ^ca3PT^M)s`TF-MUpkVmt@km zvBlPfWpS(`;);Q+2MNVUCz!THt&^B3w_<EQ=Xeu=2sxPWf)|Dbt-oir_FuIALcbv+ z=1Jbl;jskWHcWKkJx;8ttCa<LAW*9Fx`KIu>w1E>@1S#r%M2j6gRdN;)9$0w_5`bi z+2FLXe-ndjRy>sl5w|O>0KOJWo2<?JSfzj`NM?7ED=4~eR|&QGE*QN8hc7tjn8vj} zDpV^ScUlERLJFJ63I<6N0XrdKBJZPT5{{?+kE&*v@l!Y5*5?{Xa4&>A<VIkVT&#@@ z9|xOH$`32DCpIx%7zF1y4<a$)Dv|>vGGx!k5K;U~P+&ff<X^u0A=B}PJw~3Bi+FS} zmt-oBi<jFjetW$PAeCsH6d(-XUnmyn;*QtTSa<7}+dO`n0o9nzr%Wcg4N2REwxOmQ zsK*5Bj}X&s5UBWO2%A0l?Ey=W!H}3>u6<T?JGY{6gVwz_vsAsvYC7L5UB=B&<4Xh; z2zo>nv)0!d7iUNheN=<=W+dD-z8)I*qiYa~2TL-?J&{{Jhe}@2OPZ7s5r-<g#QFo; zm9pkD^iE~lMO^#huzD80!IiRlHaA*)LsQU96ait9u=iY`CmOV8c~EapUlhI|Nx~f@ z3$w{UqC)anc2o<KKonDoK+Q1w^~6AW-ayiGHaQmi-}ycj#mX|#viA`Sr2Dlg3zvlZ z(cF>JGY^$u<cm%5q!Ah!gNtHMKt!jjYxT?ZYpZK#r#)vn@+XxIhzjE&${%2?g^bRh zx6U`F6qjXbav84d?ey|}yrbuB=>rVQAgB1ZY+TyfjUn#|e5V#qPyr)L3w!DvjHQ37 zs*01O4qpg`Kf_*P4L?WkPVDteUI+`PXK(p(4<feTQW<ji7zw7lNO*Mz^tNAw`))7H zCaSAe<8r3&Tm@%F?pWOgv+g>OdwJuaruS_8Ib5?j)}?7>4Unc$p!AY6fu;rDk+#)A z2Bzz!_;ZRllMWW8k~Q6=LAY`#at~_pB#&zG`gv==?$h~5F-UkI7bi2vFmiDF7V2l& zNNqHkmNLxJkzp=U)l)Q*qS+@>xl;H_re*#HnW~bR1eXi)K*)TA?U&KI>9truN>yg7 zwHsP^IxFAxJolVl-~VaL>K{&iI8#`vOlun(`V`hTRM$7IsM`7PCT=B=#Gg7n9sXzF z;1f)pflTcgTDlzT|K6A>TXm#h!;aI%t#ddFZb>2w;Jibac-V|>#H>sz=|wA-Ozjs! z!2r;f4R7NIsH2RbN!Hc<2v&-J3ifMN_ZXcoBLy#5xJC-4>;7{?eXTNGKKF@p(^q<+ z%H(f}W%=F;JO2mu=0rJa+<q~ZGE*+06py3GIfT~G3iPdTz!#7?s<e@AHBaneun&Ym z1(g;_Tjf894ObQ^aQRiPFr6m_lYT78BhDK_8#2UA6sO1%mrtFVsxh_^(S(MJk8^en ziYp$;T0H)njRymi4(>c--sVM_N;e#1Tv7Hy+OvpBI(}j>6n$LIiVI_e9vG~TK8p}3 z+@x6b2~iQ(vs|_gyv+zw^H8EG06Fq~Jpg(Z&O{hi4XF~(T2Dl&C%BaO)7PXl{1}KQ z9~Cc@e&DJXUPc(P1*tRis0R}N{;fCUllGbfg409@!{B>+Wh?JwoDpm#wAF0c8`2F) z5RSw<!f{q>0BWB|6!|NP<u9WF$B5WDg8YDBG!y0T>>%Rf;N<{ASxRBrU@QZMW4~qG z8{ABiM`79^g8}B_B;J7eU%_5^90mfEBIk(4qdq%Qkvl-g0i3ah;P)aOP_IM?{-2>q za@p@VyNp2gjBvtuAn2h&$Y0RPcE|>7aDsRcNu%?ejo~EGzP+KC{YV3noQpkj195Ue zoG`&;vb|=}hI*X4YFK`p2=p@E;Coy$zLH23g3`C7QvULx30yib1<02AQnv(hkcP-5 zgxP`PdU&rTeP9FbsiCf1!{&@sFD_=jqoMOV3Io`ox_wfmLYd%-NT&V9p{hN9_c0km z_OwdHG9gnaWP3c#etv;O!|HSI5~upzsP;(>4C1BqRO}zspqY@+2XKW(;idGi@0;48 zPY7uN-yv6vzN4x1fecvcA@4TYk?lI+srDKSoasaF!q>36;i))U`HK#))!^NTwos2g z-l-C9PT13FWxc0nJqT$*E)iCRSR&g7oy8OMLZ}6~;%db(;UWw8ukAn#@aVDalB)%@ zVBcV^Lvs*sf^!gkg4oF52kAO=;4Ttxf}SB=1wEv?6YR>j5ZH)#0<n_{1VZkx4}|EE z>{fH~!?_UJ_(_Lq>a*;Ugc9@=*iq~n+F|bQUBeDgY^RKN_a@l6+<LjfT>kRJxfJmX z-mAAG*mblM*uA?Z++~ku=n)EC+ffaLcEz%X+(kAErVHiKr!j!#P4`4}?lm0k?z4$z z=-UgeyawCFcSW&h7*5$XAMm=Cx@EYwbj7&gz<cU<47I-I-L=)n-5tLM*`=xBd!fhk z1>3#S6T6+hrXF6(#lCr}VR=F5K%fh??V%sw(C66Iye8iLWhU|tdj5-d;D+&B<elVP z=pFT)`-QzD>Y2h7(mUYUbUEgkzJcru%4g?U)fM}O&lUUz3h$?9&feCx%Xh8ip#*)F zqy2V?drYo;ZczOG97qDe9LNGYu6Ub*?`R0b-Vi=N-|-MAzJMq=y@7p%-%&mjd7*ql znvuVHZ%8`=p3yrpJYi*eb&+Hp-znbFZ!BFAZ*=XzZs3M(w{1r`c4$3)pKYI!ZZrot zws|<iT@j**yb%eAK6`3^@QZgL{IJ7gg3clStd{xm(JlG9>((my`Vd$=(S47U`PvoZ z{ur1(zMu9foELaQ1iaov%IGeNl=$4TaDSjy%?rH5$$X`r-N*2p+{d*3*Yqx&kFn44 zjy$_J{0id!==_QSyz+dN&wr_wd@Y^ae>wAfbbp(fS)bk9A3jd6vS;^OUtd2<P_d*m zPCVP7z$nAtl4r<3eu#+5OZ5HxXd_GQrJ!%!z{Rb^t5)R?*p;X7c=9hQ+?=IYVoP<V zsBCwxH9qNtI*a`T5#H6&02b9?f){aRtBY$vpKlJ2SwD)RGCez!6(qxuUoH9D__W;{ zaL(bM5T6tArwV>J+{z+C)5@gYZG<#q#R!L4+(J4#w4Ao$uCAtnal)XM{BYhu|5<9? zx)v@CEf#Ba0?s&S2Vo3haWLu=PyT44R*e_Jvvts<AR(3FvR-Yb#6NA&cg;$awVsnQ zde8t88Nt9qJ)++Ew*dVKTrz2GJileFTFX&+%d}CvS{zIS@Sv5+$S$);HbcD#%uC`= z%Ub{oe42N_?Ukshn5iUwL<~le6QI+)^TMfoiZG+2VU=mlDa_VvQFvHsNoni30{j+a zUTRoy2}D<Dk?r2XJm^GYjr)ZE1^>Y53i-9_zUss)LttKOQA1Sx-gBdGBskl4Peznf zwty%bm;TI{P)`mn(Fbe1R~$``3oavo`U-Pl3`L1ALJ3!Xb9I-<lMU?Lh-4brJsH#$ zVL9_|)w*Kqrr^ww0y(YOo9d}J^yg{HZciTq%ss_1H=U;t?V;Scm!#ynO}k{C0%tYh zsoZ)InU^vd&QwB?IPB(TTfW;%?2E0y?G&5KrJKpZAJgns{ROgzdu!tOi0quEdJ?z7 zf`Yvy>!;4&-V63uW%EY#X<rK+npng5B8zYMty559yUL=K2Y6BMuzm&dZ!pn<>-oua zNUHPF_uie)9lzlSPfGP>kXguVvAC!rBg20#w+1Zk*J6nL619-_##ZI>0O#~j=kn0c z8TN{Fo5SmC9kgA{14=wmQ&&+*jt&Ia|5$jD@(>4ChfOD>UpO9NfK!UJsP-L`kmZse z$L7o97i}0=ub~!q$m#NWOPagVgy`nG*^G%uiCBWyK=;&t#+|Q8LO%y0Qy>a)fAWot z@1@KY8`}P0U{Tk{$L|k<Q8%PWp^?Uc`(tp!jW<UMyN9iOkX^kCA@WN%S}~=cbEKbn zf4}B$rZ2jp!^F9zqsED(Jqw#VZ(eS`0uIcd$J<>O1ZG~S!AKE<Bsj!G#+!mPm`P`z z1N0$y_N3x7E)0&99{`2ugu&Xnj9Rk`P|Cdrp8>7|pFiy6aTWY3xYe|qF1a?j_whE} z_wn!PpAWvCwXIXUvu!=R<L;Fo8pD;3UXQXLIVM?;8BZBHfEik{Tq7E;@Oxa=J|i?; zK7RP+{!hGu{FFit5N}h$?Ip-}kUV#vpEdiGmG?JUhD!NOC|C54;63Rb33ByJbfrTL zxj1RL4%Q}C_Z?9a<AF&X!b`a?<*}dk^=;+xG6fMQ7SwZ{tOWi>^9c0L52iB21xl2r ztZwCI)up^EIzC#BEIPl`OfRtOb*k}tfE*ACjcWw{CcGL`iAT0L6nw-6ygpti1}jjE zf_tU?Zp$1|f2J4{xG%^aF+|LCfBihO@eDn;_k0^}o{n}sJrFB7v|3|tF)&$!hve`4 z-gd)Z-R8XCOj)zY*?4+>J?TsXlacWzZ;N{}C^T-~K9J~Q8}yr~uTOYof9bGablU_% zBFAN7f%yGYgDeKC89lqak%Wrm*aivMpwo|7IU~_Sq>@?{bVy7ITPYZndcmhP;aDE0 zp-D;@6F+}XsHU!79Zv$SMLASEa+z0OPqrAR!>HBhRML(ki*#X7I^;2Jt*+DB>lm!- zb+cSV#VDXBwJT;xqf?P5S7Xrk8k6zr7GAC<qANu7j+DH=EQ$$;nTo=#N#eFD9?ZTp z@Mw#;z}B|1&BV~a%O_$$jgw2roT4tNA4{unFib4Jp!6$6DS9q4)iJUa;dr`kivOJK zY97^@BLIRn89P3n6sg>=LUS3Aarpdwvd2>U8mVC;Ftxv1*q0`<hyIs#<VUI2$Q*aE z4&KMQ6uM(^?3;0i`KKNnF-xGG4!}Y3`VzdnS-{=v<af!&-(~mlcQ@QDy4LTHqPJ1t zsuOMYj>BOtJ{hDOZdbY~E%pl}Qz0i~XQrF3cawSA)syDgY!`XiT-{v-m3tY}SWAqS zM5<4u`>_PO2#`z5&e7ykf}SJHOj~Zr;U1~ez1HA7V!avoo1Ef}QWHnp#Q|9bqvNr6 zqy!-CxBK{n-+N_y5-2q|j#^c|SyZeE(rAt#7&O2u5r1+raMt@@Qp#|Kd?JT_Z$E;L zI7S|{t+h%t*jL=>CUg?L;SVcDt4>^{Z%xnP;^|oC=`!BWJuqL0JP;FZQ6UtJU)pu( zH*!@Xlq{J1I*i~^6WU#@RIzAW-KCGW_#43<*D;BOnk21dJgHbh6_K<sMlPcC35jv% zfN_U%O|5e(U7zaWIMNk!4~*N2`_kc4@3bUat5HK!?xf%3j#rmTzY8c;oz3IL*Dbli zp3f5msful(Czd=jtR=Aaa6aTW;u5Q(QT^EjnxZJmL&SZuRP~0rxm;(xSA^Tn=LqRl z{V~YIv9%(4KW-~<_vSoTXw`DjQ-H`CQwEs#;oqt?uozwu=yEo_V;a((w-*MSWrM)d zhG`<Ig@*blG2no~)?8kQgqbmF;(+@x_eJiwtLA!Vbj@D7U<nn+nJbkJ3>(zpL?Pv{ ziTKZsrM(uR7ffa1Rc0EKH2FI-&JQ10v=)2bu%t=T`4NupbUAx;pY&Bl{h+XXU44Y& z_&ig&S#OGRaarc+vi0)8eTnaQlv{OptewS$Ia%a0W6cf?QLk?8z^Rj=+r~Pibo@0s z8T!3Q9Em!&I&`$JrQF6^gtMWiZ^aH+t1O(nO}>M!VCEpmDU6J?yW18~M8c-XCtk}u zNM_z6u%Yru`<RbX=hsQP>lm9PTqwsFl-h8xTFiBdRIbs;VoXM=x}}3jM-05N!N;Xb z1NsrVkivM#rQY{@B-q|Wiw1IwtJL$lJ#uuW4Ed;tNoQm*Zl&S=$l^j{30xW7rHWFA zy+>f~2cqs_7prGb%}nQr0EK7e*K20f=b8h)oxtAK(bwFE`mxoBecYS=6MHjb?i+0w z2BSDa0jY<hkV2{E6xJClc@wY-nVSb~f(mzY5F2qS#*CiE&j51|+Ln~W0IDP_10$2s zVn7{qh+oU9av%pLGRB#oHu4k#vdxKTOBAlT(tNS$@2GO64LlDzx^5{md0)lb3>+$E z5Km^ZO1YSX{v6(peDQO_)sl*zg2;mu+l>0Et@;FHTCv(P<bNfbT2-ZU>^_#e3a=z1 zoWFkVt8!#@T(0`?zrG{C9-@w`vJ2FozKV^29jwxbReheJ)Vth*<bR#X=kw0ohz~FQ z$Z<LM)bEyi+`a@wv|jJZ5+B7!JJTGXoRT&^CO{11jx_>Jq885c|4lrlB>w}{nEj2! zonKN!+;}eR5ctaK{cL004^;(t13$9?#t9Pf^BPS?hw3eb1>n@ysaBU{QctwA)^PGD zBx%9<%5FVth97sL!cTbep!F(DpZ2O$?3kW;LKaM1Z||1sz(q>Sn<|+bCs~pqtL&OC zzKf3Q#)Sv3qQn0*zAjtAo*5@MBtkXatc62G(;N+*+dCmQT&s3cG%-8P#!7h>G&y$G zrdq^)&|olkb~<9jt&(ObLHWh}HGa!b{);sNW0pEK5+aK-pQ|v%XeH9S(<m7l=*<8( z=Pl5v9yOtqu!a*e0#z`7CCnDv0l@7QP5QeMAifUdeurr<LAfC*^)r;ph9g9;i8hje z1|KCRyw4;qv@&o&+HlhPR=&PwMQVb!VtHSPS(}byROvJ5Vx!xv^F%1bN9SC=Tb9cW zr6URbGa+f1CNbf;S!Rg^qo2uCTb9A+Rz`Jve2EGYK@Aa4tz<rXWSb3oJ}_N)>U+nK zE?;_P++%hkWWPD`>THCWUFPE*oxF&Yb{{5Mi%5fT6ol@Br1E3C^%0W5E$031Q{T-` zVPn&=f21p#nmcH{{^)eg*0AG^@pcJqp8?khRd*d9CO+@=r!#_rsIr4q?y#HmaE!@= z>7JVjdwEPS9O=k~iBPDt2)t<5<<0?9#W<{M%3Ht+%o1>T+zC*SB{<oDX>HzLv`5;d z%=Luyu58O4zSZ=17ucMhIs~>`aVxK>p0dpeu*R<ZAGO5j6#@dtzn0pUW-NETf@swl zPFAkAg-4GbJj#ri)1xp72wT@>n)~T~tNT-=ELTG5coC+wL|M=eEaRnoyc@umF(^Aq z^4i3#T{<Yxz>7IZD$FPOn{htr-)=Z6+LUT;a=xzeAXZ&0{~EjU%y)Sx^}vkYT)auP zsiyJRkBk<A%sxGKHE|zp>gp`-cD=3l!K~AT;*Cn~(J1T$#*fk^J#n~ym1d4JMiLhF znHfjSOS8%UYl{M}gvx27jDvjx|3&h#j3m{1?QrT;IO@tRElNT{vI>6^#xHY+O*ZRW zO5mhbkDq#2DThTA2UewZnT^5!yrY<IMo_-iUTN6V^hlGce8+h!kMA83mipuiuH3P< zLya1Q7hqq0Qxk{dl$d_M3~BB>SF$2Ly*%2}^_w8x7;me4SJywgdZj0ZpMN@jS*bME zD_nTl<@bX$hCv>=3?^zxMA-ZwnnY!E!(h~M;Fs^uS2REFn>0<5SS}+XqH#6J0OK$b zJhdZ{HRgKS_?>olI*76XB=zdL{En_v7J{&(KfgHu`UAtd#YZ~ct=e-J);{qvTpW>? zRs==jxy#Xs^}d)g!;U89u$c{_W%fG7T8C)TCagFW+cy??`mzbt;~h2}ISRsDWXgw2 zxGNN=FZsFIqxy)C#_m2Nve(mBjdp+3ITvU%Et0=9ZMdIEY;K(}##o(1X(ZWRBj@&+ z2NiLg?EVac(}d)!9UT5So*qHF@Lnnna#`?NxN#qLci89(ITL&AYTZ-W&?)zNycA(@ zYG@W<h>a>Q+1#&VygUnHT^|=4Mcm^L^U0c!kAGn#FM(*j;+(8m*cw#kA3Iqd9>-&P zdBZsD-M`wSzB@6Creyq;2*}S%V!wnlFX)R<2%RzTgF4<4QFsq_rY_0m7ip`We?8k{ zh4=YAUlD*9-Xrn%s;j%6y1y3;`^5FbR3b<rFD{~1{!kG5yx%C6o4alh{)amEc$4+f zXOI>^{5B8^3Gx@mu~<$tCN^!bV9ucK@`i$%n$5}CX$o8J^=_ZLq}Pt;ct>Bb4lZpo zz^CUU_X|6sJj29NG6Ck<o$ZeA#G`4=W=nDd;Tgf{E#WM{=9QXbRNu3(4zXr!8ga)f zRV`Y-E0^Lp3LQN;DdIo`GlC?X{Ors?_4sH|C3PuHm&3>Og}(TarXEF}YKq}5ai6<$ zzK!8hrX4c8GHFqIk#4b2xp}Z{!yRUFFEY;Hp!`t9M3rSuLmky&Wh^Ht<6b2!?R}Bb z(2XX?C)bI_RpX@IKB74keY<lF8)_uhoLdPKOA^Gt><wVdT^ObQTT`YYVi^bjm2}fM zZvB06U%+)^qTr;!Gwy*VIOZ;>+tZp((;&N>3F9Y$t*Uoh|DEQEvAyN5%*wpL{LcKf zf;I;wSvnP(EHrEBu8hab9S6J%?I}|dE;IR*=A`+`gThR@%3;i!e7^O_JK+wu!S!tV zLql_>l>OcJk2#_+h$4wF37M%)&u}_(liZ|o<*p|}w^j+Z`bSBvd@>r6(t)Gj3Rj;; zjd+@03$%`zF}dbbn5(kIA(2=qx636tbyO2KSm|fAd#moIhZ{v$CXX@}VUR8G{Uz%) ztbOPAW;x7F&%-ti>y0aqgku=4yf&DtXW4dWrWGoG(k@fQOH&(7KOepNamDV{?Tiqp zDs{SF)6WB+#`>G9e4Hx`uJ<osT}|-RcpHsoJ6dBz>uu9n&s!S{mLi=s=sw>=Z^rO3 z7w8`7J1fcgKjO1#WPQ3H^FQ|kY=Dc_N_UarQ=(drElkii{O8bwz4CLF@7>?I^I6tz zoxA`G+}g&B$c4Rx8j5~Ieq_Cqzs!9L`n3J(>*GG%y*i5)Rj#_$1$4V-n3tT3-Xwf9 zuleqfr;)dYqkUXIciu?f)EKevg8+j~2Br+!MRZlT=D6kUf-a<QxHp*wDq1S?`LXD+ zMg{=z3UXWb`~zv(*$ykcNV~%+0W0EgDjN$%wlv{M1Qz=+CVSY(=|wfMf+JQ_c|~`j z(E)ZuHQoBuXRyVm;sfC#&-tMG2n`r#dOYG3$f>)D7-l7EyJFM^=?JJI>p&}VO>zHB za)Vf<cqU;&sZpj@BB|m!>=4u`_rRFLfhn~ZyG4?7RBYS33Y$gVU1Mt!8I>P52XI$Q zD^fdFjTHuHxVCQV_WKi$RqJER$X|aQtXEpIw{dG&IU-5yx8_jrY|)^O(4l0eU&!$1 z_R5ltc}%Qyaju?Um)<aD>cm~DTtCCgRd{3iA2>3GtlSG~_mqa)yNRF9a{z0-9EjcS zu~9sR2^4_|rA{CFx*k0NhWhw9B=#^koU5GFIMQ6jDw*RI<<<ix#P(RQl6I$*tDxj7 zxJc3ir|}Pwddw856RJqdU8KaA6#-WrW2$h8pbumdwGVzf5G>FT!Q8>_<ecQLIC${c zkS76spvl8Y!yhJ{gQl6XN6>dps%a@|>9n%xF4)evE@_iCcXWrcrFxSYCYn~7)~Hs2 zCsSq&dQlcVCf31~c5pHUK^FD3KJUaELqP0&ld<eWv6-q?FDiUX<8JDn!n_HKe{=_z z>~zLB2K@uK0TBb0OnCNtkpaImr}5ryiMXe@h^JQc!AwB}O#LH3>8EVyr`+kQuC!i4 zj8GD3?8!}xt4Bp2(<j6z)YtM3S`RkWjUkx>DuxKNCR&Wke9(fbn;)zD{~DVvpZ3?? z<NVRksrXAUEHET48JBdb0LpyB_yKN#y(M@*GJ8h@CpZ{=uE>CQ^88fCV6*YqyzRVo zN_Cdw^?vx`65E)Ki5O>P;F&$cwni|jQFEQtyyf*G`j@LeHYGZLN?!ixJf@miVHTw} zQT-2X8~+r)pX7<~p6gLy4<5H9#~S1O3X&OYSHWBT9_Us{v}{%hug{a$m{SA~R^&^X z;GY|o3(}{;m*nhA;D(r@U=__^E(QcGi%hFD!xyF3b+72mTIvBZHR*78r)Y5%;H2mo zkrU51VpZ;<3W09#?+*;ZjYTW~V}Fn-Fk}&~3KWs3a(Ih`aQ86#s^Tmq9z|$sg6Ont z1+t+d>LnFwj%Rg&KPrnc)EUs%WbOCFAa?_}ErfK<XN|@H8(2r1nxk>gTKTo4t(f}z z0tW>QY@f(DaITPXM1rS%Jn1UVX*(goN5|}&gT$zdLi1&MN)Anyu^_SCK>i^%nL&H$ zStUjnodw!TG3ogbcq`62^GrXB5YFjxoV>4d(DE<Og?ASli<S~sQ;*}9Z?50mJJArd z9_&PI2{Z*k;SRxnbC_Ux%YCo88sF^<2sh5cPI&B>^q%0QE9WJ&V2=m|^h29_Kk2-c zzMoC4ev|l>fv9-6((vA18Us&HP0wfcW#(Ol?zuI=_Sp|CD}n<8JS@|kQ(S^n8{PdJ zd^M^{Kvt1yCrnG&<nB$R6Gux*3%E;9llsaZIT7S~5K-*MIn%VnLN*W0OqV2o5U*y? z1^*CuRsn91o=F_pb^k}?=D-yQdfTEPZ0E>Z43)&Z+!|UxM{XY^As&)h=10%Cv|qna z0QI%{)Vh%L3yJ0yB7S#%@eIRvu;X)HmExVnz=Bz?d_8a4^XR~vd!E_DubpR5iUfNs zJ9}6mymrp!CD0S2&J9`$lJV#lk~9jiu4UTxCM&d(<>25Fl0TisT0Kep_C;ImNwm7Y zk1Fu#0y2rGx<As?MiD6@CoN5FtONvy)81seoEBJ&>W*qLPzIR(O0_w8F5hIKtaiE3 zx4tFkIp%<U+WLZ8&vnA%&+%pNu_9$uFE5Q_KGEIqnR$=gMv7cD1L{dVt@&q%M{u4X z$Lk(2daYow!Xkz;5#l~Pq%<=J(g1ci+`IW99=w^U`1K|R;e<2oP&wjP=NsLT)1`)f zdMhS`U7QIzy6gUPurmB-Cqt%uZc^EKlx$ls+-#<R;=@SYTYdaxeGG3#;EQ+6R^`vs zrRJFPAT3VG$nB`XDQ65$_D24IdLnu!3PXysm4;IPE7p|yJt(2-FdlSe=8<^(3!Oo| zX%OW)<2bA^^eS~B1In0jJCMzi23d81Ci_tL^fGhRGMWl@+m+qxtp<brzZIWkd*ui} zQ1F*q0KZmhBrm#|5iC<{Z$fC~Zi4}ls9nON<9>p-?XCf%g^n0$#8Yv_79~a_kH(m3 z`BV9Jt!34BDD#X*My-piOMt28sPR=6EhR=u-cmiI-oyM#5r?hyt__-q;I!Za6_=b; zp4vBFH<QbWOL!4!5iuB<eQ$Jr<y`mm`0}Jnvl9%v5l>1LLC!{FP<zydUj59?rn$4d zaC)Q~?D9JhlY%v69IA_ss4pjXr{^_S;H~Kx*Uk_=;hO<I_-yhRwLon_3Uqtx^PSb@ zgxMkdswh62?4c&c7Iu?XQjEoh+K@?$)W5C`Y(Vt8N4t((N3_jxQ2XFqEdLpIPiqJ( z(W~+7OlIW`QNJXGcmf8U8l%QbC#PKplZP#u!PO>Lb&8_SOfnc$8hBI6j+T$_4B7xn z7W29F!H3l85m)L>psu`)A$A>g#7<~tWg&D2H?6{cO|m@=4JEQ=r~7xvVid|=gU*Vh zGY3o6Pa31)18*hO?|T<-RbCE{Z<;=L2OqLJ-KQtkOI4n2ivsit%$5?}1J;GR0dG(% zuDbJxcinsqw_JJGG1hNe?gtC1seh|Lk(Rq5E=XOjfH}ew#}$W~pI!!Nh#$XC(syFS ziM8@}F~{hzmCO7R>%o{Al}VV*#CM!Lxn!@;rZEDrioxB?bs39;nzJBr5ZG<lcM+~d z6(l%S_)Y35cd?FFx&`DMu+)f<Y|cVZoS8M{^c=RL=uRB$)VIG-Uz%~u18t1ICX=m~ zYyiSM%6WEV+C(^<ZmXPc>78z;TtpktIl(oZ(mzUh!OkfQMlholETxWi@vG<t?uqg4 zN>o!C+<@e&@F{bgBc0(1&MvH4=%R{Lx08pa0LH$5FXe;SAt@=GrDfH~%JFI?4rvW$ z3xa`~`r083&RG@9*xBsbwJp>Rv>96eI?-`7RJ!IBAN&C~w5{%O>sfMH5crgPV|^WI zJ0oAW_j;2o{jpg8l?M8z43x%;{v_XJO^n53ork4pDJxKjiR=BF0D6X(IEUzl59>k= zSS+ky0$JBaHe>GMWHS5l-bA*&sZruZSbCe%K9LJWT2GO!1Od+sU*`_U`#zU}<O{3c zq+dm-Z^nevg3~&-7m1aYi50u4Fh3eQ9>Q?O_pzN;p_dT4BE;ib!<LZngmjj26!}M} z=0K%^pcQINm_mlDMj>JFKtC3Wi1i;a^~thJOt+^9QI+lyNEjwQxzLj!=1mzALvZ_s zPeF1umEP%MebwOxFZ8Yq(bE*VsE_r*=GB_+rRIi)P#*!nu}2El$0t<6Tn5r>?Ivl# zZt|4ZO3_zCWB*j=LQzP=U18Q+fh2+1+6uRfy)%NXY4_(l@8(n;|7yLe?L`{q?9_>f zoboc@*Y|%-<g+*-b{b@$PLq$*0peMqb;gDM<Te$g^xvz-vnC+({xsYRT92*ll+JUR zy5X=KoT7&^BJNJ%G^t`tIz?@ug25{7o>yNwuv(pM9Oso7s;R=WVvMG*#JL~^G_k*o zAFha;3ae5l4f&OBe;X)xq`Q8uCL}#R&u5?C2b&RIj7oUCo5yf<y4#3*RG7!mUDy{6 z%aL8wmhf4u?)@$|_>XBQP9Qu}!dEK(g+pLmmnbr@w`<=omMtsPCU7<tTq;HYgWI7_ z6hIj02ofIw@68<7=bdA~i$|0}@_j^j_vX1WZjxGFgg@x^_5@WoVPBDwLck=J+c=Pa zj6%VLKW?`?&cc1PYaS5(ci6FGJ@wI{^5S6PG|Fm<SiQr)LJIu*O3R+4st&HMrD%GC z%`xO`CM24irks?d^E_d|1_0H-IUEdmVMIRc7v>a10M#FdWra#&gm|E!5!sUnT8oiz z@Ak{ekcom7D5M)<v8Ny)WKG^Tq<p?5sWdv6THn#G1hZ}o#Rbb+j6H1xH9zXVC@42o z^=d*h{V>zY=f@tDxhi|dGIVo)X5{%HU!p!q>>NSE=UI61nWM-)@=EWmGue;Gb~_t{ zo0~P9CMJ=c*}c2D4R6c)em-^m_m`yiiUN=2embzh{pIYu$=}P3uJ%sfPm7%fU>AW+ zZw70fR)=#?m!|HNI%<9)afgH-_8~H{c=zT-$}hst>-HL)XR*Rfd<h}6E>VJ)iU=Mt zZV5U}g)SOdHar^W!Va2|=Ue4cER}3@_MSz2BV?po<8;sr&D2us*^KrceTlUcQd&`| zx31BD!$6=NYrB|hJ29s4$PFzHZmcpHW>Z^64TzsjaW0H4%0pK$Hs^KP2HF=guAAIM zs((sq+e~rTObOXc@!3>eO*NHTSd=_dNW<)blLxJi79uIGR?^vY&j@2*?3l_ir<fRJ z?wE+C1g1Lw4zm4AIGo9AYuZA#p!XNoxPXvKUb)T$Ib$~40IMmh1I;?RD3eKHd_Akh zV530QYvy`HZE#tkMNfE7(cpgbVnI?gNo#BKw{5G#>~{O6#n$j5=%09s0g!Vy*yoYY z4SMg?`MfOB#8h@3<eW)Q{%pOJDE>P;^FZ%9rIH+y$@#PKMBqo@Oa4uZq9L8Vsz;dx zX|_n!(~F18n@y26k^j}lna4x9zJI)ud_zRXR>>nYCClt%EKP{)4kr83ScaJx8e149 zWmhTL3E7wE*jr>dw(PQRkz^@EXtDpE>3mPyIlu4y*E83BU)OzK*K=Q=KjwM8-a#fU z$2X3DmRRhox4Z2Y(;5)^B($-$jV*BK$xve;F&zT5h>MHcC)gytjRqhHKyVaj>wLX( zL!v{LIvw|<ZNNwWbi&(A())>$%hLn}u}k6mM|*fHrq8!{H}ADo;h6X><+1=y3;28{ zY&dMnxITup%Xw<v8g*$yF)4AS*3quVK+Z^lrpU7=N$!jFSq-|2Q?p;1KY!D^IP0{U zST22>{HvLZsTC=bd>MY!uJ}VynMLa*MeFN@n|HQ(_L%V<mMue5;!+bY#@$aO98~ue z+!^@&s0MDqR@e$NE5DWFz1cthOm}o(YpXn65b_!M@M)&k%&>cVuOjPv;};+FHfpQK z)u<s;1(@;m$;B}mQ~VXl;>P^vJmNHdp$qdsnA)G#@iO=%z*1O>PwaXgX7&JYF!_(G zsgM)poq+#~E~k>@*%c*@*Qs6>epl~riB8TGtld!g;AFla>dg(I@c9-9ta4`;S|Vl_ z>Nq!}6zS%<xRM+9a2Jm^bTqp><%k*>FnNfpi5ZM3ViHI&>^rdnZ)qPs&(fY>Ilru^ z9K>EZ8ZV+MWFl)_W58T@`bbP!^>wF0t>l9qfSvdLoKs~}98O&;m*-=M<JU?d37^h& zCvTQQl0Lo3RCr9%4e@<Pro_)+SN8Es!SGYM9f|iUT3muBrZ*viojlJZjM<M>5A@_` z&00iAh3ll@PYJ6o;<xa1_(^;Zeg)r3+oILcCTTsi6`H1IX=aV#!FK8Ai(?piSWyS^ zv~2#!^3w8V)}|G|5PZCEVJz*bj{=3(xM^HF`O-s`&gbr_X?E0$q${(~tm~scemWDU zs6mg4+SUq<#|t$sV5baLnYl{zR)f=|Ui2bHI(bhl(^P0C14eI;OeRi`yq9}(*Rd+q zxo9y*mmU0&o3l9Ze7Dp#4pQXWrW0c!d|8Qy8uvTEdf$=xa(0hJqY*bxzNxBOXYLSN z3CW{IEPS|1JmoYkCYh7Q>j_13Gbv~*;y%o5yykE=+3)j=)9TJL`FZ_l6aCT@TM@=r z_)|>8B4zzcf{<oI)wrfK6L#$*<&0jdbp16C<`z!ANF_svPc?MQrch}imWg|X)~s&) z+{4l2RG{D3wb5&T76(O^bDNrU<CQcA(;UuRK^X+C_^64TnbP{RI>c4;c9~@TV`!A~ zr0I*s5enOfvV&+h6_Z1bhE?2Y1z}5dSIR5X&&GW`S34kA7GzTG)*G)g49R-sIyAf5 z?<7$kRbcA@r|A^?-s0aNwdhw}Zk_);sUb&qFssPDXLfYpHM0WsPg#3C{Dt{2Wv&c) z&68~GFW*R{&H3M+tiM=@N@DgRgqw`Nj8l2S!+%3xtL<89WV>n&zuaKzZcIF@&87Ya z{yOD{6Cd}bM9eNLU%lp_s0)60a>gyN?1~rUD^IS|y|eT&v4uENFi>DR1X&I@S`<P& zqEFmARJil;Hxz-AR6(v9u}E@X+4g~S`g~khUc3{&hb^YP@X5*fn^ESK#MPxsPnc~) z3KJ&<aqhHg7hr_YCM{S=eitPfd!~~pxz_xUbpO5I((PBrpxIe$SvoWEvfcKhsEDXf z`~r!=>2~`X#SV4#ys5*M4q1!tdBmgKMvNNWU2AKw3`7{~%N-d`-iI?KMZ~G=_ZuC_ zJMu=1dbiSqcQ;FI0ab}|m$Nmpt$#Z1a7@{PMc%!V8MBZCyV&I=6_<AN?x2B3p`4}q z<fy(IEMC(0I4!$9n|S!zmOG9b>0}w2-%81SB`37NBYwDvOv+uBO<d0`R+S%q&tm3w zVI$Y^eqXcqZSn0pJP2Oim$a1*i>F76DA))`y?yO|Q5~adZvq7?oE>)>a*ltZn~nuM zS?Avv?|rWAQ`1?#&t;#MGKXjU&7HjV_Y&b)wW_fR^r7I90H(OG)$976!?)bC3%ETS zZ@HhZJVta`e~g-&@OaSF!6%P^$jQ@|mTe`BBM@b)@uplWOK%V0b{q(zn%-)O!B<o_ zOqzJ1lYR7I-PkV`>xZ^8tZa`wbbNNpGjk}(G^dq4W4_|Ha2=rzcHKdZGb5<&Y=UVz zVmTUXC?bSAFzSt3VusZ@SjC23A@|KS5%bVF%aR|$mU|<1{~ouMW?eQYf$!!xpO!10 zP*Rg-RB}}yFEH>@YO=Z&Svr5$MhRX;&n9g!b}g{w0=;Up2o{#xkYS0Xj0!TD9U}`A zIcTM*dK+zYjQDU0VF*3mrrTRwpCYvP%v#nTE1Sz&*xiL*@9r0{YHTAm6)a5-W+Zef zxeqJx;_d)$rf1gl<tDORwjk=cB3(1xC2Ow3iyX+04!V$8#Qx$a)Bk)i-N176dEEIz za8TFUBF+S_xTfmh_LOJSOY=HK9dMRVvbTb7Lc)l)S8l+ew_;U?hSTmG?v6C@o~T<r zJ{Bz3DRf|v$)jAfv%yd<xSp$|x{hvtf{c53(I-o|B&Mt1y3eq0_;o{DcB9u|4(r}F zhhtpL#J%Ya60j_Ien6P29xAJ!9eLn>u@#3PeoP^!vd-^JTVZeLy_GN-w#QxStPSr~ zy+gg@y@SR7SiED*b}Q59-K|Uvpxbnyb`&ml+oA?J{`O0F?Iv6ge&ncrOs)MuD4=oT zdUIaI=~oAnsAp5`T2wU^j`O{<>4(pXaCUK)8t$*ld?c@7WpkK(q|nm3x(z*6bv8LO z#im;IiJ@iTxX41;LOET;*Ljwx|5Op9>0g#uP-8hi&$%^kH0S(c95Q;zFHBxL1+73D zV}6?XtbaUwQDad+Cj{b{GSaCw!@m1&y{Y4Lj@oc7-08-YYj!WEUdau^L<g1&(sLrk zlGbJCcReV+C_EjKiCUvn3f;xC-8|^07Hu5TBP^gz?&UXtgiYQw=nH%&?tYz&znQix zz&*GhHluElvOd)$uxUe>l6hJN<KHQMt)rN&d^y^EU%Fnzydxf8&SO<#z~X@vle%R^ zC^b=jm{WX4G~dHV3nJ$(I~2Bcw8;1#Z}^3Xm4|V9i}eGF=ZgwniEdiaq8HM}vJwh- z$K>V6m}izTr^F&(Hg=>OzGu(FMrgX2C~++_TAl|J)X3j#JxLZpm3OOOuH%oEFf_`? z+X>%I6FEQ&)bAopTfBKNInOgH`{Aanfz_hQ+@77yDvwoq&|P~Xhr@HqLPh$fJ(qZs zd%AaHzeQ(6=gzFZ`Qwhfqqhq*m|fdC&9#rq+%Iq7h6m49y@8dUjqpc#J!1AAQaahx z^hH|zOYVBYxuLyLGQP`89PwK<dqt>_UQvxT|AW5EgB;k5hG7GimCt`%^GJ6JvP(Z9 zGb-`fS`4jBe`1t>%HYbh0w-Np``8GHJyiG=_*c}2$Q_@A{x@<@7p8s>;9#Z&r_0A| z!xG9PTk4%jzTSCpTn#Btsp`3PJryZTT>b%pd;ELdDoa1dYZR@<E10dDm-_geZ`JH{ z+X?4)qAxvK-}kI|s|v%}eYy&#WVEn8(Y3>bH8QOJC-n&Xo!|2p^@zbHI%nr<LnXPm zIaB{)73mP1Kpv9f*Kt~rN+LLF<G~q6g1sw%WR+HQwE<B?02Ydpf@2U!(8377FiFB; zFexky2?cpT25xpvh5!;JC4-egB4j`i5y3^xj%4rP24G;I6%Iv!P$G2@Hbk;fq%g2x zAlis=8l|sHBuO+HDTP8oF*1N80*;izKrv7R0Ea@M01OK5EQ7)#WI$RH!vjPQ*}(w@ znuvj0`Y#U&#-J+w1Vmyi{mbEd!T_TnAY@62;A+R1|7Y3;?$&N!c}mLa=NKy(WrKs+ zsGq|-pI1MpO>hAbP_|(9BsZ_GW&<x*Fm-hb(V4+~(zmlGf#4}G;G`nRII{!cXu4F8 zdq$$z11JA>1C0jmE-sFC415{@Wx%0~7*r@T42qEi6o6weQZfjzVio`l4VWPi2q`F7 z8Q4z~SO6{qpipQj7z&O+0&pw{d;0E&0R7Ntv=kN#hcR-70vI@43WmmD83ZUa3<_=l zzNjDV79d3JSEnDAS^#Jn0FC`&OAL5q6!?O^AL9Gdx1;>~><?!F)BW~K=3A!UBYl5j zWbwTgjGg~&mme~<058lrqpubI`<DCwwEJHKD+C<#BfrWb@+CO{dN6R&H_V(O&3m)j z;viwJ_>~GhXMyQa^P>a3{AoTDBbezyNo49$ds}moEq8x=?h7(;B2iF96EPPZ6O;9U z$Xvu`5zTR>ErWYSbz3^9WAFV#6|Kmgu310!mdK)|mv-4hQjVTclYz&sE%j*>Ygi4v zFQEulU~H;LfI`X19Ptymo^lGF)0sy?0xip(Gg|Y#hUD6<8k(#_8j`W2A$r}mvWn$B zMyxH*3&Nu9EAp<EZpO}V*~VJZ-1}o8h4Nvqb4){VARVKN3x6fdK=%|2PwH=OyQWeh zUFq3-1>tULsjaE4UH4B`4z*v{p8Mc;;@BFzW8&jp&%xLk=wrcD)un(}wJbgDSSps| zb}~U!^^c$wc13e#F6Z)#w$D1;=Rcc-CYDjOww=DLJ-Dlv@V>L@2Ju3{IH&DQZplB@ ziE;h@rDeahAfxv{mH7XgRfPgV02#gYttBPZ{+1_O0;m*V1A`E>uiBswUIx7L1qzA5 z4Fq6}i|~VdT%p<#SwXFVvx1WI-vOXeC<F>10^e*fkkQ5%0Ogwv12*2THW(BXr=Qlr zp(vSuS%>^*JS+li(4TyvP_T)AwZWi>U*o~BNa(M=Ff1AgX8Kb+I24Bc!RAIKfO<-0 vARY}!-e6@wwbOTY24$O3AyBdz%Jz4)T-^v%x39H=Losj}RxvRZUDf{sKHj;P literal 0 HcmV?d00001 diff --git a/src/documents/tests/test_consumer.py b/src/documents/tests/test_consumer.py index af54255e0..fed621e01 100644 --- a/src/documents/tests/test_consumer.py +++ b/src/documents/tests/test_consumer.py @@ -1,3 +1,4 @@ +import datetime import os import re import shutil @@ -5,6 +6,8 @@ import tempfile from unittest import mock from unittest.mock import MagicMock +from dateutil import tz + try: import zoneinfo except ImportError: @@ -502,7 +505,7 @@ class TestConsumer(DirectoriesMixin, TestCase): self.assertRaisesMessage( ConsumerError, - "sample.pdf: The following error occured while consuming sample.pdf: NO.", + "sample.pdf: The following error occurred while consuming sample.pdf: NO.", self.consumer.try_consume_file, filename, ) @@ -654,6 +657,127 @@ class TestConsumer(DirectoriesMixin, TestCase): sanity_check() +@mock.patch("documents.consumer.magic.from_file", fake_magic_from_file) +class TestConsumerCreatedDate(DirectoriesMixin, TestCase): + def setUp(self): + super(TestConsumerCreatedDate, self).setUp() + + # this prevents websocket message reports during testing. + patcher = mock.patch("documents.consumer.Consumer._send_progress") + self._send_progress = patcher.start() + self.addCleanup(patcher.stop) + + self.consumer = Consumer() + + def test_consume_date_from_content(self): + """ + GIVEN: + - File content with date in DMY (default) format + + THEN: + - Should parse the date from the file content + """ + src = os.path.join( + os.path.dirname(__file__), + "samples", + "documents", + "originals", + "0000005.pdf", + ) + dst = os.path.join(self.dirs.scratch_dir, "sample.pdf") + shutil.copy(src, dst) + + document = self.consumer.try_consume_file(dst) + + self.assertEqual( + document.created, + datetime.datetime(1996, 2, 20, tzinfo=tz.gettz(settings.TIME_ZONE)), + ) + + @override_settings(FILENAME_DATE_ORDER="YMD") + def test_consume_date_from_filename(self): + """ + GIVEN: + - File content with date in DMY (default) format + - Filename with date in YMD format + + THEN: + - Should parse the date from the filename + """ + src = os.path.join( + os.path.dirname(__file__), + "samples", + "documents", + "originals", + "0000005.pdf", + ) + dst = os.path.join(self.dirs.scratch_dir, "Scan - 2022-02-01.pdf") + shutil.copy(src, dst) + + document = self.consumer.try_consume_file(dst) + + self.assertEqual( + document.created, + datetime.datetime(2022, 2, 1, tzinfo=tz.gettz(settings.TIME_ZONE)), + ) + + def test_consume_date_filename_date_use_content(self): + """ + GIVEN: + - File content with date in DMY (default) format + - Filename date parsing disabled + - Filename with date in YMD format + + THEN: + - Should parse the date from the content + """ + src = os.path.join( + os.path.dirname(__file__), + "samples", + "documents", + "originals", + "0000005.pdf", + ) + dst = os.path.join(self.dirs.scratch_dir, "Scan - 2022-02-01.pdf") + shutil.copy(src, dst) + + document = self.consumer.try_consume_file(dst) + + self.assertEqual( + document.created, + datetime.datetime(1996, 2, 20, tzinfo=tz.gettz(settings.TIME_ZONE)), + ) + + @override_settings( + IGNORE_DATES=(datetime.date(2010, 12, 13), datetime.date(2011, 11, 12)), + ) + def test_consume_date_use_content_with_ignore(self): + """ + GIVEN: + - File content with dates in DMY (default) format + - File content includes ignored dates + + THEN: + - Should parse the date from the filename + """ + src = os.path.join( + os.path.dirname(__file__), + "samples", + "documents", + "originals", + "0000006.pdf", + ) + dst = os.path.join(self.dirs.scratch_dir, "0000006.pdf") + shutil.copy(src, dst) + + document = self.consumer.try_consume_file(dst) + + self.assertEqual( + document.created, + datetime.datetime(1997, 2, 20, tzinfo=tz.gettz(settings.TIME_ZONE)), + ) + + class PreConsumeTestCase(TestCase): @mock.patch("documents.consumer.Popen") @override_settings(PRE_CONSUME_SCRIPT=None) diff --git a/src/documents/tests/test_date_parsing.py b/src/documents/tests/test_date_parsing.py index 06ad9876c..a79aecc94 100644 --- a/src/documents/tests/test_date_parsing.py +++ b/src/documents/tests/test_date_parsing.py @@ -8,6 +8,7 @@ from django.conf import settings from django.test import override_settings from django.test import TestCase from documents.parsers import parse_date +from paperless.settings import DATE_ORDER class TestDate(TestCase): @@ -160,19 +161,112 @@ class TestDate(TestCase): def test_crazy_date_with_spaces(self, *args): self.assertIsNone(parse_date("", "20 408000l 2475")) + @override_settings(FILENAME_DATE_ORDER="YMD") + def test_filename_date_parse_valid_ymd(self, *args): + """ + GIVEN: + - Date parsing from the filename is enabled + - Filename date format is with Year Month Day (YMD) + - Filename contains date matching the format + + THEN: + - Should parse the date from the filename + """ + self.assertEqual( + parse_date("/tmp/Scan-2022-04-01.pdf", "No date in here"), + datetime.datetime(2022, 4, 1, 0, 0, tzinfo=tz.gettz(settings.TIME_ZONE)), + ) + + @override_settings(FILENAME_DATE_ORDER="DMY") + def test_filename_date_parse_valid_dmy(self, *args): + """ + GIVEN: + - Date parsing from the filename is enabled + - Filename date format is with Day Month Year (DMY) + - Filename contains date matching the format + + THEN: + - Should parse the date from the filename + """ + self.assertEqual( + parse_date("/tmp/Scan-10.01.2021.pdf", "No date in here"), + datetime.datetime(2021, 1, 10, 0, 0, tzinfo=tz.gettz(settings.TIME_ZONE)), + ) + @override_settings(FILENAME_DATE_ORDER="YMD") def test_filename_date_parse_invalid(self, *args): + """ + GIVEN: + - Date parsing from the filename is enabled + - Filename includes no date + - File content includes no date + + THEN: + - No date is parsed + """ self.assertIsNone( parse_date("/tmp/20 408000l 2475 - test.pdf", "No date in here"), ) + @override_settings( + FILENAME_DATE_ORDER="YMD", + IGNORE_DATES=(datetime.date(2022, 4, 1),), + ) + def test_filename_date_ignored_use_content(self, *args): + """ + GIVEN: + - Date parsing from the filename is enabled + - Filename date format is with Day Month Year (YMD) + - Date order is Day Month Year (DMY, the default) + - Filename contains date matching the format + - Filename date is an ignored date + - File content includes a date + + THEN: + - Should parse the date from the content not filename + """ + self.assertEqual( + parse_date("/tmp/Scan-2022-04-01.pdf", "The matching date is 24.03.2022"), + datetime.datetime(2022, 3, 24, 0, 0, tzinfo=tz.gettz(settings.TIME_ZONE)), + ) + @override_settings( IGNORE_DATES=(datetime.date(2019, 11, 3), datetime.date(2020, 1, 17)), ) - def test_ignored_dates(self, *args): + def test_ignored_dates_default_order(self, *args): + """ + GIVEN: + - Ignore dates have been set + - File content includes ignored dates + - File content includes 1 non-ignored date + + THEN: + - Should parse the date non-ignored date from content + """ text = "lorem ipsum 110319, 20200117 and lorem 13.02.2018 lorem " "ipsum" - date = parse_date("", text) self.assertEqual( - date, + parse_date("", text), + datetime.datetime(2018, 2, 13, 0, 0, tzinfo=tz.gettz(settings.TIME_ZONE)), + ) + + @override_settings( + IGNORE_DATES=(datetime.date(2019, 11, 3), datetime.date(2020, 1, 17)), + DATE_ORDER="YMD", + ) + def test_ignored_dates_order_ymd(self, *args): + """ + GIVEN: + - Ignore dates have been set + - Date order is Year Month Date (YMD) + - File content includes ignored dates + - File content includes 1 non-ignored date + + THEN: + - Should parse the date non-ignored date from content + """ + text = "lorem ipsum 190311, 20200117 and lorem 13.02.2018 lorem " "ipsum" + + self.assertEqual( + parse_date("", text), datetime.datetime(2018, 2, 13, 0, 0, tzinfo=tz.gettz(settings.TIME_ZONE)), ) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 5274c356a..c58a45945 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -1,9 +1,11 @@ +import datetime import json import math import multiprocessing import os import re from typing import Final +from typing import Set from urllib.parse import urlparse from concurrent_log_handler.queue import setup_logging_queues @@ -604,15 +606,22 @@ if PAPERLESS_TIKA_ENABLED: INSTALLED_APPS.append("paperless_tika.apps.PaperlessTikaConfig") # List dates that should be ignored when trying to parse date from document text -IGNORE_DATES = set() +IGNORE_DATES: Set[datetime.date] = set() -if os.getenv("PAPERLESS_IGNORE_DATES", ""): + +def _parse_ignore_dates(env_ignore: str) -> Set[datetime.datetime]: import dateparser - for s in os.getenv("PAPERLESS_IGNORE_DATES", "").split(","): + ignored_dates = set() + for s in env_ignore.split(","): d = dateparser.parse(s) if d: - IGNORE_DATES.add(d.date()) + ignored_dates.add(d.date()) + return ignored_dates + + +if os.getenv("PAPERLESS_IGNORE_DATES") is not None: + IGNORE_DATES = _parse_ignore_dates(os.getenv("PAPERLESS_IGNORE_DATES")) ENABLE_UPDATE_CHECK = os.getenv("PAPERLESS_ENABLE_UPDATE_CHECK", "default") if ENABLE_UPDATE_CHECK != "default": diff --git a/src/paperless/tests/test_settings.py b/src/paperless/tests/test_settings.py new file mode 100644 index 000000000..cedcb0509 --- /dev/null +++ b/src/paperless/tests/test_settings.py @@ -0,0 +1,45 @@ +import datetime +from unittest import TestCase + +from paperless.settings import _parse_ignore_dates + + +class TestIgnoreDateParsing(TestCase): + """ + Tests the parsing of the PAPERLESS_IGNORE_DATES setting value + """ + + def test_no_ignore_dates_set(self): + """ + GIVEN: + - No ignore dates are set + THEN: + - No ignore dates are parsed + """ + self.assertSetEqual(_parse_ignore_dates(""), set()) + + def test_single_ignore_dates_set(self): + """ + GIVEN: + - Ignore dates are set per certain inputs + THEN: + - All ignore dates are parsed + """ + test_cases = [ + ("1985-05-01", [datetime.date(1985, 5, 1)]), + ( + "1985-05-01,1991-12-05", + [datetime.date(1985, 5, 1), datetime.date(1991, 12, 5)], + ), + ("2010-12-13", [datetime.date(2010, 12, 13)]), + ] + for env_str, expected_dates in test_cases: + expected_date_set = set() + + for expected_date in expected_dates: + expected_date_set.add(expected_date) + + self.assertSetEqual( + _parse_ignore_dates(env_str), + expected_date_set, + )