Skip to content

Commit

Permalink
Merge pull request #34 from MoritzR/embed-data-files
Browse files Browse the repository at this point in the history
Bundle data files
  • Loading branch information
MoritzR authored Oct 15, 2023
2 parents 216aaec + aca82e5 commit afbd1fd
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
run: |
export ARTIFACTS=${{ runner.os }}/fints2ledger
mkdir -p ${ARTIFACTS}
cabal install --installdir="${ARTIFACTS}" --install-method=copy --enable-executable-static -fbundle_data_dir
cabal install --installdir="${ARTIFACTS}" --install-method=copy --enable-executable-static
strip "${ARTIFACTS}/fints2ledger"
cp -r data "${ARTIFACTS}"
Expand Down
File renamed without changes.
10 changes: 0 additions & 10 deletions app/bundled/Main.hs

This file was deleted.

25 changes: 9 additions & 16 deletions fints2ledger.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 1.12

-- This file has been generated from package.yaml by hpack version 0.35.1.
-- This file has been generated from package.yaml by hpack version 0.35.2.
--
-- see: https://github.com/sol/hpack

Expand Down Expand Up @@ -30,11 +30,6 @@ source-repository head
type: git
location: https://github.com/MoritzR/fints2ledger

flag bundle_data_dir
description: Set the data dir to be relative to the executable for easier bundling
manual: True
default: False

library
exposed-modules:
App
Expand Down Expand Up @@ -75,13 +70,15 @@ library
, cryptohash-md5 ==0.11.101.*
, dates >=0.2.3.2 && <0.2.4
, directory ==1.3.6.*
, file-embed ==0.0.15.*
, filepath ==1.4.2.*
, generic-lens ==2.2.2.*
, haskeline ==0.8.2.*
, hledger-lib ==1.27.1.*
, lens ==5.1.1
, optparse-applicative ==0.17.1.*
, regex-tdfa ==1.3.2.*
, temporary
, text ==1.2.5.*
, text-format-heavy ==0.1.5.3
, time ==1.11.1.*
Expand All @@ -96,6 +93,8 @@ executable fints2ledger
main-is: Main.hs
other-modules:
Paths_fints2ledger
hs-source-dirs:
app
default-extensions:
DuplicateRecordFields
OverloadedRecordDot
Expand All @@ -114,6 +113,7 @@ executable fints2ledger
, cryptohash-md5 ==0.11.101.*
, dates >=0.2.3.2 && <0.2.4
, directory ==1.3.6.*
, file-embed ==0.0.15.*
, filepath ==1.4.2.*
, fints2ledger
, generic-lens ==2.2.2.*
Expand All @@ -122,6 +122,7 @@ executable fints2ledger
, lens ==5.1.1
, optparse-applicative ==0.17.1.*
, regex-tdfa ==1.3.2.*
, temporary
, text ==1.2.5.*
, text-format-heavy ==0.1.5.3
, time ==1.11.1.*
Expand All @@ -131,16 +132,6 @@ executable fints2ledger
, vty ==5.37
, yaml ==0.11.11.*
default-language: GHC2021
if (flag(bundle_data_dir))
other-modules:
Main
hs-source-dirs:
app/bundled
else
other-modules:
Main
hs-source-dirs:
app/default

test-suite fints2ledger-test
type: exitcode-stdio-1.0
Expand Down Expand Up @@ -174,6 +165,7 @@ test-suite fints2ledger-test
, cryptohash-md5 ==0.11.101.*
, dates >=0.2.3.2 && <0.2.4
, directory ==1.3.6.*
, file-embed ==0.0.15.*
, filepath ==1.4.2.*
, fints2ledger
, generic-lens ==2.2.2.*
Expand All @@ -184,6 +176,7 @@ test-suite fints2ledger-test
, regex-tdfa ==1.3.2.*
, sydtest ==0.13.0.*
, sydtest-discover ==0.0.0.*
, temporary
, text ==1.2.5.*
, text-format-heavy ==0.1.5.3
, time ==1.11.1.*
Expand Down
15 changes: 3 additions & 12 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ dependencies:
- generic-lens >= 2.2.2 && < 2.2.3 # automatically generate lenses
- cassava >= 0.5.3 && < 0.5.4 # csv encoding/decoding
- vector >= 0.12.3 && < 0.12.4 # list-like type, used by cassava
- file-embed >= 0.0.15 && < 0.0.16 # for embedding files into the executable
- temporary # for creating temporary files

