Skip to content

Commit f2b9af4

Browse files
authored
Merge pull request #29 from apauley/support-year-2011
Support the year 2011
2 parents f211c27 + 6a1328f commit f2b9af4

File tree

10 files changed

+108
-27
lines changed

10 files changed

+108
-27
lines changed

ChangeLog.md

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

3+
## 0.11.1.1
4+
5+
- Support input files from the year 2011 - https://github.com/apauley/hledger-flow/issues/27
6+
Use a more specific input-file pattern, so as not to match 2011-include.journal
7+
- Print command-line options if requested - https://github.com/apauley/hledger-flow/issues/11
8+
- Use the channel output functions consistently to avoid concurrency issues.
9+
310
## 0.11.1
411

512
- Create statically linked executables on Linux - https://github.com/apauley/hledger-flow/releases
613
- Add an option to disable parallel processing
714
- Log the exit status of shell commands.
815
- Upgrade to LTS 13.16 for GHC 8.6.4.
916

10-
1117
## 0.11
1218

1319
- Change the name from `hledger-makeitso` to `hledger-flow`.

app/Main.hs

+13-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Hledger.Flow.Common
1414
import Hledger.Flow.Reports
1515
import Hledger.Flow.CSVImport
1616

17-
type SubcommandParams = (Maybe FilePath, Bool, Bool)
17+
type SubcommandParams = (Maybe FilePath, Bool, Bool, Bool)
1818
data Command = Version (Maybe Text) | Import SubcommandParams | Report SubcommandParams deriving (Show)
1919

2020
main :: IO ()
@@ -26,24 +26,31 @@ main = do
2626
Report subParams -> toReportOptions subParams >>= generateReports
2727

2828
toImportOptions :: SubcommandParams -> IO IT.ImportOptions
29-
toImportOptions (maybeBaseDir, verbose, sequential) = do
29+
toImportOptions (maybeBaseDir, verbose, showOpts, sequential) = do
3030
bd <- dirOrPwd maybeBaseDir
31-
return IT.ImportOptions {IT.baseDir = bd, IT.verbose = verbose, IT.sequential = sequential}
31+
return IT.ImportOptions { IT.baseDir = bd
32+
, IT.verbose = verbose
33+
, IT.showOptions = showOpts
34+
, IT.sequential = sequential }
3235

3336
toReportOptions :: SubcommandParams -> IO RT.ReportOptions
34-
toReportOptions (maybeBaseDir, verbose, sequential) = do
37+
toReportOptions (maybeBaseDir, verbose, showOpts, sequential) = do
3538
bd <- dirOrPwd maybeBaseDir
36-
return RT.ReportOptions {RT.baseDir = bd, RT.verbose = verbose, RT.sequential = sequential}
39+
return RT.ReportOptions { RT.baseDir = bd
40+
, RT.verbose = verbose
41+
, RT.showOptions = showOpts
42+
, RT.sequential = sequential }
3743

3844
parser :: Parser Command
3945
parser = fmap Import (subcommand "import" "Converts CSV transactions into categorised journal files" subcommandParser)
4046
<|> fmap Report (subcommand "report" "Generate Reports" subcommandParser)
4147
<|> fmap Version (subcommand "version" "Display version information" noArgs)
4248

4349
subcommandParser :: Parser SubcommandParams
44-
subcommandParser = (,,)
50+
subcommandParser = (,,,)
4551
<$> optional (argPath "basedir" "The hledger-flow base directory")
4652
<*> switch (long "verbose" <> short 'v' <> help "Print more verbose output")
53+
<*> switch (long "show-options" <> help "Print the options this program will run with")
4754
<*> switch (long "sequential" <> help "Disable parallel processing")
4855

4956
noArgs :: Parser (Maybe Text)

package.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: hledger-flow
2-
version: 0.11.1.0
2+
version: 0.11.1.1
33
synopsis: An hledger workflow focusing on automated statement import and classification.
44
category: Finance, Console
55
license: GPL-3

src/Hledger/Flow/CSVImport.hs

