libdev 0.92__tar.gz → 0.94__tar.gz

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 (41) hide show
  1. {libdev-0.92 → libdev-0.94}/PKG-INFO +1 -1
  2. {libdev-0.92 → libdev-0.94}/libdev/__init__.py +1 -1
  3. libdev-0.94/libdev/num.py +437 -0
  4. {libdev-0.92 → libdev-0.94}/libdev/time.py +21 -0
  5. {libdev-0.92 → libdev-0.94}/libdev.egg-info/PKG-INFO +1 -1
  6. {libdev-0.92 → libdev-0.94}/tests/test_num.py +92 -0
  7. libdev-0.92/libdev/num.py +0 -244
  8. {libdev-0.92 → libdev-0.94}/LICENSE +0 -0
  9. {libdev-0.92 → libdev-0.94}/README.md +0 -0
  10. {libdev-0.92 → libdev-0.94}/libdev/cfg.py +0 -0
  11. {libdev-0.92 → libdev-0.94}/libdev/check.py +0 -0
  12. {libdev-0.92 → libdev-0.94}/libdev/codes.py +0 -0
  13. {libdev-0.92 → libdev-0.94}/libdev/crypt.py +0 -0
  14. {libdev-0.92 → libdev-0.94}/libdev/dev.py +0 -0
  15. {libdev-0.92 → libdev-0.94}/libdev/doc.py +0 -0
  16. {libdev-0.92 → libdev-0.94}/libdev/fin.py +0 -0
  17. {libdev-0.92 → libdev-0.94}/libdev/gen.py +0 -0
  18. {libdev-0.92 → libdev-0.94}/libdev/img.py +0 -0
  19. {libdev-0.92 → libdev-0.94}/libdev/lang.py +0 -0
  20. {libdev-0.92 → libdev-0.94}/libdev/log.py +0 -0
  21. {libdev-0.92 → libdev-0.94}/libdev/req.py +0 -0
  22. {libdev-0.92 → libdev-0.94}/libdev/s3.py +0 -0
  23. {libdev-0.92 → libdev-0.94}/libdev.egg-info/SOURCES.txt +0 -0
  24. {libdev-0.92 → libdev-0.94}/libdev.egg-info/dependency_links.txt +0 -0
  25. {libdev-0.92 → libdev-0.94}/libdev.egg-info/requires.txt +0 -0
  26. {libdev-0.92 → libdev-0.94}/libdev.egg-info/top_level.txt +0 -0
  27. {libdev-0.92 → libdev-0.94}/setup.cfg +0 -0
  28. {libdev-0.92 → libdev-0.94}/setup.py +0 -0
  29. {libdev-0.92 → libdev-0.94}/tests/test_cfg.py +0 -0
  30. {libdev-0.92 → libdev-0.94}/tests/test_check.py +0 -0
  31. {libdev-0.92 → libdev-0.94}/tests/test_codes.py +0 -0
  32. {libdev-0.92 → libdev-0.94}/tests/test_crypt.py +0 -0
  33. {libdev-0.92 → libdev-0.94}/tests/test_dev.py +0 -0
  34. {libdev-0.92 → libdev-0.94}/tests/test_doc.py +0 -0
  35. {libdev-0.92 → libdev-0.94}/tests/test_gen.py +0 -0
  36. {libdev-0.92 → libdev-0.94}/tests/test_img.py +0 -0
  37. {libdev-0.92 → libdev-0.94}/tests/test_lang.py +0 -0
  38. {libdev-0.92 → libdev-0.94}/tests/test_log.py +0 -0
  39. {libdev-0.92 → libdev-0.94}/tests/test_req.py +0 -0
  40. {libdev-0.92 → libdev-0.94}/tests/test_s3.py +0 -0
  41. {libdev-0.92 → libdev-0.94}/tests/test_time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: libdev
3
- Version: 0.92
3
+ Version: 0.94
4
4
  Summary: Set of standard functions for development
5
5
  Home-page: https://github.com/chilleco/lib
