owlplanner 2025.12.5__py3-none-any.whl → 2026.1.26__py3-none-any.whl

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 (38) hide show
  1. owlplanner/In Discussion #58, the case of Kim and Sam.md +307 -0
  2. owlplanner/__init__.py +20 -1
  3. owlplanner/abcapi.py +24 -23
  4. owlplanner/cli/README.md +50 -0
  5. owlplanner/cli/_main.py +52 -0
  6. owlplanner/cli/cli_logging.py +56 -0
  7. owlplanner/cli/cmd_list.py +83 -0
  8. owlplanner/cli/cmd_run.py +86 -0
  9. owlplanner/config.py +315 -136
  10. owlplanner/data/__init__.py +21 -0
  11. owlplanner/data/awi.csv +75 -0
  12. owlplanner/data/bendpoints.csv +49 -0
  13. owlplanner/data/newawi.csv +75 -0
  14. owlplanner/data/rates.csv +99 -98
  15. owlplanner/debts.py +315 -0
  16. owlplanner/fixedassets.py +288 -0
  17. owlplanner/mylogging.py +157 -25
  18. owlplanner/plan.py +1044 -332
  19. owlplanner/plotting/__init__.py +16 -3
  20. owlplanner/plotting/base.py +17 -3
  21. owlplanner/plotting/factory.py +16 -3
  22. owlplanner/plotting/matplotlib_backend.py +30 -7
  23. owlplanner/plotting/plotly_backend.py +33 -10
  24. owlplanner/progress.py +66 -9
  25. owlplanner/rates.py +366 -361
  26. owlplanner/socialsecurity.py +142 -22
  27. owlplanner/tax2026.py +170 -57
  28. owlplanner/timelists.py +316 -32
  29. owlplanner/utils.py +204 -5
  30. owlplanner/version.py +20 -1
  31. {owlplanner-2025.12.5.dist-info → owlplanner-2026.1.26.dist-info}/METADATA +50 -158
  32. owlplanner-2026.1.26.dist-info/RECORD +36 -0
  33. owlplanner-2026.1.26.dist-info/entry_points.txt +2 -0
  34. owlplanner-2026.1.26.dist-info/licenses/AUTHORS +15 -0
  35. owlplanner/tax2025.py +0 -339
  36. owlplanner-2025.12.5.dist-info/RECORD +0 -24
  37. {owlplanner-2025.12.5.dist-info → owlplanner-2026.1.26.dist-info}/WHEEL +0 -0
  38. {owlplanner-2025.12.5.dist-info → owlplanner-2026.1.26.dist-info}/licenses/LICENSE +0 -0
@@ -1,10 +1,23 @@
1
1
  """
2
- Plotting backends for Owl.
2
+ Plotting backends package for Owl retirement planner.
3
3
 
4
- Copyright © 2025 - Martin-D. Lacasse
4
+ This package provides a factory pattern for creating plot backends (matplotlib,
5
+ plotly) for visualizing retirement planning results.
5
6
 
6
- Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
7
+ Copyright (C) 2025-2026 The Owlplanner Authors
7
8
 
9
+ This program is free software: you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation, either version 3 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
8
21
  """
9
22
 
10
23
  from .factory import PlotFactory
@@ -1,10 +1,24 @@
1
1
  """
2
- Base classes for plot backends.
2
+ Abstract base classes for plot backends.
3
3
 
4
- Copyright &copy; 2025 - Martin-D. Lacasse
4
+ This module defines the abstract base class interface that all plotting
5
+ backends must implement for consistent plotting functionality across
6
+ different visualization libraries.
5
7
 
6
- Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
8
+ Copyright (C) 2025-2026 The Owlplanner Authors
7
9
 
10
+ This program is free software: you can redistribute it and/or modify
11
+ it under the terms of the GNU General Public License as published by
12
+ the Free Software Foundation, either version 3 of the License, or
13
+ (at your option) any later version.
14
+
15
+ This program is distributed in the hope that it will be useful,
16
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ GNU General Public License for more details.
19
+
20
+ You should have received a copy of the GNU General Public License
21
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
8
22
  """
9
23
 
10
24
  from abc import ABC, abstractmethod