+12-9
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,31 @@ importCSVs opts = sh (
1818
do
1919
ch <- liftIO newTChanIO
2020
logHandle <- fork $ consoleChannelLoop ch
21+
liftIO $ if (showOptions opts) then channelOut ch (repr opts) else return ()
2122
liftIO $ logVerbose opts ch "Starting import"
2223
(journals, diff) <- time $ liftIO $ importCSVs' opts ch
2324
liftIO $ channelOut ch $ format ("Imported "%d%" journals in "%s) (length journals) $ repr diff
2425
liftIO $ terminateChannelLoop ch
2526
wait logHandle
2627
)
2728

29+
pathSeparators :: [Char]
30+
pathSeparators = ['/', '\\', ':']
31+
32+
inputFilePattern :: Pattern Text
33+
inputFilePattern = contains (once (oneOf pathSeparators) <> asciiCI "1-in" <> once (oneOf pathSeparators) <> plus digit <> once (oneOf pathSeparators))
34+
2835
importCSVs' :: ImportOptions -> TChan LogMessage -> IO [FilePath]
2936
importCSVs' opts ch = do
3037
channelOut ch "Collecting input files..."
31-
(inputFiles, diff) <- time $ single . shellToList . onlyFiles $ find (has (suffix "1-in")) $ baseDir opts
38+
(inputFiles, diff) <- time $ single . shellToList . onlyFiles $ find inputFilePattern $ baseDir opts
3239
let fileCount = length inputFiles
3340
if (fileCount == 0) then
3441
do
3542
let msg = format ("I couldn't find any input files underneath "%fp
3643
%"\n\nhledger-makitso expects to find its input files in specifically\nnamed directories.\n\n"%
3744
"Have a look at the documentation for a detailed explanation:\n"%s) (dirname (baseDir opts) </> "import/") (docURL "input-files")
38-
stderr $ select $ textToLines msg
39-
exit $ ExitFailure 1
45+
errExit 1 ch msg []
4046
else
4147
do
4248
channelOut ch $ format ("Found "%d%" input files in "%s%". Proceeding with import...") fileCount (repr diff)
@@ -50,8 +56,7 @@ extractAndImport opts ch inputFile = do
5056
case extractImportDirs inputFile of
5157
Right importDirs -> importCSV opts ch importDirs inputFile
5258
Left errorMessage -> do
53-
stderr $ select $ textToLines errorMessage
54-
exit $ ExitFailure 1
59+
errExit 1 ch errorMessage inputFile
5560

5661
importCSV :: ImportOptions -> TChan LogMessage -> ImportDirs -> FilePath -> IO FilePath
5762
importCSV opts ch importDirs srcFile = do
@@ -93,8 +98,7 @@ hledgerImport opts ch csvSrc journalOut = do
9398
case extractImportDirs csvSrc of
9499
Right importDirs -> hledgerImport' opts ch importDirs csvSrc journalOut
95100
Left errorMessage -> do
96-
stderr $ select $ textToLines errorMessage
97-
exit $ ExitFailure 1
101+
errExit 1 ch errorMessage csvSrc
98102

99103
hledgerImport' :: ImportOptions -> TChan LogMessage -> ImportDirs -> FilePath -> FilePath -> IO FilePath
100104
hledgerImport' opts ch importDirs csvSrc journalOut = do
@@ -116,8 +120,7 @@ hledgerImport' opts ch importDirs csvSrc journalOut = do
116120
%"\n\nI will happily use the first rules file I can find from any one of these "%d%" files:\n"%s
117121
%"\n\nHere is a bit of documentation about rules files that you may find helpful:\n"%s)
118122
relCSV (length candidates) candidatesTxt (docURL "rules-files")
119-
stderr $ select $ textToLines msg
120-
exit $ ExitFailure 1
123+
errExit 1 ch msg csvSrc
121124

122125
rulesFileCandidates :: FilePath -> ImportDirs -> [FilePath]
123126
rulesFileCandidates csvSrc importDirs = statementSpecificRulesFiles csvSrc importDirs ++ generalRulesFiles importDirs

