Skip to content

Commit

Permalink
Catch when make_forecast=True on a calculated item is not allowing th…
Browse files Browse the repository at this point in the history
…e plug to flow through to the balance item, and throw an indicative error
  • Loading branch information
nickderobertis committed Dec 30, 2020
1 parent 1906691 commit 8741d53
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
16 changes: 13 additions & 3 deletions finstmt/config_manage/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def eqs_involving(self, item_key: str, include_self_eq: bool = False) -> List[Eq

return eqs

def _calculated_item_determinant_keys(self, item_key: str) -> List[str]:
def _calculated_item_determinant_keys(self, item_key: str, for_forecast: bool = True) -> List[str]:
determinant_keys: List[str] = []
to_process_keys: List[str] = [item_key]
i = -1
Expand All @@ -99,11 +99,21 @@ def _calculated_item_determinant_keys(self, item_key: str) -> List[str]:
new_keys = [
key for key in all_eq_keys if key != process_key and key not in to_process_keys + determinant_keys
]
if for_forecast:
accepted_keys: List[str] = []
for key in new_keys:
if self.get(key).forecast_config.make_forecast:
# As forecast is being made for this item, it is not calculated,
# and so shouldn't be processed. It should still be added to determinants
determinant_keys.append(key)
continue
accepted_keys.append(key)
new_keys = accepted_keys
to_process_keys.extend(new_keys)
return determinant_keys

def item_determinant_keys(self, item_key: str, include_pct_of: bool = True) -> List[str]:
determinant_keys = self._calculated_item_determinant_keys(item_key)
def item_determinant_keys(self, item_key: str, include_pct_of: bool = True, for_forecast: bool = True) -> List[str]:
determinant_keys = self._calculated_item_determinant_keys(item_key, for_forecast=for_forecast)
if include_pct_of:
for item in self.items:
# TODO: multiple passes through determinants may be necessary for complicated pct_of structures
Expand Down
20 changes: 18 additions & 2 deletions finstmt/resolver/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,11 +415,27 @@ def _adjust_x0_to_initial_balance_guess(
plug_key = plug1

if plug_key is None:
raise InvalidBalancePlugsException(
normally_calculated_but_not_keys: List[str] = []
for item in config.items:
if item.expr_str is not None and item.forecast_config.make_forecast == True:
normally_calculated_but_not_keys.append(item.key)
message = (
f'Trying to balance {adjust_side} but no plug affects it. One of the following '
f'items must have forecast_config.plug = True so that it can be balanced: '
f'{config.item_determinant_keys(adjust_side)}. Current plugs: {plug_keys}'
f'{config.item_determinant_keys(adjust_side)}. Current plugs: {plug_keys}. '
)
if normally_calculated_but_not_keys:
message += (
f'If you expected one of the plugs to affect {adjust_side} but it is not listed '
f'in the possible items, it may be that make_forecast has been set to True for an '
f'item which would normally be calculated from your plug, but as make_forecast is True '
f'it forecasts it rather than calculating and it cannot flow through. Possible items '
f'which are normally calculated but are instead being forecasted due to the config: '
f'{normally_calculated_but_not_keys}. Either change the plug to be that same item '
f'which is normally calculated but instead is forecasted, or set make_forecast=False '
f'for that item.'
)
raise InvalidBalancePlugsException(message)

# Determine index of array to increment. Array has structure of num plugs * num periods, with
# plugs in order of plug_keys and periods in order within the plugs
Expand Down

0 comments on commit 8741d53

Please sign in to comment.