Add calculator categories

This commit is contained in:
Codex
2026-03-07 21:57:54 +00:00
parent f1afaa6d3a
commit 5ba1ecf459
4 changed files with 1343 additions and 1796 deletions

View File

@@ -6,59 +6,30 @@ BASE_DIR = Path(__file__).resolve().parent
CALCLIST = BASE_DIR / 'calculators_list.md'
OUTPUT_FILE = BASE_DIR / 'hdyc-svelte/src/lib/data/calculators.ts'
FLUID_KEYWORDS = [
'flow',
'mass flux',
'volumetric',
'permeability',
'viscosity',
'kinematic',
'surface tension',
'molar',
'concentration',
'flux density',
'flow rate',
'gallon per',
'gallons per',
'liter per',
'liters per',
'cubic per',
'cubic meter per',
'cubic meters per',
'cubic foot per',
'cubic feet per',
'cubic inch per',
'cubic inches per',
'kg per',
'kilogram per',
'kilograms per',
'gram per',
'grams per',
'g per',
'lb per',
'lbs per',
'pound per',
'pounds per',
'mole per',
'moles per',
'mol per',
'mmol per',
'percent by mass',
'ppm',
'heat transfer coefficient',
'per square meter',
'per square metre',
'per square foot',
'per square inch',
'per square centimeter',
'per square centimetre',
'per cubic meter',
'per cubic metre',
'per cubic foot',
'per cubic inch'
CATEGORY_KEYS = [
'length',
'weight',
'temperature',
'volume',
'fluids',
'area',
'speed',
'pressure',
'energy',
'magnetism',
'power',
'data',
'time',
'angle',
'number-systems',
'radiation',
'electrical',
'force',
'light',
'other',
]
CURRENCY_KEYWORDS = ['currency', 'exchange rate', 'forex']
CATEGORY_SET = set(CATEGORY_KEYS)
def load_external_descriptions():
# Placeholder for future enrichment sources.
@@ -85,12 +56,14 @@ def parse_calculators_list():
parts = [p.strip() for p in line.strip().strip('|').split('|')]
name_idx = header_map.get('Calculator Name')
slug_idx = header_map.get('Slug')
category_idx = header_map.get('Category')
factor_idx = header_map.get('Conversion Factor')
if None not in (name_idx, slug_idx, factor_idx) and len(parts) > max(name_idx, slug_idx, factor_idx):
if None not in (name_idx, slug_idx, category_idx, factor_idx) and len(parts) > max(name_idx, slug_idx, category_idx, factor_idx):
name = parts[name_idx]
slug = parts[slug_idx]
category = parts[category_idx]
factor_raw = parts[factor_idx]
active_calcs.append((name, slug, factor_raw))
active_calcs.append((name, slug, category, factor_raw))
return active_calcs
@@ -107,38 +80,10 @@ def split_conversion_name(name):
return parts[0].strip(), parts[1].strip()
return None
def guess_category(name):
name_l = name.lower()
if any(keyword in name_l for keyword in CURRENCY_KEYWORDS):
return 'currency'
if any(keyword in name_l for keyword in FLUID_KEYWORDS):
return 'fluids'
if any(x in name_l for x in ['acre-foot', 'acre-feet', 'acrefoot', 'acre feet']):
return 'volume'
if 'temp scale' in name_l or 'newton (temp' in name_l:
return 'temperature'
if any(x in name_l for x in ['force', 'torque', 'newton', 'dyne', 'foot-pound']): return 'force'
if any(x in name_l for x in ['acre', 'hectare', 'square']): return 'area'
if any(x in name_l for x in ['meter', 'inch', 'feet', 'yard', 'mile', 'cable', 'fathom', 'rod', 'chain', 'nautical', 'league']): return 'length'
if any(x in name_l for x in ['gram', 'pound', 'ounce', 'carat', 'stone', 'slug', 'ton', 'pennyweight', 'grain', 'momme']): return 'weight'
if any(x in name_l for x in ['celsius', 'fahrenheit', 'kelvin', 'rankine', 'delisle', 'reaumur', 'réaumur', 'romer', 'rømer']): return 'temperature'
if any(x in name_l for x in ['liter', 'gallon', 'cup', 'pint', 'quart', 'fluid', 'milliliter', 'spoon', 'drop']): return 'volume'
if any(x in name_l for x in ['pascal', 'bar', 'psi', 'atmosphere', 'mmhg', 'torr', 'water', 'mercury']): return 'pressure'
if any(x in name_l for x in ['magnet', 'magnetic', 'tesla', 'gauss', 'oersted', 'weber', 'maxwell', 'gilbert', 'ampere-turn', 'ampere turns', 'ampere per meter', 'magnetomotive']): return 'magnetism'
if any(x in name_l for x in ['joule', 'calorie', 'btu', 'erg', 'therm', 'electron-volt']): return 'energy'
if any(x in name_l for x in ['thermal', 'heat', 'conductivity', 'resistance', 'capacity', 'expansion', 'transfer']): return 'temperature'
if any(x in name_l for x in ['watt', 'horsepower']): return 'power'
if any(x in name_l for x in ['byte', 'bit', 'nibble', 'baud']): return 'data'
if 'light' in name_l or any(x in name_l for x in ['lumen', 'lux', 'candela']): return 'light'
if any(x in name_l for x in ['degree', 'degrees', 'radian', 'radians', 'arcminute', 'arcminutes', 'arcsecond', 'arcseconds', 'gradian', 'gradians', 'mil', 'mils', 'quadrant', 'quadrants', 'sextant', 'sextants', 'turn', 'turns', 'points (compass', 'points-compass']): return 'angle'
if any(x in name_l for x in ['second', 'minute', 'hour', 'day', 'week', 'month', 'year']): return 'time'
if any(x in name_l for x in ['binary', 'hex', 'octal', 'decimal', 'ascii', 'fraction']): return 'number-systems'
if any(x in name_l for x in ['becquerel', 'curie', 'gray', 'rad', 'sievert', 'rem', 'roentgen', 'rutherford']): return 'radiation'
if any(x in name_l for x in ['volt', 'amp', 'ohm', 'siemens', 'farad', 'henry', 'coulomb']): return 'electrical'
if ' per ' in name_l or 'knot' in name_l or 'mach' in name_l or 'rpm' in name_l: return 'speed' # RPM might be frequency, close enough
if any(x in name_l for x in ['binary', 'hex', 'octal', 'decimal', 'base']):
return 'number-systems'
return 'other'
def normalize_category(raw: str) -> str:
normalized = raw.strip().lower().replace(' ', '-')
normalized = re.sub(r'[^a-z0-9-]', '', normalized)
return normalized
def process():
external_descriptions = load_external_descriptions()
@@ -146,7 +91,8 @@ def process():
calculators_ts_entries = []
for raw_name, slug, factor_raw in active_rows:
seen_slugs = set()
for raw_name, slug, category_raw, factor_raw in active_rows:
if raw_name == 'Calculator Name' or not slug:
continue
@@ -159,7 +105,14 @@ def process():
else:
in1, in2 = "From", "To"
category = guess_category(display_name)
category = normalize_category(category_raw)
if not category:
raise ValueError(f'Category required for {display_name}')
if category not in CATEGORY_SET:
raise ValueError(f'Unknown category \"{category_raw}\" resolved to \"{category}\" for {slug}')
if slug in seen_slugs:
continue
seen_slugs.add(slug)
desc_html = external_descriptions.get(slug, "")
c_type = 'standard'
@@ -368,7 +321,6 @@ export const categories: Record<string, { label: string; icon: string }> = {
speed: { label: 'Speed / Velocity', icon: '💨' },
pressure: { label: 'Pressure', icon: '🔽' },
energy: { label: 'Energy', icon: '' },
currency: { label: 'Currency', icon: '💱' },
magnetism: { label: 'Magnetism', icon: '🧲' },
power: { label: 'Power', icon: '🔌' },
data: { label: 'Data Storage', icon: '💾' },