160 lines
7.2 KiB
Python
160 lines
7.2 KiB
Python
import random
|
|
import decimal
|
|
from src.data_loader import get_value_ranges
|
|
|
|
# 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"
|
|
"""
|
|
if not isinstance(value_data, dict) or 'value' not in value_data:
|
|
return str(value_data) # Fallback
|
|
|
|
val = value_data['value']
|
|
display_precision = value_data.get('display_precision')
|
|
|
|
# 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 "unit_display" in value_data and "%" in value_data["unit_display"]:
|
|
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:
|
|
return f"{value_data['currency']} {formatted_num}"
|
|
elif "unit_display" in value_data: # e.g., "% per annum"
|
|
return f"{formatted_num}{value_data['unit_display']}" # Assumes % is part of unit_display or handled by *100
|
|
elif "unit" in value_data: # e.g., "years"
|
|
return f"{formatted_num} {value_data['unit']}"
|
|
else:
|
|
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)}")
|