absfuyu 5.6.1__py3-none-any.whl → 6.1.2__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.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

Files changed (102) hide show
  1. absfuyu/__init__.py +5 -3
  2. absfuyu/__main__.py +2 -2
  3. absfuyu/cli/__init__.py +13 -2
  4. absfuyu/cli/audio_group.py +98 -0
  5. absfuyu/cli/color.py +2 -2
  6. absfuyu/cli/config_group.py +2 -2
  7. absfuyu/cli/do_group.py +2 -2
  8. absfuyu/cli/game_group.py +20 -2
  9. absfuyu/cli/tool_group.py +68 -4
  10. absfuyu/config/__init__.py +3 -3
  11. absfuyu/core/__init__.py +10 -6
  12. absfuyu/core/baseclass.py +104 -34
  13. absfuyu/core/baseclass2.py +43 -2
  14. absfuyu/core/decorator.py +2 -2
  15. absfuyu/core/docstring.py +4 -2
  16. absfuyu/core/dummy_cli.py +3 -3
  17. absfuyu/core/dummy_func.py +2 -2
  18. absfuyu/dxt/__init__.py +2 -2
  19. absfuyu/dxt/base_type.py +93 -0
  20. absfuyu/dxt/dictext.py +188 -6
  21. absfuyu/dxt/dxt_support.py +2 -2
  22. absfuyu/dxt/intext.py +72 -4
  23. absfuyu/dxt/listext.py +495 -23
  24. absfuyu/dxt/strext.py +2 -2
  25. absfuyu/extra/__init__.py +2 -2
  26. absfuyu/extra/audio/__init__.py +8 -0
  27. absfuyu/extra/audio/_util.py +57 -0
  28. absfuyu/extra/audio/convert.py +192 -0
  29. absfuyu/extra/audio/lossless.py +281 -0
  30. absfuyu/extra/beautiful.py +2 -2
  31. absfuyu/extra/da/__init__.py +39 -3
  32. absfuyu/extra/da/dadf.py +436 -29
  33. absfuyu/extra/da/dadf_base.py +2 -2
  34. absfuyu/extra/da/df_func.py +89 -5
  35. absfuyu/extra/da/mplt.py +2 -2
  36. absfuyu/extra/ggapi/__init__.py +8 -0
  37. absfuyu/extra/ggapi/gdrive.py +223 -0
  38. absfuyu/extra/ggapi/glicense.py +148 -0
  39. absfuyu/extra/ggapi/glicense_df.py +186 -0
  40. absfuyu/extra/ggapi/gsheet.py +88 -0
  41. absfuyu/extra/img/__init__.py +30 -0
  42. absfuyu/extra/img/converter.py +402 -0
  43. absfuyu/extra/img/dup_check.py +291 -0
  44. absfuyu/extra/pdf.py +4 -6
  45. absfuyu/extra/rclone.py +253 -0
  46. absfuyu/extra/xml.py +90 -0
  47. absfuyu/fun/__init__.py +2 -20
  48. absfuyu/fun/rubik.py +2 -2
  49. absfuyu/fun/tarot.py +2 -2
  50. absfuyu/game/__init__.py +2 -2
  51. absfuyu/game/game_stat.py +2 -2
  52. absfuyu/game/schulte.py +78 -0
  53. absfuyu/game/sudoku.py +2 -2
  54. absfuyu/game/tictactoe.py +2 -2
  55. absfuyu/game/wordle.py +6 -4
  56. absfuyu/general/__init__.py +2 -2
  57. absfuyu/general/content.py +2 -2
  58. absfuyu/general/human.py +2 -2
  59. absfuyu/general/resrel.py +213 -0
  60. absfuyu/general/shape.py +3 -8
  61. absfuyu/general/tax.py +344 -0
  62. absfuyu/logger.py +806 -59
  63. absfuyu/numbers/__init__.py +13 -0
  64. absfuyu/numbers/number_to_word.py +321 -0
  65. absfuyu/numbers/shorten_number.py +303 -0
  66. absfuyu/numbers/time_duration.py +217 -0
  67. absfuyu/pkg_data/__init__.py +2 -2
  68. absfuyu/pkg_data/deprecated.py +2 -2
  69. absfuyu/pkg_data/logo.py +1462 -0
  70. absfuyu/sort.py +4 -4
  71. absfuyu/tools/__init__.py +2 -2
  72. absfuyu/tools/checksum.py +119 -4
  73. absfuyu/tools/converter.py +2 -2
  74. absfuyu/tools/generator.py +24 -7
  75. absfuyu/tools/inspector.py +2 -2
  76. absfuyu/tools/keygen.py +2 -2
  77. absfuyu/tools/obfuscator.py +2 -2
  78. absfuyu/tools/passwordlib.py +2 -2
  79. absfuyu/tools/shutdownizer.py +3 -8
  80. absfuyu/tools/sw.py +213 -10
  81. absfuyu/tools/web.py +10 -13
  82. absfuyu/typings.py +5 -8
  83. absfuyu/util/__init__.py +31 -2
  84. absfuyu/util/api.py +7 -4
  85. absfuyu/util/cli.py +119 -0
  86. absfuyu/util/gui.py +91 -0
  87. absfuyu/util/json_method.py +2 -2
  88. absfuyu/util/lunar.py +2 -2
  89. absfuyu/util/package.py +124 -0
  90. absfuyu/util/path.py +313 -4
  91. absfuyu/util/performance.py +2 -2
  92. absfuyu/util/shorten_number.py +206 -13
  93. absfuyu/util/text_table.py +2 -2
  94. absfuyu/util/zipped.py +2 -2
  95. absfuyu/version.py +22 -19
  96. {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/METADATA +37 -8
  97. absfuyu-6.1.2.dist-info/RECORD +105 -0
  98. {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/WHEEL +1 -1
  99. absfuyu/extra/data_analysis.py +0 -21
  100. absfuyu-5.6.1.dist-info/RECORD +0 -79
  101. {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/entry_points.txt +0 -0
  102. {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/licenses/LICENSE +0 -0
absfuyu/general/tax.py ADDED
@@ -0,0 +1,344 @@
1
+ """
2
+ Absfuyu: Tax calculator
3
+ -----------------------
4
+ Tax calculator
5
+
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["PersonalIncomeTaxCalculator"]
13
+
14
+
15
+ # Library
16
+ # ---------------------------------------------------------------------------
17
+ from dataclasses import dataclass
18
+ from typing import Literal, TypedDict, overload
19
+
20
+ from absfuyu.core.baseclass import BaseClass, BaseDataclass
21
+
22
+ # Class
23
+ # ---------------------------------------------------------------------------
24
+ type TaxLevel = list[tuple[float | int | None, float]]
25
+
26
+
27
+ @dataclass
28
+ class TaxLevelResult(BaseDataclass):
29
+ """
30
+ Result for a single tax level.
31
+
32
+ Parameters
33
+ ----------
34
+ lower : float
35
+ Lower bound
36
+
37
+ upper : float | None
38
+ Upper bound. ``None`` means +infinite
39
+
40
+ rate : float
41
+ Tax percentage in decimal (0.1 for 10%)
42
+
43
+ amount_taxed : float
44
+ Amount to calculate in current tax level
45
+
46
+ tax : float
47
+ Tax amount in current tax level
48
+ """
49
+
50
+ lower: float
51
+ upper: float | None
52
+ rate: float
53
+ amount_taxed: float
54
+ tax: float
55
+
56
+
57
+ class TaxCalculationResult(TypedDict):
58
+ """
59
+ Tax calculation result.
60
+
61
+ Parameters
62
+ ----------
63
+ gross_income : float
64
+ The input gross income for the calculation.
65
+
66
+ deductions : float, optional
67
+ Amount to subtract from gross income before tax.
68
+
69
+ taxable_income : float
70
+ Gross income minus deductions.
71
+
72
+ per_tax_level : list[TaxLevelResult]
73
+ Detailed breakdown of income taxed at each tax level.
74
+
75
+ gross_tax : float
76
+ Total tax before credits.
77
+
78
+ tax_credits : float, optional
79
+ Amount subtracted from the computed tax after calculation.
80
+
81
+ net_tax : float
82
+ Total tax after applying credits.
83
+
84
+ effective_rate : float
85
+ Ratio of net tax to gross income (net_tax / gross_income).
86
+
87
+ marginal_rate : float
88
+ Tax rate applied to the last amount of money of taxable income.
89
+ """
90
+
91
+ gross_income: float
92
+ deductions: float
93
+ taxable_income: float
94
+ per_tax_level: list[TaxLevelResult]
95
+ gross_tax: float
96
+ tax_credits: float
97
+ net_tax: float
98
+ effective_rate: float
99
+ marginal_rate: float
100
+
101
+
102
+ class TaxCalculationResultRaw(TypedDict):
103
+ """
104
+ Tax calculation result.
105
+
106
+ Parameters
107
+ ----------
108
+ gross_income : float
109
+ The input gross income for the calculation.
110
+
111
+ deductions : float, optional
112
+ Amount to subtract from gross income before tax.
113
+
114
+ taxable_income : float
115
+ Gross income minus deductions.
116
+
117
+ per_tax_level : list[TaxLevelResult]
118
+ Detailed breakdown of income taxed at each tax level.
119
+
120
+ gross_tax : float
121
+ Total tax before credits.
122
+
123
+ tax_credits : float, optional
124
+ Amount subtracted from the computed tax after calculation.
125
+
126
+ net_tax : float
127
+ Total tax after applying credits.
128
+
129
+ effective_rate : float
130
+ Ratio of net tax to gross income (net_tax / gross_income).
131
+
132
+ marginal_rate : float
133
+ Tax rate applied to the last amount of money of taxable income.
134
+ """
135
+
136
+ gross_income: float
137
+ deductions: float
138
+ taxable_income: float
139
+ per_tax_level: list[dict[str, float | None]]
140
+ gross_tax: float
141
+ tax_credits: float
142
+ net_tax: float
143
+ effective_rate: float
144
+ marginal_rate: float
145
+
146
+
147
+ class PersonalIncomeTaxCalculator(BaseClass):
148
+ """
149
+ Progressive personal income tax calculator.
150
+
151
+ Parameters
152
+ ----------
153
+ tax_levels : list[tuple[float | int | None, float]], optional
154
+ Ordered list of tax levels.
155
+ Each tax level is represented as a tuple of (upper_bound, rate),
156
+ where ``upper_bound`` is the cumulative upper limit of the tax level.
157
+ Use ``None`` for the last tax level (no upper limit).
158
+ Example: ``[(10000, 0.10), (50000, 0.2), (None, 0.3)]``.
159
+ Set to ``None`` to have 0% tax rate, by default ``None``
160
+
161
+ deductions : float, optional
162
+ Amount to subtract from gross income before tax, by default 0.0
163
+
164
+ tax_credits : float, optional
165
+ Amount subtracted from the computed tax after calculation, by default 0.0
166
+
167
+ Attributes
168
+ ----------
169
+ gross_income : float
170
+ The input gross income for the calculation.
171
+
172
+ taxable_income : float
173
+ Gross income minus deductions.
174
+
175
+ per_tax_level : list[TaxLevelResult]
176
+ Detailed breakdown of income taxed at each tax level.
177
+
178
+ gross_tax : float
179
+ Total tax before credits.
180
+
181
+ net_tax : float
182
+ Total tax after applying credits.
183
+
184
+ effective_rate : float
185
+ Ratio of net tax to gross income.
186
+
187
+ marginal_rate : float
188
+ Tax rate applied to the last amount of money of taxable income.
189
+
190
+
191
+ Example:
192
+ --------
193
+ >>> tax_levels = [(100, 0.05), (None, 0.1)]
194
+ >>> cal = PersonalIncomeTaxCalculator(tax_levels)
195
+ >>> cal.calculate(500)
196
+ >>> cal.to_dict(raw=True)
197
+ {...}
198
+
199
+ >>> cal.interpret_result()
200
+ ===== Tax information =====
201
+ Taxable income: 500.00 (500.00 - 0.00)
202
+ - Level 1: 0.00 - 100.00 @ 5.0%: 100.00 -> tax 5.00
203
+ - Level 2: 100.00 - 500.00 @ 10.0%: 400.00 -> tax 40.00
204
+ Net tax: 45.00 (45.00 - 0.00)
205
+ Effective rate: 9.00%
206
+ Marginal rate: 10.0%
207
+ """
208
+
209
+ def __init__(self, tax_levels: TaxLevel | None = None, deductions: float = 0.0, tax_credits: float = 0.0) -> None:
210
+ """
211
+ Progressive personal income tax calculator.
212
+
213
+ Parameters
214
+ ----------
215
+ tax_levels : list[tuple[float | int | None, float]], optional
216
+ Ordered list of tax levels.
217
+ Each tax level is represented as a tuple of (upper_bound, rate),
218
+ where ``upper_bound`` is the cumulative upper limit of the tax level.
219
+ Use ``None`` for the last tax level (no upper limit).
220
+ Example: ``[(10000, 0.10), (50000, 0.2), (None, 0.3)]``.
221
+ Set to ``None`` to have 0% tax rate, by default ``None``
222
+
223
+ deductions : float, optional
224
+ Amount to subtract from gross income before tax, by default 0.0
225
+
226
+ tax_credits : float, optional
227
+ Amount subtracted from the computed tax after calculation, by default 0.0
228
+ """
229
+ self.tax_levels = [] if tax_levels is None else tax_levels
230
+ self.deductions = deductions
231
+ self.tax_credits = tax_credits
232
+
233
+ # Results populated after calculation
234
+ self.gross_income: float = 0.0
235
+ self.taxable_income: float = 0.0
236
+ self.per_tax_level: list[TaxLevelResult] = []
237
+ self.gross_tax: float = 0.0
238
+ self.net_tax: float = 0.0
239
+ self.effective_rate: float = 0.0
240
+ self.marginal_rate: float = 0.0
241
+
242
+ def calculate(self, gross_income: float) -> None:
243
+ """
244
+ Compute tax for a given gross income.
245
+
246
+ Parameters
247
+ ----------
248
+ gross_income : float
249
+ Total gross income (unless tax levels are defined otherwise).
250
+ """
251
+ if gross_income < 0:
252
+ raise ValueError("gross_income must be non-negative")
253
+
254
+ self.gross_income = gross_income
255
+ self.taxable_income = max(0.0, gross_income - max(0.0, self.deductions))
256
+
257
+ self.per_tax_level = []
258
+ prev_upper = 0.0
259
+ remaining = self.taxable_income
260
+ self.gross_tax = 0.0
261
+ self.marginal_rate = 0.0
262
+
263
+ for upper, rate in self.tax_levels:
264
+ lower = prev_upper
265
+ if upper is None:
266
+ amount_taxed = remaining
267
+ else:
268
+ band = upper - prev_upper
269
+ amount_taxed = min(band, max(0.0, remaining))
270
+ tax = max(0.0, amount_taxed) * rate
271
+ self.per_tax_level.append(TaxLevelResult(lower, upper, rate, amount_taxed, tax))
272
+ self.gross_tax += tax
273
+ remaining -= amount_taxed
274
+ prev_upper = upper if upper is not None else prev_upper
275
+ if remaining <= 1e-9:
276
+ self.marginal_rate = rate
277
+ break
278
+ self.marginal_rate = rate
279
+
280
+ self.net_tax = max(0.0, self.gross_tax - max(0.0, self.tax_credits))
281
+ self.effective_rate = self.net_tax / gross_income if gross_income > 0 else 0.0
282
+
283
+ @overload
284
+ def to_dict(self) -> TaxCalculationResult: ... # type: ignore
285
+
286
+ @overload
287
+ def to_dict(self, *, raw: Literal[True] = ...) -> TaxCalculationResultRaw: ...
288
+
289
+ def to_dict(self, *, raw: bool = False) -> TaxCalculationResult | TaxCalculationResultRaw:
290
+ """
291
+ Returns calculation result in dict format
292
+
293
+ Parameters
294
+ ----------
295
+ raw : bool, optional
296
+ Convert every value to dict, by default ``False``
297
+
298
+ Returns
299
+ -------
300
+ TaxCalculationResult
301
+ Tax calculation result
302
+ """
303
+ result: TaxCalculationResult = {
304
+ "gross_income": self.gross_income,
305
+ "deductions": self.deductions,
306
+ "taxable_income": self.taxable_income,
307
+ "per_tax_level": self.per_tax_level,
308
+ "gross_tax": self.gross_tax,
309
+ "tax_credits": self.tax_credits,
310
+ "net_tax": self.net_tax,
311
+ "effective_rate": self.effective_rate,
312
+ "marginal_rate": self.marginal_rate,
313
+ }
314
+ if raw:
315
+ result_raw: TaxCalculationResultRaw = result # type: ignore
316
+ result_raw["per_tax_level"] = [x.to_dict() for x in result["per_tax_level"]]
317
+ return result
318
+ return result
319
+
320
+ def interpret_result(self) -> str:
321
+ result = self.to_dict()
322
+ text = ["===== Tax information ====="]
323
+
324
+ # text.append(f"Gross income: {result['gross_income']:,}")
325
+ # text.append(f"Deduction: {result['deductions']:,}")
326
+ # text.append(f"Taxable income: {result['taxable_income']:,}")
327
+ text.append(
328
+ f"Taxable income: {result['taxable_income']:,.2f} ({result['gross_income']:,.2f} - {result['deductions']:,.2f})"
329
+ )
330
+
331
+ for idx, tax_level in enumerate(result["per_tax_level"], start=1):
332
+ upper = f"{tax_level.upper:,.2f}" if tax_level.upper is not None else f"{self.gross_income:,.2f}"
333
+ text.append(
334
+ f"- Level {idx}: {tax_level.lower:,.2f} - {upper} @ {tax_level.rate*100:.1f}%: {tax_level.amount_taxed:,.2f} -> tax {tax_level.tax:,.2f}"
335
+ )
336
+
337
+ # text.append(f"Gross tax: {result['gross_tax']:,}")
338
+ # text.append(f"Tax credits: {result['tax_credits']:,}")
339
+ # text.append(f"Net tax: {result['net_tax']:,}")
340
+ text.append(f"Net tax: {result['net_tax']:,.2f} ({result['gross_tax']:,.2f} - {result['tax_credits']:,.2f})")
341
+
342
+ text.append(f"Effective rate: {result['effective_rate']*100:.2f}%")
343
+ text.append(f"Marginal rate: {result['marginal_rate']*100:.1f}%")
344
+ return "\n".join(text)