6
6
  Author: Alex Poloz
@@ -2,6 +2,6 @@
2
2
  Initializing the Python package
3
3
  """
4
4
 
5
- __version__ = "0.92"
5
+ __version__ = "0.94"
6
6
 
7
7
  __all__ = ("__version__",)
@@ -0,0 +1,437 @@
1
+ """
2
+ Numbers functionality
3
+ """
4
+
5
+ import re
6
+ import math
7
+ from decimal import Decimal, InvalidOperation
8
+
9
+
10
+ _SUBSCRIPTS = str.maketrans("0123456789", "₀₁₂₃₄₅₆₇₈₉")
11
+
12
+
13
+ def is_float(value: str) -> bool:
14
+ """Check value for float"""
15
+
16
+ try:
17
+ float(value)
18
+ except (ValueError, TypeError):
19
+ return False
20
+
21
+ return True
22
+
23
+
24
+ def to_num(value) -> bool:
25
+ """Convert value to int or float"""
26
+
27
+ if value is None:
28
+ return None
29
+
30
+ if isinstance(value, str):
31
+ value = float(value.strip())
32
+
33
+ if not value % 1:
34
+ value = int(value)
35
+
36
+ return value
37
+
38
+
39
+ def to_int(value) -> int:
40
+ """Choose only decimal"""
41
+
42
+ if not value:
43
+ return 0
44
+
45
+ return int(re.sub(r"\D", "", str(value)))
46
+
47
+
48
+ def get_float(value) -> list:
49
+ """Get a list of floats"""
50
+
51
+ if value is None:
52
+ return []
53
+
54
+ numbers = re.findall(r"[-+]?\d*\.\d+|[-+]?\d+", value)
55
+ return [float(number) for number in numbers]
56
+
57
+
58
+ def find_decimals(value):
59
+ """Get count of decimal"""
60
+
61
+ if isinstance(value, str):
62
+ while value[-1] == "0":
63
+ value = value[:-1]
64
+
65
+ return abs(Decimal(str(value)).as_tuple().exponent)
66
+
67
+
68
+ def get_whole(value):
69
+ """Get whole view of a number"""
70
+
71
+ if isinstance(value, int) or (isinstance(value, str) and "." not in value):
72
+ # NOTE: to remove 0 in the start of the string
73
+ return str(int(value))
74
+
75
+ # NOTE: float for add . to int & support str
76
+ value = float(value)
77
+
78
+ # NOTE: to avoid the exponential form of the number
79
+ return f"{value:.{find_decimals(value)}f}"
80
+
81
+
82
+ def simplify_value(value, decimals=4):
83
+ """Get the significant part of a number"""
84
+
85
+ if value is None:
86
+ return None
87
+
88
+ value = get_whole(value)
89
+ if "." not in value:
90
+ value += "."
91
+
92
+ whole, fractional = value.split(".")
93
+
94
+ if value[0] == "-":
95
+ sign = "-"
96
+ whole = whole[1:]
97
+ else:
98
+ sign = ""
99
+
100
+ if whole != "0":
101
+ digit = len(whole)
102
+ value = whole + "." + fractional[: max(0, decimals - digit)]
103
+
104
+ else:
105
+ offset = 0
106
+ while fractional and fractional[0] == "0":
107
+ offset += 1
108
+ fractional = fractional[1:]
109
+
110
+ value = "0." + "0" * offset + fractional[:decimals]
111
+
112
+ while value[-1] == "0":
113
+ value = value[:-1]
114
+
115
+ if value[-1] == ".":
116
+ value = value[:-1]
117
+
118
+ return sign + value
119
+
120
+
121
+ def pretty(
122
+ value,
123
+ decimals=None,
124
+ sign=False,
125
+ symbol="’",
126
+ zeros=4,
127
+ compress=None,
128
+ ):
129
+ """Decorate the number beautifully"""
130
+
131
+ if value is None:
132
+ return None
133
+
134
+ # Handle decimals parameter first (takes precedence)
135
+ if decimals is not None:
136
+ # Use the original decimals logic for backward compatibility
137
+ s = to_plain(abs(value))
138
+ if "." in s:
139
+ int_part = s.split(".", 1)[0]
140
+ cur = len(int_part)
141
+ target_decimals = max(0, decimals - cur)
142
+ # Apply rounding first
143
+ rounded_value = round(float(value), target_decimals)
144
+ # If target_decimals is 0, convert to int for proper formatting
145
+ if target_decimals == 0:
146
+ rounded_value = int(rounded_value)
147
+ # Then use compress_zeros without round parameter
148
+ data = compress_zeros(rounded_value, zeros=zeros)
149
+ else:
150
+ data = compress_zeros(value, zeros=zeros)
151
+ elif zeros is None and compress is None:
152
+ # No compression or special formatting requested, use plain representation
153
+ data = to_plain(value)
154
+ else:
155
+ # Use compress_zeros with specified parameters
156
+ compress_zeros_args = {}
157
+
158
+ if zeros is not None:
159
+ compress_zeros_args["zeros"] = zeros
160
+
161
+ if compress is not None:
162
+ compress_zeros_args["round"] = compress
163
+
164
+ data = compress_zeros(value, **compress_zeros_args)
165
+
166
+ if data == "0":
167
+ return "0"
168
+
169
+ # Remove trailing zeros after decimal point for cleaner formatting
170
+ if "." in data and data.rsplit(".", maxsplit=1)[-1] == "0":
171
+ data = data.split(".", maxsplit=1)[0]
172
+
173
+ if symbol:
174
+ data = add_radix(data, symbol)
175
+
176
+ if sign:
177
+ if data[0] != "-":
178
+ data = "+" + data
179
+
180
+ return data
181
+
182
+
183
+ def add_sign(value):
184
+ """Add sign to a number"""
185
+
186
+ if value is None:
187
+ return None
188
+
189
+ sign = ""
190
+
191
+ if float(value) > 0:
192
+ sign = "+"
193
+ elif value == 0:
194
+ value = abs(value)
195
+
196
+ return f"{sign}{get_whole(value)}"
197
+
198
+
199
+ def add_radix(value, symbol="’"):
200
+ """Add radix to a number"""
201
+
202
+ if value is None:
203
+ return None
204
+
205
+ value = str(value)
206
+
207
+ if "." in value:
208
+ integer, fractional = value.split(".")
209
+ else:
210
+ integer = value
211
+ fractional = ""
212
+
213
+ if integer[0] == "-":
214
+ sign = "-"
215
+ integer = integer[1:]
216
+ # elif integer[0] == '+':
217
+ # sign = '+'
218
+ # integer = integer[1:]
219
+ else:
220
+ sign = ""
221
+
222
+ data = ""
223
+ ind = 0
224
+ for i in integer[::-1]:
225
+ if ind and ind % 3 == 0:
226
+ data = symbol + data
227
+ ind += 1
228
+ data = i + data
229
+
230
+ data = sign + data
231
+ if fractional:
232
+ data += "." + fractional
233
+
234
+ return data
235
+
236
+
237
+ def mul(x, y):
238
+ """Multiply fractions correctly"""
239
+ if x is None or y is None:
240
+ return None
241
+ return float(Decimal(str(x)) * Decimal(str(y)))
242
+
243
+
244
+ def div(x, y):
245
+ """Divide fractions correctly"""
246
+ if x is None or y is None:
247
+ return None
248
+ return float(Decimal(str(x)) / Decimal(str(y)))
249
+
250
+
251
+ def add(x, y):
252
+ """Subtract fractions correctly"""
253
+ if x is None or y is None:
254
+ return None
255
+ return float(Decimal(str(x)) + Decimal(str(y)))
256
+
257
+
258
+ def sub(x, y):
259
+ """Subtract fractions correctly"""
260
+ if x is None or y is None:
261
+ return None
262
+ return float(Decimal(str(x)) - Decimal(str(y)))
263
+
264
+
265
+ def to_step(value, step=1, side=False):
266
+ """Change value step"""
267
+
268
+ if value is None:
269
+ return None
270
+
271
+ value = div(value, step)
272
+ if side:
273
+ value = math.ceil(value)
274
+ else:
275
+ value = math.floor(value)
276
+ value = mul(value, step)
277
+
278
+ if step >= 1:
279
+ value = int(value)
280
+
281
+ return value
282
+
283
+
284
+ def to_plain(value) -> str:
285
+ if value is None:
286
+ return None
287
+ try:
288
+ if isinstance(value, str):
289
+ d = Decimal(value)
290
+ elif isinstance(value, float):
291
+ d = Decimal(str(value))
292
+ else:
293
+ d = Decimal(value)
294
+
295
+ s = format(d.normalize(), "f")
296
+
297
+ if "." in s:
298
+ s = s.rstrip("0").rstrip(".")
299
+ if s == "-0":
300
+ s = "0"
301
+ return s
302
+ except (InvalidOperation, ValueError, TypeError):
303
+ return str(value)
304
+
305
+
306
+ def _round_to_decimals(x, decimals):
307
+ """Helper function to round to specified decimal places"""
308
+ if decimals <= 0:
309
+ return float(int(x))
310
+ return round(float(x), decimals)
311
+
312
+
313
+ def compress_zeros(x, zeros=2, round=None) -> str:
314
+ """
315
+ 0.000012 -> '0.0₄12'
316
+ 1.000045 -> '1.0₄45'
317
+ round: number of digits after the zero block (rounds).
318
+ zeros: minimum count of consecutive zeros to compress (default: 2).
319
+ """
320
+
321
+ if x is None:
322
+ return None
323
+
324
+ # Store original string representation for rounding calculations
325
+ original_str = None
326
+ if isinstance(x, str):
327
+ original_str = x.strip()
328
+ x = original_str
329
+ # Remove trailing zeros from string
330
+ if "." in x:
331
+ x = x.rstrip("0").rstrip(".")
332
+ # Convert to appropriate numeric type
333
+ try:
334
+ if "." in x:
335
+ x = float(x)
336
+ else:
337
+ x = int(x)
338
+ except ValueError:
339
+ return str(x)
340
+
341
+ # Determine if original was float or int to preserve format
342
+ is_float_type = isinstance(x, float) or (isinstance(x, str) and "." in str(x))
343
+
344
+ # Handle rounding if specified
345
+ if round is not None:
346
+ # For rounding, use original string if available, otherwise convert to plain string
347
+ if original_str and "." in original_str:
348
+ s = original_str.lstrip("-")
349
+ else:
350
+ # Use to_plain to avoid scientific notation
351
+ s = to_plain(abs(x))
352
+
353
+ if "." in s:
354
+ int_part, frac_part = s.split(".")
355
+ # Count leading zeros in fractional part
356
+ leading_zeros = 0
357
+ for c in frac_part:
358
+ if c == "0":
359
+ leading_zeros += 1
360
+ else:
361
+ break
362
+
363
+ # Count trailing zeros in fractional part
364
+ trailing_zeros = 0
365
+ for c in reversed(frac_part):
366
+ if c == "0":
367
+ trailing_zeros += 1
368
+ else:
369
+ break
370
+
371
+ # Apply rounding logic
372
+ if leading_zeros > 0:
373
+ # If there are leading zeros, round after them (regardless of compression)
374
+ total_decimals = leading_zeros + round
375
+ x = _round_to_decimals(x, total_decimals)
376
+ else:
377
+ # No leading zeros, apply normal rounding
378
+ x = _round_to_decimals(x, round)
379
+
380
+ # Convert to string representation
381
+ if isinstance(x, int) and not is_float_type:
382
+ s = str(x)
383
+ else:
384
+ # For floats, use format that preserves trailing decimals when needed
385
+ if x == int(x) and is_float_type:
386
+ s = f"{int(x)}.0"
387
+ else:
388
+ s = str(float(x))
389
+ # Remove scientific notation if present
390
+ if "e" in s.lower():
391
+ s = f"{float(x):.15f}".rstrip("0")
392
+ if s.endswith("."):
393
+ s += "0"
394
+
395
+ # Handle negative sign
396
+ negative = s.startswith("-")
397
+ if negative:
398
+ s = s[1:]
399
+
400
+ # Process compression
401
+ if "." not in s:
402
+ result = s
403
+ else:
404
+ int_part, frac_part = s.split(".")
405
+
406
+ # Compress leading zeros in fractional part
407
+ leading_zeros = 0
408
+ for c in frac_part:
409
+ if c == "0":
410
+ leading_zeros += 1
411
+ else:
412
+ break
413
+
414
+ # Compress trailing zeros in fractional part
415
+ trailing_zeros = 0
416
+ for c in reversed(frac_part):
417
+ if c == "0":
418
+ trailing_zeros += 1
419
+ else:
420
+ break
421
+
422
+ # Apply compression
423
+ if leading_zeros >= zeros:
424
+ # Compress leading zeros
425
+ remaining_frac = frac_part[leading_zeros:]
426
+ result = f"{int_part}.0{str(leading_zeros).translate(_SUBSCRIPTS)}{remaining_frac}"
427
+ elif trailing_zeros >= zeros and leading_zeros == 0:
428
+ # Compress trailing zeros (but not if there are leading zeros)
429
+ remaining_frac = frac_part[:-trailing_zeros]
430
+ if remaining_frac:
431
+ result = f"{int_part}.{remaining_frac}0{str(trailing_zeros).translate(_SUBSCRIPTS)}"
432
+ else:
433
+ result = f"{int_part}.0{str(trailing_zeros).translate(_SUBSCRIPTS)}"
434
+ else:
435
+ result = s
436
+
437
+ return f"-{result}" if negative else result
@@ -300,6 +300,27 @@ def get_month_start(timestamp=None, tz=0):
300
300
  return int(start_month.timestamp())
301
301
 
302
302
 
303
+ def get_week_start(timestamp=None, tz=0):
304
+ """
305
+ Get the start of the week (midnight on Monday) for a given timestamp in a specified timezone.
306
+
307
+ Args:
308
+ timestamp (float): The original timestamp (in seconds since epoch). Defaults to the current time if None.
309
+ tz (int): The timezone offset in hours (e.g., 3 for UTC+3).
310
+
311
+ Returns:
312
+ float: The timestamp for the start of the week (Monday at midnight) in the specified timezone.
313
+ """
314
+ if timestamp is None:
315
+ timestamp = time.time()
316
+ dt_local = datetime.datetime.fromtimestamp(timestamp, tz=to_tz(tz))
317
+ # Calculate days to subtract to get to Monday (weekday() returns 0 for Monday, 6 for Sunday)
318
+ days_since_monday = dt_local.weekday()
319
+ start_week = dt_local - datetime.timedelta(days=days_since_monday)
320
+ start_week = start_week.replace(hour=0, minute=0, second=0, microsecond=0)
321
+ return int(start_week.timestamp())
322
+
323
+
303
324
  def get_next_day(timestamp=None, tz=0):
304
325
  """