language: GHC2021

Expand Down Expand Up @@ -77,12 +79,7 @@ library:
executables:
fints2ledger:
main: Main.hs
when:
- condition: (flag(bundle_data_dir))
then:
source-dirs: app/bundled
else:
source-dirs: app/default
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
Expand All @@ -103,9 +100,3 @@ tests:
- sydtest >= 0.13.0 && < 0.13.1 # testing library
- sydtest-discover >= 0.0.0 && < 0.0.1 # automatic test discovery
- QuickCheck == 2.14.3 # property based testing library

flags:
bundle_data_dir:
description: Set the data dir to be relative to the executable for easier bundling
default: false
manual: true
22 changes: 18 additions & 4 deletions src/Config/Files.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE TemplateHaskell #-}

module Config.Files (getDefaultConfigDirectory, getConfigFilePath, getTemplatePath, ConfigDirectory (..)) where
module Config.Files (getDefaultConfigDirectory, getConfigFilePath, templateFile, exampleFile, pyfintsFile, ConfigDirectory (..)) where

import Data.ByteString (ByteString)
import Data.FileEmbed (embedDir)
import Data.Map (Map, fromList, (!))
import Data.String (IsString)
import Paths_fints2ledger (getDataFileName)
import Data.Text (Text)
import Data.Text.Encoding qualified as T
import System.Directory (getHomeDirectory)
import System.FilePath ((</>))

Expand All @@ -15,8 +20,17 @@ getDefaultConfigDirectory = do
getConfigFilePath :: ConfigDirectory -> FilePath
getConfigFilePath configDirectory = configDirectory.get </> "config.yml"

getTemplatePath :: IO FilePath
getTemplatePath = getDataFileName "data/template.txt"
templateFile :: Text
templateFile = T.decodeUtf8 $ dataFiles ! "template.txt"

exampleFile :: Text
exampleFile = T.decodeUtf8 $ dataFiles ! "example.json"

pyfintsFile :: Text
pyfintsFile = T.decodeUtf8 $ dataFiles ! "pyfints.py"

dataFiles :: Map FilePath ByteString
dataFiles = fromList ($(embedDir "data"))

newtype ConfigDirectory = ConfigDirectory {get :: FilePath}
deriving newtype (Show, IsString)
5 changes: 2 additions & 3 deletions src/Prompt.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module Prompt (transactionsToLedger) where

import App (App, Env (..), PromptResult (..), printText)
import Config.AppConfig (AppConfig (..))
import Config.Files (getTemplatePath)
import Config.YamlConfig (Fill, Filling (..), LedgerConfig (..))
import Control.Arrow ((>>>))
import Control.Monad (forM_, when)
Expand All @@ -26,6 +25,7 @@ import Text.Regex.TDFA (AllTextMatches (getAllTextMatches), (=~))
import Transactions (Amount (..), Transaction (..))
import Utils (calculateMd5Value, formatDouble, toLazyTemplateMap)
import Prelude hiding (appendFile, putStrLn, readFile)
import Config.Files (templateFile)

{- | A Map of key/value pairs that will be used to fill the template file
We fill these values step by step by:
Expand All @@ -40,14 +40,13 @@ transactionsToLedger transactions = do
config <- asks (.config)
readFile <- asks (.readFile)
existingMd5Sums <- getExistingMd5Sums <$> liftIO (readFile config.journalFile)
template <- liftIO $ readFile =<< getTemplatePath

printText " Controls:"
printText " - Ctrl + D or enter 's' to skip an entry"
printText " - Ctrl + C to abort"

forM_ transactions do
transactionToLedger existingMd5Sums template
transactionToLedger existingMd5Sums templateFile

transactionToLedger :: Set Text -> Text -> Transaction -> App ()
transactionToLedger existingMd5Sums template transaction = do
Expand Down
33 changes: 19 additions & 14 deletions src/Transactions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module Transactions (
where

import Config.AppConfig (AppConfig (..))
import Config.Files (exampleFile, pyfintsFile)
import Config.YamlConfig (FintsConfig (..), Password (..))
import Control.Exception (Exception, throwIO)
import Data.Aeson (FromJSON, ToJSON)
Expand All @@ -28,15 +29,15 @@ import Data.Time (Day, defaultTimeLocale, formatTime)
import Data.Vector (toList)
import GHC.Generics (Generic)
import Hledger (getCurrentDay)
import Paths_fints2ledger (getDataFileName)
import System.Console.Haskeline qualified as Haskeline
import System.IO.Temp (withSystemTempFile)
import System.Process.Typed (ExitCode (ExitFailure, ExitSuccess), readProcess, shell)
import Utils (encodeAsString, orElseThrow, (??))
import System.IO (hFlush)

getExampleTransactions :: IO [Transaction]
getExampleTransactions = do
contents <- TIO.readFile =<< getDataFileName "data/example.json"
decodeTransactions contents `orElseThrow` TransactionDecodeError
decodeTransactions exampleFile `orElseThrow` TransactionDecodeError
where
decodeTransactions = Aeson.eitherDecode . TL.encodeUtf8 . TL.fromStrict

Expand All @@ -53,7 +54,6 @@ convertTransactionsToCsv path = BS.writeFile path . Csv.encodeDefaultOrderedByNa
getTransactionsFromFinTS :: AppConfig -> IO [Transaction]
getTransactionsFromFinTS config = do
currentDay <- getCurrentDay
pyfintsFilePath <- getDataFileName "data/pyfints.py"
password <- maybe getPassword return config.fintsConfig.password

let pyfintsArgs =
Expand All @@ -66,20 +66,25 @@ getTransactionsFromFinTS config = do
, start = formatDayForPython config.startDate
, end = formatDayForPython currentDay
}
let shellCommand =
shell $
"FINTS2LEDGER_ARGS='"

withSystemTempFile "fints2ledger.py" \path handle -> do
TIO.hPutStr handle pyfintsFile
hFlush handle

let shellCommand =
shell
$ "FINTS2LEDGER_ARGS='"
++ encodeAsString pyfintsArgs
++ "' "
++ config.pythonExecutable
++ " "
++ pyfintsFilePath
(exitCode, stdOut, stdErr) <- readProcess shellCommand
case exitCode of
ExitSuccess -> Aeson.eitherDecode stdOut `orElseThrow` TransactionDecodeError
ExitFailure _ -> do
TIO.putStrLn $ TL.toStrict $ TL.decodeUtf8 stdErr
throwIO $ PyFintsError "Failed to get FinTS transactions, check the message above."
++ path
(exitCode, stdOut, stdErr) <- readProcess shellCommand
case exitCode of
ExitSuccess -> Aeson.eitherDecode stdOut `orElseThrow` TransactionDecodeError
ExitFailure _ -> do
TIO.putStrLn $ TL.toStrict $ TL.decodeUtf8 stdErr
throwIO $ PyFintsError "Failed to get FinTS transactions, check the message above."

getPassword :: IO Password
getPassword = do
Expand Down
7 changes: 3 additions & 4 deletions test/TransactionSpec.hs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
module TransactionSpec (spec) where

import Config.Files (exampleFile)
import Data.Aeson qualified as Aeson
import Data.Text.Lazy (fromStrict)
import Data.Text.Lazy.Encoding (encodeUtf8)
import Data.Text.Lazy.IO as TLIO
import Paths_fints2ledger (getDataFileName)
import Test.Syd (Spec, describe, it, shouldBe)
import Transactions (Amount (Amount), Transaction (..))

spec :: Spec
spec = do
describe "Transactions" do
it "parses the sample transactions" do
sampleTransaction <- TLIO.readFile =<< getDataFileName "data/example.json"
let parsedTransactions = Aeson.eitherDecode (encodeUtf8 sampleTransaction)
let parsedTransactions = Aeson.eitherDecode (encodeUtf8 $ fromStrict exampleFile)

head <$> parsedTransactions
`shouldBe` Right
Expand Down

0 comments on commit afbd1fd

Please sign in to comment.