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