import re from typing import Dict, Generator, Tuple, Type, Union import roman from django.apps import apps from django.db.models import Model CAMEL_CASE_TO_PSEUDO_SNAKE_CASE_CONVERSION_REGEX_1 = re.compile("(.)([A-Z][a-z]+)") CAMEL_CASE_TO_PSEUDO_SNAKE_CASE_CONVERSION_REGEX_2 = re.compile("([a-z0-9])([A-Z])") def convert_camel_case_to_pseudo_snake_case(text: str) -> str: """ Converts camel case to pseudo snake case using hyphen instead of underscore. E. g. ThisText is converted to this-text. Credits: epost (http://stackoverflow.com/a/1176023) """ s1 = CAMEL_CASE_TO_PSEUDO_SNAKE_CASE_CONVERSION_REGEX_1.sub(r"\1-\2", text) return CAMEL_CASE_TO_PSEUDO_SNAKE_CASE_CONVERSION_REGEX_2.sub(r"\1-\2", s1).lower() def to_roman(number: int) -> str: """ Converts an arabic number within range from 1 to 4999 to the corresponding roman number. Returns the input converted as string on error conditions or higher numbers. """ try: return roman.toRoman(number) except (roman.NotIntegerError, roman.OutOfRangeError): return str(number) def get_element_id(collection_string: str, id: int) -> str: """ Returns a combined string from the collection_string and an id. """ return f"{collection_string}:{id}" def split_element_id(element_id: Union[str, bytes]) -> Tuple[str, int]: """ Splits a combined element_id into the collection_string and the id. """ if isinstance(element_id, bytes): element_id = element_id.decode() collection_str, id = element_id.rsplit(":", 1) return (collection_str, int(id)) def str_dict_to_bytes(str_dict: Dict[str, str]) -> Dict[bytes, bytes]: """ Converts the key and the value of a dict from str to bytes. """ out = {} for key, value in str_dict.items(): out[key.encode()] = value.encode() return out _models_to_collection_string: Dict[str, Type[Model]] = {} def get_model_from_collection_string(collection_string: str) -> Type[Model]: """ Returns a model class which belongs to the argument collection_string. """ def model_generator() -> Generator[Type[Model], None, None]: """ Yields all models of all apps. """ for app_config in apps.get_app_configs(): for model in app_config.get_models(): yield model # On the first run, generate the dict. It can not change at runtime. if not _models_to_collection_string: for model in model_generator(): try: get_collection_string = model.get_collection_string except AttributeError: # Skip models which do not have the method get_collection_string. pass else: _models_to_collection_string[get_collection_string()] = model try: model = _models_to_collection_string[collection_string] except KeyError: raise ValueError( f"Invalid message. A valid collection_string is missing. Got {collection_string}" ) return model