Skip to content

Commit 78208c3

Browse files
committed
Add support for price includes
1 parent 1b35ddf commit 78208c3

File tree

6 files changed

+100
-24
lines changed

6 files changed

+100
-24
lines changed

ChangeLog.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
## 0.13.1
44

5-
Minor report changes - do not assume too many extra options for default reports
5+
- Automatically add [include lines for yearly price files](https://github.com/apauley/hledger-flow/#price-files) if they are present on disk.
6+
- Minor report changes - do not assume too many extra options for default reports.
67

78
## 0.13.0
89

README.org

+24
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,30 @@ change. I'm only using closing balances in one or two places, so maybe
407407
that could do with some suggestions from people who use this more than
408408
myself.
409409

410+
** Price Files
411+
:PROPERTIES:
412+
:CUSTOM_ID: price-files
413+
:END:
414+
415+
=hledger-flow= looks for [[https://hledger.org/journal.html#market-prices][price files]] to include in each yearly include file.
416+
417+
For example, the presence of a file named =${BASE}/prices/2020/prices.journal= will result in some extra include file magic.
418+
419+
The rest of this section assumes you'll have a file named =prices/2020/prices.journal= which contains price data for the year 2020.
420+
The =prices= directory should be right at the top of your =hledger-flow= base directory, next to the =import= directory.
421+
422+
=hledger-flow= does not care how the price files got there, it only cares that you should have a separate file per year,
423+
and that it follows the above naming convention.
424+
425+
Here is an example script which downloads prices and follows the naming convention:
426+
https://gist.github.com/apauley/398fa031c202733959af76b3b8ce8197
427+
428+
After running an import with available price files you'll see a line has been added to =import/2020-include.journal=:
429+
430+
#+BEGIN_EXAMPLE
431+
!include ../prices/2020/prices.journal
432+
#+END_EXAMPLE
433+
410434
** The =preprocess= Script
411435
:PROPERTIES:
412436
:CUSTOM_ID: the-preprocess-script

src/Hledger/Flow/Common.hs

+12-11
Original file line numberDiff line numberDiff line change
@@ -322,26 +322,27 @@ includeFileName = (<.> "journal") . fromText . (format (fp%"-include")) . dirnam
322322

323323
toIncludeFiles :: (HasBaseDir o, HasVerbosity o) => o -> TChan LogMessage -> InputFileBundle -> Shell (Map.Map FilePath Text)
324324
toIncludeFiles opts ch m = do
325-
preMap <- extraIncludes opts ch (Map.keys m) ["opening.journal"] ["pre-import.journal"]
326-
postMap <- extraIncludes opts ch (Map.keys m) ["closing.journal"] ["post-import.journal"]
325+
preMap <- extraIncludes opts ch (Map.keys m) ["opening.journal"] ["pre-import.journal"] []
326+
postMap <- extraIncludes opts ch (Map.keys m) ["closing.journal"] ["post-import.journal"] ["prices.journal"]
327327
return $ (addPreamble . toIncludeFiles' preMap postMap) m
328328

329-
extraIncludes :: (HasBaseDir o, HasVerbosity o) => o -> TChan LogMessage -> [FilePath] -> [Text] -> [FilePath] -> Shell (InputFileBundle)
329+
extraIncludes :: (HasBaseDir o, HasVerbosity o) => o -> TChan LogMessage -> [FilePath] -> [Text] -> [FilePath] -> [FilePath] -> Shell InputFileBundle
330330
extraIncludes opts ch = extraIncludes' opts ch Map.empty
331331

332-
extraIncludes' :: (HasBaseDir o, HasVerbosity o) => o -> TChan LogMessage -> InputFileBundle -> [FilePath] -> [Text] -> [FilePath] -> Shell (InputFileBundle)
333-
extraIncludes' _ _ acc [] _ _ = return acc
334-
extraIncludes' opts ch acc (file:files) extraSuffixes manualFiles = do
335-
extra <- extraIncludesForFile opts ch file extraSuffixes manualFiles
336-
extraIncludes' opts ch (Map.unionWith (++) acc extra) files extraSuffixes manualFiles
332+
extraIncludes' :: (HasBaseDir o, HasVerbosity o) => o -> TChan LogMessage -> InputFileBundle -> [FilePath] -> [Text] -> [FilePath] -> [FilePath] -> Shell InputFileBundle
333+
extraIncludes' _ _ acc [] _ _ _ = return acc
334+
extraIncludes' opts ch acc (file:files) extraSuffixes manualFiles prices = do
335+
extra <- extraIncludesForFile opts ch file extraSuffixes manualFiles prices
336+
extraIncludes' opts ch (Map.unionWith (++) acc extra) files extraSuffixes manualFiles prices
337337

338-
extraIncludesForFile :: (HasVerbosity o, HasBaseDir o) => o -> TChan LogMessage -> FilePath -> [Text] -> [FilePath] -> Shell (InputFileBundle)
339-
extraIncludesForFile opts ch file extraSuffixes manualFiles = do
338+
extraIncludesForFile :: (HasVerbosity o, HasBaseDir o) => o -> TChan LogMessage -> FilePath -> [Text] -> [FilePath] -> [FilePath] -> Shell InputFileBundle
339+
extraIncludesForFile opts ch file extraSuffixes manualFiles prices = do
340340
let dirprefix = fromText $ fst $ T.breakOn "-" $ format fp $ basename file
341341
let fileNames = map (\suff -> fromText $ format (fp%"-"%s) dirprefix suff) extraSuffixes
342342
let suffixFiles = map (directory file </>) fileNames
343343
let suffixDirFiles = map (directory file </> "_manual_" </> dirprefix </>) manualFiles
344-
let extraFiles = suffixFiles ++ suffixDirFiles
344+
let priceFiles = map (directory file </> ".." </> "prices" </> dirprefix </>) prices
345+
let extraFiles = suffixFiles ++ suffixDirFiles ++ priceFiles
345346
filtered <- filterPaths testfile extraFiles
346347
let logMsg = format ("Looking for possible extra include files for '"%fp%"' among these "%d%" options: "%s%". Found "%d%": "%s)
347348
(relativeToBase opts file) (length extraFiles) (repr $ relativeFilesAsText opts extraFiles)

stack.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#
1919
# resolver: ./custom-snapshot.yaml
2020
# resolver: https://example.com/snapshots/2018-01-01.yaml
21-
resolver: nightly-2020-02-19
21+
resolver: nightly-2020-02-29
2222

2323
# User packages to be built.
2424
# Various formats can be used as shown in the example below.

stack.yaml.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
packages: []
77
snapshots:
88
- completed:
9-
size: 492204
10-
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2020/2/19.yaml
11-
sha256: 21d491c4e1b6a9a0d16a4b1d22f4fd9ce77f6a983172f549e982ac57120e6e04
12-
original: nightly-2020-02-19
9+
size: 496030
10+
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2020/2/29.yaml
11+
sha256: c3f6b6cd18cb8a067e9eab5c78945d5e03a15802fe34ed650a4be241ddda5ad0
12+
original: nightly-2020-02-29

test/CSVImport/Integration.hs

+57-7
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,52 @@ testExtraIncludesForFile = TestCase (
3030

3131
ch <- liftIO newTChanIO
3232

33-
extraOpening1 <- extraIncludesForFile (defaultOpts tmpdir) ch accountInclude ["opening.journal"] []
33+
extraOpening1 <- extraIncludesForFile (defaultOpts tmpdir) ch accountInclude ["opening.journal"] [] []
3434
liftIO $ assertEqual "The opening journal should not be included when it is not on disk" expectedEmpty extraOpening1
3535

36-
extraClosing1 <- extraIncludesForFile (defaultOpts tmpdir) ch accountInclude ["closing.journal"] []
36+
extraClosing1 <- extraIncludesForFile (defaultOpts tmpdir) ch accountInclude ["closing.journal"] [] []
3737
liftIO $ assertEqual "The closing journal should not be included when it is not on disk" expectedEmpty extraClosing1
3838

3939
touchAll [opening, closing]
4040

41-
extraOpening2 <- extraIncludesForFile (defaultOpts tmpdir) ch accountInclude ["opening.journal"] []
41+
extraOpening2 <- extraIncludesForFile (defaultOpts tmpdir) ch accountInclude ["opening.journal"] [] []
4242
liftIO $ assertEqual "The opening journal should be included when it is on disk" [(accountInclude, [opening])] extraOpening2
4343

44-
extraClosing2 <- extraIncludesForFile (defaultOpts tmpdir) ch accountInclude ["closing.journal"] []
44+
extraClosing2 <- extraIncludesForFile (defaultOpts tmpdir) ch accountInclude ["closing.journal"] [] []
4545
liftIO $ assertEqual "The closing journal should be included when it is on disk" [(accountInclude, [closing])] extraClosing2
4646
))
4747

48+
testExtraIncludesPrices :: Test
49+
testExtraIncludesPrices = TestCase (
50+
sh (
51+
do
52+
tmpdir <- using (mktempdir "." "hlflow")
53+
let importedJournals = map (tmpdir </>) journalFiles :: [FilePath]
54+
touchAll $ importedJournals
55+
56+
let priceFile = "prices" </> "2020" </> "prices.journal"
57+
58+
let includeFile = tmpdir </> "import" </> "2020-include.journal"
59+
let expectedEmpty = [(includeFile, [])]
60+
61+
ch <- liftIO newTChanIO
62+
63+
price1 <- extraIncludesForFile (defaultOpts tmpdir) ch includeFile [] [] ["prices.journal"]
64+
liftIO $ assertEqual "The price file should not be included when it is not on disk" expectedEmpty price1
65+
66+
touchAll [tmpdir </> priceFile]
67+
let expectedPricePath = tmpdir </> "import" </> ".." </> priceFile
68+
69+
price2 <- extraIncludesForFile (defaultOpts tmpdir) ch includeFile [] [] ["prices.journal"]
70+
liftIO $ assertEqual "The price file should be included when it is on disk" [(includeFile, [expectedPricePath])] price2
71+
))
72+
4873
testIncludesPrePost :: Test
4974
testIncludesPrePost = TestCase (
5075
sh (
5176
do
5277
tmpdir <- using (mktempdir "." "hlflow")
53-
let ownerDir = tmpdir </> "import/john"
78+
let ownerDir = tmpdir </> "import" </> "john"
5479
let includeFile = ownerDir </> "2019-include.journal"
5580
let pre = ownerDir </> "_manual_" </> "2019" </> "pre-import.journal"
5681
let post = ownerDir </> "_manual_" </> "2019" </> "post-import.journal"
@@ -91,7 +116,32 @@ testIncludesOpeningClosing = TestCase (
91116
<> "!include 3-journal/2019/2019-01-30.journal\n"
92117
<> "!include 2019-closing.journal\n"
93118
let expectedMap = Map.singleton includeFile expectedText
94-
liftIO $ assertEqual "All pre/post files on disk should be included" expectedMap fileMap
119+
liftIO $ assertEqual "All opening/closing files on disk should be included" expectedMap fileMap
120+
))
121+
122+
testIncludesPrices :: Test
123+
testIncludesPrices = TestCase (
124+
sh (
125+
do
126+
tmpdir <- using (mktempdir "." "hlflow")
127+
let importDir = tmpdir </> "import"
128+
let includeFile = importDir </> "2020-include.journal"
129+
let prices = tmpdir </> "prices" </> "2020" </> "prices.journal"
130+
let pre = importDir </> "_manual_" </> "2020" </> "pre-import.journal"
131+
let post = importDir </> "_manual_" </> "2020" </> "post-import.journal"
132+
touchAll [prices, pre, post]
133+
134+
let includeMap = Map.singleton includeFile [importDir </> "john" </> "2020-include.journal"]
135+
136+
ch <- liftIO newTChanIO
137+
fileMap <- toIncludeFiles (defaultOpts tmpdir) ch includeMap
138+
let expectedText = includePreamble <> "\n"
139+
<> "!include _manual_/2020/pre-import.journal\n"
140+
<> "!include john/2020-include.journal\n"
141+
<> "!include ../prices/2020/prices.journal\n"
142+
<> "!include _manual_/2020/post-import.journal\n"
143+
let expectedMap = Map.singleton includeFile expectedText
144+
liftIO $ assertEqual "The price file should be included together with any pre/post files" expectedMap fileMap
95145
))
96146

97147
testWriteIncludeFiles :: Test
@@ -174,4 +224,4 @@ testWriteIncludeFiles = TestCase (
174224
)
175225

176226
tests :: Test
177-
tests = TestList [testExtraIncludesForFile, testIncludesPrePost, testIncludesOpeningClosing, testWriteIncludeFiles]
227+
tests = TestList [testExtraIncludesForFile, testExtraIncludesPrices, testIncludesPrePost, testIncludesOpeningClosing, testIncludesPrices, testWriteIncludeFiles]

0 commit comments

Comments
 (0)