94 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Haskell
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			94 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Haskell
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env runhaskell
 | |
| {-
 | |
| generatejournal.hs NUMTXNS NUMACCTS ACCTDEPTH [--chinese|--mixed]
 | |
| 
 | |
| This generates synthetic journal data for benchmarking & profiling. It
 | |
| prints a dummy journal on stdout, with NUMTXNS transactions, one per
 | |
| day, using NUMACCTS account names with depths up to ACCTDEPTH. It will
 | |
| also contain NUMACCTS P records, one per day. By default it uses only
 | |
| ascii characters, with --chinese it uses wide chinese characters, or
 | |
| with --mixed it uses both.
 | |
| -}
 | |
| 
 | |
| module Main
 | |
| where
 | |
| import Data.Char
 | |
| import Data.List
 | |
| import Data.Time.Calendar
 | |
| import Data.Time.LocalTime
 | |
| import Numeric
 | |
| import System.Environment
 | |
| import Text.Printf
 | |
| -- import Hledger.Utils.Debug
 | |
| 
 | |
| main = do
 | |
|   rawargs <- getArgs
 | |
|   let (opts,args) = partition (isPrefixOf "-") rawargs
 | |
|   let [numtxns, numaccts, acctdepth] = map read args :: [Int]
 | |
|   -- today <- getCurrentDay
 | |
|   -- let (year,_,_) = toGregorian today
 | |
|   let d = fromGregorian 2000 1 1
 | |
|   let dates = iterate (addDays 1) d
 | |
|   let accts = pair $ cycle $ take numaccts $ uniqueAccountNames opts acctdepth
 | |
|   mapM_ (\(n,d,(a,b)) -> putStr $ showtxn n d a b) $ take numtxns $ zip3 [1..] dates accts
 | |
|   let rates = [0.70, 0.71 .. 1.3]
 | |
|   mapM_ (\(d,rate) -> putStr $ showmarketprice d rate) $ take numtxns $ zip dates (cycle $ rates ++ init (tail (reverse rates)))
 | |
|   return ()
 | |
| 
 | |
| showtxn :: Int -> Day -> String -> String -> String
 | |
| showtxn txnno date acct1 acct2 =
 | |
|     printf "%s transaction %d\n  %-40s  %2d A\n  %-40s  %2d A\n\n" d txnno acct1 amt acct2 (-amt)
 | |
|     where
 | |
|       d = show date
 | |
|       amt = 1::Int
 | |
| 
 | |
| showmarketprice :: Day -> Double -> String
 | |
| showmarketprice date rate = printf "P %s A  %.2f B\n" (show date) rate
 | |
| 
 | |
| uniqueAccountNames :: [String] -> Int -> [String]
 | |
| uniqueAccountNames opts depth =
 | |
|   mkacctnames uniquenames
 | |
|   where
 | |
|     mkacctnames names = mkacctnamestodepth some ++ mkacctnames rest
 | |
|       where
 | |
|         (some, rest) = splitAt depth names
 | |
|         -- mkacctnamestodepth ["a", "b", "c"] = ["a","a:b","a:b:c"]
 | |
|         mkacctnamestodepth :: [String] -> [String]
 | |
|         mkacctnamestodepth [] = []
 | |
|         mkacctnamestodepth (a:as) = a : map ((a++":")++) (mkacctnamestodepth as)
 | |
|     uniquenames
 | |
|       | "--mixed" `elem` opts   = concat $ zipWith (\a b -> [a,b]) uniqueNamesHex uniqueNamesWide
 | |
|       | "--chinese" `elem` opts = uniqueNamesWide
 | |
|       | otherwise               = uniqueNamesHex
 | |
| 
 | |
| uniqueNamesHex = map hex [1..] where hex = flip showHex ""
 | |
| 
 | |
| uniqueNamesWide = concat [sequences n wideChars | n <- [1..]]
 | |
| 
 | |
| -- Get the sequences of specified size starting at each element of a list,
 | |
| -- cycling it if needed to fill the last sequence. If the list's elements
 | |
| -- are unique, then the sequences will be too.
 | |
| sequences :: Show a => Int -> [a] -> [[a]]
 | |
| sequences n l = go l
 | |
|   where
 | |
|     go [] = []
 | |
|     go l' = s : go (tail l')
 | |
|       where
 | |
|         s' = take n l'
 | |
|         s | length s' == n = s'
 | |
|           | otherwise      = take n (l' ++ cycle l)
 | |
| 
 | |
| wideChars = map chr [0x3400..0x4db0]
 | |
| 
 | |
| 
 | |
| pair :: [a] -> [(a,a)]
 | |
| pair [] = []
 | |
| pair [a] = [(a,a)]
 | |
| pair (a:b:rest) = (a,b):pair rest
 | |
| 
 | |
| -- getCurrentDay :: IO Day
 | |
| -- getCurrentDay = do
 | |
| --     t <- getZonedTime
 | |
| --     return $ localDay (zonedTimeToLocalTime t)
 | |
| 
 |