docassemble.AssemblyLine 2.25.0__tar.gz → 2.27.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 (88) hide show
  1. {docassemble.AssemblyLine-2.25.0/docassemble.AssemblyLine.egg-info → docassemble.AssemblyLine-2.27.0}/PKG-INFO +2 -2
  2. docassemble.AssemblyLine-2.27.0/docassemble/AssemblyLine/__init__.py +1 -0
  3. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/al_courts.py +9 -3
  4. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/al_document.py +16 -2
  5. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/al_general.py +136 -35
  6. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_code.yml +1 -1
  7. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_visual.yml +22 -6
  8. docassemble.AssemblyLine-2.27.0/docassemble/AssemblyLine/data/questions/interview_list.yml +428 -0
  9. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/ql_baseline.yml +52 -20
  10. docassemble.AssemblyLine-2.27.0/docassemble/AssemblyLine/data/questions/test_aladdendum.yml +70 -0
  11. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/test_aldocument.feature +4 -4
  12. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/al_audio.js +11 -4
  13. docassemble.AssemblyLine-2.27.0/docassemble/AssemblyLine/data/static/placeholder.png +0 -0
  14. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/styles.css +6 -1
  15. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/language.py +28 -3
  16. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/sessions.py +40 -0
  17. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/test_al_general.py +26 -0
  18. docassemble.AssemblyLine-2.27.0/docassemble/__init__.py +2 -0
  19. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0/docassemble.AssemblyLine.egg-info}/PKG-INFO +2 -2
  20. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble.AssemblyLine.egg-info/SOURCES.txt +1 -0
  21. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble.AssemblyLine.egg-info/requires.txt +1 -1
  22. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/setup.py +2 -2
  23. docassemble.AssemblyLine-2.25.0/docassemble/AssemblyLine/__init__.py +0 -1
  24. docassemble.AssemblyLine-2.25.0/docassemble/AssemblyLine/data/questions/interview_list.yml +0 -242
  25. docassemble.AssemblyLine-2.25.0/docassemble/AssemblyLine/data/static/placeholder.png +0 -0
  26. docassemble.AssemblyLine-2.25.0/docassemble/__init__.py +0 -5
  27. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/LICENSE +0 -0
  28. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/MANIFEST.in +0 -0
  29. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/README.md +0 -0
  30. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_document.yml +0 -0
  31. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_language.yml +0 -0
  32. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_package.yml +0 -0
  33. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_package_unstyled.yml +0 -0
  34. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_question_test.yml +0 -0
  35. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_reminders.yml +0 -0
  36. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_saved_sessions.yml +0 -0
  37. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_saved_sessions_store.yml +0 -0
  38. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_settings.yml +0 -0
  39. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/al_terms_of_use.yml +0 -0
  40. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/assembly_line.yml +0 -0
  41. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/assembly_line_unstyled.yml +0 -0
  42. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/feedback.yml +0 -0
  43. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/ql_catchall.yml +0 -0
  44. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/reserved_words.yml +0 -0
  45. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/test_al_table_document.yml +0 -0
  46. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/test_alcourts.yml +0 -0
  47. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/test_aldocument.yml +0 -0
  48. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/test_alexhibit.yml +0 -0
  49. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml +0 -0
  50. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/questions/test_question_library.yml +0 -0
  51. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/README.md +0 -0
  52. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/es-words.yml +0 -0
  53. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/languages.yml +0 -0
  54. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/test_alexhibit_docx_1.docx +0 -0
  55. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/test_alexhibit_jpg_1.jpg +0 -0
  56. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/test_alexhibit_pdf_1.pdf +0 -0
  57. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/test_alexhibit_png_1.png +0 -0
  58. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/test_question_library.feature +0 -0
  59. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/translation_es.xlsx +0 -0
  60. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/translation_ht.xlsx +0 -0
  61. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/sources/translation_pt.xlsx +0 -0
  62. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/README.md +0 -0
  63. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/al_audio.css +0 -0
  64. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/aldocument.css +0 -0
  65. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/aldocument.js +0 -0
  66. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/interview_list.css +0 -0
  67. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/limit_upload_size.js +0 -0
  68. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/lt1-pullout-10-getting-organized.pdf +0 -0
  69. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/ma_logo.png +0 -0
  70. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/static/seal.jpg +0 -0
  71. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/templates/README.md +0 -0
  72. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/templates/al_basic_addendum.docx +0 -0
  73. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/templates/exhibit_cover_sheet.docx +0 -0
  74. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/templates/exhibit_table_of_contents.docx +0 -0
  75. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/templates/general_cover_sheet.docx +0 -0
  76. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/templates/sample_word_template.docx +0 -0
  77. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/templates/test_aldocument_docx_1.docx +0 -0
  78. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/data/templates/test_aldocument_pdf_1.pdf +0 -0
  79. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/py.typed +0 -0
  80. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/requirements.txt +0 -0
  81. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/test_al_document.py +0 -0
  82. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble/AssemblyLine/test_language.py +0 -0
  83. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble.AssemblyLine.egg-info/dependency_links.txt +0 -0
  84. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble.AssemblyLine.egg-info/namespace_packages.txt +0 -0
  85. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble.AssemblyLine.egg-info/not-zip-safe +0 -0
  86. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/docassemble.AssemblyLine.egg-info/top_level.txt +0 -0
  87. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/pyproject.toml +0 -0
  88. {docassemble.AssemblyLine-2.25.0 → docassemble.AssemblyLine-2.27.0}/setup.cfg +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: docassemble.AssemblyLine
