Skip to content

Commit 2fa60bb

Browse files
committed
lib: move commodityStylesFromAmounts to Hledger.Data.Amount
1 parent d865ec5 commit 2fa60bb

File tree

2 files changed

+86
-109
lines changed

2 files changed

+86
-109
lines changed

hledger-lib/Hledger/Data/Amount.hs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ module Hledger.Data.Amount (
7474
noPrice,
7575
oneLine,
7676
amountstyle,
77+
commodityStylesFromAmounts,
7778
styleAmount,
7879
styleAmountExceptPrecision,
7980
amountUnstyled,
@@ -153,12 +154,14 @@ import Data.Semigroup ((<>))
153154
import qualified Data.Text as T
154155
import qualified Data.Text.Lazy.Builder as TB
155156
import Data.Word (Word8)
156-
import Safe (headDef, lastDef, lastMay)
157+
import Safe (headDef, lastDef, lastMay, headMay)
157158
import Text.Printf (printf)
158159

159160
import Hledger.Data.Types
160161
import Hledger.Data.Commodity
161162
import Hledger.Utils
163+
import Data.List.Extra (groupSort)
164+
import Data.Maybe (mapMaybe)
162165

163166
deriving instance Show MarketPrice
164167

@@ -202,6 +205,53 @@ oneLine = def{displayOneLine=True, displayPrice=False}
202205
-- | Default amount style
203206
amountstyle = AmountStyle L False (Precision 0) (Just '.') Nothing
204207

208+
-- | Given an ordered list of amounts (typically in parse order),
209+
-- build a map from their commodity names to standard commodity
210+
-- display styles, inferring styles as per docs, eg:
211+
-- "the format of the first amount, adjusted to the highest precision of all amounts".
212+
-- Can return an error message eg if inconsistent number formats are found.
213+
-- (Though, these amounts may have come from multiple files, so we
214+
-- shouldn't assume they use consistent number formats.
215+
-- Currently we don't enforce that even within a single file,
216+
-- and this function never reports an error.)
217+
commodityStylesFromAmounts :: [Amount] -> Either String (M.Map CommoditySymbol AmountStyle)
218+
commodityStylesFromAmounts amts =
219+
Right $ M.fromList commstyles
220+
where
221+
commamts = groupSort [(acommodity as, as) | as <- amts]
222+
commstyles = [(c, canonicalStyleFrom $ map astyle as) | (c,as) <- commamts]
223+
224+
-- TODO: should probably detect and report inconsistencies here.
225+
-- Though, we don't have the info for a good error message, so maybe elsewhere.
226+
-- | Given a list of amount styles (assumed to be from parsed amounts
227+
-- in a single commodity), in parse order, choose a canonical style.
228+
-- This is:
229+
-- the general style of the first amount,
230+
-- with the first digit group style seen,
231+
-- with the maximum precision of all.
232+
--
233+
canonicalStyleFrom :: [AmountStyle] -> AmountStyle
234+
canonicalStyleFrom [] = amountstyle
235+
canonicalStyleFrom ss@(s:_) =
236+
s{asprecision=prec, asdecimalpoint=Just decmark, asdigitgroups=mgrps}
237+
where
238+
-- precision is maximum of all precisions
239+
prec = maximumStrict $ map asprecision ss
240+
-- identify the digit group mark (& group sizes)
241+
mgrps = headMay $ mapMaybe asdigitgroups ss
242+
-- if a digit group mark was identified above, we can rely on that;
243+
-- make sure the decimal mark is different. If not, default to period.
244+
defdecmark =
245+
case mgrps of
246+
Just (DigitGroups '.' _) -> ','
247+
_ -> '.'
248+
-- identify the decimal mark: the first one used, or the above default,
249+
-- but never the same character as the digit group mark.
250+
-- urgh.. refactor..
251+
decmark = case mgrps of
252+
Just _ -> defdecmark
253+
_ -> headDef defdecmark $ mapMaybe asdecimalpoint ss
254+
205255
-------------------------------------------------------------------------------
206256
-- Amount
207257

@@ -953,6 +1003,41 @@ tests_Amount = tests "Amount" [
9531003
,usd (-10) @@ eur 7
9541004
])
9551005

