docassemble.AssemblyLine 2.28.1__tar.gz → 3.0.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 (89) hide show
  1. {docassemble_assemblyline-2.28.1/docassemble.AssemblyLine.egg-info → docassemble_assemblyline-3.0.1}/PKG-INFO +1 -1
  2. docassemble_assemblyline-3.0.1/docassemble/AssemblyLine/__init__.py +1 -0
  3. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/al_document.py +16 -9
  4. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/al_general.py +145 -7
  5. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_settings.yml +4 -1
  6. docassemble_assemblyline-3.0.1/docassemble/AssemblyLine/data/questions/demo_search_sessions.yml +67 -0
  7. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/ql_baseline.yml +208 -141
  8. docassemble_assemblyline-3.0.1/docassemble/AssemblyLine/data/questions/test_familiar_disambiguation.yml +13 -0
  9. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/language.py +6 -5
  10. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/sessions.py +193 -38
  11. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1/docassemble.AssemblyLine.egg-info}/PKG-INFO +1 -1
  12. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble.AssemblyLine.egg-info/SOURCES.txt +2 -0
  13. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/setup.py +1 -1
  14. docassemble_assemblyline-2.28.1/docassemble/AssemblyLine/__init__.py +0 -1
  15. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/LICENSE +0 -0
  16. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/MANIFEST.in +0 -0
  17. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/README.md +0 -0
  18. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/al_courts.py +0 -0
  19. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_code.yml +0 -0
  20. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_document.yml +0 -0
  21. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_language.yml +0 -0
  22. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_package.yml +0 -0
  23. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_package_unstyled.yml +0 -0
  24. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_question_test.yml +0 -0
  25. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_reminders.yml +0 -0
  26. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_saved_sessions.yml +0 -0
  27. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_saved_sessions_store.yml +0 -0
  28. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_terms_of_use.yml +0 -0
  29. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/al_visual.yml +0 -0
  30. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/assembly_line.yml +0 -0
  31. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/assembly_line_unstyled.yml +0 -0
  32. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/feedback.yml +0 -0
  33. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/interview_list.yml +0 -0
  34. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/ql_catchall.yml +0 -0
  35. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/reserved_words.yml +0 -0
  36. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/test_al_table_document.yml +0 -0
  37. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/test_aladdendum.yml +0 -0
  38. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/test_alcourts.yml +0 -0
  39. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/test_aldocument.yml +0 -0
  40. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/test_alexhibit.yml +0 -0
  41. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/test_alexhibit_maxsize.yml +0 -0
  42. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/test_bundle_parity.yml +0 -0
  43. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/questions/test_question_library.yml +0 -0
  44. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/README.md +0 -0
  45. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/es-words.yml +0 -0
  46. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/languages.yml +0 -0
  47. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/test_aldocument.feature +0 -0
  48. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/test_alexhibit_docx_1.docx +0 -0
  49. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/test_alexhibit_jpg_1.jpg +0 -0
  50. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/test_alexhibit_pdf_1.pdf +0 -0
  51. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/test_alexhibit_png_1.png +0 -0
  52. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/test_question_library.feature +0 -0
  53. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/translation_es.xlsx +0 -0
  54. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/translation_ht.xlsx +0 -0
  55. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/sources/translation_pt.xlsx +0 -0
  56. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/README.md +0 -0
  57. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/al_audio.css +0 -0
  58. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/al_audio.js +0 -0
  59. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/aldocument.css +0 -0
  60. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/aldocument.js +0 -0
  61. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/interview_list.css +0 -0
  62. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/limit_upload_size.js +0 -0
  63. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/lt1-pullout-10-getting-organized.pdf +0 -0
  64. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/ma_logo.png +0 -0
  65. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/placeholder.png +0 -0
  66. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/seal.jpg +0 -0
  67. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/static/styles.css +0 -0
  68. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/templates/README.md +0 -0
  69. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/templates/al_basic_addendum.docx +0 -0
  70. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/templates/exhibit_cover_sheet.docx +0 -0
  71. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/templates/exhibit_table_of_contents.docx +0 -0
  72. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/templates/general_cover_sheet.docx +0 -0
  73. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/templates/sample_word_template.docx +0 -0
  74. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/templates/test_aldocument_docx_1.docx +0 -0
  75. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/data/templates/test_aldocument_pdf_1.pdf +0 -0
  76. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/project_maintenance.py +0 -0
  77. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/py.typed +0 -0
  78. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/requirements.txt +0 -0
  79. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/test_al_document.py +0 -0
  80. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/test_al_general.py +0 -0
  81. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/AssemblyLine/test_language.py +0 -0
  82. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble/__init__.py +0 -0
  83. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble.AssemblyLine.egg-info/dependency_links.txt +0 -0
  84. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble.AssemblyLine.egg-info/namespace_packages.txt +0 -0
  85. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble.AssemblyLine.egg-info/not-zip-safe +0 -0
  86. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble.AssemblyLine.egg-info/requires.txt +0 -0
  87. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/docassemble.AssemblyLine.egg-info/top_level.txt +0 -0
  88. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/pyproject.toml +0 -0
  89. {docassemble_assemblyline-2.28.1 → docassemble_assemblyline-3.0.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: docassemble.AssemblyLine
3
- Version: 2.28.1
3
+ Version: 3.0.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__ = '3.0.1'
@@ -282,8 +282,10 @@ class ALAddendumField(DAObject):
282
282
  Any: The portion of the variable exceeding the content safe for display, considered as overflow.
283
283
  """
284
284
  # Handle a Boolean overflow first
285
- if isinstance(self.overflow_trigger, bool) and self.overflow_trigger:
286
- return self.value()
285
+ if isinstance(self.overflow_trigger, bool):
286
+ if self.overflow_trigger:
287
+ return self.value()
288
+ return ""
287
289
 
288
290
  # If trigger is not a boolean value, overflow value is the value that starts at the end of the safe value.
289
291
  original_value = self.value_if_defined()
@@ -320,7 +322,11 @@ class ALAddendumField(DAObject):
320
322
  return value_to_process[overflow_start:].lstrip()
321
323
 
322
324
  # Do not subtract length of overflow message if this is a list of objects instead of a string
323
- return original_value[self.overflow_trigger :]
325
+ elif isinstance(safe_text, (list, DAList)):
326
+ return original_value[self.overflow_trigger :]
327
+ raise ValueError(
328
+ f"Attempted ALAddendum overflow for {self.field_name } which is of type {type(safe_text)}. Overflow is for lists and strings only."
329
+ )
324
330
 
325
331
  def max_lines(self, input_width: int = 80) -> int:
326
332
  """
@@ -524,10 +530,11 @@ class ALAddendumField(DAObject):
524
530
  return value.rstrip()
525
531
 
526
532
  # If the overflow item is a list or DAList
527
- if isinstance(value, list) or isinstance(value, DAList):
533
+ if isinstance(value, (list, DAList)):
528
534
  return value[: self.overflow_trigger]
529
535
  else:
530
536
  # We can't slice objects that are not lists or strings
537
+ # TODO: is it correct to return the whole object here?
531
538
  return value
532
539
 
533
540
  def value_if_defined(self) -> Any:
@@ -774,13 +781,13 @@ class ALAddendumFieldDict(DAOrderedDict):
774
781
  self[the_key].field_name = the_key
775
782
  return newobj
776
783
 
777
- def from_list(self, data) -> None:
784
+ def from_list(self, data: List[Dict]) -> None:
778
785
  """
779
786
  Populate the dictionary using a list of field data.
780
787
 
781
788
  Args:
782
- data (list): List of dictionaries containing field data with keys "field_name"
783
- and "overflow_trigger".
789
+ data (list): List of dictionaries containing ield data with keys "field_name"
790
+ and "overflow_trigger".
784
791
  """
785
792
  for entry in data:
786
793
  new_field = self.initializeObject(entry["field_name"], ALAddendumField)
@@ -788,13 +795,13 @@ class ALAddendumFieldDict(DAOrderedDict):
788
795
  new_field.overflow_trigger = entry["overflow_trigger"]
789
796
  return
790
797
 
791
- def defined_fields(self, style="overflow_only") -> list:
798
+ def defined_fields(self, style: str = "overflow_only") -> list:
792
799
  """
793
800
  Fetch a list of fields that are defined.
794
801
 
795
802
  Args:
796
803
  style (str, optional): If set to "overflow_only", only the fields with overflow values
797
- will be returned. Defaults to "overflow_only".
804
+ will be returned. Defaults to "overflow_only".
798
805
 
799
806
  Returns:
800
807
  list: List of defined fields based on the specified style.
@@ -115,6 +115,7 @@ class ALAddress(Address):
115
115
  show_if: Union[str, Dict[str, str], None] = None,
116
116
  allow_no_address: bool = False,
117
117
  ask_if_impounded: Optional[bool] = False,
118
+ maxlengths: Optional[Dict[str, int]] = None,
118
119
  ) -> List[Dict[str, Any]]:
119
120
  """
120
121
  Return a YAML structure representing the list of fields for the object's address.
@@ -134,9 +135,10 @@ class ALAddress(Address):
134
135
  show_if (Union[str, Dict[str, str], None]): Condition to display each field. Defaults to None.
135
136
  allow_no_address (bool): Allow users to specify they don't have an address. Defaults to False.
136
137
  ask_if_impounded (Optional[bool]): Whether to ask if the address is impounded. Defaults to False.
138
+ maxlengths (Optional[Dict[str, int]]): A dictionary of field names and their maximum lengths. Defaults to None.
137
139
 
138
140
  Returns:
139
- list: A list of YAML structure representing address fields.
141
+ List[Dict[str, Any]]: A list of dictionaries representing address fields.
140
142
 
141
143
  Notes:
142
144
  - The function will set the `country` attribute of the Address to `country_code` under these
@@ -270,6 +272,11 @@ class ALAddress(Address):
270
272
  }
