diff --git a/CHANGES.rst b/CHANGES.rst index 2b0834c..7b3c803 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,11 @@ Changelog in progress =========== +2023-09-06 0.4.0 +================ +- Nagamani: Refactoring +- Nagamani: Add ``generate_nagamani19_int`` + 2023-09-06 0.3.4 ================ - Update dependencies: Munch diff --git a/vasuki/__init__.py b/vasuki/__init__.py index f0bd10e..e54c030 100644 --- a/vasuki/__init__.py +++ b/vasuki/__init__.py @@ -4,7 +4,7 @@ from vasuki.unique.uuid import generate_uuid4 from vasuki.unique.ulid import generate_ulid -from vasuki.unique.naga19 import generate_nagamani19 +from vasuki.unique.naga19 import generate_nagamani19, generate_nagamani19_int from vasuki.unique.gibberish import generate_gibberish from vasuki.unique.moment import generate_momentname from vasuki.format.sixnibblename.sixnibblename import integer_slug diff --git a/vasuki/unique/naga19.py b/vasuki/unique/naga19.py index 56d9739..0edcbce 100644 --- a/vasuki/unique/naga19.py +++ b/vasuki/unique/naga19.py @@ -4,42 +4,32 @@ """ Nagamani19, a short, unique, non-sequential identifier based on Hashids. """ -import os -from binascii import hexlify -from vasuki.unique.nagamani import nagamani_id +import warnings +from vasuki.unique.nagamani import Nagamani size_map = {'small': 's', 'medium': 'ms', 'large': 'ns'} salt = None -def generate_nagamani19(size=None): - # https://community.hiveeyes.org/t/gestaltung-der-anonymen-identifizierer-ids-fur-die-adressierung-von-imkern-messknoten-und-anderen-entitaten/1079/8 - global salt - if salt is None: - salt = gensalt() - precision = get_precision(size) - return nagamani_id(2019, salt, precision) +def generate_nagamani19_obj(size=None) -> Nagamani: + return Nagamani(year=2019, precision=get_precision(size)) -def get_precision(selector): - selector = selector or 'large' - return size_map[selector] +def generate_nagamani19(size=None) -> str: + warnings.warn(DeprecationWarning("The function `generate_nagamani19` is deprecated. Please use `generate_nagamani19_hash` instead.")) + return generate_nagamani19_obj().hash() + +def generate_nagamani19_hash(size=None) -> str: + return generate_nagamani19_obj().hash() -def gensalt(): - """ - This generates a salt from 24 random bytes from an OS-specific randomness source. - The returned data should be unpredictable enough for cryptographic applications, - though its exact quality depends on the OS implementation. - https://docs.python.org/3/library/os.html#os.urandom +def generate_nagamani19_int(size=None) -> int: + return generate_nagamani19_obj().duration() - Examples:: - b5f95cead701f2488d5668decb0d63a30e7ddb4c21f26574 - b4157e5459c88a6c454186492ee629ca097f8a60cbfb1a36 - de1ba437524e540e3b0d55617afcad5677b982d9e1f45f9d - """ - return hexlify(os.urandom(24)).decode() +def get_precision(selector): + selector = selector or 'large' + return size_map[selector] diff --git a/vasuki/unique/nagamani.py b/vasuki/unique/nagamani.py index b7f6d7e..e238933 100644 --- a/vasuki/unique/nagamani.py +++ b/vasuki/unique/nagamani.py @@ -1,51 +1,70 @@ # -*- coding: utf-8 -*- # (c) 2019 Andreas Motl # License: GNU Affero General Public License, Version 3 +import os +from binascii import hexlify from datetime import datetime from hashids import Hashids """ Nagamani - short, unique, non-sequential identifiers based on Hashids. """ -precisions = { - 's': 1, - 'ms': 1000, - 'ns': 1000000, -} - -def nagamani_id(year, salt, precision): +class Nagamani: + """ + https://community.hiveeyes.org/t/gestaltung-der-anonymen-identifizierer-ids-fur-die-adressierung-von-imkern-messknoten-und-anderen-entitaten/1079/8 """ - Generate unique ids based on Hashids. - Hashids are short, unique, non-sequential ids generated from numbers - and suitable to be used as unguessable and unpredictable short UIDs. + precisions = { + 's': 1, + 'ms': 1000, + 'ns': 1000000, + } - Here, we are generating Hashids of the current timestamp in milliseconds. - To keep the footprint low, a custom epoch is used which starts on Jan 1, 2019. + def __init__(self, year: int, salt=None, precision: str = "ns"): + self.year: str = year + self.salt: str = salt or gensalt() + self.precision: str = precision - Examples:: + def hash(self) -> str: + """ + Generate unique ids based on Hashids. - 1Zk5zBoQ - Y4Mvj5Zx - 2b4NBvYe - XaMvl962 - yzgOlvap + Hashids are short, unique, non-sequential ids generated from numbers + and suitable to be used as unguessable and unpredictable short UIDs. - """ + Here, we are generating Hashids of the current timestamp in milliseconds. + To keep the footprint low, a custom epoch is used which starts on Jan 1, 2019. + + Examples:: + + 1Zk5zBoQ + Y4Mvj5Zx + 2b4NBvYe + XaMvl962 + yzgOlvap + + """ + return hashify(self.salt, self.duration()) + + def duration(self) -> int: + """ + Return current timestamp in milliseconds with a + custom epoch which starts on Jan 1, 2019. + """ - assert type(year) is int, 'Year must be integer' - assert salt is not None, 'Salt must be given' - assert precision is not None, 'Precision must be one of s, ms, ns' + assert type(self.year) is int, 'Year must be integer' + assert self.salt is not None, 'Salt must be given' + assert self.precision is not None, 'Precision must be one of s, ms, ns' - scaling = precisions.get(precision) + scaling = self.precisions.get(self.precision) - duration = datetime.utcnow() - datetime(year, 1, 1) - duration_scaled = int(duration.total_seconds() * scaling) - return hashify(salt, duration_scaled) + duration = datetime.utcnow() - datetime(self.year, 1, 1) + duration_scaled = int(duration.total_seconds() * scaling) + return duration_scaled -def hashify(salt, *data): +def hashify(salt, *data) -> str: """ Hashids are short, unique, non-sequential ids generated from numbers and suitable to be used as short UIDs. @@ -61,3 +80,20 @@ def hashify(salt, *data): """ hashids = Hashids(salt=salt) return hashids.encode(*data) + + +def gensalt(): + """ + This generates a salt from 24 random bytes from an OS-specific randomness source. + The returned data should be unpredictable enough for cryptographic applications, + though its exact quality depends on the OS implementation. + + https://docs.python.org/3/library/os.html#os.urandom + + Examples:: + + b5f95cead701f2488d5668decb0d63a30e7ddb4c21f26574 + b4157e5459c88a6c454186492ee629ca097f8a60cbfb1a36 + de1ba437524e540e3b0d55617afcad5677b982d9e1f45f9d + """ + return hexlify(os.urandom(24)).decode()