Add support for date-format conversion directive
This commit is contained in:
parent
2c771f3d7f
commit
8c3aa657b6
@ -560,10 +560,15 @@ Notes:
|
|||||||
|
|
||||||
- Definitions must come first, one per line, all in one
|
- Definitions must come first, one per line, all in one
|
||||||
paragraph. Each is a name and a value separated by whitespace.
|
paragraph. Each is a name and a value separated by whitespace.
|
||||||
Supported names are: base-account, date-field, status-field,
|
Supported names are: base-account, date-field, date-format, status-field,
|
||||||
code-field, description-field, amount-field, currency-field,
|
code-field, description-field, amount-field, currency-field,
|
||||||
currency. All are optional and will use defaults if not specified.
|
currency. All are optional and will use defaults if not specified.
|
||||||
|
|
||||||
|
- The date-format field contains the expected format for the input dates:
|
||||||
|
this is the same as that accepted by the Haskell
|
||||||
|
[formatTime](http://hackage.haskell.org/packages/archive/time/latest/doc/html/Data-Time-Format.html#v:formatTime)
|
||||||
|
function.
|
||||||
|
|
||||||
- The remainder of the file is account-assigning rules. Each is a
|
- The remainder of the file is account-assigning rules. Each is a
|
||||||
paragraph consisting of one or more description-matching patterns
|
paragraph consisting of one or more description-matching patterns
|
||||||
(case-insensitive regular expressions), one per line, followed by
|
(case-insensitive regular expressions), one per line, followed by
|
||||||
|
|||||||
@ -35,6 +35,7 @@ convert a particular CSV data file into meaningful journal transactions. See abo
|
|||||||
-}
|
-}
|
||||||
data CsvRules = CsvRules {
|
data CsvRules = CsvRules {
|
||||||
dateField :: Maybe FieldPosition,
|
dateField :: Maybe FieldPosition,
|
||||||
|
dateFormat :: Maybe String,
|
||||||
statusField :: Maybe FieldPosition,
|
statusField :: Maybe FieldPosition,
|
||||||
codeField :: Maybe FieldPosition,
|
codeField :: Maybe FieldPosition,
|
||||||
descriptionField :: Maybe FieldPosition,
|
descriptionField :: Maybe FieldPosition,
|
||||||
@ -47,6 +48,7 @@ data CsvRules = CsvRules {
|
|||||||
|
|
||||||
nullrules = CsvRules {
|
nullrules = CsvRules {
|
||||||
dateField=Nothing,
|
dateField=Nothing,
|
||||||
|
dateFormat=Nothing,
|
||||||
statusField=Nothing,
|
statusField=Nothing,
|
||||||
codeField=Nothing,
|
codeField=Nothing,
|
||||||
descriptionField=Nothing,
|
descriptionField=Nothing,
|
||||||
@ -165,6 +167,7 @@ definitions :: GenParser Char CsvRules ()
|
|||||||
definitions = do
|
definitions = do
|
||||||
choice' [
|
choice' [
|
||||||
datefield
|
datefield
|
||||||
|
,dateformat
|
||||||
,statusfield
|
,statusfield
|
||||||
,codefield
|
,codefield
|
||||||
,descriptionfield
|
,descriptionfield
|
||||||
@ -183,6 +186,13 @@ datefield = do
|
|||||||
r <- getState
|
r <- getState
|
||||||
setState r{dateField=readMay v}
|
setState r{dateField=readMay v}
|
||||||
|
|
||||||
|
dateformat = do
|
||||||
|
string "date-format"
|
||||||
|
many1 spacenonewline
|
||||||
|
v <- restofline
|
||||||
|
r <- getState
|
||||||
|
setState r{dateFormat=Just v}
|
||||||
|
|
||||||
codefield = do
|
codefield = do
|
||||||
string "code-field"
|
string "code-field"
|
||||||
many1 spacenonewline
|
many1 spacenonewline
|
||||||
@ -271,7 +281,7 @@ printTxn debug rules rec = do
|
|||||||
transactionFromCsvRecord :: CsvRules -> CsvRecord -> Transaction
|
transactionFromCsvRecord :: CsvRules -> CsvRecord -> Transaction
|
||||||
transactionFromCsvRecord rules fields =
|
transactionFromCsvRecord rules fields =
|
||||||
let
|
let
|
||||||
date = parsedate $ normaliseDate $ maybe "1900/1/1" (atDef "" fields) (dateField rules)
|
date = parsedate $ normaliseDate (dateFormat rules) $ maybe "1900/1/1" (atDef "" fields) (dateField rules)
|
||||||
status = maybe False (null . strip . (atDef "" fields)) (statusField rules)
|
status = maybe False (null . strip . (atDef "" fields)) (statusField rules)
|
||||||
code = maybe "" (atDef "" fields) (codeField rules)
|
code = maybe "" (atDef "" fields) (codeField rules)
|
||||||
desc = maybe "" (atDef "" fields) (descriptionField rules)
|
desc = maybe "" (atDef "" fields) (descriptionField rules)
|
||||||
@ -323,9 +333,11 @@ transactionFromCsvRecord rules fields =
|
|||||||
in t
|
in t
|
||||||
|
|
||||||
-- | Convert some date string with unknown format to YYYY/MM/DD.
|
-- | Convert some date string with unknown format to YYYY/MM/DD.
|
||||||
normaliseDate :: String -> String
|
normaliseDate :: Maybe String -- ^ User-supplied date format: this should be tried in preference to all others
|
||||||
normaliseDate s = maybe "0000/00/00" showDate $
|
-> String -> String
|
||||||
firstJust
|
normaliseDate mb_user_format s = maybe "0000/00/00" showDate $
|
||||||
|
firstJust $
|
||||||
|
(maybe id (\user_format -> (parseTime defaultTimeLocale user_format s :)) mb_user_format) $
|
||||||
[parseTime defaultTimeLocale "%Y/%m/%e" s
|
[parseTime defaultTimeLocale "%Y/%m/%e" s
|
||||||
-- can't parse a month without leading 0, try adding one
|
-- can't parse a month without leading 0, try adding one
|
||||||
,parseTime defaultTimeLocale "%Y/%m/%e" (take 5 s ++ "0" ++ drop 5 s)
|
,parseTime defaultTimeLocale "%Y/%m/%e" (take 5 s ++ "0" ++ drop 5 s)
|
||||||
|
|||||||
9
tests/convert.test
Normal file
9
tests/convert.test
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Conversion from CSV to Ledger
|
||||||
|
printf 'base-account Assets:MyAccount\ndate-field 0\ndate-format %%d/%%Y/%%m\ndescription-field 1\namount-field 2\ncurrency $\n' >input.rules ; printf '10/2009/09,Flubber Co,50' > input.csv ; touch unused.journal ; bin/hledger -f unused.journal convert input.csv ; rm -rf unused.journal input.rules input.csv
|
||||||
|
>>>
|
||||||
|
2009/09/10 Flubber Co
|
||||||
|
income:unknown $-50
|
||||||
|
Assets:MyAccount $50
|
||||||
|
|
||||||
|
>>>2
|
||||||
|
using conversion rules file input.rules
|
||||||
Loading…
Reference in New Issue
Block a user