import re from typing import Tuple def parse_quantity(text: str) -> Tuple[float, str]: # supports "2,5 т" and "2.5 t" m = re.search(r"([0-9]+(?:[\.,][0-9]+)?)\s*([a-zA-Zа-яА-Я/]+)", text) if not m: raise ValueError("quantity_not_found") value = float(m.group(1).replace(',', '.')) unit = m.group(2).lower() return value, unit def parse_rate(text: str) -> Tuple[float, str]: # "150 кг/га" m = re.search(r"([0-9]+(?:[\.,][0-9]+)?)\s*([a-zA-Zа-яА-Я/]+)", text) if not m: raise ValueError("rate_not_found") value = float(m.group(1).replace(',', '.')) unit = m.group(2).lower() return value, unit def normalize_unit(unit: str, unit_map: dict): unit_cf = unit.casefold().strip() for u in unit_map: if u['id'] == unit_cf: return u['id'] if unit_cf == u.get('name','').casefold(): return u['id'] for s in u.get('synonyms', []): if unit_cf == s.casefold(): return u['id'] return None def convert(value: float, from_unit: str, to_unit: str, unit_map: dict) -> float: if from_unit == to_unit: return value units = {u['id']: u for u in unit_map} f = units.get(from_unit) t = units.get(to_unit) if not f or not t: raise ValueError("unit_not_found") # only support to_base conversion for now if f.get('to_base') and f['to_base']['base'] == to_unit: return value * float(f['to_base']['factor']) if t.get('to_base') and t['to_base']['base'] == from_unit: return value / float(t['to_base']['factor']) raise ValueError("conversion_not_supported")