src/Hledger/Flow/Common.hs

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module Hledger.Flow.Common
88
, terminateChannelLoop
99
, channelOut
1010
, channelErr
11+
, errExit
1112
, logVerbose
1213
, logVerboseTime
1314
, verboseTestFile
@@ -77,6 +78,13 @@ channelOut ch txt = atomically $ writeTChan ch $ StdOut txt
7778
channelErr :: TChan LogMessage -> Text -> IO ()
7879
channelErr ch txt = atomically $ writeTChan ch $ StdErr txt
7980

81+
errExit :: Int -> TChan LogMessage -> Text -> a -> IO a
82+
errExit exitStatus ch errorMessage dummyReturnValue = do
83+
channelErr ch errorMessage
84+
sleep 0.1
85+
_ <- exit $ ExitFailure exitStatus
86+
return dummyReturnValue
87+
8088
timestampPrefix :: Text -> IO Text
8189
timestampPrefix txt = do
8290
t <- getZonedTime

src/Hledger/Flow/Import/Types.hs

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ import Turtle
55
import Prelude hiding (FilePath, putStrLn)
66
import Hledger.Flow.Types
77

8-
data ImportOptions = ImportOptions { baseDir :: FilePath, verbose :: Bool, sequential :: Bool }
8+
data ImportOptions = ImportOptions { baseDir :: FilePath
9+
, verbose :: Bool
10+
, showOptions :: Bool
11+
, sequential :: Bool }
912
deriving (Show)
1013

1114
instance HasVerbosity ImportOptions where
12-
verbose (ImportOptions _ v _) = v
15+
verbose (ImportOptions _ v _ _) = v
1316

1417
instance HasBaseDir ImportOptions where
15-
baseDir (ImportOptions bd _ _) = bd
18+
baseDir (ImportOptions bd _ _ _) = bd
1619

1720
data ImportDirs = ImportDirs { importDir :: FilePath
1821
, ownerDir :: FilePath

src/Hledger/Flow/Report/Types.hs

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import Turtle
55
import Prelude hiding (FilePath, putStrLn)
66
import Hledger.Flow.Types
77

8-
data ReportOptions = ReportOptions { baseDir :: FilePath, verbose :: Bool, sequential :: Bool }
8+
data ReportOptions = ReportOptions { baseDir :: FilePath
9+
, verbose :: Bool
10+
, showOptions :: Bool
11+
, sequential :: Bool }
912
deriving (Show)
1013

1114
instance HasVerbosity ReportOptions where
12-
verbose (ReportOptions _ v _) = v
15+
verbose (ReportOptions _ v _ _) = v
1316

1417
instance HasBaseDir ReportOptions where
15-
baseDir (ReportOptions bd _ _) = bd
18+
baseDir (ReportOptions bd _ _ _) = bd

src/Hledger/Flow/Reports.hs

+46-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ module Hledger.Flow.Reports
55
) where
66