1006+
,tests "commodityStylesFromAmounts" $ [
1007+
1008+
-- Journal similar to the one on #1091:
1009+
-- 2019/09/24
1010+
-- (a) 1,000.00
1011+
--
1012+
-- 2019/09/26
1013+
-- (a) 1000,000
1014+
--
1015+
test "1091a" $ do
1016+
commodityStylesFromAmounts [
1017+
nullamt{aquantity=1000, astyle=AmountStyle L False (Precision 3) (Just ',') Nothing}
1018+
,nullamt{aquantity=1000, astyle=AmountStyle L False (Precision 2) (Just '.') (Just (DigitGroups ',' [3]))}
1019+
]
1020+
@?=
1021+
-- The commodity style should have period as decimal mark
1022+
-- and comma as digit group mark.
1023+
Right (M.fromList [
1024+
("", AmountStyle L False (Precision 3) (Just '.') (Just (DigitGroups ',' [3])))
1025+
])
1026+
-- same journal, entries in reverse order
1027+
,test "1091b" $ do
1028+
commodityStylesFromAmounts [
1029+
nullamt{aquantity=1000, astyle=AmountStyle L False (Precision 2) (Just '.') (Just (DigitGroups ',' [3]))}
1030+
,nullamt{aquantity=1000, astyle=AmountStyle L False (Precision 3) (Just ',') Nothing}
1031+
]
1032+
@?=
1033+
-- The commodity style should have period as decimal mark
1034+
-- and comma as digit group mark.
1035+
Right (M.fromList [
1036+
("", AmountStyle L False (Precision 3) (Just '.') (Just (DigitGroups ',' [3])))
1037+
])
1038+
1039+
]
1040+
9561041
]
9571042

9581043
]

hledger-lib/Hledger/Data/Journal.hs

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,54 +1014,6 @@ journalInferCommodityStyles j =
10141014
Left e -> Left e
10151015
Right cs -> Right j{jinferredcommodities = dbg7 "journalInferCommodityStyles" cs}
10161016

1017-
-- | Given a list of amounts, in parse order (roughly speaking; see journalStyleInfluencingAmounts),
1018-
-- build a map from their commodity names to standard commodity
1019-
-- display formats. Can return an error message eg if inconsistent
1020-
-- number formats are found.
1021-
--
1022-
-- Though, these amounts may have come from multiple files, so we
1023-
-- shouldn't assume they use consistent number formats.
1024-
-- Currently we don't enforce that even within a single file,
1025-
-- and this function never reports an error.
1026-
--
1027-
commodityStylesFromAmounts :: [Amount] -> Either String (M.Map CommoditySymbol AmountStyle)
1028-
commodityStylesFromAmounts amts =
1029-
Right $ M.fromList commstyles
1030-
where
1031-
commamts = groupSort [(acommodity as, as) | as <- amts]
1032-
commstyles = [(c, canonicalStyleFrom $ map astyle as) | (c,as) <- commamts]
1033-
1034-
-- TODO: should probably detect and report inconsistencies here.
1035-
-- Though, we don't have the info for a good error message, so maybe elsewhere.
1036-
-- | Given a list of amount styles (assumed to be from parsed amounts
1037-
-- in a single commodity), in parse order, choose a canonical style.
1038-
-- This is:
1039-
-- the general style of the first amount,
1040-
-- with the first digit group style seen,
1041-
-- with the maximum precision of all.
1042-
--
1043-
canonicalStyleFrom :: [AmountStyle] -> AmountStyle
1044-
canonicalStyleFrom [] = amountstyle
1045-
canonicalStyleFrom ss@(s:_) =
1046-
s{asprecision=prec, asdecimalpoint=Just decmark, asdigitgroups=mgrps}
1047-
where
1048-
-- precision is maximum of all precisions
1049-
prec = maximumStrict $ map asprecision ss
1050-
-- identify the digit group mark (& group sizes)
1051-
mgrps = headMay $ mapMaybe asdigitgroups ss
1052-
-- if a digit group mark was identified above, we can rely on that;
1053-
-- make sure the decimal mark is different. If not, default to period.
1054-
defdecmark =
1055-
case mgrps of
1056-
Just (DigitGroups '.' _) -> ','
1057-
_ -> '.'
1058-
-- identify the decimal mark: the first one used, or the above default,
1059-
-- but never the same character as the digit group mark.
1060-
-- urgh.. refactor..
1061-
decmark = case mgrps of
1062-
Just _ -> defdecmark
1063-
_ -> headDef defdecmark $ mapMaybe asdecimalpoint ss
1064-
10651017
-- -- | Apply this journal's historical price records to unpriced amounts where possible.
10661018
-- journalApplyPriceDirectives :: Journal -> Journal
10671019
-- journalApplyPriceDirectives j@Journal{jtxns=ts} = j{jtxns=map fixtransaction ts}
@@ -1134,31 +1086,6 @@ journalToCost j@Journal{jtxns=ts} = j{jtxns=map (transactionToCost styles) ts}
11341086
-- Just (UnitPrice ma) -> c:(concatMap amountCommodities $ amounts ma)
11351087
-- Just (TotalPrice ma) -> c:(concatMap amountCommodities $ amounts ma)
11361088

