import random import decimal import datetime # Added for type checking from src.data_loader import get_value_ranges from src import date_utils # Added for date formatting # Cache loaded data to avoid repeated file I/O VALUE_RANGES = None def _get_value_ranges_cached(): """Returns cached value_ranges data, loading if not already cached.""" global VALUE_RANGES if VALUE_RANGES is None: VALUE_RANGES = get_value_ranges() return VALUE_RANGES def get_random_float(min_val, max_val, precision=None): """Generates a random float between min_val and max_val with specified precision.""" val = random.uniform(min_val, max_val) if precision is not None: # Using Decimal for precise rounding return float(decimal.Decimal(str(val)).quantize(decimal.Decimal('1e-' + str(precision)), rounding=decimal.ROUND_HALF_UP)) return val def get_random_int(min_val, max_val): """Generates a random integer between min_val and max_val (inclusive).""" return random.randint(min_val, max_val) def get_value_for_variable(variable_key): """ Generates a random value for a given variable key based on value_ranges.json. Returns a dictionary containing the value and its unit/currency if applicable. Example: {'value': 15000.50, 'currency': 'Php', 'display_str': 'Php 15,000.50'} {'value': 0.12, 'unit_display': '% per annum', 'display_str': '12.00% per annum'} {'value': 5, 'unit': 'years', 'display_str': '5 years'} """ ranges = _get_value_ranges_cached() if not ranges or variable_key not in ranges: print(f"Error: No range definition found for variable key '{variable_key}' in value_ranges.json") return None config = ranges[variable_key] val = None result = {'key': variable_key} if config.get("integer", False): val = get_random_int(config["min"], config["max"]) result['value'] = val else: # For floats, use internal_precision for generation if available, otherwise default to a reasonable precision # Display precision will be handled separately or by a formatting function internal_precision = config.get("internal_precision", config.get("decimals", 4)) # Default to 4 if no precision specified val = get_random_float(config["min"], config["max"], internal_precision) result['value'] = val if "currency" in config: result["currency"] = config["currency"] if "unit" in config: result["unit"] = config["unit"] if "unit_display" in config: # For rates like "% per annum" result["unit_display"] = config["unit_display"] # Store precision details for later formatting result["display_precision"] = config.get("display_precision", config.get("decimals")) # Decimals is often used for currency return result def get_random_compounding_frequency(): """Selects a random compounding frequency and its 'm' value.""" ranges = _get_value_ranges_cached() if not ranges or "compounding_frequency_options" not in ranges: print("Error: 'compounding_frequency_options' not found in value_ranges.json") return None, None options = ranges["compounding_frequency_options"] frequency_name = random.choice(list(options.keys())) m_value = options[frequency_name] return {"name": frequency_name, "m_value": m_value, "key": "m_compounding_periods_per_year", "value": m_value} def format_value_for_display(value_data): """ Formats a value generated by get_value_for_variable for display. Example input: {'key': 'principal', 'value': 15000.5, 'currency': 'Php', 'display_precision': 2} Example output: "Php 15,000.50" Example input: {'key': 'simple_interest_rate_annual', 'value': 0.12, 'unit_display': '% per annum', 'display_precision': 2} Example output: "12.00% per annum" """ # --- BEGIN DEBUG PRINT --- # print(f"DEBUG: format_value_for_display received value_data: {value_data}") # --- END DEBUG PRINT --- if not isinstance(value_data, dict) or 'value' not in value_data: # print(f"DEBUG: Fallback str(value_data): {str(value_data)}") return str(value_data) # Fallback val = value_data['value'] display_precision = value_data.get('display_precision') # --- BEGIN DEBUG PRINT --- # print(f"DEBUG: val: {val}, display_precision: {display_precision}, type(val): {type(val)}") # --- END DEBUG PRINT --- # Handle date objects first if isinstance(val, datetime.date): # print(f"DEBUG: Path: Date. Result: {date_utils.format_date_for_display(val)}") return date_utils.format_date_for_display(val) # Format number with precision if display_precision is not None and isinstance(val, (float, decimal.Decimal)): # Check if it's a rate to be displayed as percentage if value_data.get("unit_display") and "%" in value_data["unit_display"]: # Check if unit_display exists and is not None formatted_num = f"{val * 100:.{display_precision}f}" else: formatted_num = f"{val:.{display_precision}f}" # Add thousands separator for currency-like values if "currency" in value_data and val >= 1000: parts = formatted_num.split('.') parts[0] = "{:,}".format(int(parts[0].replace(',', ''))) # Apply to integer part formatted_num = ".".join(parts) elif isinstance(val, int): formatted_num = str(val) if "currency" in value_data and val >= 1000: formatted_num = "{:,}".format(val) else: formatted_num = str(val) if "currency" in value_data and value_data["currency"] is not None: # Ensure currency is not None # print(f"DEBUG: Path: Currency. Result: {value_data['currency']} {formatted_num}") return f"{value_data['currency']} {formatted_num}" elif "unit_display" in value_data and value_data["unit_display"] is not None: # Ensure unit_display is not None # print(f"DEBUG: Path: Unit Display. Result: {formatted_num}{value_data['unit_display']}") return f"{formatted_num}{value_data['unit_display']}" elif "unit" in value_data and value_data["unit"] is not None: # Ensure unit is not None # print(f"DEBUG: Path: Unit. Result: {formatted_num} {value_data['unit']}") return f"{formatted_num} {value_data['unit']}" else: # print(f"DEBUG: Path: Formatted Num Only. Result: {formatted_num}") return formatted_num if __name__ == '__main__': print("Testing Value Sampler:") # Test get_value_for_variable print("\n--- Testing get_value_for_variable ---") principal_data = get_value_for_variable("principal") if principal_data: print(f"Generated Principal Data: {principal_data}") print(f"Formatted Principal: {format_value_for_display(principal_data)}") rate_data = get_value_for_variable("simple_interest_rate_annual") if rate_data: print(f"Generated Rate Data: {rate_data}") print(f"Formatted Rate: {format_value_for_display(rate_data)}") time_data = get_value_for_variable("time_years") if time_data: print(f"Generated Time Data: {time_data}") print(f"Formatted Time: {format_value_for_display(time_data)}") # Test get_random_compounding_frequency print("\n--- Testing get_random_compounding_frequency ---") comp_freq = get_random_compounding_frequency() if comp_freq: print(f"Generated Compounding Frequency: {comp_freq}") # Test edge cases or specific formatting print("\n--- Testing Specific Formatting ---") test_currency_large = {'key': 'principal', 'value': 1234567.89, 'currency': 'Php', 'display_precision': 2} print(f"Large Currency: {format_value_for_display(test_currency_large)}") test_currency_small = {'key': 'principal', 'value': 123.45, 'currency': 'Php', 'display_precision': 2} print(f"Small Currency: {format_value_for_display(test_currency_small)}") test_rate_percent = {'key': 'simple_interest_rate_annual', 'value': 0.085, 'unit_display': '% per annum', 'display_precision': 2} print(f"Rate as Percent: {format_value_for_display(test_rate_percent)}") test_integer_unit = {'key': 'time_years', 'value': 5, 'unit': 'years'} print(f"Integer with Unit: {format_value_for_display(test_integer_unit)}")