77
import Turtle
8-
import Prelude hiding (FilePath, putStrLn)
9-
import Hledger.Flow.Types
8+
import Prelude hiding (FilePath, putStrLn, writeFile)
9+
import qualified Data.Text as T
10+
import Hledger.Flow.Types (LogMessage, FullTimedOutput)
1011
import Hledger.Flow.Report.Types
1112
import Hledger.Flow.Common
1213
import Control.Concurrent.STM
@@ -16,6 +17,7 @@ generateReports opts = sh (
1617
do
1718
ch <- liftIO newTChanIO
1819
logHandle <- fork $ consoleChannelLoop ch
20+
liftIO $ if (showOptions opts) then channelOut ch (repr opts) else return ()
1921
(reports, diff) <- time $ liftIO $ generateReports' opts ch
2022
liftIO $ channelOut ch $ format ("Generated "%d%" reports in "%s) (length reports) $ repr diff
2123
liftIO $ terminateChannelLoop ch
@@ -26,4 +28,45 @@ generateReports' :: ReportOptions -> TChan LogMessage -> IO [FilePath]
2628
generateReports' opts ch = do
2729
logVerbose opts ch "Something will be here Real Soon Now (tm)"
2830
channelOut ch "Report generation has not been implemented. Yet. https://github.com/apauley/hledger-flow/pull/4"
29-
return []
31+
ownerReports opts ch "everyone"
32+
33+
ownerReports :: ReportOptions -> TChan LogMessage -> Text -> IO [FilePath]
34+
ownerReports opts ch owner = do
35+
let journal = (baseDir opts) </> "all-years" <.> "journal"
36+
let reportsDir = (baseDir opts) </> "reports" </> fromText owner
37+
let actions = map (\r -> r opts ch journal reportsDir) [accountList, incomeStatement]
38+
results <- if (sequential opts) then sequence actions else single $ shellToList $ parallel actions
39+
return $ map fst results
40+
41+
incomeStatement :: ReportOptions -> TChan LogMessage -> FilePath -> FilePath -> IO (FilePath, FullTimedOutput)
42+
incomeStatement opts ch journal reportsDir = do
43+
mktree reportsDir
44+
let outputFile = reportsDir </> "income-expenses" <.> "txt"
45+
let sharedOptions = ["--depth", "2", "--pretty-tables", "not:equity"]
46+
let reportArgs = ["incomestatement"] ++ sharedOptions ++ ["--average", "--yearly"]
47+
generateReport' opts ch journal outputFile reportArgs
48+
49+
accountList :: ReportOptions -> TChan LogMessage -> FilePath -> FilePath -> IO (FilePath, FullTimedOutput)
50+
accountList opts ch journal reportsDir = do
51+
let outputFile = reportsDir </> "accounts" <.> "txt"
52+
let reportArgs = ["accounts"]
53+
generateReport' opts ch journal outputFile reportArgs
54+
55+
generateReport' :: ReportOptions -> TChan LogMessage -> FilePath -> FilePath -> [Text] -> IO (FilePath, FullTimedOutput)
56+
generateReport' opts ch journal outputFile args = do
57+
let reportsDir = directory outputFile
58+
mktree reportsDir
59+
let relativeJournal = relativeToBase opts journal
60+
let reportArgs = ["--file", format fp journal] ++ args
61+
let reportDisplayArgs = ["--file", format fp relativeJournal] ++ args
62+
let action = procStrictWithErr "hledger" reportArgs empty
63+
let cmd = format ("hledger "%s) $ showCmdArgs reportDisplayArgs
64+
result@((exitCode, stdOut, stdErr), _) <- logVerboseTime opts ch cmd action
65+
if not (T.null stdOut) then do
66+
writeTextFile outputFile (cmd <> "\n\n"<> stdOut)
67+
channelOut ch $ format ("Wrote "%fp) $ relativeToBase opts outputFile
68+
else channelErr ch $ format ("No report output for '"%s%"' "%s) cmd (repr exitCode)
69+
if not (T.null stdErr)
70+
then channelErr ch $ stdErr
71+
else return ()
72+
return (outputFile, result)

src/Hledger/Flow/Types.hs

+8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
{-# LANGUAGE TypeSynonymInstances #-}
2+
{-# LANGUAGE FlexibleInstances #-}
3+
14
module Hledger.Flow.Types
25
where
36

47
import Turtle
58
import Prelude hiding (FilePath, putStrLn)
69

710
data LogMessage = StdOut Text | StdErr Text | Terminate deriving (Show)
11+
type FullOutput = (ExitCode, Text, Text)
12+
type FullTimedOutput = (FullOutput, NominalDiffTime)
813

914
class HasVerbosity a where
1015
verbose :: a -> Bool
@@ -17,3 +22,6 @@ class HasExitCode a where
1722

1823
instance HasExitCode ExitCode where
1924
exitCode c = c
25+
26+
instance HasExitCode FullOutput where
27+
exitCode (c, _, _) = c

test/TestHelpers.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extraFiles = ["import/john/bogartbank/savings/2017-opening.journal"] :: [FilePat
5050
hiddenFiles = [".hiddenfile", "checking/.DS_Store", "import/john/bogartbank/savings/1-in/.anotherhiddenfile", "import/john/bogartbank/checking/1-in/2018/.hidden"] :: [FilePath]
5151

5252
defaultOpts :: FilePath -> ImportOptions
53-
defaultOpts bd = ImportOptions bd False False
53+
defaultOpts bd = ImportOptions bd False False False
5454

5555
toJournals :: [FilePath] -> [FilePath]
5656
toJournals = map (changePathAndExtension "3-journal" "journal")

0 commit comments

Comments
 (0)