Source code for codemelli.codemelli

"""Main functions for Codemelli."""

from json import load
from os import path
from random import randint, choice
from re import search
from typing import Union


[docs]def city_codes_data() -> dict: """Return a dict containing city codes. :return: city codes :rtype: dict """ with open(f'{path.dirname(path.realpath(__file__))}/data/code-city.json', 'r', encoding='utf-8') as json_file: return load(json_file) # get data from json file
[docs]def validator(input_code: int, strict: bool = False) -> bool: """Validate the input code by CodeMelli rules. :param int input_code: a CodeMelli number :param bool strict: Checks validation of city code :return: validation result :rtype: bool :raises ValueError: if input code is not a 10-digit number :raises ValueError: if input code does not start with a valid city code """ # integer to string type conversion, input should be iterable input_code = str(input_code) # check if the input is formatted correctly if not search(r'^\d{10}$', input_code): raise ValueError('input code should be a 10-digit number') # check if input code does not start with a valid city code if strict is True and lookup(input_code) is None: raise ValueError( f'input code started with an invalid city code: {input_code[:3]}' ) # select the last character of input code. # it will be used for validating other characters checker = int(input_code[-1]) # convert out input code (str) to a list[int] input_list_int = [int(i) for i in input_code[:-1]] # calculate the remainder of CodeMelli formula remainder = _get_remainder(input_list_int) # return True if conditions are passed. In contrast, return False return (2 > remainder == checker) or \ (remainder >= 2 and checker + remainder == 11)
[docs]def generator(city_code: str = None) -> str: """Generate a random valid CodeMelli. :param str city_code: An string of numbers (length=3) :return: A valid CodeMelli :rtype: str :raises ValueError: if city code is defined and it is not 3-digit number """ # Get a random city code from json file if it is not defined by the user if city_code is None: data = city_codes_data() city_code = choice(list(data.keys())) # Convert city code to string city_code = str(city_code) # Raise a value error if the city code does not contain 3 numbers if not search(r'^\d{3}$', city_code): raise ValueError(f'City code should be an integer of length 3.' f'"{city_code}" is not a valid value') # Convert city code to a list of integers city_code = list(map(int, city_code)) # Generate 6 random integers for rest of the codeMelli random_codemelli = city_code + [randint(0, 9) for i in range(6)] # get remainder of generate codeMelli remainder = _get_remainder(random_codemelli) # calculating the last number of the generated codeMelli last_num = remainder if (remainder < 2) else 11 - remainder # put it all together and return return "".join([str(x) for x in random_codemelli + [last_num]])
[docs]def lookup(input_code: str) -> dict or None: """Lookup state and city of the input code. :param str input_code: a CodeMelli string :return: state and city of the given CodeMelli in the following format: {state: example_state, city: example_city} :rtype: dict or None """ # force convert input_code to string input_code = str(input_code) # select first 3 characters of input code prefix = input_code[:3] data = city_codes_data() # return a dict if the first 3 characters exist in our data if prefix in data.keys(): return data[prefix] return None
def _get_remainder(code: Union[list, int]) -> int: """Calculate remainder of validation calculations. :param Union[list, int] code: input code :return: remainder of calculations :rtype: int :raises TypeError: if code is not a list or an integer """ # raise an exception if code is not a list or an integer if not isinstance(code, (list, int)): raise TypeError('code should be a list or an integer') # convert integer code to a list of integers if isinstance(code, int): code = list(map(int, str(code))) # a 10 to 2 list, it will be used for next calculation reversed_range = range(10, 1, -1) # calculate the remainder of CodeMelli formula division return sum([i * j for i, j in zip(code, reversed_range)]) % 11 if __name__ == "__main__": # Generate a random codemelli print(generator())