packages feed

hledger-flow 0.11.1.0 → 0.11.1.1

raw patch · 10 files changed

+109/−28 lines, 10 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Hledger.Flow.Common: errExit :: Int -> TChan LogMessage -> Text -> a -> IO a
+ Hledger.Flow.Import.Types: [showOptions] :: ImportOptions -> Bool
+ Hledger.Flow.Report.Types: [showOptions] :: ReportOptions -> Bool
+ Hledger.Flow.Types: instance Hledger.Flow.Types.HasExitCode Hledger.Flow.Types.FullOutput
+ Hledger.Flow.Types: type FullOutput = (ExitCode, Text, Text)
+ Hledger.Flow.Types: type FullTimedOutput = (FullOutput, NominalDiffTime)
- Hledger.Flow.Import.Types: ImportOptions :: FilePath -> Bool -> Bool -> ImportOptions
+ Hledger.Flow.Import.Types: ImportOptions :: FilePath -> Bool -> Bool -> Bool -> ImportOptions
- Hledger.Flow.Report.Types: ReportOptions :: FilePath -> Bool -> Bool -> ReportOptions
+ Hledger.Flow.Report.Types: ReportOptions :: FilePath -> Bool -> Bool -> Bool -> ReportOptions

Files

ChangeLog.md view
@@ -1,12 +1,18 @@ # Changelog for [hledger-flow](https://github.com/apauley/hledger-flow) +## 0.11.1.1++- Support input files from the year 2011 - https://github.com/apauley/hledger-flow/issues/27+  Use a more specific input-file pattern, so as not to match 2011-include.journal+- Print command-line options if requested - https://github.com/apauley/hledger-flow/issues/11+- Use the channel output functions consistently to avoid concurrency issues.+ ## 0.11.1  - Create statically linked executables on Linux - https://github.com/apauley/hledger-flow/releases - Add an option to disable parallel processing - Log the exit status of shell commands. - Upgrade to LTS 13.16 for GHC 8.6.4.-  ## 0.11 
app/Main.hs view
@@ -14,7 +14,7 @@ import Hledger.Flow.Reports import Hledger.Flow.CSVImport -type SubcommandParams = (Maybe FilePath, Bool, Bool)+type SubcommandParams = (Maybe FilePath, Bool, Bool, Bool) data Command = Version (Maybe Text) | Import SubcommandParams | Report SubcommandParams deriving (Show)  main :: IO ()@@ -26,14 +26,20 @@     Report subParams -> toReportOptions subParams >>= generateReports  toImportOptions :: SubcommandParams -> IO IT.ImportOptions-toImportOptions (maybeBaseDir, verbose, sequential) = do+toImportOptions (maybeBaseDir, verbose, showOpts, sequential) = do   bd <- dirOrPwd maybeBaseDir-  return IT.ImportOptions {IT.baseDir = bd, IT.verbose = verbose, IT.sequential = sequential}+  return IT.ImportOptions { IT.baseDir = bd+                          , IT.verbose = verbose+                          , IT.showOptions = showOpts+                          , IT.sequential = sequential }  toReportOptions :: SubcommandParams -> IO RT.ReportOptions-toReportOptions (maybeBaseDir, verbose, sequential) = do+toReportOptions (maybeBaseDir, verbose, showOpts, sequential) = do   bd <- dirOrPwd maybeBaseDir-  return RT.ReportOptions {RT.baseDir = bd, RT.verbose = verbose, RT.sequential = sequential}+  return RT.ReportOptions { RT.baseDir = bd+                          , RT.verbose = verbose+                          , RT.showOptions = showOpts+                          , RT.sequential = sequential }  parser :: Parser Command parser = fmap Import (subcommand "import" "Converts CSV transactions into categorised journal files" subcommandParser)@@ -41,9 +47,10 @@   <|> fmap Version (subcommand "version" "Display version information" noArgs)  subcommandParser :: Parser SubcommandParams-subcommandParser = (,,)+subcommandParser = (,,,)   <$> optional (argPath "basedir" "The hledger-flow base directory")   <*> switch (long "verbose" <> short 'v' <> help "Print more verbose output")+  <*> switch (long "show-options" <> help "Print the options this program will run with")   <*> switch (long "sequential" <> help "Disable parallel processing")  noArgs :: Parser (Maybe Text)
hledger-flow.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: dff74bf821d03440baa4b4f2c9e36afa00f7ce580680c1c94e598f56f06bf1bd+-- hash: f8d8a671bb403b7d049e7310c834a0396e836ca37eb0c7717ccb48a15691213d  name:           hledger-flow-version:        0.11.1.0+version:        0.11.1.1 synopsis:       An hledger workflow focusing on automated statement import and classification. description:    Please see the README on GitHub at <https://github.com/apauley/hledger-flow#readme> category:       Finance, Console
src/Hledger/Flow/CSVImport.hs view
@@ -18,6 +18,7 @@   do     ch <- liftIO newTChanIO     logHandle <- fork $ consoleChannelLoop ch+    liftIO $ if (showOptions opts) then channelOut ch (repr opts) else return ()     liftIO $ logVerbose opts ch "Starting import"     (journals, diff) <- time $ liftIO $ importCSVs' opts ch     liftIO $ channelOut ch $ format ("Imported "%d%" journals in "%s) (length journals) $ repr diff@@ -25,18 +26,23 @@     wait logHandle   ) +pathSeparators :: [Char]+pathSeparators = ['/', '\\', ':']++inputFilePattern :: Pattern Text+inputFilePattern = contains (once (oneOf pathSeparators) <> asciiCI "1-in" <> once (oneOf pathSeparators) <> plus digit <> once (oneOf pathSeparators))+ importCSVs' :: ImportOptions -> TChan LogMessage -> IO [FilePath] importCSVs' opts ch = do   channelOut ch "Collecting input files..."-  (inputFiles, diff) <- time $ single . shellToList . onlyFiles $ find (has (suffix "1-in")) $ baseDir opts+  (inputFiles, diff) <- time $ single . shellToList . onlyFiles $ find inputFilePattern $ baseDir opts   let fileCount = length inputFiles   if (fileCount == 0) then     do       let msg = format ("I couldn't find any input files underneath "%fp                         %"\n\nhledger-makitso expects to find its input files in specifically\nnamed directories.\n\n"%                         "Have a look at the documentation for a detailed explanation:\n"%s) (dirname (baseDir opts) </> "import/") (docURL "input-files")-      stderr $ select $ textToLines msg-      exit $ ExitFailure 1+      errExit 1 ch msg []     else     do       channelOut ch $ format ("Found "%d%" input files in "%s%". Proceeding with import...") fileCount (repr diff)@@ -50,8 +56,7 @@   case extractImportDirs inputFile of     Right importDirs -> importCSV opts ch importDirs inputFile     Left errorMessage -> do-      stderr $ select $ textToLines errorMessage-      exit $ ExitFailure 1+      errExit 1 ch errorMessage inputFile  importCSV :: ImportOptions -> TChan LogMessage -> ImportDirs -> FilePath -> IO FilePath importCSV opts ch importDirs srcFile = do@@ -93,8 +98,7 @@   case extractImportDirs csvSrc of     Right importDirs -> hledgerImport' opts ch importDirs csvSrc journalOut     Left errorMessage -> do-      stderr $ select $ textToLines errorMessage-      exit $ ExitFailure 1+      errExit 1 ch errorMessage csvSrc  hledgerImport' :: ImportOptions -> TChan LogMessage -> ImportDirs -> FilePath -> FilePath -> IO FilePath hledgerImport' opts ch importDirs csvSrc journalOut = do@@ -116,8 +120,7 @@                           %"\n\nI will happily use the first rules file I can find from any one of these "%d%" files:\n"%s                           %"\n\nHere is a bit of documentation about rules files that you may find helpful:\n"%s)                   relCSV (length candidates) candidatesTxt (docURL "rules-files")-        stderr $ select $ textToLines msg-        exit $ ExitFailure 1+        errExit 1 ch msg csvSrc  rulesFileCandidates :: FilePath -> ImportDirs -> [FilePath] rulesFileCandidates csvSrc importDirs = statementSpecificRulesFiles csvSrc importDirs ++ generalRulesFiles importDirs
src/Hledger/Flow/Common.hs view
@@ -8,6 +8,7 @@     , terminateChannelLoop     , channelOut     , channelErr+    , errExit     , logVerbose     , logVerboseTime     , verboseTestFile@@ -76,6 +77,13 @@  channelErr :: TChan LogMessage -> Text -> IO () channelErr ch txt = atomically $ writeTChan ch $ StdErr txt++errExit :: Int -> TChan LogMessage -> Text -> a -> IO a+errExit exitStatus ch errorMessage dummyReturnValue = do+  channelErr ch errorMessage+  sleep 0.1+  _ <- exit $ ExitFailure exitStatus+  return dummyReturnValue  timestampPrefix :: Text -> IO Text timestampPrefix txt = do
src/Hledger/Flow/Import/Types.hs view
@@ -5,14 +5,17 @@ import Prelude hiding (FilePath, putStrLn) import Hledger.Flow.Types -data ImportOptions = ImportOptions { baseDir :: FilePath, verbose :: Bool, sequential :: Bool }+data ImportOptions = ImportOptions { baseDir :: FilePath+                                   , verbose :: Bool+                                   , showOptions :: Bool+                                   , sequential :: Bool }   deriving (Show)  instance HasVerbosity ImportOptions where-  verbose (ImportOptions _ v _) = v+  verbose (ImportOptions _ v _ _) = v  instance HasBaseDir ImportOptions where-  baseDir (ImportOptions bd _ _) = bd+  baseDir (ImportOptions bd _ _ _) = bd  data ImportDirs = ImportDirs { importDir  :: FilePath                              , ownerDir   :: FilePath
src/Hledger/Flow/Report/Types.hs view
@@ -5,11 +5,14 @@ import Prelude hiding (FilePath, putStrLn) import Hledger.Flow.Types -data ReportOptions = ReportOptions { baseDir :: FilePath, verbose :: Bool, sequential :: Bool }+data ReportOptions = ReportOptions { baseDir :: FilePath+                                   , verbose :: Bool+                                   , showOptions :: Bool+                                   , sequential :: Bool }   deriving (Show)  instance HasVerbosity ReportOptions where-  verbose (ReportOptions _ v _) = v+  verbose (ReportOptions _ v _ _) = v  instance HasBaseDir ReportOptions where-  baseDir (ReportOptions bd _ _) = bd+  baseDir (ReportOptions bd _ _ _) = bd
src/Hledger/Flow/Reports.hs view
@@ -5,8 +5,9 @@     ) where  import Turtle-import Prelude hiding (FilePath, putStrLn)-import Hledger.Flow.Types+import Prelude hiding (FilePath, putStrLn, writeFile)+import qualified Data.Text as T+import Hledger.Flow.Types (LogMessage, FullTimedOutput) import Hledger.Flow.Report.Types import Hledger.Flow.Common import Control.Concurrent.STM@@ -16,6 +17,7 @@   do     ch <- liftIO newTChanIO     logHandle <- fork $ consoleChannelLoop ch+    liftIO $ if (showOptions opts) then channelOut ch (repr opts) else return ()     (reports, diff) <- time $ liftIO $ generateReports' opts ch     liftIO $ channelOut ch $ format ("Generated "%d%" reports in "%s) (length reports) $ repr diff     liftIO $ terminateChannelLoop ch@@ -26,4 +28,45 @@ generateReports' opts ch = do   logVerbose opts ch "Something will be here Real Soon Now (tm)"   channelOut ch "Report generation has not been implemented. Yet. https://github.com/apauley/hledger-flow/pull/4"-  return []+  ownerReports opts ch "everyone"++ownerReports :: ReportOptions -> TChan LogMessage -> Text -> IO [FilePath]+ownerReports opts ch owner = do+  let journal = (baseDir opts) </> "all-years" <.> "journal"+  let reportsDir = (baseDir opts) </> "reports" </> fromText owner+  let actions = map (\r -> r opts ch journal reportsDir) [accountList, incomeStatement]+  results <- if (sequential opts) then sequence actions else single $ shellToList $ parallel actions+  return $ map fst results++incomeStatement :: ReportOptions -> TChan LogMessage -> FilePath -> FilePath -> IO (FilePath, FullTimedOutput)+incomeStatement opts ch journal reportsDir = do+  mktree reportsDir+  let outputFile = reportsDir </> "income-expenses" <.> "txt"+  let sharedOptions = ["--depth", "2", "--pretty-tables", "not:equity"]+  let reportArgs = ["incomestatement"] ++ sharedOptions ++ ["--average", "--yearly"]+  generateReport' opts ch journal outputFile reportArgs++accountList :: ReportOptions -> TChan LogMessage -> FilePath -> FilePath -> IO (FilePath, FullTimedOutput)+accountList opts ch journal reportsDir = do+  let outputFile = reportsDir </> "accounts" <.> "txt"+  let reportArgs = ["accounts"]+  generateReport' opts ch journal outputFile reportArgs++generateReport' :: ReportOptions -> TChan LogMessage -> FilePath -> FilePath -> [Text] -> IO (FilePath, FullTimedOutput)+generateReport' opts ch journal outputFile args = do+  let reportsDir = directory outputFile+  mktree reportsDir+  let relativeJournal = relativeToBase opts journal+  let reportArgs = ["--file", format fp journal] ++ args+  let reportDisplayArgs = ["--file", format fp relativeJournal] ++ args+  let action = procStrictWithErr "hledger" reportArgs empty+  let cmd = format ("hledger "%s) $ showCmdArgs reportDisplayArgs+  result@((exitCode, stdOut, stdErr), _) <- logVerboseTime opts ch cmd action+  if not (T.null stdOut) then do+    writeTextFile outputFile (cmd <> "\n\n"<> stdOut)+    channelOut ch $ format ("Wrote "%fp) $ relativeToBase opts outputFile+    else channelErr ch $ format ("No report output for '"%s%"' "%s) cmd (repr exitCode)+  if not (T.null stdErr)+    then channelErr ch $ stdErr+    else return ()+  return (outputFile, result)
src/Hledger/Flow/Types.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+ module Hledger.Flow.Types where @@ -5,6 +8,8 @@ import Prelude hiding (FilePath, putStrLn)  data LogMessage = StdOut Text | StdErr Text | Terminate deriving (Show)+type FullOutput = (ExitCode, Text, Text)+type FullTimedOutput = (FullOutput, NominalDiffTime)  class HasVerbosity a where   verbose :: a -> Bool@@ -17,3 +22,6 @@  instance HasExitCode ExitCode where   exitCode c = c++instance HasExitCode FullOutput where+  exitCode (c, _, _) = c
test/TestHelpers.hs view
@@ -50,7 +50,7 @@ hiddenFiles = [".hiddenfile", "checking/.DS_Store", "import/john/bogartbank/savings/1-in/.anotherhiddenfile", "import/john/bogartbank/checking/1-in/2018/.hidden"] :: [FilePath]  defaultOpts :: FilePath -> ImportOptions-defaultOpts bd = ImportOptions bd False False+defaultOpts bd = ImportOptions bd False False False  toJournals :: [FilePath] -> [FilePath] toJournals = map (changePathAndExtension "3-journal" "journal")