271
273
  )
272
274
 
275
+ if maxlengths:
276
+ for field in fields:
277
+ if field["field"] in maxlengths:
278
+ field["maxlength"] = maxlengths[field["field"]]
279
+
273
280
  return fields
274
281
 
275
282
  def formatted_unit(
@@ -743,22 +750,27 @@ class ALPeopleList(DAList):
743
750
  comma_string=comma_string,
744
751
  )
745
752
 
746
- def familiar(self) -> str:
753
+ def familiar(self, **kwargs) -> str:
747
754
  """Provide a list of familiar forms of names of individuals.
748
755
 
756
+ Args:
757
+ **kwargs: Keyword arguments to pass to the familiar method.
749
758
  Returns:
750
759
  str: Formatted string of familiar names.
751
760
  """
752
- return comma_and_list([person.name.familiar() for person in self])
761
+ return comma_and_list([person.familiar(**kwargs) for person in self])
753
762
 
754
- def familiar_or(self) -> str:
763
+ def familiar_or(self, **kwargs) -> str:
755
764
  """Provide a list of familiar forms of names of individuals separated by 'or'.
756
765
 
766
+ Args:
767
+ **kwargs: Keyword arguments to pass to the familiar method.
768
+
757
769
  Returns:
758
770
  str: Formatted string of familiar names separated by 'or'.
759
771
  """
