From 7616c002502b17e4c97a845fdb1b2849ecf4b80f Mon Sep 17 00:00:00 2001 From: Jonathan Pyle Date: Fri, 15 Mar 2024 14:28:39 -0400 Subject: [PATCH] noun_is_singular keyword parameter to noun_plural; describe method of DateTimeDelta expanded to describe times as well as dates; fallback to using repr when reporting errors that require formatting YAML that may be invalid; bug with ip address ban enabled; interview logic history was not displaying unless development site is protected is true --- CHANGELOG.md | 16 ++++++++++ .../base/data/sources/base-words.yml | 3 ++ .../docassemble/base/functions.py | 30 +++++++++++++++---- docassemble_base/docassemble/base/parse.py | 17 +++++++---- docassemble_base/docassemble/base/util.py | 19 ++++++++++-- .../docassemble/webapp/server.py | 2 +- .../docassemble/webapp/users/forms.py | 1 + 7 files changed, 73 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0dbdfd69..708583bd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Change Log +## [1.4.101] - 2024-03-15 + +### Added +- The `noun_is_singular` keyword parameter to the `noun_plural()` + function. + +### Changed +- The `date_difference()` function's `describe()` method will now + describe times. +- In error messages from YAML parsing, fall back to displaying YAML + with `repr()` if displaying the YAML as YAML fails. + +### Fixed +- Bug with `ip address ban enabled`. +- Bug with displaying interview logic history. + ## [1.4.100] - 2024-03-07 ### Fixed diff --git a/docassemble_base/docassemble/base/data/sources/base-words.yml b/docassemble_base/docassemble/base/data/sources/base-words.yml index 2292022c9..5a5c6e90d 100644 --- a/docassemble_base/docassemble/base/data/sources/base-words.yml +++ b/docassemble_base/docassemble/base/data/sources/base-words.yml @@ -410,6 +410,7 @@ "Home Page": Null "Honduras": Null "Hong Kong": Null +"hour": Null "How question came to be asked": Null "How this question came to be asked": Null "How would you like the data to be imported?": Null @@ -570,6 +571,7 @@ "Methods": Null "Mexico": Null "Micronesia, Federated States of": Null +"minute": Null "Models": Null "Modules available in Playground": Null "Modules defined": Null @@ -864,6 +866,7 @@ "Score": Null "Search": Null "Second factor authentication": Null +"second": Null "Second subdivision": Null "Second Subdivision": Null "Section": Null diff --git a/docassemble_base/docassemble/base/functions.py b/docassemble_base/docassemble/base/functions.py index 6ac8335c6..8f89a6563 100644 --- a/docassemble_base/docassemble/base/functions.py +++ b/docassemble_base/docassemble/base/functions.py @@ -3150,7 +3150,10 @@ def number_or_length(target): def noun_plural_en(*pargs, **kwargs): ensure_definition(*pargs, **kwargs) - noun = noun_singular_en(pargs[0]) + if kwargs.get('noun_is_singular', False): + noun = pargs[0] + else: + noun = noun_singular_en(pargs[0]) if len(pargs) >= 2 and number_or_length(pargs[1]) == 1: return str(noun) output = docassemble_pattern.en.pluralize(str(noun)) @@ -3209,7 +3212,10 @@ def verb_past_es(*pargs, **kwargs): def noun_plural_es(*pargs, **kwargs): ensure_definition(*pargs, **kwargs) - noun = noun_singular_es(pargs[0]) + if kwargs.get('noun_is_singular', False): + noun = pargs[0] + else: + noun = noun_singular_es(pargs[0]) if len(pargs) >= 2 and number_or_length(pargs[1]) == 1: return str(noun) output = docassemble_pattern.es.pluralize(str(noun)) @@ -3268,7 +3274,10 @@ def verb_past_de(*pargs, **kwargs): def noun_plural_de(*pargs, **kwargs): ensure_definition(*pargs, **kwargs) - noun = noun_singular_de(pargs[0]) + if kwargs.get('noun_is_singular', False): + noun = pargs[0] + else: + noun = noun_singular_de(pargs[0]) if len(pargs) >= 2 and number_or_length(pargs[1]) == 1: return str(noun) output = docassemble_pattern.de.pluralize(str(noun)) @@ -3327,7 +3336,10 @@ def verb_past_fr(*pargs, **kwargs): def noun_plural_fr(*pargs, **kwargs): ensure_definition(*pargs, **kwargs) - noun = noun_singular_fr(pargs[0]) + if kwargs.get('noun_is_singular', False): + noun = pargs[0] + else: + noun = noun_singular_fr(pargs[0]) if len(pargs) >= 2 and number_or_length(pargs[1]) == 1: return str(noun) output = docassemble_pattern.fr.pluralize(str(noun)) @@ -3386,7 +3398,10 @@ def verb_past_it(*pargs, **kwargs): def noun_plural_it(*pargs, **kwargs): ensure_definition(*pargs, **kwargs) - noun = noun_singular_it(pargs[0]) + if kwargs.get('noun_is_singular', False): + noun = pargs[0] + else: + noun = noun_singular_it(pargs[0]) if len(pargs) >= 2 and number_or_length(pargs[1]) == 1: return str(noun) output = docassemble_pattern.it.pluralize(str(noun)) @@ -3445,7 +3460,10 @@ def verb_past_nl(*pargs, **kwargs): def noun_plural_nl(*pargs, **kwargs): ensure_definition(*pargs, **kwargs) - noun = noun_singular_nl(pargs[0]) + if kwargs.get('noun_is_singular', False): + noun = pargs[0] + else: + noun = noun_singular_nl(pargs[0]) if len(pargs) >= 2 and number_or_length(pargs[1]) == 1: return str(noun) output = docassemble_pattern.nl.pluralize(str(noun)) diff --git a/docassemble_base/docassemble/base/parse.py b/docassemble_base/docassemble/base/parse.py index 1d340f0f3..a0cc45475 100644 --- a/docassemble_base/docassemble/base/parse.py +++ b/docassemble_base/docassemble/base/parse.py @@ -2047,11 +2047,18 @@ def process_js_vars(expr): class Question: def idebug(self, data): - if hasattr(self, 'from_source') and hasattr(self, 'package'): - if isinstance(self.line_number, int): - return f"\nIn file {self.from_source.path} in the block on line {self.line_number} from package {self.package}:\n\n" + safeyaml.dump_to_string(data) - return "\nIn file " + str(self.from_source.path) + " from package " + str(self.package) + ":\n\n" + safeyaml.dump_to_string(data) - return safeyaml.dump_to_string(data) + try: + if hasattr(self, 'from_source') and hasattr(self, 'package'): + if isinstance(self.line_number, int): + return f"\nIn file {self.from_source.path} in the block on line {self.line_number} from package {self.package}:\n\n" + safeyaml.dump_to_string(data) + return "\nIn file " + str(self.from_source.path) + " from package " + str(self.package) + ":\n\n" + safeyaml.dump_to_string(data) + return safeyaml.dump_to_string(data) + except: + if hasattr(self, 'from_source') and hasattr(self, 'package'): + if isinstance(self.line_number, int): + return f"\nIn file {self.from_source.path} in the block on line {self.line_number} from package {self.package}:\n\n" + repr(data) + return "\nIn file " + str(self.from_source.path) + " from package " + str(self.package) + ":\n\n" + repr(data) + return repr(data) def id_debug(self, data): """One liner info about a YAML block. Used in `compile` for later error reporting.""" diff --git a/docassemble_base/docassemble/base/util.py b/docassemble_base/docassemble/base/util.py index 0c2f7092c..d77032e6b 100644 --- a/docassemble_base/docassemble/base/util.py +++ b/docassemble_base/docassemble/base/util.py @@ -6784,11 +6784,24 @@ def describe(self, **kwargs): output = [] diff = dateutil.relativedelta.relativedelta(self.end, self.start) if diff.years != 0: - output.append((abs(diff.years), noun_plural(word('year'), abs(diff.years)))) + output.append((abs(diff.years), noun_plural(word('year'), abs(diff.years), noun_is_singular=True))) if diff.months != 0 and specificity != 'year': - output.append((abs(diff.months), noun_plural(word('month'), abs(diff.months)))) + output.append((abs(diff.months), noun_plural(word('month'), abs(diff.months), noun_is_singular=True))) if diff.days != 0 and specificity not in ('year', 'month'): - output.append((abs(diff.days), noun_plural(word('day'), abs(diff.days)))) + output.append((abs(diff.days), noun_plural(word('day'), abs(diff.days), noun_is_singular=True))) + if len(output) == 0 or specificity in ('hour', 'minute', 'second'): + if diff.hours != 0 and specificity not in ('year', 'month', 'day'): + output.append((abs(diff.hours), noun_plural(word('hour'), abs(diff.hours), noun_is_singular=True))) + if (abs(diff.hours) < 2 or specificity in ('minute', 'second')) and diff.minutes != 0 and specificity not in ('year', 'month', 'day', 'hour'): + output.append((abs(diff.minutes), noun_plural(word('minute'), abs(diff.minutes), noun_is_singular=True))) + if len(output) == 0 or specificity == 'second': + if diff.seconds != 0 and specificity not in ('year', 'month', 'day', 'hour', 'minute'): + output.append((abs(diff.seconds), noun_plural(word('second'), abs(diff.seconds), noun_is_singular=True))) + if len(output) == 0: + if specificity is None: + output.append((0, noun_plural(word('second'), 0, noun_is_singular=True))) + else: + output.append((0, noun_plural(word(specificity), 0, noun_is_singular=True))) if kwargs.get('nice', True): return_value = comma_and_list(["%s %s" % (nice_number(y[0]), y[1]) for y in output]) if kwargs.get('capitalize', False): diff --git a/docassemble_webapp/docassemble/webapp/server.py b/docassemble_webapp/docassemble/webapp/server.py index 9c0e8f78d..f1a323f4b 100644 --- a/docassemble_webapp/docassemble/webapp/server.py +++ b/docassemble_webapp/docassemble/webapp/server.py @@ -23324,7 +23324,7 @@ def server_error(the_error): if 'in error' not in session and docassemble.base.functions.this_thread.interview is not None and 'error action' in docassemble.base.functions.this_thread.interview.consolidated_metadata: session['in error'] = True return index(action_argument={'action': docassemble.base.functions.this_thread.interview.consolidated_metadata['error action'], 'arguments': {'error_message': orig_errmess, 'error_history': the_history, 'error_trace': the_trace}}, refer=['error']) - show_debug = not bool((not (DEBUG and daconfig.get('development site is protected', False))) and isinstance(the_error, (DAError, DAInvalidFilename))) + show_debug = not bool((not ((DEBUG and daconfig.get('development site is protected', False)) or (current_user.is_authenticated and current_user.has_role('admin', 'developer')))) and isinstance(the_error, (DAError, DAInvalidFilename))) if int(int(error_code)/100) == 4: show_debug = False if error_code == 404: diff --git a/docassemble_webapp/docassemble/webapp/users/forms.py b/docassemble_webapp/docassemble/webapp/users/forms.py index 981b24104..fd18ca350 100644 --- a/docassemble_webapp/docassemble/webapp/users/forms.py +++ b/docassemble_webapp/docassemble/webapp/users/forms.py @@ -82,6 +82,7 @@ def ldap_bind(self, connect): return username, password def validate(self): + failed_attempts = None if BAN_IP_ADDRESSES: key = 'da:failedlogin:ip:' + str(get_requester_ip(request)) failed_attempts = r.get(key)