305
326
  Get the start of the next day (midnight) for a given timestamp in a specified timezone.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: libdev
3
- Version: 0.92
3
+ Version: 0.94
4
4
  Summary: Set of standard functions for development
5
5
  Home-page: https://github.com/chilleco/lib
6
6
  Author: Alex Poloz
@@ -11,6 +11,8 @@ from libdev.num import (
11
11
  to_step,
12
12
  add,
13
13
  pretty,
14
+ to_plain,
15
+ compress_zeros,
14
16
  )
15
17
 
16
18
 
@@ -155,3 +157,93 @@ def test_pretty():
155
157
  assert pretty(123.456, 1) == "123"
156
158
  assert pretty(123.456, 1, True) == "+123"
157
159
  assert pretty(12345.6, 3, True) == "+12’346"
160
+ assert pretty(-0.000000235235, zeros=None, compress=None) == "-0.000000235235"
161
+ assert pretty(-0.000000235235, zeros=4, compress=2) == "-0.0₆24"
162
+
163
+
164
+ def test_to_plain():
165
+ assert to_plain(None) == None
166
+ assert to_plain(0) == "0"
167
+ # assert to_plain(0.0) == "0.0"
168
+ # assert to_plain(1.0) == "1.0"
169
+ assert to_plain(1.1) == "1.1"
170
+ assert to_plain(0.000000235235) == "0.000000235235"
171
+ assert to_plain(-0.000000235235) == "-0.000000235235"
172
+ assert to_plain("0.000000235235") == "0.000000235235"
173
+ assert to_plain(2.35235e-07) == "0.000000235235"
174
+ assert to_plain("1e-12") == "0.000000000001"
175
+ assert to_plain("123.4500") == "123.45"
176
+
177
+
178
+ def test_pretty_with_compression():
179
+ # Test pretty function with compression parameters
180
+
181
+ # Test zeros parameter (minimum zeros to compress)
182
+ assert pretty(0.00012, zeros=2) == "0.0₃12" # Default behavior, compress >=2 zeros
183
+ assert pretty(0.01, zeros=1) == "0.0₁1" # Compress single zero with zeros=1
184
+ assert pretty(0.01, zeros=3) == "0.01" # Don't compress with zeros=3 (only 1 zero)
185
+ assert pretty(0.0001, zeros=3) == "0.0₃1" # Compress with zeros=3 (3 zeros)
186
+
187
+ # Test compress parameter (round after zero block)
188
+ assert (
189
+ pretty(0.00012345, zeros=2, compress=2) == "0.0₃12"
190
+ ) # Round to 2 digits after zeros
191
+ assert (
192
+ pretty(0.00012345, zeros=4, compress=4) == "0.0001234"
193
+ ) # No compression (3 < 4 zeros), 4 digits after 3 zeros
194
+ assert pretty(0.00012345, zeros=4, compress=8) == "0.00012345"
195
+ assert (
196
+ pretty(0.00012345, zeros=2, compress=3) == "0.0₃123"
197
+ ) # Round to 3 digits after zeros
198
+ assert (
199
+ pretty(-0.0010959999999999997522, compress=3) == "-0.0011"
200
+ ) # Negative with rounding
201
+
202
+ # Test both parameters together
203
+ assert (
204
+ pretty("0.0000012345", zeros=4, compress=2) == "0.0₅12"
205
+ ) # 5 zeros, round to 2 digits
206
+ assert (
207
+ pretty("0.00012345", zeros=4, compress=2) == "0.00012"
208
+ ) # Only 3 zeros, no compression, 2 digits after 3 zeros
209
+
210
+ # Test that other pretty parameters still work with compression
211
+ assert pretty(0.00012, zeros=2, sign=True) == "+0.0₃12" # With sign
212
+ assert pretty(12000.00012, zeros=2, symbol="'") == "12'000.0₃12" # With radix
213
+ assert (
214
+ pretty(-0.00012, zeros=2, sign=True, symbol="'") == "-0.0₃12"
215
+ ) # Negative with sign and radix
216
+
217
+ # Test edge cases
218
+ assert pretty(0, zeros=1) == "0" # Zero value
219
+ assert pretty(None, zeros=2) == None # None value
220
+ assert (
221
+ pretty(1.0, zeros=2) == "1"
222
+ ) # No fractional zeros to compress, trailing zero removed for clean formatting
223
+
224
+
225
+ def test_compress_zeros():
226
+ assert compress_zeros(None) == None
227
+ assert compress_zeros(0) == "0"
228
+ assert compress_zeros(0.0) == "0.0"
229
+ assert compress_zeros(1) == "1"
230
+ assert compress_zeros(1.0) == "1.0"
231
+ assert compress_zeros(1.0, round=0) == "1.0"
232
+ assert compress_zeros(0.00012) == "0.0₃12"
233
+ assert compress_zeros(0.00012, round=2) == "0.0₃12"
234
+ assert compress_zeros(0.0123) == "0.0123"
235
+ assert compress_zeros(0.0123456, round=3) == "0.0123"
236
+ assert compress_zeros(1.000045) == "1.0₄45"
237
+ assert compress_zeros(0.0010859999999999997522, round=3) == "0.0₂109"
238
+ assert compress_zeros(-0.0010959999999999997522, round=3) == "-0.0₂11"
239
+ assert compress_zeros("-0.012300") == "-0.0123"
240
+ assert compress_zeros(0.01, zeros=1) == "0.0₁1"
241
+ assert compress_zeros(0.001, zeros=1) == "0.0₂1"
242
+ assert compress_zeros("1.10", zeros=1) == "1.1"
243
+ assert compress_zeros(0.01, zeros=3) == "0.01"
244
+ assert compress_zeros(0.001, zeros=3) == "0.001"
245
+ assert compress_zeros(0.0001, zeros=3) == "0.0₃1"
246
+ assert compress_zeros(0.00001, zeros=3) == "0.0₄1"
247
+ assert compress_zeros(0.0000012, zeros=1) == "0.0₅12"
248
+ assert compress_zeros(0.0000012, zeros=5) == "0.0₅12"
249
+ assert compress_zeros(0.0000012, zeros=6) == "0.0000012"
libdev-0.92/libdev/num.py DELETED
@@ -1,244 +0,0 @@
1
- """
2
- Numbers functionality
3
- """
4
-
5
- import re
6
- import math
7
- from decimal import Decimal
8
-
9
-
10
- def is_float(value: str) -> bool:
11
- """Check value for float"""
12
-
13
- try:
14
- float(value)
15
- except (ValueError, TypeError):
16
- return False
17
-
18
- return True
19
-
20
-
21
- def to_num(value) -> bool:
22
- """Convert value to int or float"""
23
-
24
- if value is None:
25
- return None
26
-
27
- if isinstance(value, str):
28
- value = float(value.strip())
29
-
30
- if not value % 1:
31
- value = int(value)
32
-
33
- return value
34
-
35
-
36
- def to_int(value) -> int:
37
- """Choose only decimal"""
38
-
39
- if not value:
40
- return 0
41
-
42
- return int(re.sub(r"\D", "", str(value)))
43
-
44
-
45
- def get_float(value) -> list:
46
- """Get a list of floats"""
47
-
48
- if value is None:
49
- return []
50
-
51
- numbers = re.findall(r"[-+]?\d*\.\d+|[-+]?\d+", value)
52
- return [float(number) for number in numbers]
53
-
54
-
55
- def find_decimals(value):
56
- """Get count of decimal"""
57
-
58
- if isinstance(value, str):
59
- while value[-1] == "0":
60
- value = value[:-1]
61
-
62
- return abs(Decimal(str(value)).as_tuple().exponent)
63
-
64
-
65
- def get_whole(value):
66
- """Get whole view of a number"""
67
-
68
- if isinstance(value, int) or (isinstance(value, str) and "." not in value):
69
- # NOTE: to remove 0 in the start of the string
70
- return str(int(value))
71
-
72
- # NOTE: float for add . to int & support str
73
- value = float(value)
74
-
75
- # NOTE: to avoid the exponential form of the number
76
- return f"{value:.{find_decimals(value)}f}"
77
-
78
-
79
- def simplify_value(value, decimals=4):
80
- """Get the significant part of a number"""
81
-
82
- if value is None:
83
- return None
84
-
85
- value = get_whole(value)
86
- if "." not in value:
87
- value += "."
88
-
89
- whole, fractional = value.split(".")
90
-
91
- if value[0] == "-":
92
- sign = "-"
93
- whole = whole[1:]
94
- else:
95
- sign = ""
96
-
97
- if whole != "0":
98
- digit = len(whole)
99
- value = whole + "." + fractional[: max(0, decimals - digit)]
100
-
101
- else:
102
- offset = 0
103
- while fractional and fractional[0] == "0":
104
- offset += 1
105
- fractional = fractional[1:]
106
-
107
- value = "0." + "0" * offset + fractional[:decimals]
108
-
109
- while value[-1] == "0":
110
- value = value[:-1]
111
-
112
- if value[-1] == ".":
113
- value = value[:-1]
114
-
115
- return sign + value
116
-
117
-
118
- def pretty(value, decimals=None, sign=False, symbol="’"):
119
- """Decorate the number beautifully"""
120
-
121
- if value is None:
122
- return None
123
-
124
- data = str(float(value))
125
-
126
- if decimals is not None:
127
- cur = len(data.split(".", maxsplit=1)[0])
128
- data = str(round(value, max(0, decimals - cur)))
129
-
130
- if data.rsplit(".", maxsplit=1)[-1] == "0":
131
- data = data.split(".", maxsplit=1)[0]
132
-
133
- if data == "0":
134
- return "0"
135
-
136
- if symbol:
137
- data = add_radix(data, symbol)
138
-
139
- if sign:
140
- if data[0] != "-":
141
- data = "+" + data
142
-
143
- return data
144
-
145
-
146
- def add_sign(value):
147
- """Add sign to a number"""
148
-
149
- if value is None:
150
- return None
151
-
152
- sign = ""
153
-
154
- if float(value) > 0:
155
- sign = "+"
156
- elif value == 0:
157
- value = abs(value)
158
-
159
- return f"{sign}{get_whole(value)}"
160
-
161
-
162
- def add_radix(value, symbol="’"):
163
- """Add radix to a number"""
164
-
165
- if value is None:
166
- return None
167
-
168
- value = str(value)
169
-
170
- if "." in value:
171
- integer, fractional = value.split(".")
172
- else:
173
- integer = value
174
- fractional = ""
175
-
176
- if integer[0] == "-":
177
- sign = "-"
178
- integer = integer[1:]
179
- # elif integer[0] == '+':
180
- # sign = '+'
181
- # integer = integer[1:]
182
- else:
183
- sign = ""
184
-
185
- data = ""
186
- ind = 0
187
- for i in integer[::-1]:
188
- if ind and ind % 3 == 0:
189
- data = symbol + data
190
- ind += 1
191
- data = i + data
192
-
193
- data = sign + data
194
- if fractional:
195
- data += "." + fractional
196
-
197
- return data
198
-
199
-
200
- def mul(x, y):
201
- """Multiply fractions correctly"""
202
- if x is None or y is None:
203
- return None
204
- return float(Decimal(str(x)) * Decimal(str(y)))
205
-
206
-
207
- def div(x, y):
208
- """Divide fractions correctly"""
209
- if x is None or y is None:
210
- return None
211
- return float(Decimal(str(x)) / Decimal(str(y)))
212
-
213
-
214
- def add(x, y):
215
- """Subtract fractions correctly"""
216
- if x is None or y is None:
217
- return None
218
- return float(Decimal(str(x)) + Decimal(str(y)))
219
-
220
-
221
- def sub(x, y):
222
- """Subtract fractions correctly"""
223
- if x is None or y is None:
224
- return None
225
- return float(Decimal(str(x)) - Decimal(str(y)))
226
-
227
-
228
- def to_step(value, step=1, side=False):
229
- """Change value step"""
230
-
231
- if value is None:
232
- return None
233
-
234
- value = div(value, step)
235
- if side:
236
- value = math.ceil(value)
237
- else:
238
- value = math.floor(value)
239
- value = mul(value, step)
240
-
241
- if step >= 1:
242
- value = int(value)
243
-
244
- return value
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes