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/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 educatonal purposes only and does not constitute financial advice.
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 educatonal purposes only and does not constitute financial advice.
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 &copy; 2024 - Martin-D. Lacasse
14
14
 
15
- Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
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
- rates_TCJA = np.array([0.10, 0.12, 0.22, 0.24, 0.32, 0.35, 0.370])
29
- rates_nonTCJA = np.array([0.10, 0.15, 0.25, 0.28, 0.33, 0.35, 0.396])
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
- taxBrackets_TCJA = np.array(
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 non-TCJA using 2017 to current year.
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
- taxBrackets_nonTCJA = np.array(
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
- stdDeduction_TCJA = np.array([15000, 30000])
75
- # These are speculated.
76
- stdDeduction_nonTCJA = np.array([8300, 16600])
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
- # These are current.
79
- extra65Deduction = np.array([2000, 1600])
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, y_TCJA=2026):
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 TCJA might expire.
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
- deltaBrackets_TCJA = np.array(taxBrackets_TCJA)
125
- deltaBrackets_nonTCJA = np.array(taxBrackets_nonTCJA)
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
- deltaBrackets_TCJA[i, t] -= deltaBrackets_TCJA[i, t - 1]
129
- deltaBrackets_nonTCJA[i, t] -= deltaBrackets_nonTCJA[i, t - 1]
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
- sigma = np.zeros((N_n))
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 < y_TCJA:
147
- sigma[n] = stdDeduction_TCJA[filingStatus]
148
- Delta[n, :] = deltaBrackets_TCJA[filingStatus, :]
219
+ if thisyear + n < yOBBBA:
220
+ sigmaBar[n] = stdDeduction_OBBBA[filingStatus] * gamma_n[n]
221
+ Delta[n, :] = deltaBrackets_OBBBA[filingStatus, :]
149
222
  else:
150
- sigma[n] = stdDeduction_nonTCJA[filingStatus]
151
- Delta[n, :] = deltaBrackets_nonTCJA[filingStatus, :]
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
- sigma[n] += extra65Deduction[filingStatus]
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 < y_TCJA:
160
- theta[n, :] = rates_TCJA[:]
234
+ if thisyear + n < yOBBBA:
235
+ theta[n, :] = rates_OBBBA[:]
161
236
  else:
162
- theta[n, :] = rates_nonTCJA[:]
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 sigma, theta, Delta
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, y_TCJA):
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 TCJA from this year.
257
+ # Number of years left in OBBBA from this year.
182
258
  thisyear = date.today().year
183
- ytc = y_TCJA - thisyear
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] = taxBrackets_TCJA[stat][t] if n < ytc else taxBrackets_nonTCJA[stat][t]
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
- # Account for increase of RMD age between 2023 and 2032.
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 &copy; 2024 - Martin-D. Lacasse
14
14
 
15
- Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
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
- Returs a dictionary of dataframes by individual's names.
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 &copy; 2024 - Martin-D. Lacasse
8
8
 
9
- Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
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.06.21"
1
+ __version__ = "2025.08.01"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.6.21
3
+ Version: 2025.8.1
4
4
  Summary: Owl: Retirement planner with great wisdom
5
5
  Project-URL: HomePage, https://github.com/mdlacasse/owl
6
6
  Project-URL: Repository, https://github.com/mdlacasse/owl
@@ -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,,