760
772
  return comma_and_list(
761
- [person.name.familiar() for person in self], and_string=word("or")
773
+ [person.familiar(**kwargs) for person in self], and_string=word("or")
762
774
  )
763
775
 
764
776
  def short_list(self, limit: int, truncate_string: str = ", et. al.") -> str:
@@ -978,6 +990,7 @@ class ALIndividual(Individual):
978
990
  show_title: bool = False,
979
991
  title_options: Optional[List[str]] = None,
980
992
  show_if: Union[str, Dict[str, str], None] = None,
993
+ maxlengths: Optional[Dict[str, int]] = None,
981
994
  ) -> List[Dict[str, str]]:
982
995
  """
983
996
  Generates suitable field prompts for a name based on the type of entity (person or business)
@@ -994,6 +1007,7 @@ class ALIndividual(Individual):
994
1007
  of common titles in English-speaking countries.
995
1008
  show_if (Union[str, Dict[str, str], None], optional): Condition to determine which fields to show.
996
1009
  It can be a string, a dictionary with conditions, or None. Default is None.
1010
+ maxlengths (Dict[str, int], optional): A dictionary of field names and their maximum lengths. Default is None.
997
1011
 
998
1012
  Returns:
999
1013
  List[Dict[str, str]]: A list of dictionaries where each dictionary contains field prompt details.
@@ -1137,6 +1151,11 @@ class ALIndividual(Individual):
1137
1151
  "show if": show_if_business,
1138
1152
  }
1139
1153
  )
1154
+
1155
+ if maxlengths:
1156
+ for field in fields:
1157
+ if field["field"] in maxlengths:
1158
+ field["maxlength"] = maxlengths[field["field"]]
1140
1159
  return fields
1141
1160
 
1142
1161
  def address_fields(
@@ -1148,6 +1167,7 @@ class ALIndividual(Individual):
1148
1167
  show_if: Union[str, Dict[str, str], None] = None,
1149
1168
  allow_no_address: bool = False,
1150
1169
  ask_if_impounded: bool = False,
1170
+ maxlengths: Optional[Dict[str, int]] = None,
1151
1171
  ) -> List[Dict[str, str]]:
1152
1172
  """