@@ -1,10 +1,23 @@
1
1
  """
2
- Factory for creating plot backends.
2
+ Factory for creating plot backend instances.
3
3
 
4
- Copyright &copy; 2025 - Martin-D. Lacasse
4
+ This module provides a factory class to create plot backends (matplotlib or
5
+ plotly) based on the specified backend type.
5
6
 
6
- Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
7
+ Copyright (C) 2025-2026 The Owlplanner Authors
7
8
 
9
+ This program is free software: you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation, either version 3 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
8
21
  """
9
22
 
10
23
  from .base import PlotBackend
@@ -1,10 +1,23 @@
1
1
  """
2
- Matplotlib implementation of plot backend.
2
+ Matplotlib backend implementation for plotting retirement planning results.
3
3
 
4
- Copyright &copy; 2025 - Martin-D. Lacasse
4
+ This module provides the Matplotlib-based implementation of the plot backend
5
+ interface for creating static visualizations of retirement planning data.
5
6
 
6
- Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
7
+ Copyright (C) 2025-2026 The Owlplanner Authors
7
8
 
9
+ This program is free software: you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation, either version 3 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
8
21
  """
9
22
 
10
23
  import numpy as np
@@ -65,10 +78,20 @@ class MatplotlibBackend(PlotBackend):
65
78
  """Core function for stacked plots."""
66
79
  nonzeroSeries = {}
67
80
  for sname in snames:
68
- for i in irange:
69
- tmp = series[sname][i]
70
- if sum(tmp) > 1.0:
71
- nonzeroSeries[sname + " " + inames[i]] = tmp
81
+ source_data = series[sname]
82
+ # Check if this is a household-level source (shape (1, N_n) when N_i > 1)
83
+ is_household = source_data.shape[0] == 1 and len(inames) > 1
84
+ if is_household:
85
+ # Show household total once without individual name
86
+ tmp = source_data[0]
87
+ if abs(sum(tmp)) > 1.0: # Use abs for debts
88
+ nonzeroSeries[sname] = tmp
89
+ else:
90
+ # Show per individual
91
+ for i in irange:
92
+ tmp = source_data[i]
93
+ if abs(sum(tmp)) > 1.0: # Use abs for debts
94
+ nonzeroSeries[sname + " " + inames[i]] = tmp
72
95
 
73
96
  if len(nonzeroSeries) == 0:
74
97
  return None, None
@@ -1,10 +1,23 @@
1
1
  """
2
- Plotly implementation of plot backend.
2
+ Plotly backend implementation for plotting retirement planning results.
3
3
 
4
- Copyright &copy; 2025 - Martin-D. Lacasse
4
+ This module provides the Plotly-based implementation of the plot backend
5
+ interface for creating interactive visualizations of retirement planning data.
5
6
 
6
- Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
7
+ Copyright (C) 2025-2026 The Owlplanner Authors
7
8
 
9
+ This program is free software: you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation, either version 3 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
8
21
  """
9
22
 
10
23
  import numpy as np
@@ -749,7 +762,7 @@ class PlotlyBackend(PlotBackend):
749
762
 
750
763
  # Add each individual's data as a separate series
751
764
  for i in range(len(inames)):
752
- if np.sum(values[i]) > 1.0: # Only show non-zero series
765
+ if np.abs(np.sum(values[i])) > 1.0: # Only show non-zero series (use abs for debts)
753
766
  stack_data.append((values[i], f"{namek} {inames[i]}"))
754
767
 
755
768
  # Add stacked area traces
@@ -829,7 +842,7 @@ class PlotlyBackend(PlotBackend):
829
842
  stack_data.append(data)
830
843
 
831
844
  # Add stacked area traces
