diff --git a/LICENSE b/LICENSE index e93d405f..be2f1ae9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2022 AssemblyLine +Copyright (c) 2023 AssemblyLine Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docassemble/ALToolbox/data/questions/collapse_template_demo.yml b/docassemble/ALToolbox/data/questions/collapse_template_demo.yml index 53d95519..678d54f8 100644 --- a/docassemble/ALToolbox/data/questions/collapse_template_demo.yml +++ b/docassemble/ALToolbox/data/questions/collapse_template_demo.yml @@ -39,11 +39,20 @@ subquestion: | What is your favorite fruit? Everybody has a favorite fruit. ${ collapse_template(fruit_explanation, closed_icon="plus", open_icon="minus") } + Don't you have a favorite fruit? + ${ collapse_template(favorite_explanation) } + You must have a favorite. fields: - Favorite fruit: favorite_fruit +css: | + --- mandatory: True question: | diff --git a/docassemble/ALToolbox/data/questions/display_template_demo.yml b/docassemble/ALToolbox/data/questions/display_template_demo.yml index 5224a82e..da348610 100644 --- a/docassemble/ALToolbox/data/questions/display_template_demo.yml +++ b/docassemble/ALToolbox/data/questions/display_template_demo.yml @@ -31,8 +31,8 @@ subquestion: | 1. **copy** (default=False) - It adds a copy button under the template for users to copy/share the template content. - This feature applies the **copy_button_html()** function to template content. See [copy button demo](${interview_url(i=user_info().package + ":copy_button_demo.yml")}). - 1. **class_name** (default=None) - - To style your template, include a class_name input in the call, and add your css rules either on the screen or to a .css file. + 1. **classname** (default="bg-light") + - To style your template content, include a `classname` keyword argument when you call the function and add your css rules either on the screen or to a .css file. You can mix and match these features. The following screens will show you how. @@ -44,10 +44,13 @@ question: | Scrollable examples subquestion: | ##### Srollable only - ${ display_template(my_template1) }
+ ${ display_template(my_template1) } ##### Style only - ${ display_template(my_template2, scrollable=False, class_name="my_color")} + ${ display_template(my_template2, scrollable=False, classname="my_color") } + + ##### Template with no subject + ${ display_template(template_no_subject) } --- id: collapse and scrollable examples continue button field: collapse_n_scrollable_examples @@ -55,26 +58,28 @@ question: | Collase and scrollable examples subquestion: | ##### Collapse only with a style - ${ display_template(my_template1, scrollable=False, collapse=True, class_name="my_color") } + ${ display_template(my_template1, scrollable=False, collapse=True, classname="my_color") } ##### Collapse and scrollable, without the copy button ${ display_template(my_template2, collapse=True) } --- id: copy button examples -continue button field: copy_button_examples +event: copy_button_examples question: | Copy button examples -fields: - - html:
Copy and scrollable
- - note: | - ${ display_template(my_template1, copy=True) } - - html:
Copy and collapse but not scrollable
- - note: | - _(Hover over the box to see the full template content. If this is annoying, keep the default value scrollable=True as shown in the very last example.)_

