docassemble.AssemblyLine 2.27.2__tar.gz → 2.28.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. {docassemble.AssemblyLine-2.27.2/docassemble.AssemblyLine.egg-info → docassemble_assemblyline-2.28.1}/PKG-INFO +1 -1
  2. docassemble_assemblyline-2.28.1/docassemble/AssemblyLine/__init__.py +1 -0
  3. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/al_courts.py +18 -4
  4. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/al_document.py +69 -3
  5. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/al_general.py +113 -33
  6. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_code.yml +5 -2
  7. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_visual.yml +4 -4
  8. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/ql_baseline.yml +7 -9
  9. docassemble_assemblyline-2.28.1/docassemble/AssemblyLine/data/questions/test_bundle_parity.yml +79 -0
  10. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/test_al_general.py +9 -0
  11. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1/docassemble.AssemblyLine.egg-info}/PKG-INFO +1 -1
  12. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble.AssemblyLine.egg-info/SOURCES.txt +1 -0
  13. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/setup.py +1 -1
  14. docassemble.AssemblyLine-2.27.2/docassemble/AssemblyLine/__init__.py +0 -1
  15. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/LICENSE +0 -0
  16. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/MANIFEST.in +0 -0
  17. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/README.md +0 -0
  18. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_document.yml +0 -0
  19. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_language.yml +0 -0
  20. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_package.yml +0 -0
  21. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_package_unstyled.yml +0 -0
  22. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_question_test.yml +0 -0
  23. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_reminders.yml +0 -0
  24. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_saved_sessions.yml +0 -0
  25. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_saved_sessions_store.yml +0 -0
  26. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_settings.yml +0 -0
  27. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/al_terms_of_use.yml +0 -0
  28. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/assembly_line.yml +0 -0
  29. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/assembly_line_unstyled.yml +0 -0
  30. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/feedback.yml +0 -0
  31. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/interview_list.yml +0 -0
  32. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/ql_catchall.yml +0 -0
  33. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/reserved_words.yml +0 -0
  34. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/test_al_table_document.yml +0 -0
  35. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/test_aladdendum.yml +0 -0
  36. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/test_alcourts.yml +0 -0
  37. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/test_aldocument.yml +0 -0
  38. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/test_alexhibit.yml +0 -0
  39. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml +0 -0
  40. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/questions/test_question_library.yml +0 -0
  41. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/README.md +0 -0
  42. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/es-words.yml +0 -0
  43. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/languages.yml +0 -0
  44. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/test_aldocument.feature +0 -0
  45. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/test_alexhibit_docx_1.docx +0 -0
  46. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/test_alexhibit_jpg_1.jpg +0 -0
  47. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/test_alexhibit_pdf_1.pdf +0 -0
  48. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/test_alexhibit_png_1.png +0 -0
  49. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/test_question_library.feature +0 -0
  50. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/translation_es.xlsx +0 -0
  51. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/translation_ht.xlsx +0 -0
  52. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/sources/translation_pt.xlsx +0 -0
  53. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/README.md +0 -0
  54. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/al_audio.css +0 -0
  55. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/al_audio.js +0 -0
  56. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/aldocument.css +0 -0
  57. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/aldocument.js +0 -0
  58. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/interview_list.css +0 -0
  59. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/limit_upload_size.js +0 -0
  60. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/lt1-pullout-10-getting-organized.pdf +0 -0
  61. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/ma_logo.png +0 -0
  62. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/placeholder.png +0 -0
  63. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/seal.jpg +0 -0
  64. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/static/styles.css +0 -0
  65. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/templates/README.md +0 -0
  66. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/templates/al_basic_addendum.docx +0 -0
  67. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/templates/exhibit_cover_sheet.docx +0 -0
  68. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/templates/exhibit_table_of_contents.docx +0 -0
  69. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/templates/general_cover_sheet.docx +0 -0
  70. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/templates/sample_word_template.docx +0 -0
  71. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/templates/test_aldocument_docx_1.docx +0 -0
  72. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/data/templates/test_aldocument_pdf_1.pdf +0 -0
  73. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/language.py +0 -0
  74. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/project_maintenance.py +0 -0
  75. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/py.typed +0 -0
  76. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/requirements.txt +0 -0
  77. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/sessions.py +0 -0
  78. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/test_al_document.py +0 -0
  79. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/AssemblyLine/test_language.py +0 -0
  80. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble/__init__.py +0 -0
  81. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble.AssemblyLine.egg-info/dependency_links.txt +0 -0
  82. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble.AssemblyLine.egg-info/namespace_packages.txt +0 -0
  83. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble.AssemblyLine.egg-info/not-zip-safe +0 -0
  84. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble.AssemblyLine.egg-info/requires.txt +0 -0
  85. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/docassemble.AssemblyLine.egg-info/top_level.txt +0 -0
  86. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/pyproject.toml +0 -0
  87. {docassemble.AssemblyLine-2.27.2 → docassemble_assemblyline-2.28.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: docassemble.AssemblyLine
3
- Version: 2.27.2
3
+ Version: 2.28.1
4
4
  Home-page: https://courtformsonline.org
5
5
  Author: Suffolk Legal Innovation and Technology Lab
6
6
  Author-email: qsteenhuis@suffolk.edu
@@ -0,0 +1 @@
1
+ __version__ = '2.28.1'
@@ -3,7 +3,7 @@ Package for a very simple / MVP list of courts that is mostly signature compatib
3
3
  """
4
4
 
5
5
  import os
6
- from typing import Any, Dict, List, Optional, Union, Set
6
+ from typing import Any, Callable, Dict, List, Mapping, Optional, Union, Set
7
7
  import pandas as pd
8
8
  import docassemble.base.functions
9
9
  from docassemble.base.util import (
@@ -110,7 +110,9 @@ class ALCourt(Court):
110
110
  all_info = f"{ all_info }[BR]{ self.address.on_one_line() }"
111
111
  return f"{ all_info }[BR]{ self.description }"
112
112
 
113
- def from_row(self, df_row: pd.Series, ensure_lat_long: bool = True) -> None:
113
+ def from_row(
114
+ self, df_row: Union[pd.Series, pd.DataFrame], ensure_lat_long: bool = True
115
+ ) -> None:
114
116
  """
115
117
  Loads data from a single Pandas Dataframe into the current court object.
116
118
  Note: It will try to convert column names that don't make valid
@@ -203,6 +205,7 @@ class ALCourtLoader(DAObject):
203
205
 
204
206
  Attributes:
205
207
  filename (str): Path to the file containing court information.
208
+ converters (Dict[str, Callable]): A dictionary of functions to apply to columns in the dataframe.
206
209
  """
207
210
 
208
211
  def init(self, *pargs, **kwargs):
@@ -411,6 +414,8 @@ class ALCourtLoader(DAObject):
411
414
 
412
415
  The method determines the file type (.csv, .xlsx, or .json) based on its extension and reads it accordingly.
413
416
 
417
+ If the "callable" attribute is defined on the instance, it will be used to convert the data in the dataframe.
418
+
414
419
  Returns:
415
420
  pd.DataFrame: A dataframe containing the list of courts.
416
421
 
@@ -427,11 +432,20 @@ class ALCourtLoader(DAObject):
427
432
  else:
428
433
  load_path = str(self.filename)
429
434
 
435
+ def convert_zip(z: Any) -> str:
436
+ return str(z).zfill(5)
437
+
438
+ merged_converters: Dict[str, Callable[[object], object]] = {
439
+ "address_zip": convert_zip
440
+ }
441
+ if hasattr(self, "converters") and self.converters:
442
+ assert isinstance(self.converters, dict)
443
+ merged_converters.update(self.converters)
430
444
  to_load = path_and_mimetype(load_path)[0]
431
445
  if self.filename.lower().endswith(".xlsx"):
432
- df = pd.read_excel(to_load)
446
+ df = pd.read_excel(to_load, converters=merged_converters) # type: ignore
433
447
  elif self.filename.lower().endswith(".csv"):
434
- df = pd.read_csv(to_load)
448
+ df = pd.read_csv(to_load, converters=merged_converters) # type: ignore
435
449
  elif self.filename.lower().endswith(".json"):
436
450
  # TODO: we may need to normalize a JSON file
437
451
  df = pd.read_json(to_load)
@@ -1,7 +1,7 @@
1
1
  import re
2
2
  import os
3
3
  import mimetypes
4
- from typing import Any, Dict, List, Union, Callable, Optional
4
+ from typing import Any, Dict, List, Literal, Union, Callable, Optional
5
5
  from docassemble.base.util import (
6
6
  Address,
7
7
  LatitudeLongitude,
@@ -34,6 +34,7 @@ from textwrap import wrap
34
34
  from math import floor
35
35
  import subprocess
36
36
  from collections import ChainMap
37
+ import pikepdf
37
38
 
38
39
  __all__ = [
39
40
  "ALAddendumField",
@@ -180,6 +181,50 @@ def table_row(title: str, button_htmls: List[str] = []) -> str:
180
181
  return html
181
182
 
182
183
 
184
+ def pdf_page_parity(pdf_path: str) -> Literal["even", "odd"]:
185
+ """
186
+ Count the number of pages in the PDF and
187
+ return "even" if it is divisible by 2 and "odd"
188
+ if it is not divisible by 2.
189
+
190
+ Args:
191
+ pdf_path (str): Path to the PDF in the filesystem
192
+
193
+ Returns:
194
+ Literal["even", "odd"]: The parity of the number of pages in the PDF
195
+ """
196
+ with pikepdf.open(pdf_path) as pdf:
197
+ num_pages = len(pdf.pages)
198
+ if num_pages % 2 == 0:
199
+ return "even"
200
+ return "odd"
201
+
202
+
203
+ def add_blank_page(pdf_path: str) -> None:
204
+ """
205
+ Add a blank page to the end of a PDF.
206
+
207
+ Args:
208
+ pdf_path (str): Path to the PDF in the filesystem
209
+ """
210
+ # Load the PDF
211
+ with pikepdf.open(pdf_path, allow_overwriting_input=True) as pdf:
212
+ # Retrieve the last page
213
+ last_page = pdf.pages[-1]
214
+
215
+ # Extract the size of the last page
216
+ media_box = last_page.MediaBox
217
+
218
+ # Create a new blank page with the same dimensions as the last page
219
+ blank_page = pikepdf.Page(pikepdf.Dictionary(MediaBox=media_box))
220
+
221
+ # Add the blank page to the end of the PDF
222
+ pdf.pages.append(blank_page)
223
+
224
+ # Overwrite the original PDF with the modified version
225
+ pdf.save(pdf_path)
226
+
227
+
183
228
  class ALAddendumField(DAObject):
184
229
  """
185
230
  Represents a field with attributes determining its display in an addendum, typically for PDF templates.
@@ -1441,6 +1486,7 @@ class ALDocumentBundle(DAList):
1441
1486
  enabled (bool, optional): Determines if the bundle is active. Defaults to True.
1442
1487
  auto_gather (bool, optional): Automatically gathers attributes. Defaults to False.
1443
1488
  gathered (bool, optional): Specifies if attributes have been gathered. Defaults to True.
1489
+ default_parity (Optional[Literal["even", "odd"]]): Default parity to enforce on the PDF. Defaults to None.
1444
1490
 
1445
1491
  Examples:
1446
1492
  Given three documents: `Cover page`, `Main motion form`, and `Notice of Interpreter Request`,
@@ -1481,6 +1527,7 @@ class ALDocumentBundle(DAList):
1481
1527
  refresh: bool = True,
1482
1528
  pdfa: bool = False,
1483
1529
  append_matching_suffix: bool = True,
1530
+ ensure_parity: Optional[Literal["even", "odd"]] = None,
1484
1531
  ) -> Optional[DAFile]:
1485
1532
  """
1486
1533
  Returns a consolidated PDF of all enabled documents in the bundle.
@@ -1491,6 +1538,8 @@ class ALDocumentBundle(DAList):
1491
1538
  pdfa (bool): If True, generates a PDF/A compliant document, defaults to False.
1492
1539
  append_matching_suffix (bool): Flag to determine if matching suffix should be appended to file name, default is True.
1493
1540
  Used primarily to enhance automated tests.
1541
+ ensure_parity (Optional[Literal["even", "odd"]]): Ensures the number of pages in the PDF is even or odd. If omitted,
1542
+ no parity is enforced. Defaults to None.
1494
1543
 
1495
1544
  Returns:
1496
1545
  Optional[DAFile]: Combined PDF file or None if no documents are enabled.
@@ -1533,6 +1582,19 @@ class ALDocumentBundle(DAList):
1533
1582
  )
1534
1583
  pdf.title = self.title
1535
1584
  setattr(self.cache, safe_key, pdf)
1585
+
1586
+ if hasattr(self, "default_parity") and not ensure_parity:
1587
+ ensure_parity = self.default_parity
1588
+
1589
+ if ensure_parity not in [None, "even", "odd"]:
1590
+ raise ValueError("ensure_parity must be either 'even', 'odd' or None")
1591
+
1592
+ if ensure_parity: # Check for odd/even requirement
1593
+ if pdf_page_parity(pdf.path()) == ensure_parity:
1594
+ return pdf
1595
+ else:
1596
+ add_blank_page(pdf.path())
1597
+
1536
1598
  return pdf
1537
1599
 
1538
1600
  def __str__(self) -> str:
@@ -2182,8 +2244,12 @@ class ALDocumentBundle(DAList):
2182
2244
  return send_email(
2183
2245
  to=to,
2184
2246
  template=template,
2185
- attachments=set(
2186
- self.as_editable_list(key=key) + self.as_pdf_list(key=key)
2247
+ # Add both DOCX and PDF versions, but if it's not possible to be a DOCX don't add the PDF
2248
+ # twice
2249
+ attachments=list(
2250
+ dict.fromkeys(
2251
+ self.as_editable_list(key=key) + self.as_pdf_list(key=key)
2252
+ )
2187
2253
  ),
2188
2254
  **kwargs,
2189
2255
  )
@@ -425,7 +425,13 @@ class ALAddress(Address):
425
425
  else:
426
426
  output += ", " + str(self.state)
427
427
  if hasattr(self, "zip") and self.zip:
428
- output += " " + str(self.zip)
428
+ current_country = (
429
+ self.country if hasattr(self, "country") else get_country()
430
+ )
431
+ if current_country == "US":
432
+ output += " " + str(self.zip).zfill(5)
433
+ else:
434
+ output += " " + str(self.zip)
429
435
  elif hasattr(self, "postal_code") and self.postal_code:
430
436
  output += " " + str(self.postal_code)
431
437
  if (
@@ -508,7 +514,13 @@ class ALAddress(Address):
508
514
  else:
509
515
  output += ", " + str(self.state)
510
516
  if hasattr(self, "zip") and self.zip:
511
- output += " " + str(self.zip)
517
+ current_country = (
518
+ self.country if hasattr(self, "country") else get_country()
519
+ )
520
+ if current_country == "US":
521
+ output += " " + str(self.zip).zfill(5)
522
+ else:
523
+ output += " " + str(self.zip)
512
524
  elif hasattr(self, "postal_code") and self.postal_code:
513
525
  output += " " + str(self.postal_code)
514
526
  return output
@@ -576,7 +588,13 @@ class ALAddress(Address):
576
588
  else:
577
589
  output += ", " + str(self.state)
578
590
  if hasattr(self, "zip") and self.zip:
579
- output += " " + str(self.zip)
591
+ current_country = (
592
+ self.country if hasattr(self, "country") else get_country()
593
+ )
594
+ if current_country == "US":
595
+ output += " " + str(self.zip).zfill(5)
596
+ else:
597
+ output += " " + str(self.zip)
580
598
  elif hasattr(self, "postal_code") and self.postal_code:
581
599
  output += " " + str(self.postal_code)
582
600
  if (
@@ -1267,9 +1285,13 @@ class ALIndividual(Individual):
1267
1285
 
1268
1286
  If the individual has selected the "self-described" option, it will use their custom input.
1269
1287
 
1288
+ Can be formatted however the author likes.
1289
+
1270
1290
  Returns:
1271
1291
  set: A set of strings representing the individual's pronouns.
1272
1292
  """
