absfuyu 3.3.3__py3-none-any.whl → 4.0.0__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.
- absfuyu/__init__.py +1 -1
- absfuyu/cli/__init__.py +1 -1
- absfuyu/cli/do_group.py +36 -2
- absfuyu/config/__init__.py +13 -13
- absfuyu/core.py +5 -16
- absfuyu/extensions/extra/data_analysis.py +74 -28
- absfuyu/fun/__init__.py +24 -23
- absfuyu/fun/tarot.py +6 -7
- absfuyu/game/__init__.py +1 -1
- absfuyu/game/sudoku.py +6 -5
- absfuyu/game/tictactoe.py +4 -4
- absfuyu/general/data_extension.py +70 -76
- absfuyu/general/human.py +77 -3
- absfuyu/pkg_data/__init__.py +13 -20
- absfuyu/tools/converter.py +9 -10
- absfuyu/util/__init__.py +13 -14
- absfuyu/util/api.py +7 -7
- absfuyu/util/json_method.py +9 -9
- absfuyu/util/lunar.py +7 -8
- absfuyu/util/path.py +23 -21
- absfuyu/util/performance.py +8 -8
- absfuyu/util/zipped.py +18 -6
- {absfuyu-3.3.3.dist-info → absfuyu-4.0.0.dist-info}/METADATA +5 -9
- {absfuyu-3.3.3.dist-info → absfuyu-4.0.0.dist-info}/RECORD +27 -27
- {absfuyu-3.3.3.dist-info → absfuyu-4.0.0.dist-info}/WHEEL +1 -1
- {absfuyu-3.3.3.dist-info → absfuyu-4.0.0.dist-info}/entry_points.txt +0 -0
- {absfuyu-3.3.3.dist-info → absfuyu-4.0.0.dist-info}/licenses/LICENSE +0 -0
absfuyu/game/tictactoe.py
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Game: Tic Tac Toe
|
|
3
3
|
-----------------
|
|
4
4
|
|
|
5
|
-
Version: 2.0.
|
|
6
|
-
Date updated:
|
|
5
|
+
Version: 2.0.4
|
|
6
|
+
Date updated: 15/11/2024 (dd/mm/yyyy)
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
__all__ = ["TicTacToe", "GameMode"]
|
|
@@ -11,11 +11,11 @@ __all__ = ["TicTacToe", "GameMode"]
|
|
|
11
11
|
|
|
12
12
|
import random
|
|
13
13
|
import time
|
|
14
|
-
from typing import
|
|
14
|
+
from typing import Literal, NamedTuple
|
|
15
15
|
|
|
16
16
|
from absfuyu.core import CLITextColor
|
|
17
17
|
|
|
18
|
-
BoardGame =
|
|
18
|
+
BoardGame = list[list[str]]
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class Pos(NamedTuple):
|
|
@@ -3,8 +3,8 @@ Absfuyu: Data extension
|
|
|
3
3
|
-----------------------
|
|
4
4
|
Extension for data type such as ``list``, ``str``, ``dict``, ...
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.15.2
|
|
7
|
+
Date updated: 06/01/2025 (dd/mm/yyyy)
|
|
8
8
|
|
|
9
9
|
Features:
|
|
10
10
|
---------
|
|
@@ -47,23 +47,7 @@ import operator
|
|
|
47
47
|
import random
|
|
48
48
|
from collections import Counter
|
|
49
49
|
from itertools import accumulate, chain, groupby
|
|
50
|
-
from
|
|
51
|
-
from typing import (
|
|
52
|
-
Any,
|
|
53
|
-
Callable,
|
|
54
|
-
Dict,
|
|
55
|
-
List,
|
|
56
|
-
NamedTuple,
|
|
57
|
-
Optional,
|
|
58
|
-
Tuple,
|
|
59
|
-
TypedDict,
|
|
60
|
-
Union,
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
if _python_version.minor >= 11:
|
|
64
|
-
from typing import NotRequired
|
|
65
|
-
else:
|
|
66
|
-
from typing_extensions import NotRequired # type: ignore
|
|
50
|
+
from typing import Any, Callable, NamedTuple, NotRequired, Self, TypedDict, Union
|
|
67
51
|
|
|
68
52
|
from deprecated.sphinx import versionadded, versionchanged
|
|
69
53
|
|
|
@@ -74,7 +58,7 @@ from absfuyu.util import set_min, set_min_max
|
|
|
74
58
|
|
|
75
59
|
# Function
|
|
76
60
|
###########################################################################
|
|
77
|
-
def _dict_bool(dict_object: dict, option: bool) ->
|
|
61
|
+
def _dict_bool(dict_object: dict, option: bool) -> dict | None:
|
|
78
62
|
"""
|
|
79
63
|
Support function DictBool class
|
|
80
64
|
"""
|
|
@@ -93,7 +77,7 @@ def _dict_bool(dict_object: dict, option: bool) -> Optional[dict]:
|
|
|
93
77
|
class Pow:
|
|
94
78
|
"""Number power by a number"""
|
|
95
79
|
|
|
96
|
-
def __init__(self, number:
|
|
80
|
+
def __init__(self, number: int | float, power_by: int | float) -> None:
|
|
97
81
|
self.number = number
|
|
98
82
|
self.power_by = power_by
|
|
99
83
|
|
|
@@ -107,7 +91,7 @@ class Pow:
|
|
|
107
91
|
def __repr__(self) -> str:
|
|
108
92
|
return self.__str__()
|
|
109
93
|
|
|
110
|
-
def to_list(self) ->
|
|
94
|
+
def to_list(self) -> list[int | float]:
|
|
111
95
|
"""
|
|
112
96
|
Convert into list
|
|
113
97
|
|
|
@@ -132,7 +116,7 @@ class ListREPR(list):
|
|
|
132
116
|
return _compress_list_for_print(self, 9)
|
|
133
117
|
|
|
134
118
|
|
|
135
|
-
class ListNoDunder(
|
|
119
|
+
class ListNoDunder(list[str]):
|
|
136
120
|
"""Use with ``object.__dir__()``"""
|
|
137
121
|
|
|
138
122
|
def __repr__(self) -> str:
|
|
@@ -140,7 +124,7 @@ class ListNoDunder(List[str]):
|
|
|
140
124
|
return out.__repr__()
|
|
141
125
|
|
|
142
126
|
|
|
143
|
-
class DictBoolTrue(
|
|
127
|
+
class DictBoolTrue(dict[Any, bool]):
|
|
144
128
|
"""Only show items when ``values == True`` in ``__repr__()``"""
|
|
145
129
|
|
|
146
130
|
def __repr__(self) -> str:
|
|
@@ -148,7 +132,7 @@ class DictBoolTrue(Dict[Any, bool]):
|
|
|
148
132
|
return _dict_bool(temp, True).__repr__()
|
|
149
133
|
|
|
150
134
|
|
|
151
|
-
class DictBoolFalse(
|
|
135
|
+
class DictBoolFalse(dict[Any, bool]):
|
|
152
136
|
"""Only show items when ``values == False`` in ``__repr__()``"""
|
|
153
137
|
|
|
154
138
|
def __repr__(self) -> str:
|
|
@@ -206,8 +190,8 @@ class DictAnalyzeResult(NamedTuple):
|
|
|
206
190
|
Result for ``DictExt.analyze()``
|
|
207
191
|
"""
|
|
208
192
|
|
|
209
|
-
max_value:
|
|
210
|
-
min_value:
|
|
193
|
+
max_value: int | float
|
|
194
|
+
min_value: int | float
|
|
211
195
|
max_list: list
|
|
212
196
|
min_list: list
|
|
213
197
|
|
|
@@ -220,7 +204,7 @@ class Text(str):
|
|
|
220
204
|
``str`` extension
|
|
221
205
|
"""
|
|
222
206
|
|
|
223
|
-
def divide(self, string_split_size: int = 60) ->
|
|
207
|
+
def divide(self, string_split_size: int = 60) -> list[str]:
|
|
224
208
|
"""
|
|
225
209
|
Divide long string into smaller size
|
|
226
210
|
|
|
@@ -255,8 +239,8 @@ class Text(str):
|
|
|
255
239
|
self,
|
|
256
240
|
split_size: int = 60,
|
|
257
241
|
split_var_len: int = 12,
|
|
258
|
-
custom_var_name:
|
|
259
|
-
) -> list:
|
|
242
|
+
custom_var_name: str | None = None,
|
|
243
|
+
) -> list[str]:
|
|
260
244
|
"""
|
|
261
245
|
Divide long string into smaller size,
|
|
262
246
|
then assign a random variable to splited
|
|
@@ -277,7 +261,7 @@ class Text(str):
|
|
|
277
261
|
|
|
278
262
|
Returns
|
|
279
263
|
-------
|
|
280
|
-
list
|
|
264
|
+
list[str]
|
|
281
265
|
A list in which each item is a smaller
|
|
282
266
|
string with the size of ``split_size``
|
|
283
267
|
and a way to concaternate them (when using ``print()``)
|
|
@@ -398,7 +382,7 @@ class Text(str):
|
|
|
398
382
|
|
|
399
383
|
return detail
|
|
400
384
|
|
|
401
|
-
def reverse(self) ->
|
|
385
|
+
def reverse(self) -> Self:
|
|
402
386
|
"""
|
|
403
387
|
Reverse the string
|
|
404
388
|
|
|
@@ -489,7 +473,7 @@ class Text(str):
|
|
|
489
473
|
else:
|
|
490
474
|
return hex_str
|
|
491
475
|
|
|
492
|
-
def random_capslock(self, probability: int = 50) ->
|
|
476
|
+
def random_capslock(self, probability: int = 50) -> Self:
|
|
493
477
|
"""
|
|
494
478
|
Randomly capslock letter in string
|
|
495
479
|
|
|
@@ -522,7 +506,7 @@ class Text(str):
|
|
|
522
506
|
logger.debug(temp)
|
|
523
507
|
return self.__class__("".join(temp))
|
|
524
508
|
|
|
525
|
-
def reverse_capslock(self) ->
|
|
509
|
+
def reverse_capslock(self) -> Self:
|
|
526
510
|
"""
|
|
527
511
|
Reverse capslock in string
|
|
528
512
|
|
|
@@ -546,7 +530,7 @@ class Text(str):
|
|
|
546
530
|
temp[i] = x.upper()
|
|
547
531
|
return self.__class__("".join(temp))
|
|
548
532
|
|
|
549
|
-
def to_list(self) ->
|
|
533
|
+
def to_list(self) -> list[str]:
|
|
550
534
|
"""
|
|
551
535
|
Convert into list
|
|
552
536
|
|
|
@@ -625,7 +609,7 @@ class Text(str):
|
|
|
625
609
|
return sum(out)
|
|
626
610
|
|
|
627
611
|
@versionadded(version="3.3.0")
|
|
628
|
-
def hapax(self, strict: bool = False) ->
|
|
612
|
+
def hapax(self, strict: bool = False) -> list[str]:
|
|
629
613
|
"""
|
|
630
614
|
A hapax legomenon (often abbreviated to hapax)
|
|
631
615
|
is a word which occurs only once in either
|
|
@@ -656,9 +640,9 @@ class Text(str):
|
|
|
656
640
|
>>> test.hapax(strict=True)
|
|
657
641
|
['b']
|
|
658
642
|
"""
|
|
659
|
-
word_list:
|
|
643
|
+
word_list: list[str] = self.lower().split()
|
|
660
644
|
if strict:
|
|
661
|
-
remove_characters:
|
|
645
|
+
remove_characters: list[str] = list(r"\"'.,:;|()[]{}\/!@#$%^&*-_=+?<>`~")
|
|
662
646
|
temp = str(self)
|
|
663
647
|
for x in remove_characters:
|
|
664
648
|
temp = temp.replace(x, "")
|
|
@@ -845,7 +829,7 @@ class IntNumber(int):
|
|
|
845
829
|
|
|
846
830
|
else:
|
|
847
831
|
# Faster way to check
|
|
848
|
-
perfect_number_index:
|
|
832
|
+
perfect_number_index: list[int] = [
|
|
849
833
|
61,
|
|
850
834
|
89,
|
|
851
835
|
107,
|
|
@@ -934,7 +918,7 @@ class IntNumber(int):
|
|
|
934
918
|
except Exception:
|
|
935
919
|
return False
|
|
936
920
|
|
|
937
|
-
def reverse(self) ->
|
|
921
|
+
def reverse(self) -> Self:
|
|
938
922
|
"""
|
|
939
923
|
Reverse a number. Reverse ``abs(number)`` if ``number < 0``
|
|
940
924
|
|
|
@@ -982,8 +966,8 @@ class IntNumber(int):
|
|
|
982
966
|
return self.is_palindromic() and self.is_prime()
|
|
983
967
|
|
|
984
968
|
# calculation stuff
|
|
985
|
-
@versionchanged(version="
|
|
986
|
-
def lcm(self, with_number: int) ->
|
|
969
|
+
@versionchanged(version="4.0.0", reason="Update")
|
|
970
|
+
def lcm(self, with_number: int) -> Self:
|
|
987
971
|
"""
|
|
988
972
|
Least common multiple of ``self`` and ``with_number``
|
|
989
973
|
|
|
@@ -1004,13 +988,10 @@ class IntNumber(int):
|
|
|
1004
988
|
>>> test.lcm(5)
|
|
1005
989
|
510
|
|
1006
990
|
"""
|
|
1007
|
-
|
|
1008
|
-
return self.__class__(math.lcm(self, with_number))
|
|
1009
|
-
except AttributeError: # Python < 3.9
|
|
1010
|
-
return self.__class__((self * with_number) // math.gcd(self, with_number))
|
|
991
|
+
return self.__class__(math.lcm(self, with_number))
|
|
1011
992
|
|
|
1012
993
|
@versionchanged(version="3.3.0", reason="Fix bug")
|
|
1013
|
-
def gcd(self, with_number: int) ->
|
|
994
|
+
def gcd(self, with_number: int) -> Self:
|
|
1014
995
|
"""
|
|
1015
996
|
Greatest common divisor of ``self`` and ``with_number``
|
|
1016
997
|
|
|
@@ -1033,7 +1014,7 @@ class IntNumber(int):
|
|
|
1033
1014
|
"""
|
|
1034
1015
|
return self.__class__(math.gcd(self, with_number))
|
|
1035
1016
|
|
|
1036
|
-
def add_to_one_digit(self, master_number: bool = False) ->
|
|
1017
|
+
def add_to_one_digit(self, master_number: bool = False) -> Self:
|
|
1037
1018
|
"""
|
|
1038
1019
|
Convert ``self`` into 1-digit number
|
|
1039
1020
|
by adding all of the digits together
|
|
@@ -1072,7 +1053,7 @@ class IntNumber(int):
|
|
|
1072
1053
|
logger.debug(f"Sum after loop: {number}")
|
|
1073
1054
|
return self.__class__(number)
|
|
1074
1055
|
|
|
1075
|
-
def divisible_list(self, short_form: bool = True) ->
|
|
1056
|
+
def divisible_list(self, short_form: bool = True) -> list[int]:
|
|
1076
1057
|
"""
|
|
1077
1058
|
A list of divisible number
|
|
1078
1059
|
|
|
@@ -1106,7 +1087,7 @@ class IntNumber(int):
|
|
|
1106
1087
|
# return ListREPR(divi_list) ## FIX LATER
|
|
1107
1088
|
return divi_list
|
|
1108
1089
|
|
|
1109
|
-
def prime_factor(self, short_form: bool = True) -> Union[
|
|
1090
|
+
def prime_factor(self, short_form: bool = True) -> Union[list[int], list[Pow]]:
|
|
1110
1091
|
"""
|
|
1111
1092
|
Prime factor
|
|
1112
1093
|
|
|
@@ -1156,7 +1137,7 @@ class IntNumber(int):
|
|
|
1156
1137
|
return factors
|
|
1157
1138
|
|
|
1158
1139
|
# analyze
|
|
1159
|
-
def analyze(self, short_form: bool = True) ->
|
|
1140
|
+
def analyze(self, short_form: bool = True) -> dict[str, dict[str, Any]]:
|
|
1160
1141
|
"""
|
|
1161
1142
|
Analyze the number with almost all ``IntNumber`` method
|
|
1162
1143
|
|
|
@@ -1220,7 +1201,7 @@ class ListExt(list):
|
|
|
1220
1201
|
``list`` extension
|
|
1221
1202
|
"""
|
|
1222
1203
|
|
|
1223
|
-
def stringify(self) ->
|
|
1204
|
+
def stringify(self) -> Self:
|
|
1224
1205
|
"""
|
|
1225
1206
|
Convert all item in ``list`` into string
|
|
1226
1207
|
|
|
@@ -1236,7 +1217,7 @@ class ListExt(list):
|
|
|
1236
1217
|
>>> test.stringify()
|
|
1237
1218
|
['1', '1', '1', '2', '2', '3']
|
|
1238
1219
|
"""
|
|
1239
|
-
return
|
|
1220
|
+
return self.__class__(map(str, self))
|
|
1240
1221
|
|
|
1241
1222
|
def head(self, number_of_items: int = 5) -> list:
|
|
1242
1223
|
"""
|
|
@@ -1278,7 +1259,7 @@ class ListExt(list):
|
|
|
1278
1259
|
)
|
|
1279
1260
|
return self[::-1][:number_of_items][::-1]
|
|
1280
1261
|
|
|
1281
|
-
def sorts(self, reverse: bool = False) ->
|
|
1262
|
+
def sorts(self, reverse: bool = False) -> Self:
|
|
1282
1263
|
"""
|
|
1283
1264
|
Sort all items (with different type) in ``list``
|
|
1284
1265
|
|
|
@@ -1318,9 +1299,9 @@ class ListExt(list):
|
|
|
1318
1299
|
def freq(
|
|
1319
1300
|
self,
|
|
1320
1301
|
sort: bool = False,
|
|
1321
|
-
num_of_first_char:
|
|
1302
|
+
num_of_first_char: int | None = None,
|
|
1322
1303
|
appear_increment: bool = False,
|
|
1323
|
-
) -> Union[dict,
|
|
1304
|
+
) -> Union[dict, list[int]]:
|
|
1324
1305
|
"""
|
|
1325
1306
|
Find frequency of each item in list
|
|
1326
1307
|
|
|
@@ -1383,7 +1364,7 @@ class ListExt(list):
|
|
|
1383
1364
|
logger.debug(times_appear)
|
|
1384
1365
|
|
|
1385
1366
|
if appear_increment:
|
|
1386
|
-
times_appear_increment:
|
|
1367
|
+
times_appear_increment: list[int] = list(
|
|
1387
1368
|
accumulate(times_appear.values(), operator.add)
|
|
1388
1369
|
)
|
|
1389
1370
|
logger.debug(times_appear_increment)
|
|
@@ -1391,7 +1372,7 @@ class ListExt(list):
|
|
|
1391
1372
|
else:
|
|
1392
1373
|
return times_appear
|
|
1393
1374
|
|
|
1394
|
-
def slice_points(self, points: list) ->
|
|
1375
|
+
def slice_points(self, points: list) -> list[list]:
|
|
1395
1376
|
"""
|
|
1396
1377
|
Slices a list at specific indices into constituent lists.
|
|
1397
1378
|
|
|
@@ -1458,7 +1439,7 @@ class ListExt(list):
|
|
|
1458
1439
|
"""
|
|
1459
1440
|
return [self.pick_one() for _ in range(number_of_items)]
|
|
1460
1441
|
|
|
1461
|
-
def len_items(self) ->
|
|
1442
|
+
def len_items(self) -> Self:
|
|
1462
1443
|
"""
|
|
1463
1444
|
``len()`` for every item in ``list[str]``
|
|
1464
1445
|
|
|
@@ -1474,7 +1455,7 @@ class ListExt(list):
|
|
|
1474
1455
|
>>> test.len_items()
|
|
1475
1456
|
[3, 3, 5]
|
|
1476
1457
|
"""
|
|
1477
|
-
out =
|
|
1458
|
+
out = self.__class__([len(str(x)) for x in self])
|
|
1478
1459
|
# out = ListExt(map(lambda x: len(str(x)), self))
|
|
1479
1460
|
logger.debug(out)
|
|
1480
1461
|
return out
|
|
@@ -1499,7 +1480,7 @@ class ListExt(list):
|
|
|
1499
1480
|
logger.debug(out)
|
|
1500
1481
|
return out
|
|
1501
1482
|
|
|
1502
|
-
def apply(self, func: Callable) ->
|
|
1483
|
+
def apply(self, func: Callable) -> Self:
|
|
1503
1484
|
"""
|
|
1504
1485
|
Apply function to each entry
|
|
1505
1486
|
|
|
@@ -1523,7 +1504,7 @@ class ListExt(list):
|
|
|
1523
1504
|
# return __class__(func(x) for x in self)
|
|
1524
1505
|
return self.__class__(map(func, self))
|
|
1525
1506
|
|
|
1526
|
-
def unique(self) ->
|
|
1507
|
+
def unique(self) -> Self:
|
|
1527
1508
|
"""
|
|
1528
1509
|
Remove duplicates
|
|
1529
1510
|
|
|
@@ -1541,7 +1522,7 @@ class ListExt(list):
|
|
|
1541
1522
|
"""
|
|
1542
1523
|
return self.__class__(set(self))
|
|
1543
1524
|
|
|
1544
|
-
def group_by_unique(self) ->
|
|
1525
|
+
def group_by_unique(self) -> Self:
|
|
1545
1526
|
"""
|
|
1546
1527
|
Group duplicated elements into list
|
|
1547
1528
|
|
|
@@ -1566,13 +1547,13 @@ class ListExt(list):
|
|
|
1566
1547
|
return self.__class__([list(g) for _, g in temp])
|
|
1567
1548
|
|
|
1568
1549
|
@staticmethod
|
|
1569
|
-
def _group_by_unique(iterable: list) ->
|
|
1550
|
+
def _group_by_unique(iterable: list) -> list[list]:
|
|
1570
1551
|
"""
|
|
1571
1552
|
Static method for ``group_by_unique``
|
|
1572
1553
|
"""
|
|
1573
1554
|
return list([list(g) for _, g in groupby(iterable)])
|
|
1574
1555
|
|
|
1575
|
-
def group_by_pair_value(self, max_loop: int = 3) ->
|
|
1556
|
+
def group_by_pair_value(self, max_loop: int = 3) -> list[list]:
|
|
1576
1557
|
"""
|
|
1577
1558
|
Assume each ``list`` in ``list`` is a pair value,
|
|
1578
1559
|
returns a ``list`` contain all paired value
|
|
@@ -1607,7 +1588,7 @@ class ListExt(list):
|
|
|
1607
1588
|
|
|
1608
1589
|
# Init loop
|
|
1609
1590
|
for _ in range(int(set_min(max_loop, min_value=3))):
|
|
1610
|
-
temp:
|
|
1591
|
+
temp: dict[Any, list] = {}
|
|
1611
1592
|
# Make dict{key: all `item` that contains `key`}
|
|
1612
1593
|
for item in iter:
|
|
1613
1594
|
for x in item:
|
|
@@ -1624,7 +1605,7 @@ class ListExt(list):
|
|
|
1624
1605
|
|
|
1625
1606
|
return list(x for x, _ in groupby(iter))
|
|
1626
1607
|
|
|
1627
|
-
def flatten(self) ->
|
|
1608
|
+
def flatten(self) -> Self:
|
|
1628
1609
|
"""
|
|
1629
1610
|
Flatten the list
|
|
1630
1611
|
|
|
@@ -1641,12 +1622,12 @@ class ListExt(list):
|
|
|
1641
1622
|
['test', 'test', 'test', 'test']
|
|
1642
1623
|
"""
|
|
1643
1624
|
try:
|
|
1644
|
-
return
|
|
1625
|
+
return self.__class__(chain(*self))
|
|
1645
1626
|
except Exception:
|
|
1646
1627
|
temp = list(map(lambda x: x if isinstance(x, list) else [x], self))
|
|
1647
|
-
return
|
|
1628
|
+
return self.__class__(chain(*temp))
|
|
1648
1629
|
|
|
1649
|
-
def numbering(self, start: int = 0) ->
|
|
1630
|
+
def numbering(self, start: int = 0) -> Self:
|
|
1650
1631
|
"""
|
|
1651
1632
|
Number the item in list
|
|
1652
1633
|
(``enumerate`` wrapper)
|
|
@@ -1670,10 +1651,10 @@ class ListExt(list):
|
|
|
1670
1651
|
[(0, 9), (1, 9), (2, 9)]
|
|
1671
1652
|
"""
|
|
1672
1653
|
start = int(set_min(start, min_value=0))
|
|
1673
|
-
return
|
|
1654
|
+
return self.__class__(enumerate(self, start=start))
|
|
1674
1655
|
|
|
1675
1656
|
@staticmethod
|
|
1676
|
-
def _numbering(iterable: list, start: int = 0) ->
|
|
1657
|
+
def _numbering(iterable: list, start: int = 0) -> list[tuple[int, Any]]:
|
|
1677
1658
|
"""
|
|
1678
1659
|
Static method for ``numbering``
|
|
1679
1660
|
"""
|
|
@@ -1717,8 +1698,8 @@ class DictExt(dict):
|
|
|
1717
1698
|
try:
|
|
1718
1699
|
dct: dict = self.copy()
|
|
1719
1700
|
|
|
1720
|
-
max_val:
|
|
1721
|
-
min_val:
|
|
1701
|
+
max_val: int | float = max(list(dct.values()))
|
|
1702
|
+
min_val: int | float = min(list(dct.values()))
|
|
1722
1703
|
max_list = []
|
|
1723
1704
|
min_list = []
|
|
1724
1705
|
|
|
@@ -1744,7 +1725,7 @@ class DictExt(dict):
|
|
|
1744
1725
|
logger.error(err_msg)
|
|
1745
1726
|
raise ValueError(err_msg) # noqa: B904
|
|
1746
1727
|
|
|
1747
|
-
def swap_items(self) ->
|
|
1728
|
+
def swap_items(self) -> Self:
|
|
1748
1729
|
"""
|
|
1749
1730
|
Swap ``dict.keys()`` with ``dict.values()``
|
|
1750
1731
|
|
|
@@ -1762,7 +1743,7 @@ class DictExt(dict):
|
|
|
1762
1743
|
"""
|
|
1763
1744
|
return self.__class__(zip(self.values(), self.keys()))
|
|
1764
1745
|
|
|
1765
|
-
def apply(self, func: Callable, apply_to_value: bool = True) ->
|
|
1746
|
+
def apply(self, func: Callable, apply_to_value: bool = True) -> Self:
|
|
1766
1747
|
"""
|
|
1767
1748
|
Apply function to ``DictExt.keys()`` or ``DictExt.values()``
|
|
1768
1749
|
|
|
@@ -1795,6 +1776,19 @@ class DictExt(dict):
|
|
|
1795
1776
|
v = self.values() # type: ignore
|
|
1796
1777
|
return self.__class__(zip(k, v))
|
|
1797
1778
|
|
|
1779
|
+
@versionadded(version="3.4.0")
|
|
1780
|
+
def aggregate(
|
|
1781
|
+
self,
|
|
1782
|
+
other_dict: dict[Any, int | float],
|
|
1783
|
+
default_value: int | float = 0,
|
|
1784
|
+
) -> Self:
|
|
1785
|
+
"""Dict with value type int or float"""
|
|
1786
|
+
out = {
|
|
1787
|
+
k: self.get(k, default_value) + other_dict.get(k, default_value)
|
|
1788
|
+
for k in set(self | other_dict)
|
|
1789
|
+
}
|
|
1790
|
+
return self.__class__(out)
|
|
1791
|
+
|
|
1798
1792
|
|
|
1799
1793
|
# Run
|
|
1800
1794
|
###########################################################################
|
absfuyu/general/human.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Human
|
|
|
3
3
|
--------------
|
|
4
4
|
Human related stuff
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.4.0
|
|
7
|
+
Date updated: 15/08/2024 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
@@ -14,13 +14,16 @@ __all__ = ["Human", "Person"]
|
|
|
14
14
|
|
|
15
15
|
# Library
|
|
16
16
|
###########################################################################
|
|
17
|
+
import re
|
|
17
18
|
from datetime import datetime, time
|
|
18
19
|
from typing import Optional, Union
|
|
20
|
+
from urllib.parse import urlencode
|
|
19
21
|
|
|
20
22
|
from dateutil.relativedelta import relativedelta
|
|
21
23
|
|
|
22
24
|
from absfuyu.fun import zodiac_sign
|
|
23
25
|
from absfuyu.general.data_extension import IntNumber
|
|
26
|
+
from absfuyu.tools.web import soup_link
|
|
24
27
|
from absfuyu.version import Version # type: ignore
|
|
25
28
|
|
|
26
29
|
|
|
@@ -349,7 +352,78 @@ class Person(Human):
|
|
|
349
352
|
return IntNumber(temp).add_to_one_digit(master_number=True)
|
|
350
353
|
|
|
351
354
|
|
|
355
|
+
class Human2:
|
|
356
|
+
"""W.I.P for cli"""
|
|
357
|
+
|
|
358
|
+
def __init__(self, birthday_string: str, is_male: bool = True) -> None:
|
|
359
|
+
"""
|
|
360
|
+
:param birthday_string: Format ``<yyyymmddhhmm>`` or ``<yyyymmdd>``
|
|
361
|
+
"""
|
|
362
|
+
if len(birthday_string) == 12:
|
|
363
|
+
day = datetime(
|
|
364
|
+
year=int(birthday_string[:4]),
|
|
365
|
+
month=int(birthday_string[4:6]),
|
|
366
|
+
day=int(birthday_string[6:8]),
|
|
367
|
+
hour=int(birthday_string[8:10]),
|
|
368
|
+
minute=int(birthday_string[10:]),
|
|
369
|
+
)
|
|
370
|
+
else:
|
|
371
|
+
day = datetime(
|
|
372
|
+
year=int(birthday_string[:4]),
|
|
373
|
+
month=int(birthday_string[4:6]),
|
|
374
|
+
day=int(birthday_string[6:]),
|
|
375
|
+
)
|
|
376
|
+
self._date_str = birthday_string[:8]
|
|
377
|
+
self.day = day
|
|
378
|
+
self.is_male = is_male
|
|
379
|
+
|
|
380
|
+
def __str__(self) -> str:
|
|
381
|
+
class_name = self.__class__.__name__
|
|
382
|
+
return f"{class_name}({str(self.day)})"
|
|
383
|
+
|
|
384
|
+
def __repr__(self) -> str:
|
|
385
|
+
class_name = self.__class__.__name__
|
|
386
|
+
return f"{class_name}({str(self.day)})"
|
|
387
|
+
|
|
388
|
+
def numerology(self) -> int:
|
|
389
|
+
# numerology
|
|
390
|
+
return IntNumber(self._date_str).add_to_one_digit(master_number=True)
|
|
391
|
+
|
|
392
|
+
def _make_fengshui_check_query(self) -> str:
|
|
393
|
+
"""
|
|
394
|
+
Generate query to check Feng-shui
|
|
395
|
+
"""
|
|
396
|
+
params = {
|
|
397
|
+
"ngay": self.day.day.__str__().rjust(2, "0"),
|
|
398
|
+
"thang": self.day.month.__str__().rjust(2, "0"),
|
|
399
|
+
"nam": self.day.year,
|
|
400
|
+
"gio": self.day.hour.__str__().rjust(2, "0"),
|
|
401
|
+
"phut": self.day.minute.__str__().rjust(2, "0"),
|
|
402
|
+
"gioitinh": "nam" if self.is_male else "nu",
|
|
403
|
+
}
|
|
404
|
+
output = urlencode(params)
|
|
405
|
+
return output
|
|
406
|
+
|
|
407
|
+
def fs(self, number_string: str) -> float:
|
|
408
|
+
# fengshui
|
|
409
|
+
base = "https://thanglongdaoquan.vn/boi-so-tai-khoan/?taikhoan="
|
|
410
|
+
link = f"{base}{number_string}&{self._make_fengshui_check_query()}"
|
|
411
|
+
soup = soup_link(link)
|
|
412
|
+
val = soup.find_all(class_="total_point")[0].get_text()
|
|
413
|
+
pattern = r"([0-9.]{1,3})/10"
|
|
414
|
+
res = re.findall(pattern, val)[0]
|
|
415
|
+
return float(res)
|
|
416
|
+
|
|
417
|
+
def info(self) -> dict:
|
|
418
|
+
out = {
|
|
419
|
+
"numerology": self.numerology(),
|
|
420
|
+
"zodiac": zodiac_sign(self.day.day, self.day.month),
|
|
421
|
+
}
|
|
422
|
+
return out
|
|
423
|
+
|
|
424
|
+
|
|
352
425
|
# Run
|
|
353
426
|
###########################################################################
|
|
354
427
|
if __name__ == "__main__":
|
|
355
|
-
print(Person.JohnDoe().__dict__)
|
|
428
|
+
# print(Person.JohnDoe().__dict__)
|
|
429
|
+
pass
|
absfuyu/pkg_data/__init__.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Package data
|
|
|
3
3
|
---------------------
|
|
4
4
|
Load package data
|
|
5
5
|
|
|
6
|
-
Version: 2.2.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 2.2.3
|
|
7
|
+
Date updated: 14/11/2024 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
@@ -16,18 +16,8 @@ __all__ = ["PkgData"]
|
|
|
16
16
|
###########################################################################
|
|
17
17
|
import zlib
|
|
18
18
|
from ast import literal_eval
|
|
19
|
-
from importlib.resources import read_binary
|
|
19
|
+
from importlib.resources import files, read_binary
|
|
20
20
|
from pathlib import Path
|
|
21
|
-
from sys import version_info as _python_version
|
|
22
|
-
from typing import List, Union
|
|
23
|
-
|
|
24
|
-
if _python_version.minor >= 10:
|
|
25
|
-
from importlib.resources import files
|
|
26
|
-
else:
|
|
27
|
-
try:
|
|
28
|
-
from importlib_resources import files # type: ignore
|
|
29
|
-
except Exception:
|
|
30
|
-
raise ImportError("Please install importlib-resources") # noqa: B904
|
|
31
21
|
|
|
32
22
|
from absfuyu.core import DATA_PATH
|
|
33
23
|
from absfuyu.logger import logger
|
|
@@ -61,7 +51,7 @@ class PkgData:
|
|
|
61
51
|
def __repr__(self) -> str:
|
|
62
52
|
return self.__str__()
|
|
63
53
|
|
|
64
|
-
def _make_dat(self, data: str, name:
|
|
54
|
+
def _make_dat(self, data: str, name: str | Path):
|
|
65
55
|
"""
|
|
66
56
|
data: string data
|
|
67
57
|
name: name and location of the data
|
|
@@ -90,14 +80,14 @@ class PkgData:
|
|
|
90
80
|
|
|
91
81
|
:param new_data: Data to be updated
|
|
92
82
|
"""
|
|
93
|
-
self._make_dat(data=new_data, name=DATA_PATH.joinpath(self.name))
|
|
83
|
+
self._make_dat(data=new_data, name=DATA_PATH.joinpath(self.name)) # type:ignore
|
|
94
84
|
logger.debug("Data updated")
|
|
95
85
|
|
|
96
86
|
|
|
97
87
|
class _ManagePkgData:
|
|
98
88
|
"""Manage this package data"""
|
|
99
89
|
|
|
100
|
-
def __init__(self, pkg_data_loc:
|
|
90
|
+
def __init__(self, pkg_data_loc: str | Path) -> None:
|
|
101
91
|
"""
|
|
102
92
|
pkg_data_loc: Package data location
|
|
103
93
|
"""
|
|
@@ -109,7 +99,7 @@ class _ManagePkgData:
|
|
|
109
99
|
def __repr__(self) -> str:
|
|
110
100
|
return self.__str__()
|
|
111
101
|
|
|
112
|
-
def get_data_list(self, *, pattern: str = "*") ->
|
|
102
|
+
def get_data_list(self, *, pattern: str = "*") -> list[Path]:
|
|
113
103
|
"""Get a list of data available"""
|
|
114
104
|
excludes = [
|
|
115
105
|
x for x in self.data_loc.glob("*.[pP][yY]")
|
|
@@ -119,7 +109,7 @@ class _ManagePkgData:
|
|
|
119
109
|
]
|
|
120
110
|
|
|
121
111
|
@property
|
|
122
|
-
def data_list(self) ->
|
|
112
|
+
def data_list(self) -> list[str]:
|
|
123
113
|
"""List of available data"""
|
|
124
114
|
return [x.name for x in self.get_data_list()]
|
|
125
115
|
|
|
@@ -135,7 +125,10 @@ class _ManagePkgData:
|
|
|
135
125
|
for data_name, data_link in _EXTERNAL_DATA.items():
|
|
136
126
|
logger.debug(f"Downloading {data_name}...")
|
|
137
127
|
data = APIRequest(data_link, encoding="utf-8")
|
|
138
|
-
data.fetch_data(
|
|
128
|
+
data.fetch_data(
|
|
129
|
+
update=True,
|
|
130
|
+
json_cache=DATA_PATH.joinpath(data_name), # type:ignore
|
|
131
|
+
)
|
|
139
132
|
logger.debug(f"Downloading {data_name}...DONE")
|
|
140
133
|
logger.debug("Downloading data...DONE")
|
|
141
134
|
except Exception:
|
|
@@ -147,7 +140,7 @@ class _ManagePkgData:
|
|
|
147
140
|
x.unlink()
|
|
148
141
|
|
|
149
142
|
|
|
150
|
-
PACKAGE_DATA = _ManagePkgData(DATA_PATH)
|
|
143
|
+
PACKAGE_DATA = _ManagePkgData(DATA_PATH) # type:ignore
|
|
151
144
|
|
|
152
145
|
|
|
153
146
|
# Run
|