import logging import re import secrets from contextlib import contextmanager from constance import config from django.conf import settings from django.core.cache import cache from django.db import transaction from django.utils.crypto import get_random_string from django.utils.translation import get_language from evibes.settings import DEBUG, EXPOSABLE_KEYS, LANGUAGE_CODE logger = logging.getLogger("django") def graphene_current_lang(): """ Determines the currently active language code. This function retrieves the current language from the available language settings. If no language is set, it defaults to the application's default language code. The language code is returned in lowercase. Returns: str: The currently active language code in lowercase. """ return (get_language() or settings.LANGUAGE_CODE).lower() def graphene_abs(request, path_or_url: str) -> str: """ Builds and returns an absolute URI for a given path or URL. Summary: This function takes a relative path or URL and constructs a fully qualified absolute URI using the request object. Args: request: The request object used to build the absolute URI. path_or_url: str The relative path or URL to be converted to an absolute URI. Returns: str: The absolute URI corresponding to the provided path or URL. """ return request.build_absolute_uri(path_or_url) def get_random_code() -> str: """ Generates a random alphanumeric string of a fixed length. This function uses a utility function to generate a random string consisting of letters and digits. The length of the string is fixed to 20 characters. The generated string can be used for purposes such as unique identifiers or tokens. Returns: str: Randomly generated alphanumeric string of length 20. """ return get_random_string(20) def get_product_uuid_as_path(instance, filename: str = "") -> str: """ Generates a file path for a product using its UUID. This function constructs a standardized file path where an uploaded file is saved for a product. The path includes a `products` directory, followed by the product's UUID, and concludes with the original filename. It can be utilized in file storage applications to ensure unique and organized file storage based on the product's identity. Args: instance: The object instance that contains a reference to the product. filename: str, optional. The name of the file being uploaded. Default is an empty string. Returns: str: A string that represents the constructed file path. """ return "products" + "/" + str(instance.product.uuid) + "/" + filename def get_brand_name_as_path(instance, filename: str = "") -> str: """ Generates a file path for a brand based on its name and the provided filename. This function constructs a unique file path within the 'brands/' directory using the name of the given instance and appends the supplied filename. Parameters: instance: An object containing a 'name' attribute. filename: str, optional. The name of the file to be appended to the path. Returns: str: A string representing the constructed file path. """ return "brands/" + str(instance.name) + "/" + filename @contextmanager def atomic_if_not_debug(): """ Context manager to wrap a block of code in an atomic transaction if the DEBUG setting is not enabled. This context manager ensures that the code block executes within an atomic database transaction, preventing partial updates to the database in case of an exception. If the DEBUG setting is enabled, no transaction is enforced, allowing for easier debugging. Yields: None: This context manager does not return any values. """ if not DEBUG: with transaction.atomic(): yield else: yield def is_url_safe(url: str) -> bool: """ Determines if a given URL starts with "https://" indicating it is a secure URL. This function checks if the provided URL adheres to secure HTTPS protocol. It uses a regular expression to validate the URL prefix. Parameters: url (str): The URL string to validate. Returns: bool: True if the URL starts with "https://", False otherwise. """ return bool(re.match(r"^https://", url, re.IGNORECASE)) def format_attributes(attributes: str | None = None) -> dict: """ Parses a string of attributes into a dictionary. This function takes a string input representing attributes and their values, formatted as `key=value` pairs separated by commas, and converts it into a dictionary. It returns an empty dictionary if the input is `None` or invalid. Invalid key-value pairs within the input string are skipped. Parameters: attributes (str | None): A comma-separated string of key-value pairs in the format `key=value`, or None. Returns: dict: A dictionary where keys are the attribute names and values are their corresponding values. """ if not attributes: return {} try: attribute_pairs = attributes.split(",") except AttributeError: return {} result = {} for attr_pair in attribute_pairs: try: key, value = attr_pair.split("=") result[key] = value except ValueError: continue return result def get_project_parameters() -> dict: """ Fetches project parameters from cache or configuration. This function retrieves project parameters from a cache if available. If they are not cached, it collects the parameters from a designated configuration source, formats their keys to lowercase, and then stores them in the cache for a limited period. Returns: dict: A dictionary containing the project parameters with lowercase keys. """ parameters = cache.get("parameters", {}) if not parameters: for key in EXPOSABLE_KEYS: parameters[key.lower()] = getattr(config, key) cache.set("parameters", parameters, 60 * 60) return parameters def resolve_translations_for_elasticsearch(instance, field_name) -> None: """ Resolves translations for a given field in an Elasticsearch-compatible format. It checks if the localized version of the field contains data, and if not, sets it to the value of the default field. Parameters: instance: The object instance containing the field to resolve. field_name (str): The base name of the field for which translations are being resolved. Returns: None """ field = getattr(instance, f"{field_name}_{LANGUAGE_CODE}", "") filled_field = getattr(instance, field_name, "") if not field: setattr(instance, f"{field_name}_{LANGUAGE_CODE}", filled_field) CROCKFORD = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" def generate_human_readable_id(length: int = 6) -> str: """ Generates a human-readable identifier using Crockford's Base32 characters. This function creates a string of a specified length composed of randomly selected characters from the Crockford Base32 alphabet. A dash is inserted at a random or mid-point position in the identifier for better readability. Parameters: length (int): The length of the identifier excluding the dash. Must be greater than 0. Default is 6. Returns: str: A human-readable identifier with the specified length plus a dash. """ chars = [secrets.choice(CROCKFORD) for _ in range(length)] pos = (secrets.randbelow(length - 1) + 1) if secrets.choice([True, False]) else (length // 2) chars.insert(pos, "-") return "".join(chars) def generate_human_readable_token() -> str: """ Generates a human-readable token. This function creates a random token using characters from the CROCKFORD base32 set. The generated token is 20 characters long and is designed to be human-readable. Returns: str: A 20-character random token. """ return "".join([secrets.choice(CROCKFORD) for _ in range(20)])