- ${ display_template(my_template1, scrollable=False, collapse=True, copy=True) } - - html:
Copy and collapse and scrollable
- - note: | - ${ display_template(my_template2, collapse=True, copy=True) } +subquestion: | +
Copy and scrollable
+ + ${ display_template(my_template1, copy=True) } + +
Copy and collapse but not scrollable
+ + _(Hover over the box to see the full template content. If this is annoying, keep the default value scrollable=True as shown in the very last example.)_ + + ${ display_template(my_template1, scrollable=False, collapse=True, copy=True) } +
Copy and collapse and scrollable
+ + ${ display_template(my_template2, collapse=True, copy=True) } --- template: my_template1 subject: | @@ -94,4 +99,10 @@ content: | Est ad autem vitae, ius graeco delicata eu. Vim quod postea ad. Vero omnis iuvaret in ius. Eu noster volumus sed. - Qui eu dictas senserit. Ne affert doctus his. Eius dicta utinam ex pri, tota equidem vel at. Cu nec habemus definitiones, eos oratio impedit mediocrem ex, inermis necessitatibus duo et. Malis graeco dicunt ei mea. Ei idque cetero est. \ No newline at end of file + Qui eu dictas senserit. Ne affert doctus his. Eius dicta utinam ex pri, tota equidem vel at. Cu nec habemus definitiones, eos oratio impedit mediocrem ex, inermis necessitatibus duo et. Malis graeco dicunt ei mea. Ei idque cetero est. +--- +template: template_no_subject +content: | + Qui eu dictas senserit. Ne affert doctus his. Eius dicta utinam ex pri, tota equidem vel at. Cu nec habemus definitiones, eos oratio impedit mediocrem ex, inermis necessitatibus duo et. Malis graeco dicunt ei mea. Ei idque cetero est. + + Est ad autem vitae, ius graeco delicata eu. Vim quod postea ad. Vero omnis iuvaret in ius. Eu noster volumus sed. \ No newline at end of file diff --git a/docassemble/ALToolbox/data/static/collapse_template.css b/docassemble/ALToolbox/data/static/collapse_template.css index 868c8470..e1ad2daf 100644 --- a/docassemble/ALToolbox/data/static/collapse_template.css +++ b/docassemble/ALToolbox/data/static/collapse_template.css @@ -1,3 +1,7 @@ +.al_collapse_template { + padding-bottom: 1rem; +} + a span.pdcaretopen { display: inline; } diff --git a/docassemble/ALToolbox/data/static/display_template.css b/docassemble/ALToolbox/data/static/display_template.css index 03e4aaa4..572d8a3b 100644 --- a/docassemble/ALToolbox/data/static/display_template.css +++ b/docassemble/ALToolbox/data/static/display_template.css @@ -1,5 +1,9 @@ +.al_display_template { + padding-bottom: 1rem; +} + .scrollable-panel{ - height:200px; + max-height:200px; overflow-y:scroll; width:100%; } diff --git a/docassemble/ALToolbox/data/static/styles.css b/docassemble/ALToolbox/data/static/styles.css index 73414d97..06600c61 100644 --- a/docassemble/ALToolbox/data/static/styles.css +++ b/docassemble/ALToolbox/data/static/styles.css @@ -5,6 +5,7 @@ background: lightcyan; color: darkgreen; } -h5 { + +h5, .h5 { color: brown; } \ No newline at end of file diff --git a/docassemble/ALToolbox/display_template.py b/docassemble/ALToolbox/display_template.py index b9047ba1..017a5faa 100644 --- a/docassemble/ALToolbox/display_template.py +++ b/docassemble/ALToolbox/display_template.py @@ -1,9 +1,15 @@ import re from .copy_button import * +from base64 import b64encode def display_template( - template, scrollable=True, collapse=False, copy=False, class_name="bg-light" + template, + scrollable=True, + collapse=False, + copy=False, + classname="bg-light", + class_name=None # depricated ) -> str: # 1. Initialize if scrollable: @@ -14,14 +20,24 @@ def display_template( adjust_height = ( f"onmouseover=\"this.style.height = (this.scrollHeight) + 'px';\"" ) - + + # Introducing `classname` to try to align with `collapse_template` + if not class_name: + class_name = classname class_name = class_name.strip() + + container_classname = "al_display_template" - the_id = re.sub(r"[^A-Za-z0-9]", "", template.instanceName) + container_id = b64encode(str(template.instanceName).encode()).decode().replace('=', '') + contents_id = f"{ container_id }_contents" + + subject_html = '' + if not template.subject == "": + subject_html = f'

{template.subject_as_html(trim=True)}

' # 2. If copiable, call copy_button_html() to generate the template content along with a copy button if copy: - text = copy_button_html( + contents = copy_button_html( template, copy_template_block=True, scroll_class=scroll_class, @@ -31,16 +47,21 @@ def display_template( # 2.1 If collapsible, add collapsible elements to the output if collapse: - return f'
{text}
' + return f'
{contents}
' # 2.2 If not collapsible, simply return output from copy_button_html() else: - return text + return f""" +
+{subject_html} +{contents} +
+""" # 3. If not copiable, generate the whole output else: - if not collapse: - return f'

{template.subject_as_html(trim=True)}

{template.content_as_html()}
' + if collapse: + return f'
{template.content_as_html()}
' else: - return f'
{template.content_as_html()}
' + return f'
{subject_html}
{template.content_as_html()}
' diff --git a/docassemble/ALToolbox/misc.py b/docassemble/ALToolbox/misc.py index fe852524..017a5faa 100644 --- a/docassemble/ALToolbox/misc.py +++ b/docassemble/ALToolbox/misc.py @@ -1,330 +1,67 @@ -from typing import Optional, Union - -from decimal import Decimal -import docassemble.base.functions -from docassemble.base.util import ( - defined, - value, - showifdef, - space_to_underscore, - action_button_html, - Address, - word, -) import re +from .copy_button import * +from base64 import b64encode -__all__ = [ - "shortenMe", - "thousands", - "tel", - "fa_icon", - "space", - "yes_no_unknown", - "number_to_letter", - "collapse_template", - "tabbed_templates_html", - "review_widget", - "sum_if_defined", - "add_records", - "output_checkbox", - "nice_county_name", -] - - -class shortenMe: - def __init__(self, originalURL): - self.shortenedURL = docassemble.base.functions.temp_redirect( - originalURL, 60 * 60 * 24 * 7, False, False - ) - - -# The following three functions are from Quinten -def thousands(num: Union[float, str, Decimal], show_decimals=False) -> str: - """ - Return a whole number formatted with thousands separator. - Optionally, format with 2 decimal points (for a PDF form with the - currency symbol already present in the form) - If `show_decimals`, will call `int(num)`, truncating the decimals instead of - rounding to the closest int. - """ - try: - if show_decimals: - return f"{num:,.2f}" - else: - return f"{int(num):,}" - except: - return str(num) - - -def tel(phone_number) -> str: - """Format a phone number so you can click on it to open in your phone dialer""" - return '' + str(phone_number) + "" - - -def fa_icon( - icon: str, - color: Optional[str] = "primary", - color_css: Optional[str] = None, - size: Optional[str] = "sm", - fa_class: str = "fa-solid", +def display_template( + template, + scrollable=True, + collapse=False, + copy=False, + classname="bg-light", + class_name=None # depricated ) -> str: - """Display a fontawesome icon inline. - - Docassemble allows you to display an icon from [fontawesome](https://fontawesome.com), - but it does not provide control over the size or color of the icon. This function gives - you more control over the icon that is inserted. - - Args: - icon: a string representing a fontawesome icon. The icon needs to be in the - [free library](https://fontawesome.com/search?o=r&m=free). - color: can be any [Bootstrap color variable](https://getbootstrapc.mo/docs/4.0/utilities/colors). - For example: `primary`, `secondary`, `warning` - color_css: allows you to use a CSS code to represent the color, e.g., `blue`, or `#fff` for black - size: used to control the [fontawesome size](https://fontawesome.com/v6.0/docs/web/style/size) - (without the `fa-` prefix). Valid values include `2xs`, `xs`, the default of `sm`, - `md`, `lg`, `xl`, `2x1`, and the python `None`, which defaults to `md`. - fa_class: let's you specify the fontawesome class, needed for any icon that isn't - the default class of `fa-solid`, like `fa-brands`, or `fa-regular` and `fa-light`. - - Returns: - HTML for a font-awesome icon of the specified size and color. - """ - if not size or size == "md": - size_str = "" + # 1. Initialize + if scrollable: + scroll_class = "scrollable-panel" + adjust_height = "" else: - size_str = " fa-" + size - if not size and not color and not color_css: - return ":" + icon + ":" # Default to letting Docassemble handle it - if color_css: - return ( - f'' + scroll_class = "not-scrollable" + adjust_height = ( + f"onmouseover=\"this.style.height = (this.scrollHeight) + 'px';\"" ) - if color: - return ( - f'' + + # Introducing `classname` to try to align with `collapse_template` + if not class_name: + class_name = classname + class_name = class_name.strip() + + container_classname = "al_display_template" + + container_id = b64encode(str(template.instanceName).encode()).decode().replace('=', '') + contents_id = f"{ container_id }_contents" + + subject_html = '' + if not template.subject == "": + subject_html = f'

{template.subject_as_html(trim=True)}

' + + # 2. If copiable, call copy_button_html() to generate the template content along with a copy button + if copy: + contents = copy_button_html( + template, + copy_template_block=True, + scroll_class=scroll_class, + style_class=class_name, + adjust_height=adjust_height, ) - return f'' - - -def space(var_name: str, prefix=" ", suffix="") -> str: - """If the value as a string is defined, return it prefixed/suffixed. Defaults to prefix - of a space. Helps build a sentence with less cruft. Equivalent to SPACE function in - HotDocs.""" - if ( - var_name - and isinstance(var_name, str) - and re.search(r"[A-Za-z][A-Za-z0-9\_]*", var_name) - and defined(var_name) - and value(var_name) - ): - return prefix + showifdef(var_name) + suffix - else: - return "" - - -def yes_no_unknown( - var_name: str, condition: Optional[bool], unknown="Unknown", placeholder=0 -): - """Return 'unknown' if the value is None rather than False. Helper for PDF filling with - yesnomaybe fields""" - if condition: - return value(var_name) - elif condition is None: - return unknown - else: - return placeholder - - -def number_to_letter(n: Optional[int]) -> str: - """Returns a capital letter representing ordinal position. E.g., 1=A, 2=B, etc. Appends letters - once you reach 26 in a way compatible with Excel/Google Sheets column naming conventions. 27=AA, 28=AB... - """ - string = "" - if n is None: - n = 0 - while n > 0: - n, remainder = divmod(n - 1, 26) - string = chr(65 + remainder) + string - return string - -def collapse_template( - template, - classname=None, - closed_icon="caret-right", - open_icon="caret-down", -): - """ - Insert HTML for a Bootstrap "collapse" div. - - Optionally, you can specify a custom icon to override the defaults: + # 2.1 If collapsible, add collapsible elements to the output + if collapse: + return f'
{contents}
' - The default icons are "right caret" which displays when the text is collapsed (`closed_icon`) and - "down caret" which displays when the text is open (`open_icon`). - """ - if not template.subject_as_html(trim=True) and not template.content_as_html(): - return "" - if classname is None: - classname = " bg-light" - else: - classname = " " + classname.strip() - the_id = re.sub(r"[^A-Za-z0-9]", "", template.instanceName) - return f"""\ - -
{ template.content_as_html() }
\ + # 2.2 If not collapsible, simply return output from copy_button_html() + else: + return f""" +
+{subject_html} +{contents} +
""" - -def tabbed_templates_html(tab_group_name: str, *pargs) -> str: - """ - Provided a list of templates, create Bootstrap v 4.5 tabs with the `subject` as the tab label. - """ - if isinstance(tab_group_name, str): - tab_group_name = space_to_underscore(tab_group_name) + # 3. If not copiable, generate the whole output else: - tab_group_name = "tabbed-group" - tabs = f'" - tab_content += "" - - return tabs + tab_content - - -def review_widget( - *, - up_action: str, - down_action: str, - review_action: Optional[str] = None, - thumbs_display: str = "Did we help you?", - review_display: str = "Thank you for your feedback. Let us know what we could do better", - submit_review_button: str = "Add your review", - post_review_display: str = "Thank you for your review!", -) -> str: - """ - A widget that allows people to give a quick review (thumbs up and down, with an optional text - component) in the middle of an interview without triggering a page reload. - - If `review_action` is provided, once you press either of the thumbs, a text input screen with - a submit button appears, and once the text review is submitted (or after the thumbs, if no - `review_action` was provided), a final "thank you" message is displayed. - - Args: - up_action: the variable name of an event to be executed on the server if the - thumbs up is pressed - down_action: the variable name of an event to be executed on the server if the - thumbs down is pressed - review_action: the variable name of an event to be execute on the - server when someone submits their text review - thumbs_display: text displayed to user describing the thumbs - review_display: text displayed to user describing the text input - submit_review_button: text on the button to submit their text review - post_review_display: text displayed to user after review is submitted - Returns: - the HTML string of the widget - """ - js_thumbs_up = f"javascript:altoolbox_thumbs_up_send('{up_action}', {'true' if review_action else 'false'})" - js_thumbs_down = f"javascript:altoolbox_thumbs_down_send('{down_action}', {'true' if review_action else 'false'})" - widget = f""" -
-
-

{word(thumbs_display)}

- {fa_icon('thumbs-up', size='md')} - {fa_icon('thumbs-down', size='md')} - """ - if review_action: - review_area_id = review_action + "_area_id" - js_review = ( - f"javascript:altoolbox_review_send('{review_action}', '{review_area_id}')" - ) - widget += f""" -

{word(review_display)}

- -
- {action_button_html(js_review, label=word(submit_review_button), size='md', classname='al-review-text al-hidden')} - """ - widget += f""" -

{word(post_review_display)}

-
-
""" - return widget - - -def sum_if_defined(*pargs): - """Lets you add up the value of variables that are not in a list""" - total = 0 - for source in pargs: - if defined(source): - total += value(source) - return total - - -def add_records(obj, labels): - """List demo interviews in the current package to be run from the landing page""" - index = 0 - for key, val in labels.items(): - obj.appendObject() - obj[index].name = key - obj[index].description = val[0] - obj[index].reference = val[1] - index += 1 - return obj - - -def output_checkbox( - value_to_check: bool, checked_value: str = "[X]", unchecked_value: str = "[ ]" -): - """Generate a conditional checkbox for docx templates - - Args: - checked_value: defaults to `[X]` but can be set to any string or even a `DAFile` or `DAStaticFile` - with the image of a checkbox - unchecked_value: opposite meaning of `checked_value` and defaults to `[ ]` - - """ - if value_to_check: - return checked_value - else: - return unchecked_value - - -def nice_county_name(address: Address) -> str: - """ - If the county name contains the word "County", which Google Address - Autocomplete does by default, remove it. - """ - if not hasattr(address, "county"): - return "" - if address.county.endswith(" County"): - return address.county[: -len(" County")] - else: - return address.county + return f'
{subject_html}
{template.content_as_html()}
' diff --git a/docassemble/__init__.py b/docassemble/__init__.py index ce5ab0dd..8d17c21c 100644 --- a/docassemble/__init__.py +++ b/docassemble/__init__.py @@ -1,5 +1,2 @@ -try: - __import__('pkg_resources').declare_namespace(__name__) -except ImportError: - __path__ = __import__('pkgutil').extend_path(__path__, __name__) +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup.cfg b/setup.cfg index b88034e4..08aedd7e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ [metadata] -description-file = README.md +description_file = README.md diff --git a/setup.py b/setup.py index 5f2d42a7..d2ae94c2 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ def find_package_data(where='.', package='', exclude=standard_exclude, exclude_d setup(name='docassemble.ALToolbox', version='0.7.1', description=('Collection of small utility functions, classes, and web components for Docassemble interviews'), - long_description='# ALToolbox\r\n\r\nThis repository is used to host small Python modules, widgets, and JavaScript web components js files that enhance Docassemble interviews. These modules were\r\nbuilt as part of the Suffolk University Law School LIT Lab\'s [Document Assembly Line project](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/).\r\nThey are placed here\r\nrather than in https://github.com/SuffolkLitLab/docassemble-AssemblyLine because we believe these small components can easily be used\r\nby anyone, regardless of whether they use any other code from the Document Assembly Line project.\r\n\r\nIf you want to add a small fuction to this project, consider adding it to the existing misc.py to avoid creating too many module files.\r\n\r\n## Documentation\r\n\r\nRead the [documentation for the functions and components](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/framework/altoolbox) to learn\r\nhow to use these components in your own [Docassemble](https://github.com/jhpyle/docassemble) projects.\r\n\r\n## Suffolk LIT Lab Document Assembly Line\r\n\r\ndrawing\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-AssemblyLine\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## Contributors: \r\n* @plocket \r\n* @nonprofittechy\r\n* @purplesky2016\r\n* @brycestevenwilley\r\n', + long_description='# ALToolbox\r\n\r\n[![PyPI version](https://badge.fury.io/py/docassemble-ALToolbox.svg)](https://badge.fury.io/py/docassemble-ALToolbox)\r\n\r\nThis repository is used to host small Python modules, widgets, and JavaScript web components js files that enhance Docassemble interviews. These modules were\r\nbuilt as part of the Suffolk University Law School LIT Lab\'s [Document Assembly Line project](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/).\r\nThey are placed here\r\nrather than in https://github.com/SuffolkLitLab/docassemble-AssemblyLine because we believe these small components can easily be used\r\nby anyone, regardless of whether they use any other code from the Document Assembly Line project.\r\n\r\nIf you want to add a small fuction to this project, consider adding it to the existing misc.py to avoid creating too many module files.\r\n\r\n## Documentation\r\n\r\nRead the [documentation for the functions and components](https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/framework/altoolbox) to learn\r\nhow to use these components in your own [Docassemble](https://github.com/jhpyle/docassemble) projects.\r\n\r\n## Suffolk LIT Lab Document Assembly Line\r\n\r\ndrawing\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-AssemblyLine\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## Contributors:\r\n* @plocket \r\n* @nonprofittechy\r\n* @purplesky2016\r\n* @brycestevenwilley\r\n', long_description_content_type='text/markdown', author='AssemblyLine', author_email='52798256+plocket@users.noreply.github.com', @@ -54,7 +54,7 @@ def find_package_data(where='.', package='', exclude=standard_exclude, exclude_d url='https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/framework/altoolbox', packages=find_packages(), namespace_packages=['docassemble'], - install_requires=['holidays>=0.14.2', 'pandas>=1.4.2'], + install_requires=['holidays>=0.27.1', 'pandas>=1.5.3'], zip_safe=False, package_data=find_package_data(where='docassemble/ALToolbox/', package='docassemble.ALToolbox'), )