3
- Version: 2.25.0
3
+ Version: 2.27.0
4
4
  Home-page: https://courtformsonline.org
5
5
  Author: Suffolk Legal Innovation and Technology Lab
6
6
  Author-email: qsteenhuis@suffolk.edu
7
7
  License: The MIT License (MIT)
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
- Requires-Dist: docassemble.ALToolbox>=0.8.1
10
+ Requires-Dist: docassemble.ALToolbox>=0.8.3
11
11
  Requires-Dist: docassemble.GithubFeedbackForm>=0.2.1
12
12
 
13
13
  # Suffolk LIT Lab Document Assembly Line
@@ -0,0 +1 @@
1
+ __version__ = '2.27.0'
@@ -4,6 +4,7 @@ Package for a very simple / MVP list of courts that is mostly signature compatib
4
4
  import os
5
5
  from typing import Any, Dict, List, Optional, Union, Set
6
6
  import pandas as pd
7
+ import docassemble.base.functions
7
8
  from docassemble.base.util import (
8
9
  path_and_mimetype,
9
10
  Address,
@@ -205,6 +206,7 @@ class ALCourtLoader(DAObject):
205
206
 
206
207
  def init(self, *pargs, **kwargs):
207
208
  super().init(*pargs, **kwargs)
209
+ self.package = docassemble.base.functions.this_thread.current_question.package
208
210
  if not hasattr(self, "filename"):
209
211
  self.filename = (
210
212
  self.file_name
@@ -416,11 +418,15 @@ class ALCourtLoader(DAObject):
416
418
  """
417
419
  if not hasattr(self, "filename") and hasattr(self, "file_name"):
418
420
  self.filename = self.file_name
419
- if "/" in self.filename:
420
- to_load = path_and_mimetype(self.filename)[0]
421
+ if ":" not in self.filename and hasattr(self, "package"):
422
+ load_path = self.package + ":"
423
+ if "/" not in str(self.filename):
424
+ load_path += "data/sources/"
425
+ load_path += str(self.filename)
421
426
  else:
422
- to_load = path_and_mimetype(os.path.join("data/sources", self.filename))[0]
427
+ load_path = str(self.filename)
423
428
 
429
+ to_load = path_and_mimetype(load_path)[0]
424
430
  if self.filename.lower().endswith(".xlsx"):
425
431
  df = pd.read_excel(to_load)
426
432
  elif self.filename.lower().endswith(".csv"):
@@ -3,6 +3,8 @@ import os
3
3
  import mimetypes
4
4
  from typing import Any, Dict, List, Union, Callable, Optional
5
5
  from docassemble.base.util import (
6
+ Address,
7
+ LatitudeLongitude,
6
8
  log,
7
9
  DADict,
8
10
  DAList,
@@ -116,14 +118,22 @@ def safeattr(object: Any, key: str) -> str:
116
118
 
117
119
  Returns:
118
120
  str: The retrieved value or an empty string if not found or if an error occurred.
121
+
122
+ Note:
123
+ The `location` attribute of an Address object or any LatitudeLongitude attribute of a DAObject is always skipped.
119
124
  """
120
125
  try:
121
126
  if isinstance(object, dict) or isinstance(object, DADict):
122
127
  return str(object.get(key, ""))
123
128
  elif isinstance(object, DAObject):
124
129
  # `location` is not an attribute people usually want shown in the table of people's attributes
125
- if key == "location":
130
+ if key == "location" and isinstance(object, Address):
126
131
  return ""
132
+ elif key == "location" and isinstance(object, DAObject):
133
+ if hasattr(object, "location") and isinstance(
134
+ object.location, LatitudeLongitude
135
+ ):
136
+ return ""
127
137
  return str(getattr(object, key))
128
138
  else:
129
139
  return ""
@@ -512,6 +522,9 @@ class ALAddendumField(DAObject):
512
522
 
513
523
  Returns:
514
524
  Optional[list]: A list of columns or None if no meaningful columns can be determined.
525
+
526
+ Note:
527
+ The "location" attribute of an Address object is always skipped in the column list.
515
528
  """
516
529
  if not skip_attributes:
517
530
  skip_attributes = {"complete"}
@@ -2078,7 +2091,8 @@ class ALDocumentBundle(DAList):
2078
2091
  show_editable_checkbox (bool, optional): Flag indicating if the checkbox
2079
2092
  for deciding the inclusion of an editable (Word) copy should be displayed.
2080
2093
  Defaults to True.
2081
- template_name (str, optional): The name of the template to be used. Defaults to an empty string.
2094
+ template_name (str, optional): Name of the template variable that is used to fill
2095
+ the email contents. By default, the `x.send_email_template` template will be used.
2082
2096
 
2083
2097
  Returns:
2084
2098
  str: The generated HTML string for the input box and button.
@@ -24,6 +24,7 @@ from docassemble.base.util import (
24
24
  name_suffix,
25
25
  phone_number_formatted,
26
26
  phone_number_is_valid,
27
+ showifdef,
27
28
  state_name,
28
29
  states_list,
29
30
  subdivision_type,
@@ -101,6 +102,7 @@ class ALAddress(Address):
101
102
  state (str): The state where the person lives.
102
103
  zip (str): The zip code where the person lives.
103
104
  country (str): The country where the person lives.
105
+ impounded (Optional[bool]): Whether the address is impounded.
104
106
  """
105
107
 
106
108
  def address_fields(
@@ -111,6 +113,7 @@ class ALAddress(Address):
111
113
  show_county: bool = False,
112
114
  show_if: Union[str, Dict[str, str], None] = None,
113
115
  allow_no_address: bool = False,
116
+ ask_if_impounded: Optional[bool] = False,
114
117
  ) -> List[Dict[str, Any]]:
115
118
  """
116
119
  Return a YAML structure representing the list of fields for the object's address.
@@ -129,6 +132,7 @@ class ALAddress(Address):
129
132
  show_county (bool): Whether to display the county field. Defaults to False.
130
133
  show_if (Union[str, Dict[str, str], None]): Condition to display each field. Defaults to None.
131
134
  allow_no_address (bool): Allow users to specify they don't have an address. Defaults to False.
135
+ ask_if_impounded (Optional[bool]): Whether to ask if the address is impounded. Defaults to False.
132
136
 
133
137
  Returns:
134
138
  list: A list of YAML structure representing address fields.
@@ -147,6 +151,10 @@ class ALAddress(Address):
147
151
  # differs from the server's country.
148
152
  if country_code and country_code != get_country() and not show_country:
149
153
  self.country = country_code
154
+ # Priority order for country: already answered country, passed in country_code, then get_country
155
+ prev_selected_country = showifdef(self.attr_name("country"), None, prior=True)
156
+ if prev_selected_country:
157
+ country_code = prev_selected_country
150
158
  if not country_code:
151
159
  country_code = get_country()
152
160
  if allow_no_address:
@@ -190,7 +198,7 @@ class ALAddress(Address):
190
198
 
191
199
  fields.append({"label": str(self.city_label), "field": self.attr_name("city")})
192
200
 
193
- if country_code:
201
+ if country_code and not show_country:
194
202
  fields.append(
195
203
  {
196
204
  "label": str(self.state_label),
@@ -199,15 +207,15 @@ class ALAddress(Address):
199
207
  "default": default_state if default_state else "",
200
208
  }
201
209
  )
202
- else: # not showing country and not showing country code
210
+ else: # when you are allowed to change country
203
211
  fields.append(
204
212
  {
205
- "label": str(self.state_label),
213
+ "label": str(self.state_or_province_label),
206
214
  "field": self.attr_name("state"),
207
215
  "default": default_state if default_state else "",
208
216
  }
209
217
  )
210
- if country_code == "US":
218
+ if country_code == "US" and not show_country:
211
219
  fields.append(
212
220
  {
213
221
  "label": str(self.zip_label),
@@ -252,6 +260,15 @@ class ALAddress(Address):
252
260
  for field in fields:
253
261
  field["show if"] = show_if
254
262
 
263
+ if ask_if_impounded:
264
+ fields.append(
265
+ {
266
+ "label": str(self.impounded_label),
267
+ "field": self.attr_name("impounded"),
268
+ "datatype": "yesno",
269
+ }
270
+ )
271
+
255
272
  return fields
256
273
 
257
274
  def formatted_unit(
@@ -322,6 +339,7 @@ class ALAddress(Address):
322
339
  show_country: Optional[bool] = None,
323
340
  bare: bool = False,
324
341
  long_state: bool = False,
342
+ show_impounded: bool = False,
325
343
  ) -> str:
326
344
  """Returns a one-line formatted address, primarily for geocoding.
327
345
 
@@ -332,6 +350,7 @@ class ALAddress(Address):
332
350
  If None, decides based on the country attribute.
333
351
  bare (bool): If True, excludes certain formatting elements. Defaults to False.
334
352
  long_state (bool): If True, uses the full state name. Defaults to False.
353
+ show_impounded (bool): If True, shows the address even if impounded. Defaults to False.
335
354
 
336
355
  Returns:
337
356
  str: The one-line formatted address.
@@ -341,6 +360,8 @@ class ALAddress(Address):
341
360
  else:
342
361
  line_breaker = " [NEWLINE] "
343
362
 
363
+ if not show_impounded and (hasattr(self, "impounded") and self.impounded):
364
+ return str(self.impounded_output_label)
344
365
  if (
345
366
  hasattr(self, "has_no_address")
346
367
  and self.has_no_address
@@ -417,16 +438,24 @@ class ALAddress(Address):
417
438
  output += line_breaker + country_name(self._get_country())
418
439
  return output
419
440
 
420
- def line_one(self, language: Optional[str] = None, bare: bool = False) -> str:
441
+ def line_one(
442
+ self,
443
+ language: Optional[str] = None,
444
+ bare: bool = False,
445
+ show_impounded: bool = False,
446
+ ) -> str:
421
447
  """Returns the first line of the address, including the unit number if it exists.
422
448
 
423
449
  Args:
424
450
  language (str, optional): Language for the address format.
425
451
  bare (bool): If True, excludes certain formatting elements. Defaults to False.
452
+ show_impounded (bool): If True, shows the address even if impounded. Defaults to False.
426
453
 
427
454
  Returns:
428
455
  str: The first line of the address.
429
456
  """
457
+ if not show_impounded and (hasattr(self, "impounded") and self.impounded):
458
+ return str(self.impounded_output_label)
430
459
  if (
431
460
  hasattr(self, "has_no_address")
432
461
  and self.has_no_address
@@ -448,16 +477,24 @@ class ALAddress(Address):
448
477
  output += ", " + the_unit
449
478
  return output
450
479
 
451
- def line_two(self, language: Optional[str] = None, long_state: bool = False) -> str:
480
+ def line_two(
481
+ self,
482
+ language: Optional[str] = None,
483
+ long_state: bool = False,
484
+ show_impounded: bool = False,
485
+ ) -> str:
452
486
  """Returns the second line of the address, including city, state, and postal code.
453
487
 
454
488
  Args:
455
489
  language (str, optional): Language for the address format.
456
490
  long_state (bool): If True, uses the full state name. Defaults to False.
491
+ show_impounded (bool): If True, shows the address even if impounded. Defaults to False.
457
492
 
458
493
  Returns:
459
494
  str: The second line of the address.
460
495
  """
496
+ if not show_impounded and (hasattr(self, "impounded") and self.impounded):
497
+ return str(self.impounded_output_label)
461
498
  output = ""
462
499
  # if hasattr(self, 'sublocality') and self.sublocality:
463
500
  # output += str(self.sublocality) + ", "
@@ -483,21 +520,25 @@ class ALAddress(Address):
483
520
  show_country: Optional[bool] = None,
484
521
  bare: bool = False,
485
522
  long_state: bool = False,
523
+ show_impounded: bool = False,
486
524
  ) -> str:
487
525
  """Returns a one-line formatted address.
488
526
 
489
527
  Args:
490
528
  include_unit (bool): If True, includes the unit in the formatted address. Defaults to True.
491
- omit_default_country (bool): If True, omits the default country from the formatted address. Defaults to True.
529
+ omit_default_country (bool): If True, doesn't show the Docassemble default country in the formatted address. Defaults to True.
492
530
  language (str, optional): Language for the address format.
493
531
  show_country (bool, optional): If True, includes the country in the formatted address.
494
532
  If None, decides based on the country attribute.
495
533
  bare (bool): If True, excludes certain formatting elements. Defaults to False.
496
534
  long_state (bool): If True, uses the full state name. Defaults to False.
535
+ show_impounded (bool): If True, shows the address even if impounded. Defaults to False.
497
536
 
498
537
  Returns:
499
538
  str: The one-line formatted address.
500
539
  """
540
+ if not show_impounded and (hasattr(self, "impounded") and self.impounded):
541
+ return str(self.impounded_output_label)
501
542
  if (
502
543
  hasattr(self, "has_no_address")
503
544
  and self.has_no_address
@@ -557,9 +598,11 @@ class ALAddress(Address):
557
598
  be a standard Address object, not an ALAddress object. If geocoding fails, it returns
558
599
  the version of the address as entered by the user.
559
600
 
601
+ Warning: currently the normalized address will not be redacted if the address is impounded.
602
+
560
603
  Returns:
561
604
  Union[Address, "ALAddress"]: Normalized address if geocoding is successful, otherwise
562
- the original address.
605
+ the original address.
563
606
  """
564
607
  try:
565
608
  self.geocode()
@@ -791,49 +834,54 @@ class ALIndividual(Individual):
791
834
  else:
792
835
  return ""
793
836
 
794
- def phone_numbers(self, country: Optional[str] = None) -> str:
837
+ def phone_numbers(
838
+ self, country: Optional[str] = None, show_impounded: bool = False
839
+ ) -> str:
795
840
  """Fetches and formats the phone numbers of the individual.
796
841
 
797
842
  Args:
798
843
  country (str, optional): The country for phone number formatting. Defaults to the country of the docassemble server.
844
+ show_impounded (bool): If True, shows the phone numbers even if impounded. Defaults to False.
799
845
 
800
846
  Returns:
801
847
  str: Formatted string of phone numbers.
802
848
  """
803
849
  nums = []
804
850
  if hasattr(self, "mobile_number") and self.mobile_number:
851
+ fmt_number = None
805
852
  try:
806
- nums.append(
807
- {
808
- phone_number_formatted(
809
- self.mobile_number, country=country
810
- ): "cell"
811
- }
812
- )
853
+ fmt_number = phone_number_formatted(self.mobile_number, country=country)
813
854
  except:
855
+ fmt_number = None
856
+ if fmt_number:
857
+ nums.append({fmt_number: "cell"})
858
+ else:
814
859
  nums.append({self.mobile_number: "cell"})
815
860
  if hasattr(self, "phone_number") and self.phone_number:
861
+ fmt_number = None
816
862
  try:
817
- nums.append(
818
- {
819
- phone_number_formatted(
820
- self.phone_number, country=country
821
- ): "other"
822
- }
823
- )
863
+ fmt_number = phone_number_formatted(self.phone_number, country=country)
824
864
  except:
865
+ fmt_number = None
866
+ if fmt_number:
867
+ nums.append({fmt_number: "other"})
868
+ else:
825
869
  nums.append({self.phone_number: "other"})
826
- if len(nums) > 1:
870
+ if len(nums) < 1:
871
+ return ""
872
+ # Check for impounded phone number
873
+ elif not show_impounded and (
874
+ hasattr(self, "phone_impounded") and self.phone_impounded
875
+ ):
876
+ return str(self.impounded_phone_output_label)
877
+ elif len(nums) > 1:
827
878
  return comma_list(
828
- [
829
- list(num.keys())[0] + " (" + list(num.values())[0] + ")"
830
- for num in nums
831
- ]
879
+ [f"{list(num.keys())[0]} ({list(num.values())[0]})" for num in nums]
832
880
  )
833
881
  elif len(nums):
834
882
  return list(nums[0].keys())[0]
835
- else:
836
- return ""
883
+
884
+ assert False # We should never get here, no default return is necessary
837
885
 
838
886
  def contact_methods(self) -> str:
839
887
  """Generates a formatted string of all provided contact methods.
@@ -843,15 +891,17 @@ class ALIndividual(Individual):
843
891
  """
844
892
  methods = []
845
893
  if self.phone_numbers():
846
- methods.append({self.phone_numbers(): word("by phone at ")})
894
+ methods.append({self.phone_numbers(): str(self.phone_number_contact_label)})
847
895
  if hasattr(self, "email") and self.email:
848
- methods.append({self.email: word("by email at ")})
896
+ methods.append({self.email: str(self.email_contact_label)})
849
897
  if hasattr(self, "other_contact_method") and self.other_contact_method:
850
- methods.append({self.other_contact_method: "by "})
898
+ methods.append(
899
+ {self.other_contact_method: str(self.other_contact_method_label)}
900
+ )
851
901
 
852
902
  return comma_and_list(
853
903
  [
854
- list(method.values())[0] + list(method.keys())[0]
904
+ list(method.values())[0] + " " + list(method.keys())[0]
855
905
  for method in methods
856
906
  if len(method)
857
907
  ],
@@ -900,6 +950,8 @@ class ALIndividual(Individual):
900
950
  self,
901
951
  person_or_business: str = "person",
902
952
  show_suffix: bool = True,
953
+ show_title: bool = False,
954
+ title_options: Optional[List[str]] = None,
903
955
  show_if: Union[str, Dict[str, str], None] = None,
904
956
  ) -> List[Dict[str, str]]:
905
957
  """
@@ -911,6 +963,10 @@ class ALIndividual(Individual):
911
963
  Default is "person".
912
964
  show_suffix (bool, optional): Determines if the name's suffix (e.g., Jr., Sr.) should be included in the prompts.
913
965
  Default is True.
966
+ show_title: (bool, optional): Determines if the name's title (e.g., Mr., Ms.) should be included in the prompts.
967
+ Default is False.
968
+ title_options (List[str], optional): A list of title options to use in the prompts. Default is defined as a list
969
+ of common titles in English-speaking countries.
914
970
  show_if (Union[str, Dict[str, str], None], optional): Condition to determine which fields to show.
915
971
  It can be a string, a dictionary with conditions, or None. Default is None.
916
972
 
@@ -921,6 +977,29 @@ class ALIndividual(Individual):
921
977
  If `person_or_business` is set to None, the method will offer the end user a choice
922
978
  and will set appropriate "show ifs" conditions for each type.
923
979
  """
980
+ if not title_options:
981
+ title_options = [
982
+ "Mr.",
983
+ "Mrs.",
984
+ "Miss",
985
+ "Ms.",
986
+ "Mx.",
987
+ "Dr.",
988
+ "Prof.",
989
+ "Hon.",
990
+ "Rev.",
991
+ "Sir",
992
+ "Lord",
993
+ "Lady",
994
+ "Dame",
995
+ "Maj.",
996
+ "Gen.",
997
+ "Capt.",
998
+ "Lt.",
999
+ "Sgt.",
1000
+ "Fr.",
1001
+ "Sr.",
1002
+ ]
924
1003
  if person_or_business == "person":
925
1004
  fields = [
926
1005
  {
@@ -946,6 +1025,16 @@ class ALIndividual(Individual):
946
1025
  "required": False,
947
1026
  }
948
1027
  )
1028
+ if show_title:
1029
+ fields.insert(
1030
+ 0,
1031
+ {
1032
+ "label": str(self.name_title_label),
1033
+ "field": self.attr_name("name.title"),
1034
+ "choices": title_options,
1035
+ "required": False,
1036
+ },
1037
+ )
949
1038
  if show_if:
950
1039
  for field in fields:
951
1040
  field["show if"] = show_if
@@ -1033,6 +1122,7 @@ class ALIndividual(Individual):
1033
1122
  show_county: bool = False,
1034
1123
  show_if: Union[str, Dict[str, str], None] = None,
1035
1124
  allow_no_address: bool = False,
1125
+ ask_if_impounded: bool = False,
1036
1126
  ) -> List[Dict[str, str]]:
1037
1127
  """
1038
1128
  Generate field prompts for capturing an address.
@@ -1044,6 +1134,7 @@ class ALIndividual(Individual):
1044
1134
  show_county (bool): Whether to display the county field. Defaults to False.
1045
1135
  show_if (Union[str, Dict[str, str], None]): Condition to determine if the field should be shown. Defaults to None.
1046
1136
  allow_no_address (bool): Whether to permit entries with no address. Defaults to False.
1137
+ ask_if_impounded (bool): Whether to ask if the address is impounded. Defaults to False.
1047
1138
 
1048
1139
  Returns:
1049
1140
  List[Dict[str, str]]: A list of dictionaries with field prompts for addresses.
@@ -1057,6 +1148,7 @@ class ALIndividual(Individual):
1057
1148
  show_county=show_county,
1058
1149
  show_if=show_if,
1059
1150
  allow_no_address=allow_no_address,
1151
+ ask_if_impounded=ask_if_impounded,
1060
1152
  )
1061
1153
 
1062
1154
  def gender_fields(
@@ -1323,7 +1415,12 @@ class ALIndividual(Individual):
1323
1415
  return f"{self.name.first[:1]}{self.name.middle[:1] if hasattr(self.name,'middle') else ''}{self.name.last[:1] if hasattr(self.name, 'last') else ''}"
1324
1416
 
1325
1417
  def address_block(
1326
- self, language=None, international=False, show_country=False, bare=False
1418
+ self,
1419
+ language=None,
1420
+ international=False,
1421
+ show_country=False,
1422
+ bare=False,
1423
+ show_impounded=False,
1327
1424
  ) -> str:
1328
1425
  """
1329
1426
  Generate a formatted address block for mailings.
@@ -1333,6 +1430,7 @@ class ALIndividual(Individual):
1333
1430
  international (bool): If True, format for international mailing. Defaults to False.
1334
1431
  show_country (bool): If True, include the country in the address. Defaults to False.
1335
1432
  bare (bool): If True, produce the address without additional formatting. Defaults to False.
1433
+ show_impounded (bool): If True, show the address even if it is impounded. Defaults to False.
1336
1434
 
1337
1435
  Returns:
1338
1436
  str: The formatted address block.
@@ -1345,6 +1443,8 @@ class ALIndividual(Individual):
1345
1443
  language=language,
1346
1444
  international=international,
1347
1445
  show_country=show_country,
1446
+ bare=bare,
1447
+ show_impounded=show_impounded,
1348
1448
  )
1349
1449
  )
1350
1450
  return (
@@ -1356,6 +1456,7 @@ class ALIndividual(Individual):
1356
1456
  international=international,
1357
1457
  show_country=show_country,
1358
1458
  bare=bare,
1459
+ show_impounded=show_impounded,
1359
1460
  )
1360
1461
  )
1361
1462
 
@@ -53,7 +53,7 @@ code: |
53
53
  # email_to_use = "massaccess@suffolk.edu"
54
54
 
55
55
  if task_not_yet_performed('send email'):
56
- log("Court email is ".format(court_email))
56
+ log(f"Court email is {court_email}, sending email to {email_to_use}")
57
57
  email_success = False
58
58
  if should_cc_user:
59
59
  log('Sending email to ' + email_to_use + ' and ccing ' + cc_email + ' for ' + str(users) + ' bcc to ' + bcc_email)
@@ -366,10 +366,18 @@ content: |
366
366
  MAIN_METADATA = next(iter(interview_metadata.values()))
367
367
  %>
368
368
  % else:
369
- <% MAIN_METADATA = {} %>
369
+ <% MAIN_METADATA = all_variables(special='metadata') %>
370
370
  % endif
371
- % if MAIN_METADATA.get("original_form") and not MAIN_METADATA.get("original_form", "").strip() == "None" and MAIN_METADATA.get("original_form").startswith("http"):
372
- [View the original version of this form](${ MAIN_METADATA.get("original_form") }).
371
+ <% ORIGINAL_FORMS = MAIN_METADATA.get("original_form", []) if isinstance(MAIN_METADATA.get("original_form"), list) else ([MAIN_METADATA.get("original_form", "")] if MAIN_METADATA.get("original_form") else []) %>
372
+
373
+ % if len(ORIGINAL_FORMS) > 1:
374
+ View the original version of this form at the links below:
375
+
376
+ % for url in [url for url in ORIGINAL_FORMS if url.strip() != "None" and url.startswith("http")]:
377
+ * [${ url }](${ url })
378
+ % endfor
379
+ % elif len(ORIGINAL_FORMS) == 1:
380
+ [View the original version of this form](${ ORIGINAL_FORMS[0] }).
373
381
  % endif
374
382
 
375
383
  % if package_updated_on:
@@ -426,8 +434,10 @@ continue button field: al_exit_logout_confirmation
426
434
  metadata:
427
435
  error action: al_custom_error_action
428
436
  ---
437
+ # Only necessary to ignore playground "undefined names" warning
429
438
  objects:
430
439
  - MAIN_METADATA: DAEmpty()
440
+ - ORIGINAL_FORMS: DAEmpty()
431
441
  ---
432
442
  id: custom error action
433
443
  event: al_custom_error_action
@@ -445,8 +455,9 @@ subquestion: |
445
455
  MAIN_METADATA = next(iter(interview_metadata.values()))
446
456
  %>
447
457
  % else:
448
- <% MAIN_METADATA = {} %>
458
+ <% MAIN_METADATA = all_variables(special='metadata') %>
449
459
  % endif
460
+ <% ORIGINAL_FORMS = MAIN_METADATA.get("original_form", []) if isinstance(MAIN_METADATA.get("original_form"), list) else ([MAIN_METADATA.get("original_form", "")] if MAIN_METADATA.get("original_form") else []) %>
450
461
 
451
462
  We ran into an error when we tried to load this screen.
452
463
 
@@ -459,8 +470,13 @@ subquestion: |
459
470
  % if al_enable_incomplete_downloads:
460
471
  * Try to [download your work in progress](${ url_action("al_error_action_download_screen") })
461
472
  % endif
462
- % if MAIN_METADATA.get("original_form") and not MAIN_METADATA.get("original_form", "").strip() == "None":
463
- * [Download a blank "${ all_variables(special='metadata').get("title", "").strip() }"](${ MAIN_METADATA.get("original_form") } )
473
+ % if len(ORIGINAL_FORMS) > 1:
474
+ * Download a blank "${ all_variables(special='metadata').get("title", "").strip() }":
475
+ % for url in ORIGINAL_FORMS:
476
+ * [${ url }](${ url } )
477
+ % endfor
478
+ % elif len(ORIGINAL_FORMS) == 1:
479
+ * [Download a blank "${ all_variables(special='metadata').get("title", "").strip() }"]( ${ ORIGINAL_FORMS[0] })
464
480
  % endif
465
481
  * Visit the [${ AL_ORGANIZATION_TITLE } home page](${ AL_ORGANIZATION_HOMEPAGE }) and try another interview
466
482
  % if al_show_email_to_user_on_errors and get_config("error notification email"):