Skip to content

Commit d404f77

Browse files
committed
Make --enable-future-rundir the default, and deprecate the option.
Ensure that the deepest rundir is the account directory, because the program doesn't generate include files correctly in directories below the account level.
1 parent 2f83bc2 commit d404f77

16 files changed

+138
-64
lines changed

CONTRIBUTING.org

-14
Original file line numberDiff line numberDiff line change
@@ -120,20 +120,6 @@ Usually this means adding this to your =~/.bashrc=:
120120
PATH="${HOME}/.local/bin:${PATH}"
121121
#+END_SRC
122122

123-
**** Building with older Haskell Versions
124-
:PROPERTIES:
125-
:CUSTOM_ID: building-with-older-haskell-versions
126-
:END:
127-
128-
To build using an older version of GHC and related dependencies, point
129-
stack to one of the other yaml files:
130-
131-
#+NAME: stack-build-versions
132-
#+BEGIN_SRC sh
133-
stack test --stack-yaml stack-8.4.4.yaml
134-
stack test --stack-yaml stack-8.2.2.yaml
135-
#+END_SRC
136-
137123
*** Find an Issue
138124
:PROPERTIES:
139125
:CUSTOM_ID: find-an-issue

ChangeLog.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog for [hledger-flow](https://github.com/apauley/hledger-flow)
22

3+
## 0.14.1
4+
5+
- Make `--enable-future-rundir` the default, and deprecate the command-line option. To be removed in a future release.
6+
- Ensure that the deepest rundir is the account directory, because the program doesn't generate include files correctly in directories below the account level.
7+
38
## 0.14.0
49

510
- Add a new performance-related command-line option to import: `--new-files-only`. [PR #89](https://github.com/apauley/hledger-flow/pull/89)

app/Main.hs

+7-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
module Main where
55

66
import Path
7-
import qualified Turtle as Turtle hiding (switch)
7+
import qualified Turtle hiding (switch)
88
import Prelude hiding (putStrLn)
99

1010
import Options.Applicative
@@ -16,6 +16,9 @@ import qualified Hledger.Flow.RuntimeOptions as RT
1616
import Hledger.Flow.Reports
1717
import Hledger.Flow.CSVImport
1818

19+
import Control.Monad (when)
20+
import qualified Data.Text.IO as T
21+
1922
data ImportParams = ImportParams { maybeImportBaseDir :: Maybe TurtlePath
2023
, importUseRunDir :: Bool
2124
, onlyNewFiles :: Bool
@@ -43,11 +46,12 @@ main = do
4346
toRuntimeOptionsImport :: MainParams -> ImportParams -> IO RT.RuntimeOptions
4447
toRuntimeOptionsImport mainParams' subParams' = do
4548
let maybeBD = maybeImportBaseDir subParams' :: Maybe TurtlePath
49+
Control.Monad.when (importUseRunDir subParams') $ do
50+
T.putStrLn "The enable-future-rundir option is now the default, no need to specify it. This option is currently being ignored and will be removed in future."
4651
(bd, runDir) <- determineBaseDir maybeBD
4752
hli <- hledgerInfoFromPath $ hledgerPathOpt mainParams'
4853
return RT.RuntimeOptions { RT.baseDir = bd
4954
, RT.importRunDir = runDir
50-
, RT.useRunDir = importUseRunDir subParams'
5155
, RT.onlyNewFiles = onlyNewFiles subParams'
5256
, RT.hfVersion = versionInfo'
5357
, RT.hledgerInfo = hli
@@ -63,7 +67,6 @@ toRuntimeOptionsReport mainParams' subParams' = do
6367
hli <- hledgerInfoFromPath $ hledgerPathOpt mainParams'
6468
return RT.RuntimeOptions { RT.baseDir = bd
6569
, RT.importRunDir = [reldir|.|]
66-
, RT.useRunDir = False
6770
, RT.onlyNewFiles = False
6871
, RT.hfVersion = versionInfo'
6972
, RT.hledgerInfo = hli
@@ -90,7 +93,7 @@ verboseParser = MainParams
9093
subcommandParserImport :: Parser ImportParams
9194
subcommandParserImport = ImportParams
9295
<$> optional (Turtle.argPath "dir" "The directory to import. Use the base directory for a full import or a sub-directory for a partial import. Defaults to the current directory. This behaviour is changing: see --enable-future-rundir")
93-
<*> switch (long "enable-future-rundir" <> help "Enable the future (0.14.x) default behaviour now: start importing only from the directory that was given as an argument, or the currect directory. Previously a full import was always done. This switch will be removed in 0.14.x")
96+
<*> switch (long "enable-future-rundir" <> help "This switch is currently being ignored, since the behaviour it previously enabled is now the default. It will be removed in future.")
9497
<*> switch (long "new-files-only" <> help "Don't regenerate transaction files if they are already present. This applies to hledger journal files as well as files produced by the preprocess and construct scripts.")
9598

9699
subcommandParserReport :: Parser ReportParams

package.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
name: hledger-flow
2-
version: 0.14.0.0
2+
version: 0.14.1.0
33
synopsis: An hledger workflow focusing on automated statement import and classification.
44
category: Finance, Console
55
license: GPL-3
66
author: "Andreas Pauley <[email protected]>"
77
maintainer: "Andreas Pauley <[email protected]>"
8-
copyright: "2018 Andreas Pauley"
8+
copyright: "2020 Andreas Pauley"
99
github: "apauley/hledger-flow"
1010
bug-reports: https://github.com/apauley/hledger-flow/issues
1111

src/Hledger/Flow/BaseDir.hs

+36-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
{-# LANGUAGE OverloadedStrings #-}
22
{-# LANGUAGE QuasiQuotes #-}
33

4-
module Hledger.Flow.BaseDir where
4+
module Hledger.Flow.BaseDir (
5+
determineBaseDir
6+
, relativeToBase
7+
, relativeToBase'
8+
, turtleBaseDir
9+
, effectiveRunDir
10+
) where
511

612
import Path
713
import Path.IO
@@ -12,9 +18,11 @@ import Data.Maybe
1218

1319
import Control.Monad.Catch (MonadThrow, throwM)
1420
import Control.Monad.IO.Class (MonadIO)
21+
import Control.Monad (when)
1522

16-
17-
import qualified Turtle as Turtle (stripPrefix)
23+
import qualified Turtle (liftIO, repr, stripPrefix)
24+
import qualified Data.Text as T
25+
import qualified Data.Text.IO as T
1826

1927
determineBaseDir :: Maybe TurtlePath -> IO (BaseDir, RunDir)
2028
determineBaseDir suppliedDir = do
@@ -32,14 +40,34 @@ determineBaseDirFromStartDir startDir = determineBaseDirFromStartDir' startDir s
3240

3341
determineBaseDirFromStartDir' :: (MonadIO m, MonadThrow m) => AbsDir -> AbsDir -> m (BaseDir, RunDir)
3442
determineBaseDirFromStartDir' startDir possibleBaseDir = do
35-
_ <- if (parent possibleBaseDir == possibleBaseDir) then throwM (MissingBaseDir startDir) else return ()
43+
Control.Monad.when (parent possibleBaseDir == possibleBaseDir) $ throwM (MissingBaseDir startDir)
3644
foundBaseDir <- doesDirExist $ possibleBaseDir </> [reldir|import|]
3745
if foundBaseDir then
3846
do
39-
runDir <- makeRelative possibleBaseDir startDir
47+
runDir <- limitRunDir possibleBaseDir startDir
4048
return (possibleBaseDir, runDir)
4149
else determineBaseDirFromStartDir' startDir $ parent possibleBaseDir
4250

51+
-- | We have unexpected behaviour when the runDir is deeper than the account directory,
52+
-- e.g. "1-in" or the year directory. Specifically, include files are generated incorrectly
53+
-- and some journals are written entirely outside of the baseDir.
54+
-- limitRunDir can possibly removed if the above is fixed.
55+
limitRunDir :: (MonadIO m, MonadThrow m) => BaseDir -> AbsDir -> m RunDir
56+
limitRunDir bd absRunDir = do
57+
rel <- makeRelative bd absRunDir
58+
let runDirDepth = pathSize rel
59+
let fun = composeN (runDirDepth - 4) parent
60+
let newRunDir = fun rel
61+
when (runDirDepth > 4) $ do
62+
let msg = T.pack $ "Changing runDir from " ++ Turtle.repr rel ++ " to " ++ Turtle.repr newRunDir :: T.Text
63+
Turtle.liftIO $ T.putStrLn msg
64+
return newRunDir
65+
66+
composeN :: Int -> (a -> a) -> (a -> a)
67+
composeN n f | n < 1 = id
68+
| n == 1 = f
69+
| otherwise = composeN (n-1) (f . f)
70+
4371
relativeToBase :: HasBaseDir o => o -> TurtlePath -> TurtlePath
4472
relativeToBase opts = relativeToBase' $ pathToTurtle (baseDir opts)
4573

@@ -50,10 +78,8 @@ relativeToBase' bd p = if forceTrailingSlash bd == forceTrailingSlash p then "./
5078
turtleBaseDir :: HasBaseDir o => o -> TurtlePath
5179
turtleBaseDir opts = pathToTurtle $ baseDir opts
5280

53-
effectiveRunDir :: BaseDir -> RunDir -> Bool -> AbsDir
54-
effectiveRunDir bd rd useRunDir = do
81+
effectiveRunDir :: BaseDir -> RunDir -> AbsDir
82+
effectiveRunDir bd rd = do
5583
let baseImportDir = bd </> [Path.reldir|import|]
5684
let absRunDir = bd </> rd
57-
if useRunDir
58-
then if absRunDir == bd then baseImportDir else absRunDir
59-
else baseImportDir
85+
if absRunDir == bd then baseImportDir else absRunDir

src/Hledger/Flow/CSVImport.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Hledger.Flow.CSVImport
44
( importCSVs
55
) where
66

7-
import qualified Turtle as Turtle hiding (stdout, stderr, proc, procStrictWithErr)
7+
import qualified Turtle hiding (stdout, stderr, proc, procStrictWithErr)
88
import Turtle ((%), (</>), (<.>))
99
import Prelude hiding (putStrLn, take)
1010
import qualified Data.Text as T
@@ -43,7 +43,7 @@ inputFilePattern = Turtle.contains (Turtle.once (Turtle.oneOf pathSeparators) <>
4343

4444
importCSVs' :: RuntimeOptions -> TChan FlowTypes.LogMessage -> IO [(TurtlePath, FileWasGenerated)]
4545
importCSVs' opts ch = do
46-
let effectiveDir = effectiveRunDir (baseDir opts) (importRunDir opts) (useRunDir opts)
46+
let effectiveDir = effectiveRunDir (baseDir opts) (importRunDir opts)
4747
channelOutLn ch $ Turtle.format ("Collecting input files from "%Turtle.fp) $ pathToTurtle effectiveDir
4848
(inputFiles, diff) <- Turtle.time $ Turtle.single . shellToList . onlyFiles $ Turtle.find inputFilePattern (pathToTurtle effectiveDir)
4949
let fileCount = length inputFiles

src/Hledger/Flow/PathHelpers.hs

+9-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import Control.Monad.Catch (MonadThrow, Exception, throwM)
66
import Control.Monad.IO.Class (MonadIO)
77

88
import qualified Data.Text as T
9-
import qualified Path as Path
9+
import qualified Path
1010
import qualified Path.IO as Path
11-
import qualified Turtle as Turtle
11+
import qualified Turtle
1212

1313
import Hledger.Flow.DocHelpers (docURL)
1414

@@ -27,7 +27,7 @@ instance Show PathException where
2727
" (or in any of its parent directories).\n\n" ++
2828
"Have a look at the documentation for more information:\n" ++
2929
T.unpack (docURL "getting-started")
30-
show (InvalidTurtleDir d) = "Expected a directory but got this instead: " ++ Turtle.encodeString d
30+
show (InvalidTurtleDir d) = "Expected a directory but got this instead: " ++ Turtle.encodeString d
3131

3232
instance Exception PathException
3333

@@ -55,3 +55,9 @@ pathToTurtle = Turtle.decodeString . Path.toFilePath
5555

5656
forceTrailingSlash :: TurtlePath -> TurtlePath
5757
forceTrailingSlash p = Turtle.directory (p Turtle.</> "temp")
58+
59+
pathSize :: Path.Path b Path.Dir -> Int
60+
pathSize p = pathSize' p 0
61+
62+
pathSize' :: Path.Path b Path.Dir -> Int -> Int
63+
pathSize' p count = if Path.parent p == p then count else pathSize' (Path.parent p) (count+1)

src/Hledger/Flow/RuntimeOptions.hs

+4-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import Hledger.Flow.Types
77

88
data RuntimeOptions = RuntimeOptions { baseDir :: BaseDir
99
, importRunDir :: RunDir
10-
, useRunDir :: Bool
1110
, onlyNewFiles :: Bool
1211
, hfVersion :: T.Text
1312
, hledgerInfo :: HledgerInfo
@@ -19,13 +18,13 @@ data RuntimeOptions = RuntimeOptions { baseDir :: BaseDir
1918
deriving (Show)
2019

2120
instance HasVerbosity RuntimeOptions where
22-
verbose (RuntimeOptions _ _ _ _ _ _ _ v _ _) = v
21+
verbose (RuntimeOptions _ _ _ _ _ _ v _ _) = v
2322

2423
instance HasSequential RuntimeOptions where
25-
sequential (RuntimeOptions _ _ _ _ _ _ _ _ _ sq) = sq
24+
sequential (RuntimeOptions _ _ _ _ _ _ _ _ sq) = sq
2625

2726
instance HasBaseDir RuntimeOptions where
28-
baseDir (RuntimeOptions bd _ _ _ _ _ _ _ _ _) = bd
27+
baseDir (RuntimeOptions bd _ _ _ _ _ _ _ _) = bd
2928

3029
instance HasRunDir RuntimeOptions where
31-
importRunDir (RuntimeOptions _ rd _ _ _ _ _ _ _ _) = rd
30+
importRunDir (RuntimeOptions _ rd _ _ _ _ _ _ _) = rd

stack-8.2.2.yaml

-3
This file was deleted.

stack-8.4.4.yaml

-3
This file was deleted.

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-08-27
21+
resolver: nightly-2020-09-02
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: 523649
10-
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2020/8/27.yaml
11-
sha256: 95012648db1b2180e3d7e0a3d44bc2a61b6854028382674724fee7ffa24300c3
12-
original: nightly-2020-08-27
9+
size: 527775
10+
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2020/9/2.yaml
11+
sha256: 92ec78ae38830f06ec9307c5ce346ae93982fab6179eb10dec9d57d5069c7f14
12+
original: nightly-2020-09-02

test/BaseDir/Integration.hs

+41-11
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Test.HUnit
1010
import Path
1111
import Path.IO
1212

13-
import qualified Turtle as Turtle
13+
import qualified Turtle
1414
import qualified Data.Text as T
1515

1616
import Hledger.Flow.Common
@@ -20,8 +20,7 @@ import Hledger.Flow.PathHelpers
2020

2121
assertSubDirsForDetermineBaseDir :: AbsDir -> BaseDir -> [Path.Path b Dir] -> IO ()
2222
assertSubDirsForDetermineBaseDir initialPwd expectedBaseDir importDirs = do
23-
_ <- sequence $ map (assertDetermineBaseDir initialPwd expectedBaseDir) importDirs
24-
return ()
23+
sequence_ $ map (assertDetermineBaseDir initialPwd expectedBaseDir) importDirs
2524

2625
assertDetermineBaseDir :: AbsDir -> BaseDir -> Path.Path b Dir -> IO ()
2726
assertDetermineBaseDir initialPwd expectedBaseDir subDir = do
@@ -40,9 +39,8 @@ assertDetermineBaseDir initialPwd expectedBaseDir subDir = do
4039
assertFindTestFileUsingRundir bd4 runDir4
4140

4241
setCurrentDir initialPwd
43-
let msg dir = "determineBaseDir searches from pwd upwards until it finds a dir containing 'import' - " ++ (show dir)
44-
_ <- sequence $ map (\dir -> assertEqual (msg dir) expectedBaseDir dir) [bd1, bd2, bd3, bd4]
45-
return ()
42+
let msg dir = "determineBaseDir searches from pwd upwards until it finds a dir containing 'import' - " ++ show dir
43+
sequence_ $ map (\ dir -> assertEqual (msg dir) expectedBaseDir dir) [bd1, bd2, bd3, bd4]
4644

4745
assertFindTestFileUsingRundir :: BaseDir -> RunDir -> IO ()
4846
assertFindTestFileUsingRundir baseDir runDir = do
@@ -62,10 +60,9 @@ assertCurrentDirVariations absoluteTempDir bdRelativeToTempDir = do
6260
(bd3, runDir3) <- determineBaseDir $ Just "./"
6361
(bd4, runDir4) <- determineBaseDir $ Just $ pathToTurtle absBaseDir
6462

65-
let msg label dir = "When pwd is the base dir, determineBaseDir returns the same " ++ label ++ ", regardless of the input variation. " ++ (show dir)
66-
_ <- sequence $ map (\dir -> assertEqual (msg "baseDir" dir) absBaseDir dir) [bd1, bd2, bd3, bd4]
67-
_ <- sequence $ map (\dir -> assertEqual (msg "runDir" dir) [reldir|.|] dir) [runDir1, runDir2, runDir3, runDir4]
68-
return ()
63+
let msg label dir = "When pwd is the base dir, determineBaseDir returns the same " ++ label ++ ", regardless of the input variation. " ++ show dir
64+
sequence_ $ map (\ dir -> assertEqual (msg "baseDir" dir) absBaseDir dir) [bd1, bd2, bd3, bd4]
65+
sequence_ $ map (\dir -> assertEqual (msg "runDir" dir) [reldir|.|] dir) [runDir1, runDir2, runDir3, runDir4]
6966

7067
testBaseDirWithTempDir :: AbsDir -> AbsDir -> IO ()
7168
testBaseDirWithTempDir initialPwd absoluteTempDir = do
@@ -108,6 +105,39 @@ testBaseDirWithTempDir initialPwd absoluteTempDir = do
108105
assertSubDirsForDetermineBaseDir initialPwd absoluteBaseDir subDirsRelativeToTop
109106
return ()
110107

108+
assertRunDirs :: RelDir -> [RelDir] -> [RelDir] -> IO ()
109+
assertRunDirs accDir businessAsUsualRundirs specialTreatmentRundirs = do
110+
sequence_ $ map (assertRunDir id "Normal rundirs should not be modified") businessAsUsualRundirs
111+
sequence_ $ map (assertRunDir (\_ -> accDir) "Rundirs deeper than account-level should return the account dir instead") specialTreatmentRundirs
112+
113+
assertRunDir :: (RelDir -> RelDir) -> String -> RelDir -> IO ()
114+
assertRunDir expectedRunDir msg subDir = do
115+
(_, runDir) <- determineBaseDir $ Just $ pathToTurtle subDir
116+
assertEqual msg (expectedRunDir subDir) runDir
117+
118+
testRunDirsWithTempDir :: AbsDir -> IO ()
119+
testRunDirsWithTempDir absoluteTempDir = do
120+
let baseDir = absoluteTempDir </> [reldir|bd1|]
121+
122+
let importDir = [reldir|import|]
123+
let ownerDir = importDir </> [reldir|john|]
124+
let bankDir = ownerDir </> [reldir|mybank|]
125+
let accDir = bankDir </> [reldir|myacc|]
126+
let inDir = accDir </> [reldir|1-in|]
127+
let yearDir = inDir </> [reldir|2019|]
128+
129+
createDirIfMissing True $ baseDir </> yearDir
130+
131+
withCurrentDir baseDir $ assertRunDirs accDir [accDir, bankDir, ownerDir, importDir] [yearDir, inDir]
132+
133+
testRunDirs :: Test
134+
testRunDirs = TestCase (
135+
do
136+
initialPwd <- getCurrentDir
137+
let tmpbase = initialPwd </> [reldir|test|] </> [reldir|tmp|]
138+
withTempDir tmpbase "hlflowtest" testRunDirsWithTempDir
139+
)
140+
111141
testDetermineBaseDir :: Test
112142
testDetermineBaseDir = TestCase (
113143
do
@@ -118,4 +148,4 @@ testDetermineBaseDir = TestCase (
118148
)
119149

120150
tests :: Test
121-
tests = TestList [testDetermineBaseDir]
151+
tests = TestList [testDetermineBaseDir, testRunDirs]

test/PathHelpers/Unit.hs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{-# LANGUAGE QuasiQuotes #-}
2+
3+
module PathHelpers.Unit where
4+
5+
import Test.HUnit
6+
import Path
7+
import Hledger.Flow.PathHelpers
8+
9+
testPathSize :: Test
10+
testPathSize = TestCase (
11+
do
12+
let d0 = [reldir|.|]
13+
let d1 = [reldir|d1|]
14+
let d1ond0 = d0 </> [reldir|d1|]
15+
let d2 = d1 </> [reldir|d2|]
16+
let d3 = d2 </> [reldir|d3|]
17+
assertEqual "Calculate the path size correctly" 0 (pathSize d0)
18+
assertEqual "Calculate the path size correctly" 1 (pathSize d1)
19+
assertEqual "Calculate the path size correctly" 1 (pathSize d1ond0)
20+
assertEqual "Calculate the path size correctly" 2 (pathSize d2)
21+
assertEqual "Calculate the path size correctly" 3 (pathSize d3)
22+
)
23+
24+
tests :: Test
25+
tests = TestList [testPathSize]

0 commit comments

Comments
 (0)