From a1e4903a261c8c31b006591b984d3184c8ba2a77 Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Wed, 17 Jan 2024 14:51:10 -0500 Subject: [PATCH 1/5] Add an 'equity' method to ALIncome class --- docassemble/ALToolbox/al_income.py | 25 +++++++++++++++++++ .../ALToolbox/data/questions/al_income.yml | 10 ++++++-- docassemble/ALToolbox/misc.py | 19 +++++++++++++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/docassemble/ALToolbox/al_income.py b/docassemble/ALToolbox/al_income.py index e8aa3aa..4703508 100644 --- a/docassemble/ALToolbox/al_income.py +++ b/docassemble/ALToolbox/al_income.py @@ -598,6 +598,12 @@ def total(self, times_per_year: float = 1) -> Decimal: return Decimal(0) else: return super(ALAsset, self).total(times_per_year=times_per_year) + + def equity(self, loan_attribute="balance") -> Decimal: + """Returns the total equity in the asset (e.g., market value minus balance)""" + if getattr(self, loan_attribute, None) is None: + return Decimal(self.market_value) + return Decimal(self.market_value) - Decimal(getattr(self, loan_attribute)) class ALAssetList(ALIncomeList): @@ -650,6 +656,25 @@ def balance( ): result += _currency_float_to_decimal(asset.balance) return result + + def equity( + self, + source: Optional[SourceType] = None, + exclude_source: Optional[SourceType] = None, + loan_attribute: str = "balance", + ) -> Decimal: + """ + Returns the total equity in the assets (e.g., market value minus balance) + """ + self._trigger_gather() + result = Decimal(0) + satisfies_sources = _source_to_callable(source, exclude_source) + for asset in self.elements: + if (source is None and exclude_source is None) or ( + satisfies_sources(asset.source) + ): + result += asset.equity(loan_attribute=loan_attribute) + return result def owners( self, diff --git a/docassemble/ALToolbox/data/questions/al_income.yml b/docassemble/ALToolbox/data/questions/al_income.yml index f852143..4bfaedd 100644 --- a/docassemble/ALToolbox/data/questions/al_income.yml +++ b/docassemble/ALToolbox/data/questions/al_income.yml @@ -846,15 +846,21 @@ fields: asset_terms_ordered exclude: | ['vehicle'] - - How often do you get this income?: x.times_per_year + - Does this asset earn any income (like rent or interest)?: x.has_income + datatype: yesnoradio + - How often do you get the income?: x.times_per_year input type: radio code: | times_per_year_list datatype: integer - - Interest or other income from the asset: x.value + - Amount of income: x.value datatype: currency - Who owns this?: x.owner required: False +validation code: | + if not x.has_income: + x.times_per_year = 0 + x.value = 0 --- # UNIQUE FOR ALAssetList --- diff --git a/docassemble/ALToolbox/misc.py b/docassemble/ALToolbox/misc.py index 2c92288..e733d74 100644 --- a/docassemble/ALToolbox/misc.py +++ b/docassemble/ALToolbox/misc.py @@ -15,6 +15,10 @@ ) import re +from docassemble.webapp.files import SavedFile +from docassemble.webapp.backend import directory_for +import os + __all__ = [ "shortenMe", "thousands", @@ -31,8 +35,8 @@ "output_checkbox", "nice_county_name", "button_array", -] +] class shortenMe: def __init__(self, originalURL): @@ -387,3 +391,16 @@ def button_array( """ output += "" return output + + +def get_files(user_id, section='playground', project='default'): + area = SavedFile(user_id, fix=True, section=section) + the_directory = directory_for(area, project) + files = [ + os.path.join(the_directory,f) + for f in os.listdir(the_directory) if os.path.isfile(os.path.join(the_directory, f))] + return files + +def get_list_of_projects(user_id): + playground = SavedFile(user_id, fix=False, section='playground') + return playground.list_of_dirs() \ No newline at end of file From 7c4f01f767da89b97a520e2041218b6da6b91ae9 Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Wed, 17 Jan 2024 15:36:15 -0500 Subject: [PATCH 2/5] Fix formatting with Black --- docassemble/ALToolbox/al_income.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docassemble/ALToolbox/al_income.py b/docassemble/ALToolbox/al_income.py index 4703508..d546252 100644 --- a/docassemble/ALToolbox/al_income.py +++ b/docassemble/ALToolbox/al_income.py @@ -598,7 +598,7 @@ def total(self, times_per_year: float = 1) -> Decimal: return Decimal(0) else: return super(ALAsset, self).total(times_per_year=times_per_year) - + def equity(self, loan_attribute="balance") -> Decimal: """Returns the total equity in the asset (e.g., market value minus balance)""" if getattr(self, loan_attribute, None) is None: @@ -656,7 +656,7 @@ def balance( ): result += _currency_float_to_decimal(asset.balance) return result - + def equity( self, source: Optional[SourceType] = None, From b84b66258464265c53561ba5b81ad76bfcd80e42 Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Wed, 17 Jan 2024 15:41:34 -0500 Subject: [PATCH 3/5] Restore misc.py --- docassemble/ALToolbox/misc.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/docassemble/ALToolbox/misc.py b/docassemble/ALToolbox/misc.py index e733d74..2c92288 100644 --- a/docassemble/ALToolbox/misc.py +++ b/docassemble/ALToolbox/misc.py @@ -15,10 +15,6 @@ ) import re -from docassemble.webapp.files import SavedFile -from docassemble.webapp.backend import directory_for -import os - __all__ = [ "shortenMe", "thousands", @@ -35,9 +31,9 @@ "output_checkbox", "nice_county_name", "button_array", - ] + class shortenMe: def __init__(self, originalURL): self.shortenedURL = docassemble.base.functions.temp_redirect( @@ -391,16 +387,3 @@ def button_array( """ output += "" return output - - -def get_files(user_id, section='playground', project='default'): - area = SavedFile(user_id, fix=True, section=section) - the_directory = directory_for(area, project) - files = [ - os.path.join(the_directory,f) - for f in os.listdir(the_directory) if os.path.isfile(os.path.join(the_directory, f))] - return files - -def get_list_of_projects(user_id): - playground = SavedFile(user_id, fix=False, section='playground') - return playground.list_of_dirs() \ No newline at end of file From f097caf0bd8d6c099a51f564ada00bf523a4a25e Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Wed, 17 Jan 2024 17:10:08 -0500 Subject: [PATCH 4/5] Update docassemble/ALToolbox/data/questions/al_income.yml --- docassemble/ALToolbox/data/questions/al_income.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docassemble/ALToolbox/data/questions/al_income.yml b/docassemble/ALToolbox/data/questions/al_income.yml index 4bfaedd..ba640ef 100644 --- a/docassemble/ALToolbox/data/questions/al_income.yml +++ b/docassemble/ALToolbox/data/questions/al_income.yml @@ -859,7 +859,7 @@ fields: required: False validation code: | if not x.has_income: - x.times_per_year = 0 + x.times_per_year = 1 x.value = 0 --- # UNIQUE FOR ALAssetList From 65d592b6053b3190ae8fcf640c74c737c935119e Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Thu, 18 Jan 2024 09:34:15 -0500 Subject: [PATCH 5/5] Improve some docstrings near last change --- docassemble/ALToolbox/al_income.py | 90 +++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/docassemble/ALToolbox/al_income.py b/docassemble/ALToolbox/al_income.py index d546252..5633514 100644 --- a/docassemble/ALToolbox/al_income.py +++ b/docassemble/ALToolbox/al_income.py @@ -588,11 +588,15 @@ class ALAsset(ALIncome): def total(self, times_per_year: float = 1) -> Decimal: """ - Returns the .value attribute divided by the times per year you want to - calculate. The value defaults to 0. + Returns the .value attribute divided by the times per year you want to calculate. The value defaults to 0. - `times_per_year` is some denominator of a year. E.g, to express a weekly - period, use 52. The default is 1 (a year). + `times_per_year` is some denominator of a year. E.g, to express a weekly period, use 52. The default is 1 (a year). + + Args: + times_per_year (float, optional): The number of times per year to calculate. Defaults to 1. + + Returns: + Decimal: The .value attribute divided by the times per year. """ if not hasattr(self, "value") or self.value == "": return Decimal(0) @@ -600,7 +604,15 @@ def total(self, times_per_year: float = 1) -> Decimal: return super(ALAsset, self).total(times_per_year=times_per_year) def equity(self, loan_attribute="balance") -> Decimal: - """Returns the total equity in the asset (e.g., market value minus balance)""" + """ + Returns the total equity in the asset (e.g., market value minus balance). + + Args: + loan_attribute (str, optional): The attribute of the asset to use as the loan value. Defaults to "balance". + + Returns: + Decimal: The total equity in the asset. + """ if getattr(self, loan_attribute, None) is None: return Decimal(self.market_value) return Decimal(self.market_value) - Decimal(getattr(self, loan_attribute)) @@ -611,6 +623,20 @@ class ALAssetList(ALIncomeList): A list of ALAssets. The `total()` of the list will be the total income earned, which may not be what you want for a list of assets. To get the total value of all assets, use the `market_value()` method. + + Attributes: + market_value (float | Decimal): Market value of the asset. + balance (float | Decimal): Current balance of the account, e.g., like + the balance in a checking account, but could also represent a loan + amount. + value (float | Decimal, optional): Represents the income the asset earns + for a given `times_per_year`, such as interest earned in a checking + account. If not defined, the income will be set to 0, to simplify + representing the many common assets that do not earn any income. + times_per_year (float, optional): Number of times per year the asset + earns the income listed in the `value` attribute. + owner (str, optional): Full name of the asset owner as a single string. + source (str, optional): The "source" of the asset, like "vase". """ def init(self, *pargs, **kwargs): @@ -623,8 +649,18 @@ def market_value( exclude_source: Optional[SourceType] = None, ) -> Decimal: """ - Returns the total `.market_value` of assets in the list. You can filter - the assets by `source`. `source` can be a string or a list. + Returns the total `.market_value` of assets in the list. + + You can filter the assets by `source`. `source` can be a string or a list. + + Args: + source (Optional[SourceType]): The source of the assets to include in the calculation. + If None, all sources are included. Can be a string or a list. + exclude_source (Optional[SourceType]): The source of the assets to exclude from the calculation. + If None, no sources are excluded. + + Returns: + Decimal: The total market value of the assets. """ result = Decimal(0) satisfies_sources = _source_to_callable(source, exclude_source) @@ -641,11 +677,18 @@ def balance( exclude_source: Optional[SourceType] = None, ) -> Decimal: """ - Returns the total `.balance` of assets in the list, - which typically corresponds to the available funds - in a financial account. + Returns the total `.balance` of assets in the list, which typically corresponds to the available funds in a financial account. You can filter the assets by `source`. `source` can be a string or a list. + + Args: + source (Optional[SourceType]): The source of the assets to include in the calculation. + If None, all sources are included. Can be a string or a list. + exclude_source (Optional[SourceType]): The source of the assets to exclude from the calculation. + If None, no sources are excluded. + + Returns: + Decimal: The total balance of the assets. """ self._trigger_gather() result = Decimal(0) @@ -664,7 +707,18 @@ def equity( loan_attribute: str = "balance", ) -> Decimal: """ - Returns the total equity in the assets (e.g., market value minus balance) + Calculates and returns the total equity in the assets. + + This method triggers the gathering of assets, then iterates over each asset. If a source or exclude_source is not + specified, or if the asset's source satisfies the source criteria, the equity of the asset is added to the total. + + Args: + source (Optional[SourceType]): The source of the assets to include in the calculation. If None, all sources are included. + exclude_source (Optional[SourceType]): The source of the assets to exclude from the calculation. If None, no sources are excluded. + loan_attribute (str, optional): The attribute of the asset to use as the loan value. Defaults to "balance". + + Returns: + Decimal: The total equity in the assets. """ self._trigger_gather() result = Decimal(0) @@ -682,8 +736,18 @@ def owners( exclude_source: Optional[SourceType] = None, ) -> Set[str]: """ - Returns a set of the unique owners of the assets. You can filter the - assets by `source`. `source` can be a string or a list. + Returns a set of the unique owners of the assets. + + You can filter the assets by `source`. `source` can be a string or a list. + + Args: + source (Optional[SourceType]): The source of the assets to include in the calculation. + If None, all sources are included. Can be a string or a list. + exclude_source (Optional[SourceType]): The source of the assets to exclude from the calculation. + If None, no sources are excluded. + + Returns: + Set[str]: A set of the unique owners of the assets. """ owners = set() if source is None and exclude_source is None: