owlplanner 2025.6.21__py3-none-any.whl → 2025.8.1__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.
- owlplanner/abcapi.py +1 -1
- owlplanner/config.py +3 -5
- owlplanner/mylogging.py +1 -1
- owlplanner/plan.py +309 -208
- owlplanner/progress.py +1 -1
- owlplanner/rates.py +1 -23
- owlplanner/tax2025.py +120 -41
- owlplanner/timelists.py +3 -2
- owlplanner/utils.py +1 -1
- owlplanner/version.py +1 -1
- {owlplanner-2025.6.21.dist-info → owlplanner-2025.8.1.dist-info}/METADATA +1 -1
- owlplanner-2025.8.1.dist-info/RECORD +22 -0
- owlplanner-2025.6.21.dist-info/RECORD +0 -22
- {owlplanner-2025.6.21.dist-info → owlplanner-2025.8.1.dist-info}/WHEEL +0 -0
- {owlplanner-2025.6.21.dist-info → owlplanner-2025.8.1.dist-info}/licenses/LICENSE +0 -0
owlplanner/progress.py
CHANGED
|
@@ -3,7 +3,7 @@ A simple object to display progress.
|
|
|
3
3
|
|
|
4
4
|
Copyright © 2024 - Martin-D. Lacasse
|
|
5
5
|
|
|
6
|
-
Disclaimers: This code is for
|
|
6
|
+
Disclaimers: This code is for educational purposes only and does not constitute financial advice.
|
|
7
7
|
|
|
8
8
|
"""
|
|
9
9
|
|
owlplanner/rates.py
CHANGED
|
@@ -23,7 +23,7 @@ to the last current data year.
|
|
|
23
23
|
|
|
24
24
|
Copyright © 2024 - Martin-D. Lacasse
|
|
25
25
|
|
|
26
|
-
Disclaimers: This code is for
|
|
26
|
+
Disclaimers: This code is for educational purposes only and does not constitute financial advice.
|
|
27
27
|
|
|
28
28
|
"""
|
|
29
29
|
|
|
@@ -32,7 +32,6 @@ import numpy as np
|
|
|
32
32
|
import pandas as pd
|
|
33
33
|
import os
|
|
34
34
|
import sys
|
|
35
|
-
from datetime import date
|
|
36
35
|
|
|
37
36
|
from owlplanner import mylogging as log
|
|
38
37
|
from owlplanner import utils as u
|
|
@@ -120,27 +119,6 @@ def getRatesDistributions(frm, to, mylog=None):
|
|
|
120
119
|
return means, stdev, corr, covar
|
|
121
120
|
|
|
122
121
|
|
|
123
|
-
def historicalValue(amount, year):
|
|
124
|
-
"""
|
|
125
|
-
Return the deflated value of amount given in this year's dollars as
|
|
126
|
-
valued at the beginning of the year specified.
|
|
127
|
-
"""
|
|
128
|
-
thisyear = date.today().year
|
|
129
|
-
if TO != thisyear - 1:
|
|
130
|
-
raise RuntimeError(f"Rates file needs to be updated to be current to {thisyear}.")
|
|
131
|
-
if year < FROM:
|
|
132
|
-
raise ValueError(f"Only data from {FROM} is available.")
|
|
133
|
-
if year > thisyear:
|
|
134
|
-
raise ValueError(f"Year must be < {thisyear} for historical data.")
|
|
135
|
-
|
|
136
|
-
span = thisyear - year
|
|
137
|
-
ub = len(Inflation)
|
|
138
|
-
for n in range(ub - span, ub):
|
|
139
|
-
amount /= 1 + Inflation[n] / 100
|
|
140
|
-
|
|
141
|
-
return amount
|
|
142
|
-
|
|
143
|
-
|
|
144
122
|
class Rates(object):
|
|
145
123
|
"""
|
|
146
124
|
Rates are stored in a 4-array in the following order:
|
owlplanner/tax2025.py
CHANGED
|
@@ -12,7 +12,7 @@ Module to handle all tax calculations.
|
|
|
12
12
|
|
|
13
13
|
Copyright © 2024 - Martin-D. Lacasse
|
|
14
14
|
|
|
15
|
-
Disclaimers: This code is for
|
|
15
|
+
Disclaimers: This code is for educational purposes only and does not constitute financial advice.
|
|
16
16
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
@@ -25,16 +25,16 @@ from datetime import date
|
|
|
25
25
|
|
|
26
26
|
taxBracketNames = ["10%", "12/15%", "22/25%", "24/28%", "32/33%", "35%", "37/40%"]
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
rates_OBBBA = np.array([0.10, 0.12, 0.22, 0.24, 0.32, 0.35, 0.370])
|
|
29
|
+
rates_preTCJA = np.array([0.10, 0.15, 0.25, 0.28, 0.33, 0.35, 0.396])
|
|
30
30
|
|
|
31
31
|
###############################################################################
|
|
32
32
|
# Start of section where rates need to be actualized every year.
|
|
33
33
|
###############################################################################
|
|
34
34
|
# Single [0] and married filing jointly [1].
|
|
35
35
|
|
|
36
|
-
# These are current.
|
|
37
|
-
|
|
36
|
+
# These are 2025 current.
|
|
37
|
+
taxBrackets_OBBBA = np.array(
|
|
38
38
|
[
|
|
39
39
|
[11925, 48475, 103350, 197300, 250525, 626350, 9999999],
|
|
40
40
|
[23850, 96950, 206700, 394600, 501050, 751600, 9999999],
|
|
@@ -52,7 +52,7 @@ irmaaBrackets = np.array(
|
|
|
52
52
|
# Following values are incremental IRMAA part B monthly fees.
|
|
53
53
|
irmaaFees = 12 * np.array([185.00, 74.00, 111.00, 110.90, 111.00, 37.00])
|
|
54
54
|
|
|
55
|
-
# Make projection for
|
|
55
|
+
# Make projection for pre-TCJA using 2017 to current year.
|
|
56
56
|
# taxBrackets_2017 = np.array(
|
|
57
57
|
# [ [9325, 37950, 91900, 191650, 416700, 418400, 9999999],
|
|
58
58
|
# [18650, 75900, 153100, 233350, 416700, 470700, 9999999],
|
|
@@ -63,26 +63,99 @@ irmaaFees = 12 * np.array([185.00, 74.00, 111.00, 110.90, 111.00, 37.00])
|
|
|
63
63
|
# For 2025, I used a 30.5% adjustment from 2017, rounded to closest 50.
|
|
64
64
|
#
|
|
65
65
|
# These are speculated.
|
|
66
|
-
|
|
66
|
+
taxBrackets_preTCJA = np.array(
|
|
67
67
|
[
|
|
68
|
-
[12150, 49550, 119950, 250200, 544000, 546200, 9999999],
|
|
69
|
-
[24350, 99100, 199850, 304600, 543950, 614450, 9999999],
|
|
68
|
+
[12150, 49550, 119950, 250200, 544000, 546200, 9999999], # Single
|
|
69
|
+
[24350, 99100, 199850, 304600, 543950, 614450, 9999999], # MFJ
|
|
70
70
|
]
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
-
# These are current.
|
|
74
|
-
|
|
75
|
-
# These are speculated.
|
|
76
|
-
|
|
73
|
+
# These are 2025 current (adjusted for inflation).
|
|
74
|
+
stdDeduction_OBBBA = np.array([15750, 31500]) # Single, MFJ
|
|
75
|
+
# These are speculated (adjusted for inflation).
|
|
76
|
+
stdDeduction_preTCJA = np.array([8300, 16600]) # Single, MFJ
|
|
77
|
+
|
|
78
|
+
# These are current (adjusted for inflation) per individual.
|
|
79
|
+
extra65Deduction = np.array([2000, 1600]) # Single, MFJ
|
|
80
|
+
|
|
81
|
+
# Thresholds for capital gains (adjusted for inflation).
|
|
82
|
+
capGainRates = np.array(
|
|
83
|
+
[
|
|
84
|
+
[48350, 533400],
|
|
85
|
+
[96700, 600050],
|
|
86
|
+
]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Thresholds for net investment income tax (not adjusted for inflation).
|
|
90
|
+
niitThreshold = np.array([200000, 250000])
|
|
91
|
+
niitRate = 0.038
|
|
77
92
|
|
|
78
|
-
#
|
|
79
|
-
|
|
93
|
+
# Thresholds for 65+ bonus for circumventing tax on social security.
|
|
94
|
+
bonusThreshold = np.array([75000, 150000])
|
|
80
95
|
|
|
81
96
|
###############################################################################
|
|
82
97
|
# End of section where rates need to be actualized every year.
|
|
83
98
|
###############################################################################
|
|
84
99
|
|
|
85
100
|
|
|
101
|
+
def mediVals(yobs, horizons, gamma_n, Nn, Nq):
|
|
102
|
+
"""
|
|
103
|
+
Return tuple (nm, L, C) of year index when Medicare starts and vectors L, and C
|
|
104
|
+
defining end points of constant piecewise linear functions representing IRMAA fees.
|
|
105
|
+
"""
|
|
106
|
+
thisyear = date.today().year
|
|
107
|
+
assert Nq == len(irmaaFees), f"Inconsistent value of Nq: {Nq}."
|
|
108
|
+
assert Nq == len(irmaaBrackets[0]), "Inconsistent IRMAA brackets array."
|
|
109
|
+
Ni = len(yobs)
|
|
110
|
+
# What index year will Medicare start? 65 - age.
|
|
111
|
+
nm = 65 - (thisyear - yobs)
|
|
112
|
+
nm = np.min(nm)
|
|
113
|
+
# Has it already started?
|
|
114
|
+
nm = max(0, nm)
|
|
115
|
+
Nmed = Nn - nm
|
|
116
|
+
|
|
117
|
+
L = np.zeros((Nmed, Nq-1))
|
|
118
|
+
C = np.zeros((Nmed, Nq))
|
|
119
|
+
|
|
120
|
+
# Year starts at offset nm in the plan.
|
|
121
|
+
for nn in range(Nmed):
|
|
122
|
+
imed = 0
|
|
123
|
+
n = nm + nn
|
|
124
|
+
if thisyear + n - yobs[0] >= 65 and n < horizons[0]:
|
|
125
|
+
imed += 1
|
|
126
|
+
if Ni == 2 and thisyear + n - yobs[1] >= 65 and n < horizons[1]:
|
|
127
|
+
imed += 1
|
|
128
|
+
if imed:
|
|
129
|
+
status = 0 if Ni == 1 else 1 if n < horizons[0] and n < horizons[1] else 0
|
|
130
|
+
L[nn] = gamma_n[n] * irmaaBrackets[status][1:]
|
|
131
|
+
C[nn] = imed * gamma_n[n] * irmaaFees
|
|
132
|
+
else:
|
|
133
|
+
raise RuntimeError("mediVals: This should never happen.")
|
|
134
|
+
|
|
135
|
+
return nm, L, C
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def capitalGainTaxRate(Ni, magi_n, gamma_n, nd, Nn):
|
|
139
|
+
"""
|
|
140
|
+
Return an array of decimal rates for capital gains.
|
|
141
|
+
Parameter nd is the index year of first passing of a spouse, if applicable,
|
|
142
|
+
nd == Nn for single individuals.
|
|
143
|
+
"""
|
|
144
|
+
status = Ni - 1
|
|
145
|
+
cgRate_n = np.zeros(Nn)
|
|
146
|
+
|
|
147
|
+
for n in range(Nn):
|
|
148
|
+
if status and n == nd:
|
|
149
|
+
status -= 1
|
|
150
|
+
|
|
151
|
+
if magi_n[n] > gamma_n[n] * capGainRates[status][1]:
|
|
152
|
+
cgRate_n[n] = 0.20
|
|
153
|
+
elif magi_n[n] > gamma_n[n] * capGainRates[status][0]:
|
|
154
|
+
cgRate_n[n] = 0.15
|
|
155
|
+
|
|
156
|
+
return cgRate_n
|
|
157
|
+
|
|
158
|
+
|
|
86
159
|
def mediCosts(yobs, horizons, magi, prevmagi, gamma_n, Nn):
|
|
87
160
|
"""
|
|
88
161
|
Compute Medicare costs directly.
|
|
@@ -107,11 +180,11 @@ def mediCosts(yobs, horizons, magi, prevmagi, gamma_n, Nn):
|
|
|
107
180
|
return costs
|
|
108
181
|
|
|
109
182
|
|
|
110
|
-
def taxParams(yobs, i_d, n_d, N_n,
|
|
183
|
+
def taxParams(yobs, i_d, n_d, N_n, gamma_n, MAGI_n, yOBBBA=2099):
|
|
111
184
|
"""
|
|
112
185
|
Input is year of birth, index of shortest-lived individual,
|
|
113
186
|
lifespan of shortest-lived individual, total number of years
|
|
114
|
-
in the plan, and the year that
|
|
187
|
+
in the plan, and the year that preTCJA rates might come back.
|
|
115
188
|
|
|
116
189
|
It returns 3 time series:
|
|
117
190
|
1) Standard deductions at year n (sigma_n).
|
|
@@ -121,15 +194,15 @@ def taxParams(yobs, i_d, n_d, N_n, y_TCJA=2026):
|
|
|
121
194
|
Returned values are not indexed for inflation.
|
|
122
195
|
"""
|
|
123
196
|
# Compute the deltas in-place between brackets, starting from the end.
|
|
124
|
-
|
|
125
|
-
|
|
197
|
+
deltaBrackets_OBBBA = np.array(taxBrackets_OBBBA)
|
|
198
|
+
deltaBrackets_preTCJA = np.array(taxBrackets_preTCJA)
|
|
126
199
|
for t in range(6, 0, -1):
|
|
127
200
|
for i in range(2):
|
|
128
|
-
|
|
129
|
-
|
|
201
|
+
deltaBrackets_OBBBA[i, t] -= deltaBrackets_OBBBA[i, t - 1]
|
|
202
|
+
deltaBrackets_preTCJA[i, t] -= deltaBrackets_preTCJA[i, t - 1]
|
|
130
203
|
|
|
131
204
|
# Prepare the 3 arrays to return - use transpose for easy slicing.
|
|
132
|
-
|
|
205
|
+
sigmaBar = np.zeros((N_n))
|
|
133
206
|
Delta = np.zeros((N_n, 7))
|
|
134
207
|
theta = np.zeros((N_n, 7))
|
|
135
208
|
|
|
@@ -143,51 +216,57 @@ def taxParams(yobs, i_d, n_d, N_n, y_TCJA=2026):
|
|
|
143
216
|
souls.remove(i_d)
|
|
144
217
|
filingStatus -= 1
|
|
145
218
|
|
|
146
|
-
if thisyear + n <
|
|
147
|
-
|
|
148
|
-
Delta[n, :] =
|
|
219
|
+
if thisyear + n < yOBBBA:
|
|
220
|
+
sigmaBar[n] = stdDeduction_OBBBA[filingStatus] * gamma_n[n]
|
|
221
|
+
Delta[n, :] = deltaBrackets_OBBBA[filingStatus, :]
|
|
149
222
|
else:
|
|
150
|
-
|
|
151
|
-
Delta[n, :] =
|
|
223
|
+
sigmaBar[n] = stdDeduction_preTCJA[filingStatus] * gamma_n[n]
|
|
224
|
+
Delta[n, :] = deltaBrackets_preTCJA[filingStatus, :]
|
|
152
225
|
|
|
153
|
-
# Add 65+ additional exemption(s).
|
|
226
|
+
# Add 65+ additional exemption(s) and "bonus" phasing out.
|
|
154
227
|
for i in souls:
|
|
155
228
|
if thisyear + n - yobs[i] >= 65:
|
|
156
|
-
|
|
229
|
+
sigmaBar[n] += extra65Deduction[filingStatus] * gamma_n[n]
|
|
230
|
+
if thisyear + n <= 2028:
|
|
231
|
+
sigmaBar[n] += 6000 * max(0, 1 - 0.06*max(0, MAGI_n[n] - bonusThreshold[filingStatus]))
|
|
157
232
|
|
|
158
233
|
# Fill in future tax rates for year n.
|
|
159
|
-
if thisyear + n <
|
|
160
|
-
theta[n, :] =
|
|
234
|
+
if thisyear + n < yOBBBA:
|
|
235
|
+
theta[n, :] = rates_OBBBA[:]
|
|
161
236
|
else:
|
|
162
|
-
theta[n, :] =
|
|
237
|
+
theta[n, :] = rates_preTCJA[:]
|
|
163
238
|
|
|
164
239
|
Delta = Delta.transpose()
|
|
165
240
|
theta = theta.transpose()
|
|
166
241
|
|
|
167
|
-
# Return series unadjusted for inflation, in STD order.
|
|
168
|
-
return
|
|
242
|
+
# Return series unadjusted for inflation, except for sigmaBar, in STD order.
|
|
243
|
+
return sigmaBar, theta, Delta
|
|
169
244
|
|
|
170
245
|
|
|
171
|
-
def taxBrackets(N_i, n_d, N_n,
|
|
246
|
+
def taxBrackets(N_i, n_d, N_n, yOBBBA=2099):
|
|
172
247
|
"""
|
|
173
248
|
Return dictionary containing future tax brackets
|
|
174
249
|
unadjusted for inflation for plotting.
|
|
175
250
|
"""
|
|
176
251
|
if not (0 < N_i <= 2):
|
|
177
252
|
raise ValueError(f"Cannot process {N_i} individuals.")
|
|
253
|
+
|
|
178
254
|
n_d = min(n_d, N_n)
|
|
179
255
|
status = N_i - 1
|
|
180
256
|
|
|
181
|
-
# Number of years left in
|
|
257
|
+
# Number of years left in OBBBA from this year.
|
|
182
258
|
thisyear = date.today().year
|
|
183
|
-
|
|
259
|
+
if yOBBBA < thisyear:
|
|
260
|
+
raise ValueError(f"Expiration year {yOBBBA} cannot be in the past.")
|
|
261
|
+
|
|
262
|
+
ytc = yOBBBA - thisyear
|
|
184
263
|
|
|
185
264
|
data = {}
|
|
186
265
|
for t in range(len(taxBracketNames) - 1):
|
|
187
266
|
array = np.zeros(N_n)
|
|
188
267
|
for n in range(N_n):
|
|
189
268
|
stat = status if n < n_d else 0
|
|
190
|
-
array[n] =
|
|
269
|
+
array[n] = taxBrackets_OBBBA[stat][t] if n < ytc else taxBrackets_preTCJA[stat][t]
|
|
191
270
|
|
|
192
271
|
data[taxBracketNames[t]] = array
|
|
193
272
|
|
|
@@ -247,12 +326,12 @@ def rho_in(yobs, N_n):
|
|
|
247
326
|
thisyear = date.today().year
|
|
248
327
|
for i in range(N_i):
|
|
249
328
|
agenow = thisyear - yobs[i]
|
|
329
|
+
# Account for increase of RMD age between 2023 and 2032.
|
|
330
|
+
yrmd = 70 if yobs[i] < 1949 else 72 if 1949 <= yobs[i] <= 1950 else 73 if 1951 <= yobs[i] <= 1959 else 75
|
|
250
331
|
for n in range(N_n):
|
|
251
|
-
year = thisyear + n
|
|
252
332
|
yage = agenow + n
|
|
253
333
|
|
|
254
|
-
|
|
255
|
-
if (yage < 73) or (year > 2032 and yage < 75):
|
|
334
|
+
if yage < yrmd:
|
|
256
335
|
pass # rho[i][n] = 0
|
|
257
336
|
else:
|
|
258
337
|
rho[i][n] = 1.0 / rmdTable[yage - 72]
|
owlplanner/timelists.py
CHANGED
|
@@ -12,7 +12,7 @@ Utility functions to read and check timelists.
|
|
|
12
12
|
|
|
13
13
|
Copyright © 2024 - Martin-D. Lacasse
|
|
14
14
|
|
|
15
|
-
Disclaimers: This code is for
|
|
15
|
+
Disclaimers: This code is for educational purposes only and does not constitute financial advice.
|
|
16
16
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
@@ -42,7 +42,7 @@ def read(finput, inames, horizons, mylog):
|
|
|
42
42
|
year, anticipated wages, taxable ctrb, 401k ctrb, Roth 401k ctrb,
|
|
43
43
|
IRA ctrb, Roth IRA ctrb, Roth conv, and big-ticket items.
|
|
44
44
|
Supports xls, xlsx, xlsm, xlsb, odf, ods, and odt file extensions.
|
|
45
|
-
|
|
45
|
+
Return a dictionary of dataframes by individual's names.
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
48
|
mylog.vprint("Reading wages, contributions, conversions, and big-ticket items over time...")
|
|
@@ -93,6 +93,7 @@ def _condition(dfDict, inames, horizons, mylog):
|
|
|
93
93
|
# Only consider lines in proper year range. Go back 5 years for Roth maturation.
|
|
94
94
|
df = df[df["year"] >= (thisyear - 5)]
|
|
95
95
|
df = df[df["year"] < endyear]
|
|
96
|
+
df = df.drop_duplicates("year")
|
|
96
97
|
missing = []
|
|
97
98
|
for n in range(-5, horizons[i]):
|
|
98
99
|
year = thisyear + n
|
owlplanner/utils.py
CHANGED
|
@@ -6,7 +6,7 @@ This file contains functions for handling data.
|
|
|
6
6
|
|
|
7
7
|
Copyright © 2024 - Martin-D. Lacasse
|
|
8
8
|
|
|
9
|
-
Disclaimers: This code is for
|
|
9
|
+
Disclaimers: This code is for educational purposes only and does not constitute financial advice.
|
|
10
10
|
|
|
11
11
|
"""
|
|
12
12
|
|
owlplanner/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2025.
|
|
1
|
+
__version__ = "2025.08.01"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
owlplanner/__init__.py,sha256=hJ2i4m2JpHPAKyQLjYOXpJzeEsgcTcKD-Vhm0AIjjWg,592
|
|
2
|
+
owlplanner/abcapi.py,sha256=rtg7d0UbftinokR9VlB49VUjDjzUq3ONnJbhMXVIrgo,6879
|
|
3
|
+
owlplanner/config.py,sha256=JJOtS6HyqA4qUHUZydSGG_RMWaCfVMRSOAfWbt4evMI,12461
|
|
4
|
+
owlplanner/mylogging.py,sha256=OVGeDFO7LIZG91R6HMpZBzjno-B8PH8Fo00Jw2Pdgqw,2558
|
|
5
|
+
owlplanner/plan.py,sha256=keKC9-XeQxza9--D7TaQfcZCLkGDy5yVV9D5pN25MHg,115211
|
|
6
|
+
owlplanner/progress.py,sha256=dUUlFmSAKUei36rUj2BINRY10f_YEUo_e23d0es6nrc,530
|
|
7
|
+
owlplanner/rates.py,sha256=9Nmo8AKsyi5PoCUrzhr06phkSlNTv-TXzj5iYFU76AY,14113
|
|
8
|
+
owlplanner/tax2025.py,sha256=2Jb_UbPT6ye-znRjA0nSaF8T8M17QW4MoRPDoW9XJ8s,10833
|
|
9
|
+
owlplanner/timelists.py,sha256=Q4kBt9kKAa5qxsvOe9wfyUtCQVgiwPmJXTwXUPRBBv8,4066
|
|
10
|
+
owlplanner/utils.py,sha256=afAjeO6Msf6Rn4jwz_7Ody9rHGWlBR7iQFqe1xzLNQc,2513
|
|
11
|
+
owlplanner/version.py,sha256=Or3KaHd8BXidAsAkScGwQRvtWIF5uufpsnfgNdR-Kpw,28
|
|
12
|
+
owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
|
|
14
|
+
owlplanner/plotting/__init__.py,sha256=uhxqtUi0OI-QWNOO2LkXgQViW_9yM3rYb-204Wit974,250
|
|
15
|
+
owlplanner/plotting/base.py,sha256=UimGKpMTV-dVm3BX5Apr_Ltorc7dlDLCRPRQ3RF_v7c,2578
|
|
16
|
+
owlplanner/plotting/factory.py,sha256=EDopIAPQr9zHRgemObko18FlCeRNhNCoLNNFAOq-X6s,1030
|
|
17
|
+
owlplanner/plotting/matplotlib_backend.py,sha256=AOEkapD94U5hGNoS0EdbRoe8mgdMHH4oOvkXADZS914,17957
|
|
18
|
+
owlplanner/plotting/plotly_backend.py,sha256=AO33GxBHGYG5osir_H1iRRtGxdhs4AjfLV2d_xm35nY,33138
|
|
19
|
+
owlplanner-2025.8.1.dist-info/METADATA,sha256=9WRoRbNMdk2kxbT4-Coq912VJixT9rlF1Cv48Po4VJw,54044
|
|
20
|
+
owlplanner-2025.8.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
21
|
+
owlplanner-2025.8.1.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
22
|
+
owlplanner-2025.8.1.dist-info/RECORD,,
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
owlplanner/__init__.py,sha256=hJ2i4m2JpHPAKyQLjYOXpJzeEsgcTcKD-Vhm0AIjjWg,592
|
|
2
|
-
owlplanner/abcapi.py,sha256=8VCXS7nH_QZYxCUU3lwO0_UPR9Q5fuYQ6DHDLvHVLPg,6878
|
|
3
|
-
owlplanner/config.py,sha256=onGIMqW2WwB9_CUZauDL6LtHGvc8O1cPUKKcK7Oh70M,12617
|
|
4
|
-
owlplanner/mylogging.py,sha256=RKUr-y-1XvKZzLMcfdtm4IM30LuRpJwb2qUeXmAWqME,2557
|
|
5
|
-
owlplanner/plan.py,sha256=VVHyKJiooLXXVLiRFJcauZ3oOYU62CCBe4DlpA08P38,109765
|
|
6
|
-
owlplanner/progress.py,sha256=2DOjOLo6Mo7m21wY-9iZhoUksAyi4VCbb6UL2RegNCw,529
|
|
7
|
-
owlplanner/rates.py,sha256=7jXcuHbkJ3AVIeBYZdwme18rdYslIzCuT-c0cLzvKUU,14819
|
|
8
|
-
owlplanner/tax2025.py,sha256=HVYJq8po28jL5Z_il39ZY7qvf2riUEfxio15Zp7TGj8,7890
|
|
9
|
-
owlplanner/timelists.py,sha256=95rKYknGMi1bonDVIc3xNmiwG0zTSejKyQy_uWCLSiA,4024
|
|
10
|
-
owlplanner/utils.py,sha256=6Ky8ZKfNE9x--3znsZ8VZaT2PptDinszRxWsOCPanu8,2512
|
|
11
|
-
owlplanner/version.py,sha256=Q6lAE5sS9Y-tuy3jNnv43HcB70y7l1kmDPNzx4CR9tc,28
|
|
12
|
-
owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
|
|
14
|
-
owlplanner/plotting/__init__.py,sha256=uhxqtUi0OI-QWNOO2LkXgQViW_9yM3rYb-204Wit974,250
|
|
15
|
-
owlplanner/plotting/base.py,sha256=UimGKpMTV-dVm3BX5Apr_Ltorc7dlDLCRPRQ3RF_v7c,2578
|
|
16
|
-
owlplanner/plotting/factory.py,sha256=EDopIAPQr9zHRgemObko18FlCeRNhNCoLNNFAOq-X6s,1030
|
|
17
|
-
owlplanner/plotting/matplotlib_backend.py,sha256=AOEkapD94U5hGNoS0EdbRoe8mgdMHH4oOvkXADZS914,17957
|
|
18
|
-
owlplanner/plotting/plotly_backend.py,sha256=AO33GxBHGYG5osir_H1iRRtGxdhs4AjfLV2d_xm35nY,33138
|
|
19
|
-
owlplanner-2025.6.21.dist-info/METADATA,sha256=bZI1gHxPSbxJvJP2DuSqiB6Y33x4tdiVgHlKK73wGD4,54045
|
|
20
|
-
owlplanner-2025.6.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
21
|
-
owlplanner-2025.6.21.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
22
|
-
owlplanner-2025.6.21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|