1293
+ if hasattr(self, "pronouns") and isinstance(self.pronouns, str):
1294
+ return {self.pronouns}
1273
1295
  if self.pronouns.all_false():
1274
1296
  return {str(self.pronoun_prefer_not_to_say_label)}
1275
1297
  pronouns = set(self.pronouns.true_values()) - {"self-described"}
@@ -1277,6 +1299,16 @@ class ALIndividual(Individual):
1277
1299
  pronouns = pronouns.union(self.pronouns_self_described.splitlines())
1278
1300
  return pronouns
1279
1301
 
1302
+ def list_pronouns(self) -> str:
1303
+ """
1304
+ Retrieve a formatted string of the individual's pronouns, arranged with
1305
+ the comma_list() function.
1306
+
1307
+ Returns:
1308
+ str: A formatted string of the individual's pronouns.
1309
+ """
1310
+ return comma_list(sorted(self.get_pronouns()))
1311
+
1280
1312
  def language_fields(
1281
1313
  self,
1282
1314
  choices: Optional[List[Dict[str, str]]] = None,
@@ -1500,32 +1532,38 @@ class ALIndividual(Individual):
1500
1532
  Returns:
1501
1533
  str: The appropriate pronoun.
1502
1534
  """
1535
+ # Developers can do funky things; be a little flexible
1536
+ if hasattr(self, "pronouns") and isinstance(self.pronouns, str):
1537
+ pronouns = DADict(elements={self.pronouns.lower(): True})
1538
+ else:
1539
+ pronouns = self.pronouns
1540
+
1503
1541
  if self == this_thread.global_vars.user:
1504
1542
  output = word("you", **kwargs)
1505
1543
  elif (
1506
1544
  hasattr(self, "pronouns")
1507
- and isinstance(self.pronouns, DADict)
1508
- and len(self.pronouns.true_values()) == 1
1545
+ and isinstance(pronouns, DADict)
1546
+ and len(pronouns.true_values()) == 1
1509
1547
  and (
1510
1548
  (
1511
- self.pronouns.true_values()[0]
1549
+ pronouns.true_values()[0]
1512
1550
  in ["she/her/hers", "he/him/his", "they/them/theirs", "ze/zir/zirs"]
1513
1551
  )
1514
1552
  or (
1515
- self.pronouns.get("self-described")
1553
+ pronouns.get("self-described")
1516
1554
  and has_parsable_pronouns(self.pronouns_self_described)
1517
1555
  )
1518
1556
  )
1519
1557
  ):
1520
- if self.pronouns["she/her/hers"]:
1558
+ if pronouns.get("she/her/hers"):
1521
1559
  output = word("her", **kwargs)
1522
- elif self.pronouns["he/him/his"]:
1560
+ elif pronouns.get("he/him/his"):
1523
1561
  output = word("him", **kwargs)
1524
- elif self.pronouns["they/them/theirs"]:
1562
+ elif pronouns.get("they/them/theirs"):
1525
1563
  output = word("them", **kwargs)
1526
- elif self.pronouns["ze/zir/zirs"]:
1564
+ elif pronouns.get("ze/zir/zirs"):
1527
1565
  output = word("zir", **kwargs)
1528
- elif self.pronouns.get("self-described"):
1566
+ elif pronouns.get("self-described"):
1529
1567
  output = parse_custom_pronouns(self.pronouns_self_described)["o"]
1530
1568
  elif hasattr(self, "person_type") and self.person_type in [
1531
1569
  "business",
@@ -1567,34 +1605,39 @@ class ALIndividual(Individual):
1567
1605
  Returns:
1568
1606
  str: The appropriate possessive phrase.
1569
1607
  """
1608
+ if hasattr(self, "pronouns") and isinstance(self.pronouns, str):
1609
+ pronouns = DADict(elements={self.pronouns.lower(): True})
1610
+ else:
1611
+ pronouns = self.pronouns
1612
+
1570
1613
  if self == this_thread.global_vars.user and (
1571
1614
  "thirdperson" not in kwargs or not kwargs["thirdperson"]
1572
1615
  ):
1573
1616
  output = your(target, **kwargs)
1574
1617
  elif (
1575
1618
  hasattr(self, "pronouns")
1576
- and isinstance(self.pronouns, DADict)
1577
- and len(self.pronouns.true_values()) == 1
1619
+ and isinstance(pronouns, DADict)
1620
+ and len(pronouns.true_values()) == 1
1578
1621
  and (
1579
1622
  (
1580
- self.pronouns.true_values()[0]
1623
+ pronouns.true_values()[0]
1581
1624
  in ["she/her/hers", "he/him/his", "they/them/theirs", "ze/zir/zirs"]
1582
1625
  )
1583
1626
  or (
1584
- self.pronouns.get("self-described")
1627
+ pronouns.get("self-described")
1585
1628
  and has_parsable_pronouns(self.pronouns_self_described)
1586
1629
  )
1587
1630
  )
1588
1631
  ):
1589
- if self.pronouns["she/her/hers"]:
1632
+ if pronouns.get("she/her/hers"):
1590
1633
  output = her(target, **kwargs)
1591
- elif self.pronouns["he/him/his"]:
1634
+ elif pronouns.get("he/him/his"):
1592
1635
  output = his(target, **kwargs)
1593
- elif self.pronouns["they/them/theirs"]:
1636
+ elif pronouns.get("they/them/theirs"):
1594
1637
  output = their(target, **kwargs)
1595
- elif self.pronouns["ze/zir/zirs"]:
1638
+ elif pronouns.get("ze/zir/zirs"):
1596
1639
  output = word("zir", **kwargs) + " " + target
1597
- elif self.pronouns.get("self-described"):
1640
+ elif pronouns.get("self-described"):
1598
1641
  output = (
1599
1642
  parse_custom_pronouns(self.pronouns_self_described)["p"]
1600
1643
  + " "
@@ -1628,34 +1671,39 @@ class ALIndividual(Individual):
1628
1671
  Returns:
1629
1672
  str: The appropriate subjective pronoun.
1630
1673
  """
1674
+ if hasattr(self, "pronouns") and isinstance(self.pronouns, str):
1675
+ pronouns = DADict(elements={self.pronouns.lower(): True})
1676
+ else:
1677
+ pronouns = self.pronouns
1678
+
1631
1679
  if self == this_thread.global_vars.user and (
1632
1680
  "thirdperson" not in kwargs or not kwargs["thirdperson"]
1633
1681
  ):
1634
1682
  output = word("you", **kwargs)
1635
1683
  elif (
1636
1684
  hasattr(self, "pronouns")
1637
- and isinstance(self.pronouns, DADict)
1638
- and len(self.pronouns.true_values()) == 1
1685
+ and isinstance(pronouns, DADict)
1686
+ and len(pronouns.true_values()) == 1
1639
1687
  and (
1640
1688
  (
1641
- self.pronouns.true_values()[0]
1689
+ pronouns.true_values()[0]
1642
1690
  in ["she/her/hers", "he/him/his", "they/them/theirs", "ze/zir/zirs"]
1643
1691
  )
1644
1692
  or (
1645
- self.pronouns.get("self-described")
1693
+ pronouns.get("self-described")
1646
1694
  and has_parsable_pronouns(self.pronouns_self_described)
1647
1695
  )
1648
1696
  )
1649
1697
  ):
1650
- if self.pronouns["she/her/hers"]:
1698
+ if pronouns.get("she/her/hers"):
1651
1699
  output = word("she", **kwargs)
1652
- elif self.pronouns["he/him/his"]:
1700
+ elif pronouns.get("he/him/his"):
1653
1701
  output = word("he", **kwargs)
1654
- elif self.pronouns["they/them/theirs"]:
1702
+ elif pronouns.get("they/them/theirs"):
1655
1703
  output = word("they", **kwargs)
1656
- elif self.pronouns["ze/zir/zirs"]:
1704
+ elif pronouns.get("ze/zir/zirs"):
1657
1705
  output = word("ze", **kwargs)
1658
- elif self.pronouns.get("self-described"):
1706
+ elif pronouns.get("self-described"):
1659
1707
  output = parse_custom_pronouns(self.pronouns_self_described)["s"]
1660
1708
  elif hasattr(self, "person_type") and self.person_type in [
1661
1709
  "business",
@@ -1672,6 +1720,36 @@ class ALIndividual(Individual):
1672
1720
  return capitalize(output)
1673
1721
  return output
1674
1722
 
1723
+ def name_full(self) -> str:
1724
+ """Returns the individual's full name.
1725
+
1726
+ Returns:
1727
+ str: The individual's full name.
1728
+ """
1729
+ return self.name.full(middle="full")
1730
+
1731
+ def name_initials(self) -> str:
1732
+ """
1733
+ Returns the individual's name with the middle name as an initial.
1734
+ Equivalent to `name.full(middle="initial")`, which is also the default.
1735
+ Defined only to make it possible to be explicit about the name form.
1736
+
1737
+ Returns:
1738
+ str: The individual's name with the middle name as an initial.
1739
+ """
1740
+ return self.name.full(middle="initial")
1741
+
1742
+ def name_short(self) -> str:
1743
+ """
1744
+ Returns the individual's name without any middle name.
1745
+
1746
+ Equivalent to self.name.firstlast()
1747
+
1748
+ Returns:
1749
+ str: The individual'
1750
+ """
1751
+ return self.name.firstlast()
1752
+
1675
1753
 
1676
1754
  # (DANav isn't in public DA API, but currently in functions.py)
1677
1755
  def section_links(nav) -> List[str]: # type: ignore
@@ -2048,7 +2126,7 @@ def get_visible_al_nav_items(
2048
2126
 
2049
2127
  # For dictionaries at top level
2050
2128
  item_copy = deepcopy(item)
2051
- if not item_copy.pop("hidden", False): # if not hidden
2129
+ if not str(item_copy.pop("hidden", "False")).lower() == "true": # if not hidden
2052
2130
  for key, val in item_copy.items():
2053
2131
  if isinstance(val, list): # if value of a key is a list
2054
2132
  new_sublist: List[Union[str, dict]] = []
@@ -2057,8 +2135,10 @@ def get_visible_al_nav_items(
2057
2135
  if isinstance(subitem, str):
2058
2136
  new_sublist.append(subitem)
2059
2137
  # Add dictionaries if not hidden
2060
- elif isinstance(subitem, dict) and not subitem.pop(
2061
- "hidden", False
2138
+ elif (
2139
+ isinstance(subitem, dict)
2140
+ and not str(subitem.pop("hidden", "False")).lower()
2141
+ == "true"
2062
2142
  ):
2063
2143
  new_sublist.append(subitem)
2064
2144
  item_copy[key] = new_sublist
@@ -186,13 +186,16 @@ code: |
186
186
  # If the user role was set to "unknown" at the time wizard run
187
187
  user_role = user_ask_role
188
188
  ---
189
- # Old HTML for ALKiln deprecated 3-column Story Tables.
189
+ # HTML for interviews using ALKiln tests at ALKiln
190
+ # Necessary for servers that set their `restrict input variables` config value to `True`
191
+ # If you are using al_package_unstyled.yml, copy it into your `default screen parts: post`
190
192
  template: alkiln_trigger_html
191
193
  content: |
192
194
  <div data-variable="${ encode_name(str( user_info().variable )) }" id="trigger" aria-hidden="true" style="display: none;"></div>
193
195
  ---
194
196
  # HTML for interviews using ALKiln tests at ALKiln versions 5.9.0 and above.
195
- # If you are using al_package_unstyled.yml, include it in your `default screen parts: post`
197
+ # Necessary for interviews that use docassemble generic objects or index variables
198
+ # If you are using al_package_unstyled.yml, copy it into your `default screen parts: post`
196
199
  template: alkiln_proxy_html
197
200
  content: |
198
201
  <div id="alkiln_proxy_var_values"
@@ -41,10 +41,10 @@ default screen parts:
41
41
  corner back button label: |
42
42
  Undo
43
43
  pre: |
44
- % if get_config('debug'):
45
- `id: ${ user_info().question_id }`
46
- `Variable triggering this screen: ${ user_info().variable }`
47
- `Package: ${ user_info().package } ${ package_version_number }; ${ al_version }`
44
+ % if get_config('debug') and not url_args.get("hide_hud", "").lower() in ["1", "on", "true", "yes"]:
45
+ `id: ${ user_info().question_id }`[BR]
46
+ `Variable: ${ user_info().variable }`[BR]
47
+ `Version: ${ user_info().package.replace("docassemble.", "") + " " if not user_info().package.startswith("docassemble.playground") else "" }${ package_version_number }; ${ al_version }`
48
48
  <div data-variable="${ encode_name(str( user_info().variable )) }" id="sought_variable" aria-hidden="true" style="display: none;"></div>
49
49
  % endif
50
50
  # We need both pre and post until old interviews are updated to v2 of the testing framework or v1 of the testing framework is updated. In future, we will need this in `post` so that it will work on signature pages.
@@ -71,19 +71,17 @@ subquestion: |
71
71
  fields:
72
72
  - note: |
73
73
  ${ display_template(al_terms_of_use, scrollable=True) }
74
- - To continue, you must accept our terms of use: acknowledged_information_use
75
- datatype: checkboxes
76
- none of the above: False
77
- minlength: 1
78
- choices:
79
- - I accept the terms of use.
80
- validation messages:
81
- minlength: |
82
- You cannot continue unless you agree to the terms of use.
74
+ To continue, you must accept our terms of use
75
+ - I accept the terms of use: acknowledged_information_use
76
+ datatype: yesnowide
77
+ grid: 8
83
78
  continue button field: al_intro_screen
84
79
  terms:
85
80
  green words: |
86
81
  Green words are legal terms or a short way of referring to something that needs more explanation. The definition or explanation pops up when you tap the green words.
82
+ validation code: |
83
+ if not acknowledged_information_use:
84
+ validation_error("You must accept the terms of use to continue.", field="acknowledged_information_use")
87
85
  ---
88
86
  ##### Backwards compatibility #####
89
87
  code: |
@@ -0,0 +1,79 @@
1
+ ---
2
+ include:
3
+ - assembly_line.yml
4
+ ---
5
+ objects:
6
+ - the_doc: ALDocument.using(
7
+ title="The document",
8
+ filename="the_document",
9
+ enabled=True,
10
+ has_addendum=False
11
+ )
12
+ ---
13
+ objects:
14
+ - even_bundle: ALDocumentBundle.using(
15
+ title="Even bundle",
16
+ filename="the_bundle",
17
+ elements=[
18
+ the_doc
19
+ ],
20
+ default_parity="even",
21
+ enabled=True
22
+ )
23
+ - odd_bundle: ALDocumentBundle.using(
24
+ title="Even bundle",
25
+ filename="the_bundle",
26
+ elements=[
27
+ the_doc,
28
+ the_doc
29
+ ],
30
+ enabled=True
31
+ )
32
+ - bundle_with_default: ALDocumentBundle.using(
33
+ title="Even bundle",
34
+ filename="the_bundle",
35
+ elements=[
36
+ the_doc
37
+ ],
38
+ default_parity="even",
39
+ enabled=True
40
+ )
41
+
42
+ ---
43
+ objects:
44
+ - bundle_of_bundles: ALDocumentBundle.using(
45
+ title="Bundle of bundles",
46
+ filename="the_bundle",
47
+ elements=[
48
+ even_bundle,
49
+ bundle_with_default
50
+ ],
51
+ enabled=True
52
+ )
53
+
54
+ ---
55
+ attachment:
56
+ variable name: the_doc[i]
57
+ content: |
58
+ Test content
59
+ ---
60
+ mandatory: True
61
+ question: |
62
+ About to make a PDF
63
+ continue button field: the_field
64
+ ---
65
+ mandatory: True
66
+ question: |
67
+ Download
68
+ subquestion: |
69
+ # Should be even
70
+ ${ even_bundle.as_pdf(ensure_parity="even") }
71
+
72
+ # should be odd
73
+ ${ odd_bundle.as_pdf(ensure_parity="odd") }
74
+
75
+ # should be even
76
+ ${ bundle_with_default.as_pdf() }
77
+
78
+ # Should be even
79
+ ${ bundle_of_bundles.as_pdf() }
@@ -427,6 +427,15 @@ class TestALIndividual(unittest.TestCase):
427
427
  with self.assertRaises(DAAttributeError):
428
428
  self.individual.pronoun_objective()
429
429
 
430
+ def test_name_methods(self):
431
+ self.individual.name.first = "John"
432
+ self.individual.name.middle = "Jacob"
433
+ self.individual.name.last = "Jingleheimer"
434
+
435
+ self.assertEqual(self.individual.name_full(), "John Jacob Jingleheimer")
436
+ self.assertEqual(self.individual.name_initials(), "John J. Jingleheimer")
437
+ self.assertEqual(self.individual.name_short(), "John Jingleheimer")
438
+
430
439
 
431
440
  class test_get_visible_al_nav_items(unittest.TestCase):
432
441
  def test_case_1(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: docassemble.AssemblyLine
3
- Version: 2.27.2
3
+ Version: 2.28.1
4
4
  Home-page: https://courtformsonline.org
5
5
  Author: Suffolk Legal Innovation and Technology Lab
6
6
  Author-email: qsteenhuis@suffolk.edu
@@ -49,6 +49,7 @@ docassemble/AssemblyLine/data/questions/test_alcourts.yml
49
49
  docassemble/AssemblyLine/data/questions/test_aldocument.yml
50
50
  docassemble/AssemblyLine/data/questions/test_alexhibit.yml
51
51
  docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml
52
+ docassemble/AssemblyLine/data/questions/test_bundle_parity.yml
52
53
  docassemble/AssemblyLine/data/questions/test_question_library.yml
53
54
  docassemble/AssemblyLine/data/sources/README.md
54
55
  docassemble/AssemblyLine/data/sources/es-words.yml
@@ -44,7 +44,7 @@ def find_package_data(where='.', package='', exclude=standard_exclude, exclude_d
44
44
  return out
45
45
 
46
46
  setup(name='docassemble.AssemblyLine',
47
- version='2.27.2',
47
+ version='2.28.1',
48
48
  description=(''),
49
49
  long_description='# Suffolk LIT Lab Document Assembly Line\r\n\r\n[![PyPI version](https://badge.fury.io/py/docassemble-AssemblyLine.svg)](https://badge.fury.io/py/docassemble-AssemblyLine)\r\n\r\n<img src="https://user-images.githubusercontent.com/7645641/142245862-c2eb02ab-3090-4e97-9653-bb700bf4c54d.png" alt="drawing" width="300" alt="work together" style="align: center;"/>\r\n\r\nThe Assembly Line Project is a collection of volunteers, students, and institutions who joined together\r\nduring the COVID-19 pandemic to help increase access to the court system. Our vision is mobile-friendly,\r\neasy to use **guided** online forms that help empower litigants to access the court remotely.\r\n\r\nOur signature project is [CourtFormsOnline.org](https://courtformsonline.org).\r\n\r\nWe designed a step-by-step, assembly line style process for automating court forms on top of Docassemble\r\nand built several tools along the way that **you** can use in your home jurisdiction.\r\n\r\nThis package contains **runtime code** and **pre-written questions** to support authoring robust, \r\nconsistent, and attractive Docassemble interviews that help complete court forms.\r\n\r\nRead more on our [documentation page](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/).\r\n\r\n\r\n# Related repositories\r\n\r\n* https://github.com/SuffolkLitLab/docassemble-ALWeaver\r\n* https://github.com/SuffolkLitLab/docassemble-ALMassachusetts\r\n* https://github.com/SuffolkLitLab/docassemble-MassAccess\r\n* https://github.com/SuffolkLitLab/docassemble-ALThemeTemplate\r\n* https://github.com/SuffolkLitLab/EfileProxyServer\r\n\r\n# Documentation\r\n\r\nhttps://suffolklitlab.org/docassemble-AssemblyLine-documentation/\r\n\r\n# Installation\r\n\r\n## Menu-driven installation\r\n\r\nThe recommended installation method is with the [guided installation script](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/installation).\r\n\r\n## Manual installation\r\n\r\nNormally you do not need to manually install the Assembly Line. Use the [installation script](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/installation)\r\nif you can. The instructions below are for your optional reference.\r\n\r\nThis package depends on the following configuration changes on your Docassemble server:\r\n\r\n* Ensure that [font-awesome](https://docassemble.org/docs/config.html#default%20icons) is enabled (this may be on by default:\r\n```yaml\r\ndefault icons: font awesome\r\n```\r\n* Add a [Google API key](https://docassemble.org/docs/config.html#google) that has access to:\r\n * Google Places API\r\n * Google Geocoding API\r\n* Add a [VoiceRSS API key](https://docassemble.org/docs/config.html#voicerss)\r\n* Add a [Twilio API key](https://docassemble.org/docs/config.html#twilio) for SMS support\r\n* Add an email account: [Mailgun](https://docassemble.org/docs/config.html#mailgun%20api) or [SendGrid](https://docassemble.org/docs/config.html#sendgrid%20api) recommended for email support\r\n* To show package update time and to enable the feedback form, add a GitHub Private Access token to your config.yml file, like this:\r\n```yaml\r\n# Needs access to create new issues on repositories\r\ngithub issues:\r\n username: "suffolklitlab-issues"\r\n token: "12345"\r\n# Does not need any special access to public repositories \r\ngithub readonly:\r\n username: "suffolklitlab-issues"\r\n password: "45678"\r\n type: "basic"\r\n```\r\n* If you are also using the [Assembly Line Weaver](https://github.com/SuffolkLITLab/docassemble-assemblylinewizard), you may want to set up a [Docassemble API key](https://docassemble.org/docs/api.html#manage_api) in your config.yml file to allow you to install packages automatically, like this:\r\n```yaml\r\ninstall packages api key: 12345\r\n```\r\n\r\n# Migration\r\n\r\nSee [discussion here](https://github.com/SuffolkLITLab/docassemble-AssemblyLine/issues/69)\r\n\r\n\r\n# ALDocument class\r\n\r\n## Purpose\r\n\r\nThe ALDocument class is a small utility library that makes it simpler to use the following features in an interview:\r\n\r\n* Conditional assembly of multiple, optional documents that are triggered in different ways in your interview\r\n* An addendum for PDF files that makes it simple to deal with overflow text\r\n* A customizable download screen that lists the documents in a neat table\r\n* A customizable "send" button that allows the user to email the final forms to a location of their choice\r\n\r\nHere is a small snippet that you can copy and modify that shows how to use the most important features of the ALDocument class.\r\n\r\n```\r\n---\r\nobjects:\r\n - CRA_Motion_to_Dismiss_attachment: ALDocument.using(filename="CRA_Motion_to_Dismiss", title="Motion to Dismiss CRA", enabled=True, has_addendum=True, default_overflow_message="[See addendum]") \r\n---\r\nobjects:\r\n - al_user_bundle: ALDocumentBundle.using(elements=[CRA_Motion_to_Dismiss_attachment], title="Forms to download and deliver to court", filename="motion_to_dismiss_CRA.pdf")\r\n - al_court_bundle: ALDocumentBundle.using(elements=[CRA_Motion_to_Dismiss_attachment], title="Forms to download and deliver to court", filename="motion_to_dismiss_CRA.pdf")\r\n---\r\ngeneric object: ALDocument\r\nattachment:\r\n variable name: x.addendum\r\n docx template file: docx_addendum.docx\r\n---\r\ncode: |\r\n CRA_Motion_to_Dismiss_attachment.overflow_fields[\'reasons_for_request\'].overflow_trigger = 640\r\n CRA_Motion_to_Dismiss_attachment.overflow_fields[\'reasons_for_request\'].label = "Reasons for request"\r\n CRA_Motion_to_Dismiss_attachment.overflow_fields.gathered = True\r\n \r\n---\r\nattachment:\r\n variable name: CRA_Motion_to_Dismiss_attachment[i]\r\n name: CRA Motion to Dismiss\r\n filename: CRA_Motion_to_Dismiss\r\n skip undefined: True\r\n pdf template file: CRA_Motion_to_Dismiss.pdf\r\n fields: \r\n - "court_county": ${ trial_court.address.county }\r\n - "docket_number": ${ docket_number }\r\n - "user_signature": ${ users[0].signature_if_final(i) }\r\n - "signature_date": ${ signature_date }\r\n```\r\n\r\nIt is very common to have a *contingent* document in ALDocument. If your document is contingent, remove the `enabled=True` from the object declaration, and use\r\nsome other method to "turn on" the attachment.\r\n\r\nE.g.,\r\n\r\n```\r\ncode: |\r\n CRA_Motion_to_Dismiss_attachment.enabled = condition1 and condition2\r\n```\r\n\r\n# Changelog\r\n\r\nSee [CHANGELOG.MD](https://github.com/SuffolkLITLab/docassemble-AssemblyLine/blob/main/CHANGELOG.md)\r\n',
50
50
  long_description_content_type='text/markdown',
@@ -1 +0,0 @@
1
- __version__ = '2.27.2'