Skip to content

Commit

Permalink
Nagamani: Refactoring, and add generate_nagamani19_int
Browse files Browse the repository at this point in the history
  • Loading branch information
amotl committed Sep 5, 2023
1 parent 72b947d commit 9292fd3
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 53 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion vasuki/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
40 changes: 15 additions & 25 deletions vasuki/unique/naga19.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
90 changes: 63 additions & 27 deletions vasuki/unique/nagamani.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,70 @@
# -*- coding: utf-8 -*-
# (c) 2019 Andreas Motl <[email protected]>
# 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.
Expand All @@ -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()

0 comments on commit 9292fd3

Please sign in to comment.