none-shall-parse 0.4.0__tar.gz → 0.4.2__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.
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/PKG-INFO +1 -1
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/pyproject.toml +1 -1
- none_shall_parse-0.4.2/src/none_shall_parse/__init__.py +133 -0
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/src/none_shall_parse/dates.py +100 -22
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/src/none_shall_parse/imeis.py +7 -8
- none_shall_parse-0.4.0/src/none_shall_parse/__init__.py +0 -15
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/README.md +0 -0
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/src/none_shall_parse/lists.py +0 -0
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/src/none_shall_parse/parse.py +0 -0
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/src/none_shall_parse/strings.py +0 -0
- {none_shall_parse-0.4.0 → none_shall_parse-0.4.2}/src/none_shall_parse/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: none-shall-parse
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Trinity Shared Python utilities.
|
|
5
5
|
Author: Andries Niemandt, Jan Badenhorst
|
|
6
6
|
Author-email: Andries Niemandt <andries.niemandt@trintel.co.za>, Jan Badenhorst <jan@trintel.co.za>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Trinity Shared Python utilities.
|
|
2
|
+
|
|
3
|
+
A collection of shared utilities for Trinity projects.
|
|
4
|
+
|
|
5
|
+
Originally intended to be parsing utilities, this grew to include
|
|
6
|
+
other useful functions.
|
|
7
|
+
|
|
8
|
+
Named for its author Andries Niemandt - whose surname loosely
|
|
9
|
+
translates to "none". Combined this with our parsing intentions
|
|
10
|
+
to create a name which nods to the Black Knight in Monty Python's Holy Grail.
|
|
11
|
+
https://www.youtube.com/watch?v=zKhEw7nD9C4
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
from .dates import (
|
|
15
|
+
DateUtilsError, assert_week_start_date_is_valid,
|
|
16
|
+
assert_month_start_date_is_valid,
|
|
17
|
+
get_datetime_now, za_now,
|
|
18
|
+
za_ordinal_year_day_now,
|
|
19
|
+
za_ordinal_year_day_tomorrow, utc_epoch_start,
|
|
20
|
+
now_offset_n_minutes, now_offset_n_hours,
|
|
21
|
+
now_offset_n_days, get_datetime_tomorrow,
|
|
22
|
+
get_datetime_yesterday,
|
|
23
|
+
get_utc_datetime_offset_n_days,
|
|
24
|
+
epoch_to_datetime, epoch_to_utc_datetime,
|
|
25
|
+
is_office_hours_in_timezone,
|
|
26
|
+
get_datetime_from_ordinal_and_sentinel,
|
|
27
|
+
day_span, week_span, month_span, arb_span,
|
|
28
|
+
calendar_month_start_end, unix_timestamp,
|
|
29
|
+
sentinel_date_and_ordinal_to_date,
|
|
30
|
+
seconds_to_end_of_month, standard_tz_timestring,
|
|
31
|
+
get_notice_end_date, dt_to_za_time_string,
|
|
32
|
+
months_ago_selection, is_aware, make_aware,
|
|
33
|
+
unaware_to_utc_aware, timer_decorator, ZA_TZ,
|
|
34
|
+
UTC_TZ, keys_for_span_func,
|
|
35
|
+
)
|
|
36
|
+
from .imeis import (
|
|
37
|
+
get_luhn_digit,
|
|
38
|
+
is_valid_luhn,
|
|
39
|
+
is_valid_imei,
|
|
40
|
+
normalize_imei,
|
|
41
|
+
get_tac_from_imei,
|
|
42
|
+
decrement_imei,
|
|
43
|
+
increment_imei,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
from .lists import (
|
|
47
|
+
flatten,
|
|
48
|
+
safe_list_get,
|
|
49
|
+
)
|
|
50
|
+
from .parse import (
|
|
51
|
+
str_to_bool,
|
|
52
|
+
is_true,
|
|
53
|
+
is_false,
|
|
54
|
+
str_to_strs_list,
|
|
55
|
+
int_to_bool,
|
|
56
|
+
int_or_none,
|
|
57
|
+
choices_code_to_string,
|
|
58
|
+
choices_string_to_code,
|
|
59
|
+
none_or_empty,
|
|
60
|
+
)
|
|
61
|
+
from .strings import (
|
|
62
|
+
slugify,
|
|
63
|
+
random_16,
|
|
64
|
+
to_human_string,
|
|
65
|
+
is_quoted_string,
|
|
66
|
+
is_numeric_string,
|
|
67
|
+
custom_slug,
|
|
68
|
+
b64_encode,
|
|
69
|
+
b64_decode,
|
|
70
|
+
calc_hash,
|
|
71
|
+
generate_random_password,
|
|
72
|
+
)
|
|
73
|
+
from .types import (
|
|
74
|
+
StringLike,
|
|
75
|
+
ChoicesType
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
__author__ = "Andries Niemandt, Jan Badenhorst"
|
|
79
|
+
__email__ = "andries.niemandt@trintel.co.za, jan@trintel.co.za"
|
|
80
|
+
__license__ = "MIT"
|
|
81
|
+
|
|
82
|
+
__all__ = (
|
|
83
|
+
"DateUtilsError", "assert_week_start_date_is_valid",
|
|
84
|
+
"assert_month_start_date_is_valid",
|
|
85
|
+
"get_datetime_now", "za_now",
|
|
86
|
+
"za_ordinal_year_day_now",
|
|
87
|
+
"za_ordinal_year_day_tomorrow", "utc_epoch_start",
|
|
88
|
+
"now_offset_n_minutes", "now_offset_n_hours",
|
|
89
|
+
"now_offset_n_days", "get_datetime_tomorrow",
|
|
90
|
+
"get_datetime_yesterday",
|
|
91
|
+
"get_utc_datetime_offset_n_days",
|
|
92
|
+
"epoch_to_datetime", "epoch_to_utc_datetime",
|
|
93
|
+
"is_office_hours_in_timezone",
|
|
94
|
+
"get_datetime_from_ordinal_and_sentinel",
|
|
95
|
+
"day_span", "week_span", "month_span", "arb_span",
|
|
96
|
+
"calendar_month_start_end", "unix_timestamp",
|
|
97
|
+
"sentinel_date_and_ordinal_to_date",
|
|
98
|
+
"seconds_to_end_of_month", "standard_tz_timestring",
|
|
99
|
+
"get_notice_end_date", "dt_to_za_time_string",
|
|
100
|
+
"months_ago_selection", "is_aware", "make_aware",
|
|
101
|
+
"unaware_to_utc_aware", "timer_decorator", "ZA_TZ",
|
|
102
|
+
"UTC_TZ", "keys_for_span_func",
|
|
103
|
+
"get_luhn_digit",
|
|
104
|
+
"is_valid_luhn",
|
|
105
|
+
"is_valid_imei",
|
|
106
|
+
"normalize_imei",
|
|
107
|
+
"get_tac_from_imei",
|
|
108
|
+
"decrement_imei",
|
|
109
|
+
"increment_imei",
|
|
110
|
+
"flatten",
|
|
111
|
+
"safe_list_get",
|
|
112
|
+
"str_to_bool",
|
|
113
|
+
"is_true",
|
|
114
|
+
"is_false",
|
|
115
|
+
"str_to_strs_list",
|
|
116
|
+
"int_to_bool",
|
|
117
|
+
"int_or_none",
|
|
118
|
+
"choices_code_to_string",
|
|
119
|
+
"choices_string_to_code",
|
|
120
|
+
"none_or_empty",
|
|
121
|
+
"slugify",
|
|
122
|
+
"random_16",
|
|
123
|
+
"to_human_string",
|
|
124
|
+
"is_quoted_string",
|
|
125
|
+
"is_numeric_string",
|
|
126
|
+
"custom_slug",
|
|
127
|
+
"b64_encode",
|
|
128
|
+
"b64_decode",
|
|
129
|
+
"calc_hash",
|
|
130
|
+
"generate_random_password",
|
|
131
|
+
"StringLike",
|
|
132
|
+
"ChoicesType"
|
|
133
|
+
)
|
|
@@ -200,7 +200,7 @@ def epoch_to_datetime(epoch_int: int | float, naive: bool = False,
|
|
|
200
200
|
return pendulum.from_timestamp(int(epoch_int), tz=tz)
|
|
201
201
|
|
|
202
202
|
|
|
203
|
-
def epoch_to_utc_datetime(epoch_int: int | float) -> datetime:
|
|
203
|
+
def epoch_to_utc_datetime(epoch_int: int | float | str) -> datetime:
|
|
204
204
|
"""
|
|
205
205
|
Converts an epoch timestamp to a UTC datetime object.
|
|
206
206
|
|
|
@@ -216,7 +216,7 @@ def epoch_to_utc_datetime(epoch_int: int | float) -> datetime:
|
|
|
216
216
|
return pendulum.from_timestamp(int(epoch_int), tz=UTC_TZ)
|
|
217
217
|
|
|
218
218
|
|
|
219
|
-
def is_office_hours_in_timezone(epoch_int: int | float, tz: str | None = None) -> bool:
|
|
219
|
+
def is_office_hours_in_timezone(epoch_int: int | float | str, tz: str | None = None) -> bool:
|
|
220
220
|
"""
|
|
221
221
|
Determines if a given epoch timestamp falls within office hours for a
|
|
222
222
|
specific timezone.
|
|
@@ -259,23 +259,18 @@ def get_datetime_from_ordinal_and_sentinel(
|
|
|
259
259
|
# Check timezone awareness
|
|
260
260
|
naive = sentinel.tzinfo is None
|
|
261
261
|
|
|
262
|
-
# Convert to Pendulum for easier manipulation
|
|
263
262
|
sentinel_pdt = pendulum.instance(sentinel)
|
|
264
263
|
sentinel_doy = sentinel_pdt.day_of_year
|
|
265
264
|
sentinel_year = sentinel_pdt.year
|
|
266
265
|
|
|
267
|
-
# Create start of years with CORRECT timezone handling
|
|
268
266
|
if naive:
|
|
269
|
-
# Use pendulum.naive() to create naive datetimes
|
|
270
267
|
this_year = pendulum.naive(sentinel_year, 1, 1)
|
|
271
268
|
last_year = pendulum.naive(sentinel_year - 1, 1, 1)
|
|
272
269
|
else:
|
|
273
|
-
# Create in the same timezone as sentinel (not UTC!)
|
|
274
270
|
this_year = pendulum.datetime(sentinel_year, 1, 1, tz=sentinel_pdt.timezone)
|
|
275
271
|
last_year = pendulum.datetime(sentinel_year - 1, 1, 1, tz=sentinel_pdt.timezone)
|
|
276
272
|
|
|
277
273
|
def f(ordinal: int) -> datetime:
|
|
278
|
-
# Choose year based on whether ordinal is before or after sentinel's day of year
|
|
279
274
|
dt = this_year if ordinal <= sentinel_doy else last_year
|
|
280
275
|
|
|
281
276
|
# Handle leap year edge case
|
|
@@ -285,10 +280,7 @@ def get_datetime_from_ordinal_and_sentinel(
|
|
|
285
280
|
else:
|
|
286
281
|
return pendulum.datetime(1970, 1, 1, tz=sentinel_pdt.timezone)
|
|
287
282
|
|
|
288
|
-
# Add (ordinal - 1) days to start of year
|
|
289
283
|
result = dt.add(days=ordinal - 1)
|
|
290
|
-
|
|
291
|
-
# Return as-is (Pendulum datetime preserves timezone awareness)
|
|
292
284
|
return result
|
|
293
285
|
|
|
294
286
|
return f
|
|
@@ -412,26 +404,25 @@ def month_span(mso: int) -> Callable[[datetime], Tuple[datetime, datetime]]:
|
|
|
412
404
|
|
|
413
405
|
|
|
414
406
|
def arb_span(dates: Sequence[str | datetime], naive: bool = False) -> Callable[
|
|
415
|
-
[], Tuple[datetime, datetime]]:
|
|
407
|
+
[Any], Tuple[datetime, datetime]]:
|
|
416
408
|
"""
|
|
417
409
|
Parses two given dates and returns a callable function that provides the date range
|
|
418
410
|
as a tuple of datetime objects. The function ensures the date range is valid and
|
|
419
411
|
always returns the earlier date as the start and the later date as the end.
|
|
420
412
|
|
|
421
413
|
Parameters:
|
|
422
|
-
dates (Sequence[str | datetime]): A sequence containing exactly two dates where
|
|
423
|
-
|
|
414
|
+
dates (Sequence[str | datetime]): A sequence containing exactly two dates where
|
|
415
|
+
each date is either a string or a datetime object.
|
|
424
416
|
naive (bool): Optional flag. If True, the returned datetime objects will not
|
|
425
|
-
|
|
417
|
+
have timezone information (naive datetime). Defaults to False.
|
|
426
418
|
|
|
427
419
|
Returns:
|
|
428
|
-
Callable[[], Tuple[datetime, datetime]]: A function that, when invoked,
|
|
429
|
-
|
|
430
|
-
representing the date range.
|
|
420
|
+
Callable[[Any], Tuple[datetime, datetime]]: A function that, when invoked,
|
|
421
|
+
returns a tuple of datetime objects (start, end) representing the date range.
|
|
431
422
|
|
|
432
423
|
Raises:
|
|
433
424
|
DateUtilsError: If the provided dates are invalid, identical, or there's an error
|
|
434
|
-
|
|
425
|
+
during parsing.
|
|
435
426
|
"""
|
|
436
427
|
try:
|
|
437
428
|
parsed_dates = []
|
|
@@ -475,7 +466,7 @@ def arb_span(dates: Sequence[str | datetime], naive: bool = False) -> Callable[
|
|
|
475
466
|
except Exception as ex:
|
|
476
467
|
raise DateUtilsError(f"Error parsing dates: {ex}")
|
|
477
468
|
|
|
478
|
-
def find_dates() -> Tuple[datetime, datetime]:
|
|
469
|
+
def find_dates(*args) -> Tuple[datetime, datetime]:
|
|
479
470
|
"""
|
|
480
471
|
:return: tuple of datetime objects (start, end)
|
|
481
472
|
"""
|
|
@@ -486,6 +477,94 @@ def arb_span(dates: Sequence[str | datetime], naive: bool = False) -> Callable[
|
|
|
486
477
|
return find_dates
|
|
487
478
|
|
|
488
479
|
|
|
480
|
+
def unroll_span_func(
|
|
481
|
+
f: Callable[[datetime], Tuple[datetime, datetime]],
|
|
482
|
+
cover: datetime | None = None,
|
|
483
|
+
) -> Tuple[List[datetime], List[int], List[str], datetime, datetime]:
|
|
484
|
+
"""
|
|
485
|
+
Generate keys for a date range based on a provided function.
|
|
486
|
+
|
|
487
|
+
This function computes a date range using the provided function `f`, which takes a base date and returns
|
|
488
|
+
start and end dates. It generates input and output keys for each day in the range based on ordinal days
|
|
489
|
+
and optionally returns only ordinal day integers.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
f: Function that takes a base date and returns a tuple of start and end dates.
|
|
493
|
+
cover: Base date for computing the range. Defaults to the current date if None.
|
|
494
|
+
|
|
495
|
+
Returns:
|
|
496
|
+
A tuple containing:
|
|
497
|
+
- List of datetime objects.
|
|
498
|
+
- List of ordinal day integers.
|
|
499
|
+
- Start date of the range (as datetime).
|
|
500
|
+
- End date of the range (as datetime).
|
|
501
|
+
If ord_ints_only is True, returns (ordinal_days, start, end, iso_dates).
|
|
502
|
+
|
|
503
|
+
Raises:
|
|
504
|
+
DateUtilsError: If the date range cannot be processed due to invalid dates or formatting.
|
|
505
|
+
"""
|
|
506
|
+
cover = pendulum.now() if cover is None else cover
|
|
507
|
+
naive = cover.tzinfo is None
|
|
508
|
+
try:
|
|
509
|
+
start, end = f(cover)
|
|
510
|
+
except Exception as e:
|
|
511
|
+
raise DateUtilsError(f"Function f failed to compute date range: {str(e)}")
|
|
512
|
+
|
|
513
|
+
# Make sure we can use pendulum with these dates
|
|
514
|
+
start = pendulum.instance(start) if isinstance(start, datetime) else start
|
|
515
|
+
start = start.naive() if naive else start
|
|
516
|
+
end = pendulum.instance(end) if isinstance(end, datetime) else end
|
|
517
|
+
end = end.naive() if naive else end
|
|
518
|
+
|
|
519
|
+
try:
|
|
520
|
+
# Generate date range using pendulum.interval
|
|
521
|
+
# The absolute kwarg ensures that we do not have to care about dates passed
|
|
522
|
+
# in the wrong order. We will always range from start to end, inclusive.
|
|
523
|
+
interval = pendulum.interval(start, end.subtract(days=1), absolute=True)
|
|
524
|
+
date_range = []
|
|
525
|
+
ord_days = []
|
|
526
|
+
iso_date_strings = []
|
|
527
|
+
for dt in interval.range(unit="days"):
|
|
528
|
+
date_range.append(dt)
|
|
529
|
+
ord_days.append(dt.day_of_year)
|
|
530
|
+
iso_date_strings.append(dt.format('YYYY-MM-DD'))
|
|
531
|
+
|
|
532
|
+
return date_range, ord_days, iso_date_strings, start, end
|
|
533
|
+
|
|
534
|
+
except (TypeError, ValueError) as e:
|
|
535
|
+
raise DateUtilsError(f"Error processing date range: {str(e)}")
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def keys_for_span_func(
|
|
539
|
+
f: Callable[[datetime], Tuple[datetime, datetime]],
|
|
540
|
+
cover: datetime | None = None,
|
|
541
|
+
key_in_format: str = "ODIN_{}",
|
|
542
|
+
key_out_format: str = "ODOUT_{}",
|
|
543
|
+
):
|
|
544
|
+
"""
|
|
545
|
+
Generate keys for a date range based on a provided function.
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
f: Function that takes a base date and returns a tuple of start and end dates.
|
|
549
|
+
cover: Base date for computing the range. Defaults to the current date if None.
|
|
550
|
+
key_in_format: Format string for input keys. Defaults to "ODIN_{}".
|
|
551
|
+
key_out_format: Format string for output keys, Defaults to "ODOUT_{}"
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
- List of input keys (empty if key_in_format is None).
|
|
555
|
+
- List of output keys (empty if key_out_format is None).
|
|
556
|
+
- Start date of the range (as datetime).
|
|
557
|
+
- End date of the range (as datetime).
|
|
558
|
+
|
|
559
|
+
Raises:
|
|
560
|
+
DateUtilsError: If the date range cannot be processed.
|
|
561
|
+
"""
|
|
562
|
+
date_range, ord_days, iso_date_strings, start, end = unroll_span_func(f=f, cover=cover)
|
|
563
|
+
keys_in = [key_in_format.format(d) for d in ord_days]
|
|
564
|
+
keys_out = [key_out_format.format(d) for d in ord_days]
|
|
565
|
+
return keys_in, keys_out, start, end
|
|
566
|
+
|
|
567
|
+
|
|
489
568
|
def calendar_month_start_end(date_in_month: datetime | None = None) -> Tuple[
|
|
490
569
|
datetime, datetime]:
|
|
491
570
|
naive = date_in_month.tzinfo is None
|
|
@@ -495,7 +574,6 @@ def calendar_month_start_end(date_in_month: datetime | None = None) -> Tuple[
|
|
|
495
574
|
|
|
496
575
|
pdt = pendulum.instance(date_in_month)
|
|
497
576
|
|
|
498
|
-
# One-liner for both values
|
|
499
577
|
start = pdt.start_of('month')
|
|
500
578
|
end = start.add(months=1)
|
|
501
579
|
|
|
@@ -515,8 +593,8 @@ def unix_timestamp() -> int:
|
|
|
515
593
|
return round(time.time())
|
|
516
594
|
|
|
517
595
|
|
|
518
|
-
def sentinel_date_and_ordinal_to_date(sentinel_date: datetime,
|
|
519
|
-
ordinal: int | float) -> date:
|
|
596
|
+
def sentinel_date_and_ordinal_to_date(sentinel_date: datetime | date,
|
|
597
|
+
ordinal: int | float | str) -> date:
|
|
520
598
|
"""Convert sentinel date and ordinal day to actual date"""
|
|
521
599
|
year = sentinel_date.year
|
|
522
600
|
int_ordinal = int(ordinal)
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
from typing import Union
|
|
2
1
|
|
|
3
2
|
LUHN_DOUBLES = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
|
|
4
3
|
|
|
5
4
|
|
|
6
|
-
def get_luhn_digit(n:
|
|
5
|
+
def get_luhn_digit(n: str | int) -> int:
|
|
7
6
|
"""
|
|
8
7
|
Calculates the Luhn checksum digit for a given number.
|
|
9
8
|
|
|
@@ -26,7 +25,7 @@ def get_luhn_digit(n: Union[str, int]) -> int:
|
|
|
26
25
|
return divmod(check, 10)[1]
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
def is_valid_luhn(n:
|
|
28
|
+
def is_valid_luhn(n: str | int) -> bool:
|
|
30
29
|
"""
|
|
31
30
|
Determines if a given number, represented as a string or integer, adheres
|
|
32
31
|
to the Luhn algorithm.
|
|
@@ -78,7 +77,7 @@ def is_valid_luhn(n: Union[str, int]) -> bool:
|
|
|
78
77
|
return divmod(final, 10)[1] == 0
|
|
79
78
|
|
|
80
79
|
|
|
81
|
-
def is_valid_imei(n:
|
|
80
|
+
def is_valid_imei(n: str | int) -> bool:
|
|
82
81
|
"""
|
|
83
82
|
Determines whether the given number is a valid IMEI (International Mobile
|
|
84
83
|
Equipment Identity) number.
|
|
@@ -98,7 +97,7 @@ def is_valid_imei(n: Union[str, int]) -> bool:
|
|
|
98
97
|
return len(str(n)) == 15 and is_valid_luhn(n)
|
|
99
98
|
|
|
100
99
|
|
|
101
|
-
def normalize_imei(c:
|
|
100
|
+
def normalize_imei(c: str | int) -> str:
|
|
102
101
|
"""
|
|
103
102
|
Normalizes the given IMEI number by extracting the first 14 digits and appending
|
|
104
103
|
the calculated Luhn check digit to make it a valid IMEI.
|
|
@@ -131,7 +130,7 @@ def normalize_imei(c: Union[str, int]) -> str:
|
|
|
131
130
|
return "%s%s" % (t, check_digit)
|
|
132
131
|
|
|
133
132
|
|
|
134
|
-
def get_tac_from_imei(n:
|
|
133
|
+
def get_tac_from_imei(n: str | int) -> tuple[bool, str]:
|
|
135
134
|
"""
|
|
136
135
|
Determines the validity of an IMEI number and extracts its TAC if valid.
|
|
137
136
|
|
|
@@ -156,7 +155,7 @@ def get_tac_from_imei(n: Union[str, int]) -> tuple[bool, str]:
|
|
|
156
155
|
return True, tac
|
|
157
156
|
|
|
158
157
|
|
|
159
|
-
def decrement_imei(n:
|
|
158
|
+
def decrement_imei(n: str | int) -> tuple[bool, str]:
|
|
160
159
|
"""
|
|
161
160
|
Decrements the given IMEI number by one and normalizes it.
|
|
162
161
|
|
|
@@ -184,7 +183,7 @@ def decrement_imei(n: Union[str, int]) -> tuple[bool, str]:
|
|
|
184
183
|
return True, result
|
|
185
184
|
|
|
186
185
|
|
|
187
|
-
def increment_imei(n:
|
|
186
|
+
def increment_imei(n: str | int) -> tuple[bool, str]:
|
|
188
187
|
"""
|
|
189
188
|
Determines if a given IMEI number is valid and increments it by 1 if valid.
|
|
190
189
|
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"""Trinity Shared Python utilities.
|
|
2
|
-
|
|
3
|
-
A collection of shared utilities for Trinity projects.
|
|
4
|
-
|
|
5
|
-
Originally intended to be parsing utilities, this grew to include
|
|
6
|
-
other useful functions.
|
|
7
|
-
|
|
8
|
-
Named for its author Andries Niemandt - whose surname loosely
|
|
9
|
-
translates to "none". Combined this with our parsing intentions
|
|
10
|
-
to create a name which nods to the Black Knight in Monty Python's Holy Grail.
|
|
11
|
-
https://www.youtube.com/watch?v=zKhEw7nD9C4
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
__author__ = "Andries Niemandt, Jan Badenhorst"
|
|
15
|
-
__email__ = "andries.niemandt@trintel.co.za, jan@trintel.co.za"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|