1153
1173
  Generate field prompts for capturing an address.
@@ -1160,6 +1180,7 @@ class ALIndividual(Individual):
1160
1180
  show_if (Union[str, Dict[str, str], None]): Condition to determine if the field should be shown. Defaults to None.
1161
1181
  allow_no_address (bool): Whether to permit entries with no address. Defaults to False.
1162
1182
  ask_if_impounded (bool): Whether to ask if the address is impounded. Defaults to False.
1183
+ maxlengths (Dict[str, int], optional): A dictionary of field names and their maximum lengths. Default is None.
1163
1184
 
1164
1185
  Returns:
1165
1186
  List[Dict[str, str]]: A list of dictionaries with field prompts for addresses.
@@ -1174,10 +1195,14 @@ class ALIndividual(Individual):
1174
1195
  show_if=show_if,
1175
1196
  allow_no_address=allow_no_address,
1176
1197
  ask_if_impounded=ask_if_impounded,
1198
+ maxlengths=maxlengths,
1177
1199
  )
1178
1200
 
1179
1201
  def gender_fields(
1180
- self, show_help=False, show_if: Union[str, Dict[str, str], None] = None
1202
+ self,
1203
+ show_help=False,
1204
+ show_if: Union[str, Dict[str, str], None] = None,
1205
+ maxlengths: Optional[Dict[str, int]] = None,
1181
1206
  ) -> List[Dict[str, str]]:
1182
1207
  """
1183
1208
  Generate fields for capturing gender information, including a
@@ -1186,6 +1211,7 @@ class ALIndividual(Individual):
1186
1211
  Args:
1187
1212
  show_help (bool): Whether to show additional help text. Defaults to False.
1188
1213
  show_if (Union[str, Dict[str, str], None]): Condition to determine if the field should be shown. Defaults to None.
1214
+ maxlengths (Dict[str, int], optional): A dictionary of field names and their maximum lengths. Default is None.
1189
1215
 
1190
1216
  Returns:
1191
1217
  List[Dict[str, str]]: A list of dictionaries with field prompts for gender.
@@ -1217,6 +1243,11 @@ class ALIndividual(Individual):
1217
1243
  if show_if:
1218
1244
  fields[0]["show if"] = show_if
1219
1245
 
1246
+ if maxlengths:
1247
+ for field in fields:
1248
+ if field["field"] in maxlengths:
1249
+ field["maxlength"] = maxlengths[field["field"]]
1250
+
1220
1251
  return fields
1221
1252
 
1222
1253
  def pronoun_fields(
@@ -1226,6 +1257,7 @@ class ALIndividual(Individual):
1226
1257
  required: bool = False,
1227
1258
  shuffle: bool = False,
1228
1259
  show_unknown: Optional[Union[Literal["guess"], bool]] = "guess",
1260
+ maxlengths: Optional[Dict[str, int]] = None,
1229
1261
  ) -> List[Dict[str, str]]:
1230
1262
  """
1231
1263
  Generate fields for capturing pronoun information.
@@ -1236,6 +1268,7 @@ class ALIndividual(Individual):
1236
1268
  required (bool): Whether the field is required. Defaults to False.
1237
1269
  shuffle (bool): Whether to shuffle the order of pronouns. Defaults to False.
1238
1270
  show_unknown (Union[Literal["guess"], bool]): Whether to show an "unknown" option. Can be "guess", True, or False. Defaults to "guess".
1271
+ maxlengths (Dict[str, int], optional): A dictionary of field names and their maximum lengths. Default is None.
1239
1272
 
1240
1273
  Returns:
1241
1274
  List[Dict[str, str]]: A list of dictionaries with field prompts for pronouns.
@@ -1277,6 +1310,11 @@ class ALIndividual(Individual):
1277
1310
  if show_if:
1278
1311
  fields[0]["show if"] = show_if
1279
1312
 
1313
+ if maxlengths:
1314
+ for field in fields:
1315
+ if field["field"] in maxlengths:
1316
+ field["maxlength"] = maxlengths[field["field"]]
1317
+
1280
1318
  return fields
1281
1319
 
1282
1320
  def get_pronouns(self) -> set:
@@ -1314,6 +1352,7 @@ class ALIndividual(Individual):
1314
1352
  choices: Optional[List[Dict[str, str]]] = None,
1315
1353
  style: str = "radio",
1316
1354
  show_if: Union[str, Dict[str, str], None] = None,
1355
+ maxlengths: Optional[Dict[str, int]] = None,
1317
1356
  ) -> List[Dict[str, str]]:
1318
1357
  """
1319
1358
  Generate fields for capturing language preferences.
@@ -1322,6 +1361,7 @@ class ALIndividual(Individual):
1322
1361
  choices (Optional[List[Dict[str, str]]]): A list of language choices. Defaults to None.
1323
1362
  style (str): The display style of choices. Defaults to "radio".
1324
1363
  show_if (Union[str, Dict[str, str], None]): Condition to determine if the field should be shown. Defaults to None.
1364
+ maxlengths (Dict[str, int], optional): A dictionary of field names and their maximum lengths. Default is None.
1325
1365
 
1326
1366
  Returns:
1327
1367
  List[Dict[str, str]]: A list of dictionaries with field prompts for language preferences.
@@ -1349,6 +1389,11 @@ class ALIndividual(Individual):
1349
1389
  fields[0]["input type"] = "radio"
1350
1390
  if show_if:
1351
1391
  fields[0]["show if"] = show_if
1392
+
1393
+ if maxlengths:
1394
+ for field in fields:
1395
+ if field["field"] in maxlengths:
1396
+ field["maxlength"] = maxlengths[field["field"]]
1352
1397
  return fields
1353
1398
 
1354
1399
  def language_name(self) -> str:
@@ -1532,7 +1577,12 @@ class ALIndividual(Individual):
1532
1577
  Returns:
1533
1578
  str: The appropriate pronoun.
1534
1579
  """
1535
- # Developers can do funky things; be a little flexible
1580
+ person = str(kwargs.get("person", self.get_point_of_view()))
1581
+
1582
+ if person in ("1", "1p", "2", "2p"):
1583
+ # Use the parent version of pronoun
1584
+ return super().pronoun(**kwargs)
1585
+
1536
1586
  if hasattr(self, "pronouns") and isinstance(self.pronouns, str):
1537
1587
  pronouns = DADict(elements={self.pronouns.lower(): True})
1538
1588
  else:
@@ -1605,6 +1655,12 @@ class ALIndividual(Individual):
1605
1655
  Returns:
1606
1656
  str: The appropriate possessive phrase.
1607
1657
  """
1658
+ person = str(kwargs.get("person", self.get_point_of_view()))
1659
+
1660
+ if person in ("1", "1p", "2", "2p"):
1661
+ # Use the parent version of pronoun
1662
+ return super().pronoun_possessive(target, **kwargs)
1663
+
1608
1664
  if hasattr(self, "pronouns") and isinstance(self.pronouns, str):
1609
1665
  pronouns = DADict(elements={self.pronouns.lower(): True})
1610
1666
  else:
@@ -1671,6 +1727,12 @@ class ALIndividual(Individual):
1671
1727
  Returns:
1672
1728
  str: The appropriate subjective pronoun.
1673
1729
  """
1730
+ person = str(kwargs.get("person", self.get_point_of_view()))
1731
+
1732
+ if person in ("1", "1p", "2", "2p"):
1733
+ # Use the parent version of pronoun
1734
+ return super().pronoun_subjective(**kwargs)
1735
+
1674
1736
  if hasattr(self, "pronouns") and isinstance(self.pronouns, str):
1675
1737
  pronouns = DADict(elements={self.pronouns.lower(): True})
1676
1738
  else:
