Add calculator categories
This commit is contained in:
128
migrate.py
128
migrate.py
@@ -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: '💾' },
|
||||
|
||||
Reference in New Issue
Block a user