docassemble.AssemblyLine 2.27.1__tar.gz → 2.28.0__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.1/docassemble.AssemblyLine.egg-info → docassemble_assemblyline-2.28.0}/PKG-INFO +1 -1
  2. docassemble_assemblyline-2.28.0/docassemble/AssemblyLine/__init__.py +1 -0
  3. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/al_courts.py +4 -1
  4. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/al_document.py +63 -1
  5. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/al_general.py +30 -0
  6. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_code.yml +19 -3
  7. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_saved_sessions.yml +7 -2
  8. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_visual.yml +5 -4
  9. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/ql_baseline.yml +1 -1
  10. docassemble_assemblyline-2.28.0/docassemble/AssemblyLine/data/questions/test_bundle_parity.yml +79 -0
  11. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/language.py +5 -3
  12. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/sessions.py +54 -13
  13. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/test_al_general.py +9 -0
  14. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0/docassemble.AssemblyLine.egg-info}/PKG-INFO +1 -1
  15. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble.AssemblyLine.egg-info/SOURCES.txt +1 -0
  16. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/setup.py +1 -1
  17. docassemble.AssemblyLine-2.27.1/docassemble/AssemblyLine/__init__.py +0 -1
  18. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/LICENSE +0 -0
  19. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/MANIFEST.in +0 -0
  20. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/README.md +0 -0
  21. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_document.yml +0 -0
  22. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_language.yml +0 -0
  23. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_package.yml +0 -0
  24. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_package_unstyled.yml +0 -0
  25. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_question_test.yml +0 -0
  26. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_reminders.yml +0 -0
  27. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_saved_sessions_store.yml +0 -0
  28. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_settings.yml +0 -0
  29. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/al_terms_of_use.yml +0 -0
  30. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/assembly_line.yml +0 -0
  31. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/assembly_line_unstyled.yml +0 -0
  32. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/feedback.yml +0 -0
  33. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/interview_list.yml +0 -0
  34. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/ql_catchall.yml +0 -0
  35. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/reserved_words.yml +0 -0
  36. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/test_al_table_document.yml +0 -0
  37. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/test_aladdendum.yml +0 -0
  38. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/test_alcourts.yml +0 -0
  39. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/test_aldocument.yml +0 -0
  40. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/test_alexhibit.yml +0 -0
  41. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml +0 -0
  42. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/questions/test_question_library.yml +0 -0
  43. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/README.md +0 -0
  44. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/es-words.yml +0 -0
  45. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/languages.yml +0 -0
  46. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/test_aldocument.feature +0 -0
  47. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/test_alexhibit_docx_1.docx +0 -0
  48. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/test_alexhibit_jpg_1.jpg +0 -0
  49. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/test_alexhibit_pdf_1.pdf +0 -0
  50. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/test_alexhibit_png_1.png +0 -0
  51. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/test_question_library.feature +0 -0
  52. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/translation_es.xlsx +0 -0
  53. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/translation_ht.xlsx +0 -0
  54. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/sources/translation_pt.xlsx +0 -0
  55. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/README.md +0 -0
  56. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/al_audio.css +0 -0
  57. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/al_audio.js +0 -0
  58. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/aldocument.css +0 -0
  59. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/aldocument.js +0 -0
  60. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/interview_list.css +0 -0
  61. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/limit_upload_size.js +0 -0
  62. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/lt1-pullout-10-getting-organized.pdf +0 -0
  63. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/ma_logo.png +0 -0
  64. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/placeholder.png +0 -0
  65. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/seal.jpg +0 -0
  66. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/static/styles.css +0 -0
  67. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/templates/README.md +0 -0
  68. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/templates/al_basic_addendum.docx +0 -0
  69. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/templates/exhibit_cover_sheet.docx +0 -0
  70. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/templates/exhibit_table_of_contents.docx +0 -0
  71. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/templates/general_cover_sheet.docx +0 -0
  72. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/templates/sample_word_template.docx +0 -0
  73. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/templates/test_aldocument_docx_1.docx +0 -0
  74. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/data/templates/test_aldocument_pdf_1.pdf +0 -0
  75. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/project_maintenance.py +0 -0
  76. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/py.typed +0 -0
  77. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/requirements.txt +0 -0
  78. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/test_al_document.py +0 -0
  79. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/AssemblyLine/test_language.py +0 -0
  80. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble/__init__.py +0 -0
  81. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble.AssemblyLine.egg-info/dependency_links.txt +0 -0
  82. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble.AssemblyLine.egg-info/namespace_packages.txt +0 -0
  83. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble.AssemblyLine.egg-info/not-zip-safe +0 -0
  84. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble.AssemblyLine.egg-info/requires.txt +0 -0
  85. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/docassemble.AssemblyLine.egg-info/top_level.txt +0 -0
  86. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/pyproject.toml +0 -0
  87. {docassemble.AssemblyLine-2.27.1 → docassemble_assemblyline-2.28.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: docassemble.AssemblyLine
3
- Version: 2.27.1
3
+ Version: 2.28.0
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.0'
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Package for a very simple / MVP list of courts that is mostly signature compatible w/ MACourts for now
3
3
  """
4
+
4
5
  import os
5
6
  from typing import Any, Dict, List, Optional, Union, Set
6
7
  import pandas as pd
@@ -109,7 +110,9 @@ class ALCourt(Court):
109
110
  all_info = f"{ all_info }[BR]{ self.address.on_one_line() }"
110
111
  return f"{ all_info }[BR]{ self.description }"
111
112
 
112
- 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:
113
116
  """
114
117
  Loads data from a single Pandas Dataframe into the current court object.
115
118
  Note: It will try to convert column names that don't make valid
@@ -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:
@@ -1672,6 +1672,36 @@ class ALIndividual(Individual):
1672
1672
  return capitalize(output)
1673
1673
  return output
1674
1674
 
1675
+ def name_full(self) -> str:
1676
+ """Returns the individual's full name.
1677
+
1678
+ Returns:
1679
+ str: The individual's full name.
1680
+ """
1681
+ return self.name.full(middle="full")
1682
+
1683
+ def name_initials(self) -> str:
1684
+ """
1685
+ Returns the individual's name with the middle name as an initial.
1686
+ Equivalent to `name.full(middle="initial")`, which is also the default.
1687
+ Defined only to make it possible to be explicit about the name form.
1688
+
1689
+ Returns:
1690
+ str: The individual's name with the middle name as an initial.
1691
+ """
1692
+ return self.name.full(middle="initial")
1693
+
1694
+ def name_short(self) -> str:
1695
+ """
1696
+ Returns the individual's name without any middle name.
1697
+
1698
+ Equivalent to self.name.firstlast()
1699
+
1700
+ Returns:
1701
+ str: The individual'
1702
+ """
1703
+ return self.name.firstlast()
1704
+
1675
1705
 
1676
1706
  # (DANav isn't in public DA API, but currently in functions.py)
1677
1707
  def section_links(nav) -> List[str]: # type: ignore
@@ -186,8 +186,24 @@ 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
- # A bit of HTML required for ALKiln to be used in interviews.
190
- # To add to your unstyled alkiln interviews, include it in your `default screen parts: post`
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`
191
192
  template: alkiln_trigger_html
192
193
  content: |
193
- <div data-variable="${ encode_name(str( user_info().variable )) }" id="trigger" aria-hidden="true" style="display: none;"></div>
194
+ <div data-variable="${ encode_name(str( user_info().variable )) }" id="trigger" aria-hidden="true" style="display: none;"></div>
195
+ ---
196
+ # HTML for interviews using ALKiln tests at ALKiln versions 5.9.0 and above.
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`
199
+ template: alkiln_proxy_html
200
+ content: |
201
+ <div id="alkiln_proxy_var_values"
202
+ data-generic_object="${ encode_name(str( x.instanceName if defined('x') else '' )) }"
203
+ % for letter in [ 'i', 'j', 'k', 'l', 'm', 'n' ]:
204
+ data-index_var_${ letter }="${ encode_name(str( value( letter ) if defined( letter ) else '' )) }"
205
+ % endfor
206
+ aria-hidden="true" style="display: none;"></div>
207
+ ---
208
+ code: |
209
+ al_sessions_additional_variables_to_filter = []
@@ -180,13 +180,18 @@ code: |
180
180
  event: al_sessions_fast_forward_session
181
181
  code: |
182
182
  # set_save_status('ignore')
183
- al_sessions_snapshot_results = load_interview_answers(action_argument("i"), action_argument("session"), new_session=False)
183
+ al_sessions_snapshot_results = load_interview_answers(
184
+ action_argument("i"),
185
+ action_argument("session"),
186
+ new_session=False,
187
+ additional_variables_to_filter=al_sessions_additional_variables_to_filter)
184
188
  force_ask('al_sessions_load_status')
185
189
  ---
186
190
  code: |
187
191
  if user_logged_in():
188
192
  new_session_id = save_interview_answers(
189
- metadata = { "title": al_sessions_snapshot_label }
193
+ metadata = { "title": al_sessions_snapshot_label },
194
+ additional_variables_to_filter=al_sessions_additional_variables_to_filter,
190
195
  )
191
196
  log(f"Saved interview {al_sessions_snapshot_label} with id {new_session_id}")
192
197
  else:
@@ -41,16 +41,17 @@ 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.
51
51
  # Uses `alkiln_trigger_html` template in al_code.yml
52
52
  post: |
53
53
  ${ alkiln_trigger_html }
54
+ ${ alkiln_proxy_html }
54
55
  ---
55
56
  features:
56
57
  small screen navigation: dropdown
@@ -924,7 +924,7 @@ continue button field: users.revisit
924
924
  id: other parties revisit
925
925
  question: |
926
926
  Edit your answers about people on the other side of your case
927
- subquesiton: |
927
+ subquestion: |
928
928
  ${ other_parties.table }
929
929
 
930
930
  ${ other_parties.add_action() }
@@ -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() }
@@ -76,9 +76,11 @@ def get_tuples(
76
76
  # Get the english name for the language code from pycountry as a fallback
77
77
  return [
78
78
  (
79
- pycountry.languages.get(alpha_2=lang).name
80
- if pycountry.languages.get(alpha_2=lang)
81
- else lang,
79
+ (
80
+ pycountry.languages.get(alpha_2=lang).name
81
+ if pycountry.languages.get(alpha_2=lang)
82
+ else lang
83
+ ),
82
84
  lang,
83
85
  )
84
86
  for lang in lang_codes
@@ -143,6 +143,7 @@ al_sessions_variables_to_remove: Set = {
143
143
  "al_sessions_url_ask_snapshot",
144
144
  "al_sessions_variables_to_remove_from_new_interview",
145
145
  "al_sessions_variables_to_remove",
146
+ "al_sessions_additional_variables_to_filter",
146
147
  "al_simple_filtered_vars",
147
148
  "filtered_vars_tmp",
148
149
  "is_file_like",
@@ -510,6 +511,7 @@ def interview_list_html(
510
511
  limit: int = 50,
511
512
  offset: int = 0,
512
513
  display_interview_title: bool = True,
514
+ show_view_button: bool = True,
513
515
  ) -> str:
514
516
  """Return a string containing an HTML-formatted table with the list of saved answers
515
517
  associated with the specified filename.
@@ -538,6 +540,7 @@ def interview_list_html(
538
540
  limit (int, optional): Limit for the number of sessions returned. Defaults to 50.
539
541
  offset (int, optional): Offset for the session list. Defaults to 0.
540
542
  display_interview_title (bool, optional): If True, displays the title of the interview. Defaults to True.
543
+ show_view_button (bool, optional): If True, shows the view button. Defaults to True.
541
544
 
542
545
  Returns:
543
546
  str: HTML-formatted table containing the list of saved answers.
@@ -596,10 +599,15 @@ def interview_list_html(
596
599
  </td>
597
600
  <td>
598
601
  <a href="{ url_action(delete_action, filename=answer.get("filename"), session=answer.get("key")) }"><i class="far fa-trash-alt" title="{ delete_label }" aria-hidden="true"></i><span class="sr-only">{ delete_label }</span></a>
599
- <a target="_blank" href="{ interview_url(i=answer.get("filename"), session=answer.get("key")) }">
600
- <i class="far fa-eye" aria-hidden="true" title="{ view_label }"></i>
601
- <span class="sr-only">{ view_label }</span>
602
- </a>
602
+ """
603
+ if show_view_button:
604
+ table += f"""
605
+ <a target="_blank" href="{ interview_url(i=answer.get("filename"), session=answer.get("key")) }">
606
+ <i class="far fa-eye" aria-hidden="true" title="{ view_label }"></i>
607
+ <span class="sr-only">{ view_label }</span>
608
+ </a>
609
+ """
610
+ table += """
603
611
  </td>
604
612
  """
605
613
  table += "</tr>"
@@ -746,6 +754,7 @@ def session_list_html(
746
754
  copy_action: str = "interview_list_copy_action",
747
755
  clone_label: str = word("Copy as answer set"),
748
756
  show_title: bool = True,
757
+ show_copy_button: bool = True,
749
758
  limit: int = 50,
750
759
  offset: int = 0,
751
760
  ) -> str:
@@ -772,6 +781,7 @@ def session_list_html(
772
781
  copy_action (str, optional): Name of the copy action. Defaults to "interview_list_copy_action".
773
782
  clone_label (str, optional): Label for the action to copy as an answer set. Defaults to translated word "Copy as answer set".
774
783
  show_title (bool, optional): If True, shows the title of the session. Defaults to True.
784
+ show_copy_button (bool, optional): If True, show a copy button for answer sets. Defaults to True.
775
785
  limit (int, optional): Limit for the number of sessions returned. Defaults to 50.
776
786
  offset (int, optional): Offset for the session list. Defaults to 0.
777
787
 
@@ -823,7 +833,8 @@ def session_list_html(
823
833
  "arguments": {
824
834
  "session": answer.get("key"),
825
835
  "filename": answer.get("filename"),
826
- "title": nice_interview_subtitle(answer),
836
+ "title": nice_interview_subtitle(answer)
837
+ or nice_interview_title(answer),
827
838
  },
828
839
  },
829
840
  ]
@@ -879,7 +890,10 @@ def session_list_html(
879
890
  <td>
880
891
  <a class="al-sessions-action-rename" href="{ url_ask_rename }"><i class="fa-solid fa-tag" aria-hidden="true" title="{ rename_label }"></i><span class="sr-only">{ rename_label }</span></a>
881
892
  """
882
- if get_config("assembly line", {}).get("enable answer sets"):
893
+ if (
894
+ get_config("assembly line", {}).get("enable answer sets")
895
+ and show_copy_button
896
+ ):
883
897
  table += f"""
884
898
  &nbsp;
885
899
  <a class="al-sessions-actions-clone" href="{ url_ask_copy }"><i class="fa-regular fa-clone" aria-hidden="true" title="{clone_label}"></i><span class="sr-only">{ clone_label }</span></a>
@@ -981,18 +995,20 @@ def save_interview_answers(
981
995
  original_interview_filename=None,
982
996
  source_filename=None,
983
997
  source_session=None,
998
+ additional_variables_to_filter=None,
984
999
  ) -> str:
985
1000
  """
986
1001
  Copies the answers from a given session into a new session with a specified interview filename.
987
1002
 
988
1003
  Args:
989
1004
  filename (str, optional): The desired filename for the new session. Defaults to `al_session_store_default_filename`.
990
- variables_to_filter (Union[Set[str], List[str], None], optional): List or set of variables to filter out. Defaults to `al_sessions_variables_to_remove`.
1005
+ variables_to_filter (Union[Set[str], List[str], None], optional): The "base" list or set of variables to filter out. Defaults to `al_sessions_variables_to_remove`. There's usually no reason to change this and changing it might break sessions.
991
1006
  metadata (Optional[Dict], optional): Dictionary containing metadata. Defaults to an empty dictionary.
992
1007
  metadata_key_name (str, optional): Key name for metadata storage. Defaults to "metadata".
993
1008
  original_interview_filename (str, optional): Original filename of the interview. Defaults to None.
994
1009
  source_filename (str, optional): Source filename to get session variables from. Defaults to None.
995
1010
  source_session (str, optional): Session ID of the source file. Defaults to None.
1011
+ additional_variables_to_filter (Union[Set[str], List[str], None], optional): List or set of variables to filter out. Defaults to None.
996
1012
 
997
1013
  Returns:
998
1014
  str: ID of the new session.
@@ -1000,6 +1016,8 @@ def save_interview_answers(
1000
1016
  # Avoid using mutable default parameter
1001
1017
  if not variables_to_filter:
1002
1018
  variables_to_filter = al_sessions_variables_to_remove
1019
+ if not additional_variables_to_filter:
1020
+ additional_variables_to_filter = []
1003
1021
  if not metadata:
1004
1022
  metadata = {}
1005
1023
 
@@ -1008,6 +1026,7 @@ def save_interview_answers(
1008
1026
  filename=source_filename,
1009
1027
  session_id=source_session,
1010
1028
  variables_to_filter=variables_to_filter,
1029
+ additional_variables_to_filter=additional_variables_to_filter,
1011
1030
  )
1012
1031
 
1013
1032
  try:
@@ -1056,7 +1075,8 @@ def save_interview_answers(
1056
1075
  def get_filtered_session_variables(
1057
1076
  filename: Optional[str] = None,
1058
1077
  session_id: Optional[int] = None,
1059
- variables_to_filter: Union[Set[str], List[str], None] = None,
1078
+ variables_to_filter: Optional[Union[Set[str], List[str]]] = None,
1079
+ additional_variables_to_filter: Optional[Union[Set[str], List[str]]] = None,
1060
1080
  ) -> Dict[str, Any]:
1061
1081
  """
1062
1082
  Retrieves a filtered subset of variables from a specified interview and session.
@@ -1067,12 +1087,18 @@ def get_filtered_session_variables(
1067
1087
  filename (Optional[str], optional): Filename of the session. Defaults to None.
1068
1088
  session_id (Optional[int], optional): Session ID to retrieve variables from. Defaults to None.
1069
1089
  variables_to_filter (Union[Set[str], List[str], None], optional): List or set of variables to exclude. Defaults to `al_sessions_variables_to_remove`.
1090
+ additional_variables_to_filter (Union[Set[str], List[str], None], optional): List or set of additional variables to exclude. Defaults to None.
1070
1091
 
1071
1092
  Returns:
1072
1093
  Dict[str, Any]: A dictionary of filtered session variables.
1073
1094
  """
1074
1095
  if not variables_to_filter:
1075
1096
  variables_to_filter = al_sessions_variables_to_remove
1097
+ if not additional_variables_to_filter:
1098
+ additional_variables_to_filter = []
1099
+ variables_to_filter = set(variables_to_filter).union(
1100
+ set(additional_variables_to_filter)
1101
+ )
1076
1102
 
1077
1103
  if filename and session_id:
1078
1104
  all_vars = get_session_variables(filename, session_id, simplify=False)
@@ -1124,6 +1150,7 @@ def get_filtered_session_variables_string(
1124
1150
  filename: Optional[str] = None,
1125
1151
  session_id: Optional[int] = None,
1126
1152
  variables_to_filter: Union[Set[str], List[str], None] = None,
1153
+ additional_variables_to_filter: Optional[Union[Set[str], List[str]]] = None,
1127
1154
  ) -> str:
1128
1155
  """
1129
1156
  Returns a JSON string that represents the filtered contents of a specified filename and session ID.
@@ -1133,12 +1160,18 @@ def get_filtered_session_variables_string(
1133
1160
  filename (Optional[str], optional): Filename of the session. Defaults to None.
1134
1161
  session_id (Optional[int], optional): Session ID to retrieve variables from. Defaults to None.
1135
1162
  variables_to_filter (Union[Set[str], List[str], None], optional): List or set of variables to exclude. Defaults to `al_sessions_variables_to_remove`.
1163
+ additional_variables_to_filter (Union[Set[str], List[str], None], optional): List or set of additional variables to exclude. Defaults to None.
1136
1164
 
1137
1165
  Returns:
1138
1166
  str: A JSON-formatted string of filtered session variables.
1139
1167
  """
1140
1168
  simple_vars = serializable_dict(
1141
- get_filtered_session_variables(filename, session_id, variables_to_filter)
1169
+ get_filtered_session_variables(
1170
+ filename=filename,
1171
+ session_id=session_id,
1172
+ variables_to_filter=variables_to_filter,
1173
+ additional_variables_to_filter=additional_variables_to_filter,
1174
+ )
1142
1175
  )
1143
1176
  return json.dumps(simple_vars)
1144
1177
 
@@ -1149,6 +1182,7 @@ def load_interview_answers(
1149
1182
  new_session: bool = False,
1150
1183
  new_interview_filename: Optional[str] = None,
1151
1184
  variables_to_filter: Optional[List[str]] = None,
1185
+ additional_variables_to_filter: Optional[List[str]] = None,
1152
1186
  ) -> Optional[Union[int, bool]]:
1153
1187
  """
1154
1188
  Loads answers from a specified session. If the parameter `new_session` is set to True, it will create
@@ -1161,13 +1195,17 @@ def load_interview_answers(
1161
1195
  new_session (bool, optional): Determines whether to create a new session. Defaults to False.
1162
1196
  new_interview_filename (Optional[str], optional): Filename for the new session. Defaults to None.
1163
1197
  variables_to_filter (Optional[List[str]], optional): List of variables to exclude. Defaults to None.
1198
+ additional_variables_to_filter (Optional[List[str]], optional): List of additional variables to exclude. Defaults to None.
1164
1199
 
1165
1200
  Returns:
1166
1201
  Optional[Union[int, bool]]: ID of the newly created session if `new_session` is True, otherwise True or False based on success.
1167
1202
  """
1168
1203
 
1169
1204
  old_variables = get_filtered_session_variables(
1170
- old_interview_filename, old_session_id, variables_to_filter
1205
+ filename=old_interview_filename,
1206
+ session_id=old_session_id,
1207
+ variables_to_filter=variables_to_filter,
1208
+ additional_variables_to_filter=additional_variables_to_filter,
1171
1209
  )
1172
1210
 
1173
1211
  if new_session:
@@ -1227,6 +1265,7 @@ def export_interview_variables(
1227
1265
  session_id: Optional[int] = None,
1228
1266
  variables_to_filter: Union[Set, List[str], None] = None,
1229
1267
  output: DAFile = None,
1268
+ additional_variables_to_filter: Union[Set, List[str], None] = None,
1230
1269
  ) -> DAFile:
1231
1270
  """
1232
1271
  Generates a DAFile containing a JSON representation of a specified session's interview answers.
@@ -1238,6 +1277,7 @@ def export_interview_variables(
1238
1277
  session_id (Optional[int], optional): Session ID to retrieve variables from. Defaults to None.
1239
1278
  variables_to_filter (Union[Set, List[str], None], optional): List or set of variables to exclude. Defaults to None.
1240
1279
  output (DAFile, optional): DAFile to write the JSON output to. If None, a new DAFile is created.
1280
+ additional_variables_to_filter (Union[Set, List[str], None], optional): List or set of additional variables to exclude. Defaults to None.
1241
1281
 
1242
1282
  Returns:
1243
1283
  DAFile: DAFile with a JSON representation of the answers
@@ -1246,9 +1286,10 @@ def export_interview_variables(
1246
1286
  output = DAFile()
1247
1287
  output.initialize(filename="variables.json")
1248
1288
  variables_string = get_filtered_session_variables_string(
1249
- filename,
1250
- session_id,
1251
- variables_to_filter,
1289
+ filename=filename,
1290
+ session_id=session_id,
1291
+ variables_to_filter=variables_to_filter,
1292
+ additional_variables_to_filter=additional_variables_to_filter,
1252
1293
  )
1253
1294
 
1254
1295
  output.write(variables_string)
@@ -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.1
3
+ Version: 2.28.0
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.1',
47
+ version='2.28.0',
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.1'