1137-
-- | Get an ordered list of amounts in this journal which can
1138-
-- influence canonical amount display styles. Those amounts are, in
1139-
-- the following order:
1140-
--
1141-
-- * amounts in market price (P) directives (in parse order)
1142-
-- * posting amounts in transactions (in parse order)
1143-
-- * the amount in the final default commodity (D) directive
1144-
--
1145-
-- Transaction price amounts (posting amounts' aprice field) are not included.
1146-
--
1147-
journalStyleInfluencingAmounts :: Journal -> [Amount]
1148-
journalStyleInfluencingAmounts j =
1149-
dbg7 "journalStyleInfluencingAmounts" $
1150-
catMaybes $ concat [
1151-
[mdefaultcommodityamt]
1152-
,map (Just . pdamount) $ jpricedirectives j
1153-
,map Just $ concatMap amounts $ map pamount $ journalPostings j
1154-
]
1155-
where
1156-
-- D's amount style isn't actually stored as an amount, make it into one
1157-
mdefaultcommodityamt =
1158-
case jparsedefaultcommodity j of
1159-
Just (symbol,style) -> Just nullamt{acommodity=symbol,astyle=style}
1160-
Nothing -> Nothing
1161-
11621089
-- overcomplicated/unused amount traversal stuff
11631090
--
11641091
-- | Get an ordered list of 'AmountStyle's from the amounts in this
@@ -1510,39 +1437,4 @@ tests_Journal = tests "Journal" [
15101437

15111438
]
15121439

1513-
,tests "commodityStylesFromAmounts" $ [
1514-
1515-
-- Journal similar to the one on #1091:
1516-
-- 2019/09/24
1517-
-- (a) 1,000.00
1518-
--
1519-
-- 2019/09/26
1520-
-- (a) 1000,000
1521-
--
1522-
test "1091a" $ do
1523-
commodityStylesFromAmounts [
1524-
nullamt{aquantity=1000, astyle=AmountStyle L False (Precision 3) (Just ',') Nothing}
1525-
,nullamt{aquantity=1000, astyle=AmountStyle L False (Precision 2) (Just '.') (Just (DigitGroups ',' [3]))}
1526-
]
1527-
@?=
1528-
-- The commodity style should have period as decimal mark
1529-
-- and comma as digit group mark.
1530-
Right (M.fromList [
1531-
("", AmountStyle L False (Precision 3) (Just '.') (Just (DigitGroups ',' [3])))
1532-
])
1533-
-- same journal, entries in reverse order
1534-
,test "1091b" $ do
1535-
commodityStylesFromAmounts [
1536-
nullamt{aquantity=1000, astyle=AmountStyle L False (Precision 2) (Just '.') (Just (DigitGroups ',' [3]))}
1537-
,nullamt{aquantity=1000, astyle=AmountStyle L False (Precision 3) (Just ',') Nothing}
1538-
]
1539-
@?=
1540-
-- The commodity style should have period as decimal mark
1541-
-- and comma as digit group mark.
1542-
Right (M.fromList [
1543-
("", AmountStyle L False (Precision 3) (Just '.') (Just (DigitGroups ',' [3])))
1544-
])
1545-
1546-
]
1547-
15481440
]

0 commit comments

Comments
 (0)