@@ -1750,6 +1812,82 @@ class ALIndividual(Individual):
1750
1812
  """
1751
1813
  return self.name.firstlast()
1752
1814
 
1815
+ def familiar(
1816
+ self, unique_names: Optional[List[Any]] = None, default: Optional[str] = None
1817
+ ) -> str:
1818
+ """
1819
+ Returns the individual's name in the most familiar form possible.
1820
+
1821
+ The purpose is to allow using a short version of the individual's name in an unambiguous
1822
+ way in the interview. For example: referring to the child in a guardianship petition
1823
+ by first name instead of "the minor". But there may be a problem if context doesn't make
1824
+ it clear if you are talking about the child or their parent when they share a name.
1825
+
1826
+ In order, it will try to use:
1827
+
1828
+ * just the first name
1829
+ * the first name and suffix
1830
+ * the first and middle name
1831
+ * the first and last name
1832
+ * the full name
1833
+ * the default value, e.g., "the minor", if provided
1834
+ * the full name
1835
+
1836
+ Args:
1837
+ unique_names (Optional[List[Any]]): A list of unique names to compare against. Defaults to None.
1838
+ default (Optional[str]): The default name to return if no unique name is found. Defaults to None.
1839
+
1840
+ Returns:
1841
+ str: The individual's name in the most familiar form possible.
1842
+
1843
+ Example:
1844
+ ```mako
1845
+ Who do you want to take care of ${ children.familiar(unique_names=parents + petitioners, default="the minor") }
1846
+ ```
1847
+ """
1848
+ if unique_names is None:
1849
+ unique_names = []
1850
+
1851
+ first_name_candidates = [person.familiar() for person in unique_names]
1852
+ if self.name.first not in first_name_candidates:
1853
+ return self.name.first
1854
+
1855
+ first_name_and_suffix_candidates = [
1856
+ f"{person.familiar()} {person.name.suffix if hasattr(person.name, 'suffix') else ''}"
1857
+ for person in unique_names
1858
+ ]
1859
+ if (
1860
+ f"{self.name.first} {self.name.suffix if hasattr(self.name, 'suffix') else ''}"
1861
+ not in first_name_and_suffix_candidates
1862
+ ):
1863
+ if hasattr(self.name, "suffix") and self.name.suffix:
1864
+ return f"{self.name.first} {self.name.suffix if hasattr(self.name, 'suffix') else ''}"
1865
+ return self.name.first
1866
+
1867
+ first_and_middle_candidates = [
1868
+ f"{person.name.first} {person.name.middle if hasattr(person.name, 'middle') and person.name.middle else ''}"
1869
+ for person in unique_names
1870
+ ]
1871
+ if (
1872
+ f"{self.name.first} {self.name.middle if hasattr(self.name, 'middle') and self.name.middle else ''}"
1873
+ not in first_and_middle_candidates
1874
+ ):
1875
+ if hasattr(self.name, "middle") and self.name.middle:
1876
+ return f"{self.name.first} {self.name.middle}"
1877
+ return self.name.first
1878
+
1879
+ first_and_last_candidates = [person.name.firstlast() for person in unique_names]
1880
+ if self.name_short() not in first_and_last_candidates:
1881
+ return self.name_short()
1882
+
1883
+ full_name_candidates = [person.name.full() for person in unique_names]
1884
+ if self.name_full() not in full_name_candidates:
1885
+ return self.name_full()
1886
+
1887
+ if default:
1888
+ return default
1889
+ return self.name_full() # We tried but couldn't disambiguate
1890
+
1753
1891
 
1754
1892
  # (DANav isn't in public DA API, but currently in functions.py)
1755
1893
  def section_links(nav) -> List[str]: # type: ignore
@@ -103,4 +103,7 @@ code: |
103
103
  AL_DEFAULT_OVERFLOW_MESSAGE = "..."
104
104
  ---
105
105
  code: |
106
- al_form_requires_digital_signature = True
106
+ al_form_requires_digital_signature = True
107
+ ---
108
+ code: |
109
+ al_person_answering = "user"
@@ -0,0 +1,67 @@
1
+ ---
2
+ include:
3
+ - assembly_line.yml
4
+ ---
5
+ metadata:
6
+ title: |
7
+ Search sessions demo
8
+ required privileges:
9
+ - admin
10
+ - developer
11
+ - advocate
12
+ ---
13
+ mandatory: True
14
+ code: |
15
+ search_keyword
16
+ if not matching_results:
17
+ no_matches
18
+ show_results
19
+ show_selected_interview
20
+ ---
21
+ id: search_keyword
22
+ question: |
23
+ What would you like to search for?
24
+ subquestion: |
25
+ Searching is not case sensitive.
26
+ fields:
27
+ - Keyword: search_keyword
28
+ ---
29
+ code: |
30
+ matching_results = find_matching_sessions(search_keyword, user_id="all")
31
+ ---
32
+ event: no_matches
33
+ question: |
34
+ We didn't find any matches
35
+ subquestion: |
36
+ Check your spelling and try again.
37
+ ---
38
+ continue button field: show_results
39
+ question: |
40
+ We found ${ len(matching_results) } results that match your keyword
41
+ subquestion: |
42
+ % for result in matching_results:
43
+ * ${ nice_interview_subtitle(result, exclude_identical=False)}
44
+ ${ result["modtime"]}
45
+ % endfor
46
+
47
+ Raw results:
48
+ <div class="overflow-scroll" style="height: 200px;">
49
+ ${ matching_results }
50
+ </div>
51
+ fields:
52
+ - Which session do you want to load: interview_to_load
53
+ datatype: integer
54
+ code: |
55
+ [{idx: f"<b>{nice_interview_subtitle(answer, exclude_identical=False)}</b>" + f' ({answer.get("modtime", DAEmpty()).strftime("%B %d, %Y")})'} for idx, answer in enumerate(matching_results)]
56
+ ---
57
+ event: show_selected_interview
58
+ id: show the selected interview
59
+ question: |
60
+ Here is some information from the session you chose
61
+ subquestion: |
62
+ Filename: ${ matching_results[interview_to_load]["filename"] } [BR]
63
+ Session ID: ${ matching_results[interview_to_load]["key"] }
64
+
65
+ <div class="overflow-scroll" style="height: 200px;">
66
+ <pre>${ get_filtered_session_variables_string(filename=matching_results[interview_to_load]["filename"], session_id=matching_results[interview_to_load]["key"]) }</pre>
67
+ </div