commodutil 4.2.0__tar.gz → 4.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {commodutil-4.2.0 → commodutil-4.3.2}/PKG-INFO +1 -1
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/__init__.py +4 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/convfactors.py +1 -2
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/standards/__init__.py +6 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/standards/currency.py +45 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/standards/units.py +54 -1
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil.egg-info/PKG-INFO +1 -1
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_price_conv.py +5 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_standards_currency.py +17 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_standards_units.py +43 -4
- {commodutil-4.2.0 → commodutil-4.3.2}/.coveragerc +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/.github/workflows/1_tests.yml +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/.github/workflows/2_coverage.yml +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/.github/workflows/3_linting.yml +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/.github/workflows/4_release.yml +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/.gitignore +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/.pypirc +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/azure-build-pipelines.yml +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/arb.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/dates.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forward/__init__.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forward/calendar.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forward/continuous.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forward/fly.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forward/quarterly.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forward/spreads.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forward/structure.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forward/util.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/forwards.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/pandasutil.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/standards/analysis_types.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/standards/commodities.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/standards/commodity_groups.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/standards/regions.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/stats.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil/transforms.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil.egg-info/SOURCES.txt +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil.egg-info/dependency_links.txt +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil.egg-info/requires.txt +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/commodutil.egg-info/top_level.txt +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/pyproject.toml +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/requirements-test.txt +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/requirements.txt +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/requirements_dev.txt +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/setup.cfg +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/__init__.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/conftest.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/__init__.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/conftest.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/test_calendar.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/test_continuous.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/test_fly.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/test_quarterly.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/test_spreads.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/test_structure.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/forward/test_util.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_arb.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_cl.csv +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_conv.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_dates.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_forwards.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_pandasutils.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_standards_analysis_types.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_standards_commodities.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_standards_commodity_groups.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_standards_regions.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_stats.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_transforms.py +0 -0
- {commodutil-4.2.0 → commodutil-4.3.2}/tests/test_weekly.csv +0 -0
|
@@ -35,7 +35,11 @@ _LAZY_EXPORTS = {
|
|
|
35
35
|
"VALID_CURRENCY_TOKENS": "commodutil.standards.currency",
|
|
36
36
|
"fractional_to_major": "commodutil.standards.currency",
|
|
37
37
|
"is_fractional_currency": "commodutil.standards.currency",
|
|
38
|
+
"normalize_currency_token": "commodutil.standards.currency",
|
|
38
39
|
"split_currency_unit": "commodutil.standards.currency",
|
|
40
|
+
# standards.units (stdlib-only -- cheap, no pint)
|
|
41
|
+
"canonical_quantity_unit": "commodutil.standards.units",
|
|
42
|
+
"quantity_unit_from_price_unit": "commodutil.standards.units",
|
|
39
43
|
# dates
|
|
40
44
|
"curmon": "commodutil.dates",
|
|
41
45
|
"curmonyear": "commodutil.dates",
|
|
@@ -11,6 +11,7 @@ from dataclasses import dataclass
|
|
|
11
11
|
import pandas as pd
|
|
12
12
|
from functools import lru_cache
|
|
13
13
|
|
|
14
|
+
from commodutil.standards import currency as _currency
|
|
14
15
|
from commodutil.standards.units import to_pint_token as _to_pint_token
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
@@ -542,8 +543,6 @@ def convfactor(from_unit: str, to_unit: str, commodity: Optional[str] = None) ->
|
|
|
542
543
|
# above) and reads currency vocabulary directly from _currency. Callers
|
|
543
544
|
# wanting currency vocabulary should import from commodutil.standards.currency.
|
|
544
545
|
|
|
545
|
-
from commodutil.standards import currency as _currency
|
|
546
|
-
|
|
547
546
|
|
|
548
547
|
def convert_price(
|
|
549
548
|
value: Union[float, pd.Series],
|
|
@@ -28,6 +28,7 @@ from commodutil.standards.currency import (
|
|
|
28
28
|
VALID_CURRENCY_TOKENS,
|
|
29
29
|
fractional_to_major,
|
|
30
30
|
is_fractional_currency,
|
|
31
|
+
normalize_currency_token,
|
|
31
32
|
required_fx_pair,
|
|
32
33
|
split_currency_unit,
|
|
33
34
|
to_symbol,
|
|
@@ -43,7 +44,9 @@ from commodutil.standards.regions import (
|
|
|
43
44
|
)
|
|
44
45
|
from commodutil.standards.units import (
|
|
45
46
|
UNIT_MAP,
|
|
47
|
+
canonical_quantity_unit,
|
|
46
48
|
default_unit_for_commodity,
|
|
49
|
+
quantity_unit_from_price_unit,
|
|
47
50
|
to_pint_token,
|
|
48
51
|
)
|
|
49
52
|
|
|
@@ -68,6 +71,7 @@ __all__ = [
|
|
|
68
71
|
"VALID_CURRENCY_TOKENS",
|
|
69
72
|
"fractional_to_major",
|
|
70
73
|
"is_fractional_currency",
|
|
74
|
+
"normalize_currency_token",
|
|
71
75
|
"required_fx_pair",
|
|
72
76
|
"split_currency_unit",
|
|
73
77
|
"to_symbol",
|
|
@@ -81,6 +85,8 @@ __all__ = [
|
|
|
81
85
|
"normalize_region",
|
|
82
86
|
# units
|
|
83
87
|
"UNIT_MAP",
|
|
88
|
+
"canonical_quantity_unit",
|
|
84
89
|
"default_unit_for_commodity",
|
|
90
|
+
"quantity_unit_from_price_unit",
|
|
85
91
|
"to_pint_token",
|
|
86
92
|
]
|
|
@@ -80,12 +80,56 @@ _VALID_CURRENCY_TOKENS = {
|
|
|
80
80
|
"$",
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
_CURRENCY_TOKEN_LOOKUP = {token.upper(): token for token in _VALID_CURRENCY_TOKENS}
|
|
84
|
+
_CURRENCY_TOKEN_ALIASES = {
|
|
85
|
+
"USC": "USc",
|
|
86
|
+
"US CENTS": "USc",
|
|
87
|
+
"U S CENTS": "USc",
|
|
88
|
+
"GBX": "GBp",
|
|
89
|
+
"GBPENCE": "GBp",
|
|
90
|
+
"GB PENCE": "GBp",
|
|
91
|
+
"PENCE": "GBp",
|
|
92
|
+
"RMB": "CNY",
|
|
93
|
+
}
|
|
94
|
+
|
|
83
95
|
# Public aliases — these are the import names downstream code should use.
|
|
84
96
|
VALID_CURRENCY_TOKENS = _VALID_CURRENCY_TOKENS
|
|
85
97
|
FRACTIONAL_TO_MAJOR = _FRACTIONAL_TO_MAJOR
|
|
86
98
|
FRACTIONAL_CURRENCY_DIVISORS = _FRACTIONAL_CURRENCY_DIVISORS
|
|
87
99
|
|
|
88
100
|
|
|
101
|
+
def _normalized_currency_lookup_key(token: str) -> str:
|
|
102
|
+
return " ".join(str(token).replace(".", " ").strip().upper().split())
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def normalize_currency_token(token: Optional[str]) -> Optional[str]:
|
|
106
|
+
"""Return the canonical commodutil currency token for common aliases.
|
|
107
|
+
|
|
108
|
+
The canonical tokens remain ``VALID_CURRENCY_TOKENS``. This helper accepts
|
|
109
|
+
mixed case and legacy/fractional aliases used in vendor specs, such as
|
|
110
|
+
``USC``/``US cents`` -> ``USc`` and ``GBX``/``pence`` -> ``GBp``. Unknown
|
|
111
|
+
values return ``None`` so callers can distinguish currency-like tokens
|
|
112
|
+
from physical units.
|
|
113
|
+
"""
|
|
114
|
+
if token is None:
|
|
115
|
+
return None
|
|
116
|
+
cleaned = str(token).strip()
|
|
117
|
+
if not cleaned:
|
|
118
|
+
return None
|
|
119
|
+
if cleaned in _VALID_CURRENCY_TOKENS:
|
|
120
|
+
return cleaned
|
|
121
|
+
|
|
122
|
+
lower = cleaned.lower()
|
|
123
|
+
mapped = CURRENCY_MAP.get(lower)
|
|
124
|
+
if mapped:
|
|
125
|
+
return mapped
|
|
126
|
+
|
|
127
|
+
lookup_key = _normalized_currency_lookup_key(cleaned)
|
|
128
|
+
return _CURRENCY_TOKEN_LOOKUP.get(lookup_key) or _CURRENCY_TOKEN_ALIASES.get(
|
|
129
|
+
lookup_key
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
89
133
|
def is_fractional_currency(token: str) -> bool:
|
|
90
134
|
"""Return True if `token` is a recognised fractional currency
|
|
91
135
|
(e.g. 'GBp', 'USc'). Useful for detecting pure-scale conversions that
|
|
@@ -240,6 +284,7 @@ __all__ = [
|
|
|
240
284
|
"FRACTIONAL_TO_MAJOR",
|
|
241
285
|
"FRACTIONAL_CURRENCY_DIVISORS",
|
|
242
286
|
"CURRENCY_MAP",
|
|
287
|
+
"normalize_currency_token",
|
|
243
288
|
"is_fractional_currency",
|
|
244
289
|
"fractional_to_major",
|
|
245
290
|
"split_currency_unit",
|
|
@@ -26,6 +26,8 @@ from __future__ import annotations
|
|
|
26
26
|
|
|
27
27
|
from typing import Optional
|
|
28
28
|
|
|
29
|
+
from commodutil.standards.currency import split_currency_unit
|
|
30
|
+
|
|
29
31
|
|
|
30
32
|
# ---- Alias -> canonical normalisation ----
|
|
31
33
|
|
|
@@ -45,8 +47,13 @@ UNIT_MAP = {
|
|
|
45
47
|
"metric tons": "mt",
|
|
46
48
|
"metric tonne": "mt",
|
|
47
49
|
"metric tonnes": "mt",
|
|
50
|
+
"mt": "mt",
|
|
48
51
|
"tonne": "mt",
|
|
49
52
|
"tonnes": "mt",
|
|
53
|
+
"pound": "lb",
|
|
54
|
+
"pounds": "lb",
|
|
55
|
+
"lb": "lb",
|
|
56
|
+
"lbs": "lb",
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
|
|
@@ -72,6 +79,43 @@ def default_unit_for_commodity(commodity: Optional[str]) -> str:
|
|
|
72
79
|
return _DEFAULT_UNIT.get(str(commodity).lower(), "bbl")
|
|
73
80
|
|
|
74
81
|
|
|
82
|
+
def canonical_quantity_unit(unit: object) -> str | None:
|
|
83
|
+
"""Return the canonical quantity unit token for free-form unit text.
|
|
84
|
+
|
|
85
|
+
This intentionally covers quoted physical units only: ``bbl``, ``gal``,
|
|
86
|
+
and ``mt``. Rate periods and currencies are outside this vocabulary.
|
|
87
|
+
"""
|
|
88
|
+
if unit is None:
|
|
89
|
+
return None
|
|
90
|
+
text = str(unit).strip().lower()
|
|
91
|
+
if not text:
|
|
92
|
+
return None
|
|
93
|
+
return UNIT_MAP.get(text)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def quantity_unit_from_price_unit(price_unit: object) -> str | None:
|
|
97
|
+
"""Return the denominator quantity unit from a price-unit string.
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
``USD/MT`` -> ``mt``
|
|
101
|
+
``USc/GAL`` -> ``gal``
|
|
102
|
+
``$/BBL`` -> ``bbl``
|
|
103
|
+
|
|
104
|
+
Bare quantity units are also accepted. Bare rate units such as
|
|
105
|
+
``bbl/day`` return ``None`` because the denominator is a time period, not
|
|
106
|
+
a physical quantity.
|
|
107
|
+
"""
|
|
108
|
+
if price_unit is None:
|
|
109
|
+
return None
|
|
110
|
+
text = str(price_unit).strip()
|
|
111
|
+
if not text:
|
|
112
|
+
return None
|
|
113
|
+
_, unit_text = split_currency_unit(text)
|
|
114
|
+
if "/" in unit_text:
|
|
115
|
+
_, _, unit_text = text.partition("/")
|
|
116
|
+
return canonical_quantity_unit(unit_text)
|
|
117
|
+
|
|
118
|
+
|
|
75
119
|
# ---- Pint-token normalisation ----
|
|
76
120
|
|
|
77
121
|
|
|
@@ -89,6 +133,7 @@ def to_pint_token(unit: Optional[str]) -> Optional[str]:
|
|
|
89
133
|
/ ``CUBIC_METER`` / ``m3`` (case-insensitive exact match) -> ``m^3``.
|
|
90
134
|
- Rate forms: ``m3/...`` / ``M3/...`` -> ``m^3/...``.
|
|
91
135
|
- Energy casing: ``BTU`` -> ``Btu``; ``MMBTU`` -> ``MMBtu``.
|
|
136
|
+
- Pound casing/plurals: ``LBS`` / ``lbs`` -> ``lb``.
|
|
92
137
|
|
|
93
138
|
Other tokens pass through unchanged. Aliases like ``barrel``, ``tonne``,
|
|
94
139
|
``gallon`` are not handled here -- they are registered as pint aliases
|
|
@@ -119,7 +164,15 @@ def to_pint_token(unit: Optional[str]) -> Optional[str]:
|
|
|
119
164
|
u = "Btu"
|
|
120
165
|
if u == "MMBTU":
|
|
121
166
|
u = "MMBtu"
|
|
167
|
+
if u.upper() == "LBS":
|
|
168
|
+
u = "lb"
|
|
122
169
|
return u
|
|
123
170
|
|
|
124
171
|
|
|
125
|
-
__all__ = [
|
|
172
|
+
__all__ = [
|
|
173
|
+
"UNIT_MAP",
|
|
174
|
+
"canonical_quantity_unit",
|
|
175
|
+
"default_unit_for_commodity",
|
|
176
|
+
"quantity_unit_from_price_unit",
|
|
177
|
+
"to_pint_token",
|
|
178
|
+
]
|
|
@@ -206,6 +206,11 @@ def test_bug2_usc_to_usd_no_fx_required():
|
|
|
206
206
|
assert out == pytest.approx(0.005, rel=1e-9)
|
|
207
207
|
|
|
208
208
|
|
|
209
|
+
def test_usc_per_pound_to_usd_per_pound_no_fx_required():
|
|
210
|
+
out = convfactors.convert_price(71.26, "USc/LBS", "USD/lb")
|
|
211
|
+
assert out == pytest.approx(0.7126, rel=1e-9)
|
|
212
|
+
|
|
213
|
+
|
|
209
214
|
def test_bug2_gbp_pence_to_gbp_no_fx_required():
|
|
210
215
|
"""Bug 2: GBp -> GBP is a pure /100 scale (no FX)."""
|
|
211
216
|
out = convfactors.convert_price(50.0, "GBp/therm", "GBP/therm")
|
|
@@ -7,6 +7,7 @@ from commodutil.standards.currency import (
|
|
|
7
7
|
VALID_CURRENCY_TOKENS,
|
|
8
8
|
fractional_to_major,
|
|
9
9
|
is_fractional_currency,
|
|
10
|
+
normalize_currency_token,
|
|
10
11
|
required_fx_pair,
|
|
11
12
|
split_currency_unit,
|
|
12
13
|
to_symbol,
|
|
@@ -40,6 +41,21 @@ def test_fractional_to_major():
|
|
|
40
41
|
assert fractional_to_major("") == ""
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
def test_normalize_currency_token():
|
|
45
|
+
assert normalize_currency_token("USD") == "USD"
|
|
46
|
+
assert normalize_currency_token("usd") == "USD"
|
|
47
|
+
assert normalize_currency_token("$") == "$"
|
|
48
|
+
assert normalize_currency_token("USC") == "USc"
|
|
49
|
+
assert normalize_currency_token("U.S. cents") == "USc"
|
|
50
|
+
assert normalize_currency_token("GBX") == "GBp"
|
|
51
|
+
assert normalize_currency_token("pence") == "GBp"
|
|
52
|
+
assert normalize_currency_token("RMB") == "CNY"
|
|
53
|
+
assert normalize_currency_token("us dollars and cents") == "USD"
|
|
54
|
+
assert normalize_currency_token("bbl") is None
|
|
55
|
+
assert normalize_currency_token("") is None
|
|
56
|
+
assert normalize_currency_token(None) is None
|
|
57
|
+
|
|
58
|
+
|
|
43
59
|
def test_fractional_currency_cac_auc_correctly_routed():
|
|
44
60
|
# Regression guard for the pre-2026-05-14 bug where CAc/AUc were valid
|
|
45
61
|
# currency tokens but had no fractional rule, so required_fx_pair
|
|
@@ -155,6 +171,7 @@ def test_legacy_convfactors_currency_shims_are_gone():
|
|
|
155
171
|
"FRACTIONAL_TO_MAJOR",
|
|
156
172
|
"fractional_to_major",
|
|
157
173
|
"is_fractional_currency",
|
|
174
|
+
"normalize_currency_token",
|
|
158
175
|
"split_currency_unit",
|
|
159
176
|
"_split_currency_unit",
|
|
160
177
|
"_FRACTIONAL_CURRENCY_DIVISORS",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"""Tests for commodutil.standards.units."""
|
|
2
2
|
|
|
3
|
-
from commodutil.standards.units import
|
|
3
|
+
from commodutil.standards.units import (
|
|
4
|
+
canonical_quantity_unit,
|
|
5
|
+
default_unit_for_commodity,
|
|
6
|
+
quantity_unit_from_price_unit,
|
|
7
|
+
)
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
def test_natgas_defaults_to_mmbtu():
|
|
@@ -62,11 +66,39 @@ def test_unit_map_metric_ton_variants():
|
|
|
62
66
|
assert UNIT_MAP["tonnes"] == "mt"
|
|
63
67
|
|
|
64
68
|
|
|
65
|
-
def
|
|
66
|
-
"""UNIT_MAP normalises to exactly the canonical units bbl / gal / mt."""
|
|
69
|
+
def test_unit_map_pound_variants():
|
|
67
70
|
from commodutil.standards.units import UNIT_MAP
|
|
68
71
|
|
|
69
|
-
assert
|
|
72
|
+
assert UNIT_MAP["pound"] == "lb"
|
|
73
|
+
assert UNIT_MAP["pounds"] == "lb"
|
|
74
|
+
assert UNIT_MAP["lb"] == "lb"
|
|
75
|
+
assert UNIT_MAP["lbs"] == "lb"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_unit_map_canonical_set():
|
|
79
|
+
"""UNIT_MAP normalises to the canonical physical quote units."""
|
|
80
|
+
from commodutil.standards.units import UNIT_MAP
|
|
81
|
+
|
|
82
|
+
assert set(UNIT_MAP.values()) == {"bbl", "gal", "mt", "lb"}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_canonical_quantity_unit_normalizes_common_labels():
|
|
86
|
+
assert canonical_quantity_unit("BBL") == "bbl"
|
|
87
|
+
assert canonical_quantity_unit("barrels") == "bbl"
|
|
88
|
+
assert canonical_quantity_unit("GAL") == "gal"
|
|
89
|
+
assert canonical_quantity_unit("Metric Tonnes") == "mt"
|
|
90
|
+
assert canonical_quantity_unit("mmbtu") is None
|
|
91
|
+
assert canonical_quantity_unit(None) is None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_quantity_unit_from_price_unit_uses_quote_denominator():
|
|
95
|
+
assert quantity_unit_from_price_unit("USD/MT") == "mt"
|
|
96
|
+
assert quantity_unit_from_price_unit("usd/mt") == "mt"
|
|
97
|
+
assert quantity_unit_from_price_unit("USc/GAL") == "gal"
|
|
98
|
+
assert quantity_unit_from_price_unit("USC/GAL") == "gal"
|
|
99
|
+
assert quantity_unit_from_price_unit("$/BBL") == "bbl"
|
|
100
|
+
assert quantity_unit_from_price_unit("BBL") == "bbl"
|
|
101
|
+
assert quantity_unit_from_price_unit("bbl/day") is None
|
|
70
102
|
|
|
71
103
|
|
|
72
104
|
# ---- to_pint_token tests ----
|
|
@@ -97,6 +129,13 @@ def test_to_pint_token_energy_casing():
|
|
|
97
129
|
assert to_pint_token("MMBTU") == "MMBtu"
|
|
98
130
|
|
|
99
131
|
|
|
132
|
+
def test_to_pint_token_pound_casing():
|
|
133
|
+
from commodutil.standards.units import to_pint_token
|
|
134
|
+
|
|
135
|
+
assert to_pint_token("LBS") == "lb"
|
|
136
|
+
assert to_pint_token("lbs") == "lb"
|
|
137
|
+
|
|
138
|
+
|
|
100
139
|
def test_to_pint_token_whitespace_stripped():
|
|
101
140
|
from commodutil.standards.units import to_pint_token
|
|
102
141
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|