832
- for data, name in zip(stack_data, stack_names):
845
+ for data, name in zip(stack_data, stack_names, strict=True):
833
846
  fig.add_trace(go.Scatter(
834
847
  x=year_n,
835
848
  y=data,
@@ -887,7 +900,7 @@ class PlotlyBackend(PlotBackend):
887
900
  for sname in savings:
888
901
  for i in range(len(inames)):
889
902
  data = savings[sname][i] / 1000
890
- if np.sum(data) > 1.0e-3: # Only show non-zero series
903
+ if np.abs(np.sum(data)) > 1.0e-3: # Only show non-zero series (use abs for debts)
891
904
  nonzero_series[f"{sname} {inames[i]}"] = data
892
905
 
893
906
  # Add stacked area traces for each account type
@@ -942,10 +955,20 @@ class PlotlyBackend(PlotBackend):
942
955
  # Filter out zero series and create individual series names
943
956
  nonzero_series = {}
944
957
  for sname in sources:
945
- for i in range(len(inames)):
946
- data = sources[sname][i] / 1000
947
- if np.sum(data) > 1.0e-3: # Only show non-zero series
948
- nonzero_series[f"{sname} {inames[i]}"] = data
958
+ source_data = sources[sname]
959
+ # Check if this is a household-level source (shape (1, N_n) when N_i > 1)
960
+ is_household = source_data.shape[0] == 1 and len(inames) > 1
961
+ if is_household:
962
+ # Show household total once without individual name
963
+ data = source_data[0] / 1000
964
+ if np.abs(np.sum(data)) > 1.0e-3: # Only show non-zero series (use abs for debts)
965
+ nonzero_series[sname] = data
966
+ else:
967
+ # Show per individual
968
+ for i in range(len(inames)):
969
+ data = source_data[i] / 1000
970
+ if np.abs(np.sum(data)) > 1.0e-3: # Only show non-zero series (use abs for debts)
971
+ nonzero_series[f"{sname} {inames[i]}"] = data
949
972
 
950
973
  # Add stacked area traces for each source type
951
974
  for source_name, data in nonzero_series.items():
owlplanner/progress.py CHANGED
@@ -1,24 +1,81 @@
1
1
  """
2
- A simple object to display progress.
2
+ Progress indicator for long-running operations.
3
3
 
4
- Copyright &copy; 2024 - Martin-D. Lacasse
4
+ This module provides a simple progress indicator class that displays
5
+ progress as a percentage on a single line that updates in place.
5
6
 
6
- Disclaimers: This code is for educational purposes only and does not constitute financial advice.
7
+ Copyright (C) 2025-2026 The Owlplanner Authors
7
8
 
9
+ This program is free software: you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation, either version 3 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
8
21
  """
9
22
 
23
+ from typing import Optional
10
24
  from owlplanner import utils as u
11
25
 
12
26
 
13
- class Progress(object):
14
- def __init__(self, mylog):
27
+ class Progress:
28
+ """
29
+ A simple progress indicator for long-running operations.
30
+
31
+ Displays progress as a percentage (0-100%) on a single line that updates
32
+ in place using carriage return.
33
+
34
+ Example:
35
+ prog = Progress(mylog)
36
+ prog.start()
37
+ for i in range(100):
38
+ prog.show(i / 100)
39
+ prog.finish()
40
+ """
41
+
42
+ def __init__(self, mylog: Optional[object] = None):
43
+ """
44
+ Initialize the progress indicator.
45
+
46
+ Args:
47
+ mylog: Logger object with a print() method. If None, progress
48
+ updates will be silently ignored (useful for Streamlit UI).
49
+ """
15
50
  self.mylog = mylog
16
51
 
17
52
  def start(self):
18
- self.mylog.print("|--- progress ---|")
53
+ """
54
+ Display the progress header.
55
+ """
56
+ if self.mylog is not None:
57
+ self.mylog.print("|--- progress ---|")
58
+
59
+ def show(self, x: float):
60
+ """
61
+ Display the current progress percentage.
62
+
63
+ Args:
64
+ x: Progress value between 0.0 and 1.0 (will be clamped to this range).
65
+ Values outside this range will be clamped.
66
+ """
67
+ if self.mylog is None:
68
+ return
69
+
70
+ # Clamp x to [0, 1] range
71
+ x = max(0.0, min(1.0, x))
19
72
 
20
- def show(self, x):
21
- self.mylog.print(f"\r\r{u.pc(x, f=0)}", end="")
73
+ # Use single \r for carriage return (double \r\r is unnecessary)
74
+ self.mylog.print(f"\r{u.pc(x, f=0)}", end="")
22
75
 
23
76
  def finish(self):
24
- self.mylog.print()
77
+ """
78
+ Finish the progress display by printing a newline.
79
+ """
80
+ if self.mylog is not None:
81
+ self.mylog.print()