From 57df4abad5af35d86793527ba7f1a898cc19147a Mon Sep 17 00:00:00 2001 From: Bryce Willey Date: Thu, 3 Nov 2022 11:03:19 -0400 Subject: [PATCH] Added a max size feature to the ExhibitDocument (#604) * Added a max size feature to the ExhibitDocument Also give suggested remaining upload size in human readable file sizes in errors and in between upload screens. --- docassemble/AssemblyLine/al_document.py | 13 ++++ .../data/questions/ql_baseline.yml | 65 ++++++++++++++----- .../data/questions/test_alexhibit_maxsize.yml | 23 +++++++ 3 files changed, 84 insertions(+), 17 deletions(-) create mode 100644 docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml diff --git a/docassemble/AssemblyLine/al_document.py b/docassemble/AssemblyLine/al_document.py index cf71f24f..715aa132 100644 --- a/docassemble/AssemblyLine/al_document.py +++ b/docassemble/AssemblyLine/al_document.py @@ -1598,6 +1598,7 @@ def __str__(self): class ALExhibitList(DAList): """ Attributes: + maximum_size (int): the maximum size in bytes that the whole document is allowed to be auto_label (bool): Set to True if you want exhibits to be automatically numbered for purposes of cover page and table of contents. Defaults to True. auto_labeler (Callable): (optional) a function or lambda to transform the index for each exhibit to a label. @@ -1642,6 +1643,9 @@ def as_pdf( Returns: A DAfile containing the rendered exhibit list as a single file. """ + if self.include_exhibit_cover_pages: + for exhibit in self: + exhibit.cover_page if self.include_table_of_contents and toc_pages != 1: self._update_page_numbers(toc_guess_pages=toc_pages) return pdf_concatenate( @@ -1657,6 +1661,13 @@ def as_pdf( pdfa=pdfa, ) + def size_in_bytes(self): + """Gets the total size in bytes of each of the exhibit documents.""" + full_size = 0 + for exhibit in self.complete_elements(): + full_size += sum((a_page.size_in_bytes() for a_page in exhibit.pages)) + return full_size + def _update_labels(self, auto_labeler: Callable = None) -> None: """ Private method to refresh labels on all exhibits. @@ -1763,6 +1774,8 @@ def init(self, *pargs, **kwargs): else: self.include_exhibit_cover_pages = True self.exhibits.include_exhibit_cover_pages = True + if hasattr(self, "maximum_size"): + self.exhibits.maximum_size = self.maximum_size if hasattr(self, "include_table_of_contents"): self.exhibits.include_table_of_contents = self.include_table_of_contents else: diff --git a/docassemble/AssemblyLine/data/questions/ql_baseline.yml b/docassemble/AssemblyLine/data/questions/ql_baseline.yml index 202cbc92..cc747148 100644 --- a/docassemble/AssemblyLine/data/questions/ql_baseline.yml +++ b/docassemble/AssemblyLine/data/questions/ql_baseline.yml @@ -16,7 +16,7 @@ objects: - attorneys: ALPeopleList - translators: ALPeopleList - debt_collectors: ALPeopleList - - creditors: ALPeopleList + - creditors: ALPeopleList - spouses: ALPeopleList - parents: ALPeopleList - decedents: ALPeopleList.using(ask_number=True, target_number=1) @@ -41,8 +41,8 @@ subquestion: | % endif Step 1. Answer questions that will fill in your form for you. - Step 2. Preview the completed form. - % if form_approved_for_email_filing: + Step 2. Preview the completed form. + % if form_approved_for_email_filing: Step 3. Email the form to the court using this secure website and save copies for yourself for later reference. % elif al_form_type in ['starts_case','existing_case','appeal']: @@ -1555,6 +1555,9 @@ code: | ######################################################### # ALExhibitDocument questions --- +imports: + - humanize +--- generic object: ALExhibitDocument code: | x.enabled = x.exhibits.has_exhibits @@ -1576,22 +1579,26 @@ fields: show if: x.has_exhibits - First document title: x[0].title maxlength: 60 # longer might break TOC - show if: x.has_exhibits + show if: x.has_exhibits - Upload the first document: x[0].pages - show if: x.has_exhibits + show if: x.has_exhibits datatype: files maximum image size: 1024 - image upload type: jpeg + image upload type: jpeg accept: | "image/png, image/jpeg, .doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf,.pdf" validation code: | if x.has_exhibits: - if sum(exhibit.size_in_bytes() for exhibit in x[0].pages) > (15 * 1024 * 1024): + full_size = sum(a_page.size_in_bytes() for a_page in x[0].pages) + if full_size > (15 * 1024 * 1024): validation_error("Upload a file smaller than 15 MB.") + if hasattr(x, 'maximum_size'): + if full_size > x.maximum_size: + validation_error(f"Upload a file smaller than {humanize.naturalsize(x.maximum_size)}") try: pdf_concatenate(x[0].pages) except: - validation_error("Unable to convert this file. Please upload a new one.", field="x[0].pages") + validation_error("Unable to convert this file. Please upload a new one.") x[0].pages.reset_gathered() # docassemble sets this attribute but we want to force gathering additional pages --- generic object: ALExhibitList @@ -1603,23 +1610,29 @@ id: exhibit i question: | Upload the ${ ordinal(i) } document subquestion: | - You will have a chance to upload additional pages for this document later. + You will have a chance to upload additional pages for this document later. fields: - Document title: x[i].title maxlength: 60 # longer might break TOC - - Upload the first exhibit: x[i].pages + - Upload the exhibit: x[i].pages datatype: files maximum image size: 1024 - image upload type: jpeg + image upload type: jpeg accept: | "image/png, image/jpeg, .doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf,.pdf" validation code: | - if sum(exhibit.size_in_bytes() for exhibit in x[i].pages) > (15 * 1024 * 1024): + this_doc_size = sum(a_page.size_in_bytes() for a_page in x[i].pages) + if this_doc_size > (15 * 1024 * 1024): validation_error("Upload a file smaller than 15 MB.") + if hasattr(x, 'maximum_size'): + full_size = x.size_in_bytes() + if full_size > x.maximum_size: + suggested_size = x.maximum_size - (full_size - this_doc_size) + validation_error(f"All exhibits combined must be smaller than {humanize.naturalsize(x.maximum_size)}. Upload a file smaller than {humanize.naturalsize(suggested_size)}.") try: pdf_concatenate(x[i].pages) except: - validation_error("Unable to convert this file. Please upload a new one.", field="x[i].pages") + validation_error("Unable to convert this file. Please upload a new one.") x[i].pages.reset_gathered() # docassemble sets this attribute but we want to force gathering additional pages --- generic object: ALExhibitList @@ -1627,7 +1640,12 @@ id: exhibit i has additional pages question: | Does "**${ x[i] }**" have any additional pages? subquestion: | - You have uploaded ${ x[i].pages.num_pages() } pages so far. + You have uploaded ${ x[i].pages.num_pages() } pages so far. + + % if hasattr(x, 'maximum_size'): + The total size of all exhibits must be less than ${ humanize.naturalsize(x.maximum_size) }. + You can upload ${ humanize.naturalsize(x.maximum_size - x.size_in_bytes() - sum(ap.size_in_bytes() for ap in x[i].pages.complete_elements()))} more. + % endif ${ collapse_template(x[i].in_progress_template )} @@ -1653,16 +1671,24 @@ fields: - Upload a PDF, Word, or image file: x[i].pages[j] datatype: file maximum image size: 1024 - image upload type: jpeg + image upload type: jpeg accept: | "image/png, image/jpeg, .doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf,.pdf" validation code: | - if x[i].pages[j].size_in_bytes() > (15 * 1024 * 1024): + page_size = x[i].pages[j].size_in_bytes() + if page_size > (15 * 1024 * 1024): validation_error("Upload a file smaller than 15 MB.") + if hasattr(x, 'maximum_size'): + # this_doc_size already includes `page_size` + this_doc_size = sum(a_page.size_in_bytes() for a_page in x[i].pages.complete_elements()) + full_size = x.size_in_bytes() + this_doc_size + if full_size > x.maximum_size: + suggested_size = x.maximum_size - (full_size - page_size) + validation_error(f"All exhibits combined must be smaller than {humanize.naturalsize(x.maximum_size)}. Upload a file smaller than {humanize.naturalsize(suggested_size)}.") try: pdf_concatenate(x[i].pages[j]) except: - validation_error("Unable to convert this file. Please upload a new one.", field="x[i].pages[j]") + validation_error("Unable to convert this file. Please upload a new one.") x[i].pages[j] = unpack_dafilelist(x[i].pages[j]) --- @@ -1672,6 +1698,11 @@ question: | You have ${ x.number_gathered() } document(s) so far. Do you have another document you want to upload? subquestion: | + % if hasattr(x, 'maximum_size'): + The total size of all exhibits must be less than ${ humanize.naturalsize(x.maximum_size) }. + You can upload ${ humanize.naturalsize(x.maximum_size - x.size_in_bytes())} more. + % endif + ${ collapse_template(x.in_progress_exhibits) } field: x.there_is_another buttons: diff --git a/docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml b/docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml new file mode 100644 index 00000000..5c2b5e59 --- /dev/null +++ b/docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml @@ -0,0 +1,23 @@ +--- +include: + - al_package.yml +--- +mandatory: True +code: | + gather_main +--- +objects: + - exhibits_bundle_defaults_1: ALDocumentBundle.using(elements=[exhibit_doc_defaults_1], filename="exhibits_bundle_defaults", title="Exhibits with defaults") +--- +objects: + - exhibit_doc_defaults_1: ALExhibitDocument.using(title="Exhibits doc defaults", filename="exhibits_doc_defaults", maximum_size=1*1024*1024 ) +--- +id: gather_main +event: gather_main +question: Default ALDocumentBundle thumbnail method args +subquestion: | + exhibits_bundle_defaults_1.as_pdf() + + ${ exhibits_bundle_defaults_1.as_pdf() } + + ${ exhibits_bundle_defaults_1.as_pdf().size_in_bytes() } \ No newline at end of file