mirror of
https://github.com/simonmichael/hledger.git
synced 2024-11-10 05:39:31 +03:00
116 lines
4.2 KiB
Haskell
116 lines
4.2 KiB
Haskell
{-|
|
|
|
|
A reader for the timeclock file format generated by timeclock.el
|
|
(<http://www.emacswiki.org/emacs/TimeClock>). Example:
|
|
|
|
@
|
|
i 2007\/03\/10 12:26:00 hledger
|
|
o 2007\/03\/10 17:26:02
|
|
@
|
|
|
|
From timeclock.el 2.6:
|
|
|
|
@
|
|
A timeclock contains data in the form of a single entry per line.
|
|
Each entry has the form:
|
|
|
|
CODE YYYY/MM/DD HH:MM:SS [COMMENT]
|
|
|
|
CODE is one of: b, h, i, o or O. COMMENT is optional when the code is
|
|
i, o or O. The meanings of the codes are:
|
|
|
|
b Set the current time balance, or \"time debt\". Useful when
|
|
archiving old log data, when a debt must be carried forward.
|
|
The COMMENT here is the number of seconds of debt.
|
|
|
|
h Set the required working time for the given day. This must
|
|
be the first entry for that day. The COMMENT in this case is
|
|
the number of hours in this workday. Floating point amounts
|
|
are allowed.
|
|
|
|
i Clock in. The COMMENT in this case should be the name of the
|
|
project worked on.
|
|
|
|
o Clock out. COMMENT is unnecessary, but can be used to provide
|
|
a description of how the period went, for example.
|
|
|
|
O Final clock out. Whatever project was being worked on, it is
|
|
now finished. Useful for creating summary reports.
|
|
@
|
|
|
|
-}
|
|
|
|
{-# LANGUAGE OverloadedStrings, PackageImports #-}
|
|
|
|
module Hledger.Read.TimeclockReader (
|
|
-- * Reader
|
|
reader,
|
|
-- * Misc other exports
|
|
timeclockfilep,
|
|
)
|
|
where
|
|
import Prelude ()
|
|
import "base-compat-batteries" Prelude.Compat
|
|
import Control.Monad
|
|
import Control.Monad.Except (ExceptT)
|
|
import Control.Monad.State.Strict
|
|
import Data.Maybe (fromMaybe)
|
|
import Data.Text (Text)
|
|
import qualified Data.Text as T
|
|
import Text.Megaparsec hiding (parse)
|
|
|
|
import Hledger.Data
|
|
-- XXX too much reuse ?
|
|
import Hledger.Read.Common
|
|
import Hledger.Utils
|
|
|
|
|
|
reader :: Reader
|
|
reader = Reader
|
|
{rFormat = "timeclock"
|
|
,rExtensions = ["timeclock"]
|
|
,rParser = parse
|
|
,rExperimental = False
|
|
}
|
|
|
|
-- | Parse and post-process a "Journal" from timeclock.el's timeclock
|
|
-- format, saving the provided file path and the current time, or give an
|
|
-- error.
|
|
parse :: InputOpts -> FilePath -> Text -> ExceptT String IO Journal
|
|
parse = parseAndFinaliseJournal' timeclockfilep
|
|
|
|
timeclockfilep :: MonadIO m => JournalParser m ParsedJournal
|
|
timeclockfilep = do many timeclockitemp
|
|
eof
|
|
j@Journal{jparsetimeclockentries=es} <- get
|
|
-- Convert timeclock entries in this journal to transactions, closing any unfinished sessions.
|
|
-- Doing this here rather than in journalFinalise means timeclock sessions can't span file boundaries,
|
|
-- but it simplifies code above.
|
|
now <- liftIO getCurrentLocalTime
|
|
-- entries have been parsed in reverse order. timeclockEntriesToTransactions
|
|
-- expects them to be in normal order, then we must reverse again since
|
|
-- journalFinalise expects them in reverse order
|
|
let j' = j{jtxns = reverse $ timeclockEntriesToTransactions now $ reverse es, jparsetimeclockentries = []}
|
|
return j'
|
|
where
|
|
-- As all ledger line types can be distinguished by the first
|
|
-- character, excepting transactions versus empty (blank or
|
|
-- comment-only) lines, can use choice w/o try
|
|
timeclockitemp = choice [
|
|
void (lift emptyorcommentlinep)
|
|
, timeclockentryp >>= \e -> modify' (\j -> j{jparsetimeclockentries = e : jparsetimeclockentries j})
|
|
] <?> "timeclock entry, or default year or historical price directive"
|
|
|
|
-- | Parse a timeclock entry.
|
|
timeclockentryp :: JournalParser m TimeclockEntry
|
|
timeclockentryp = do
|
|
sourcepos <- genericSourcePos <$> lift getSourcePos
|
|
code <- oneOf ("bhioO" :: [Char])
|
|
lift (skipSome spacenonewline)
|
|
datetime <- datetimep
|
|
account <- fromMaybe "" <$> optional (lift (skipSome spacenonewline) >> modifiedaccountnamep)
|
|
description <- T.pack . fromMaybe "" <$> lift (optional (skipSome spacenonewline >> restofline))
|
|
return $ TimeclockEntry sourcepos (read [code]) datetime account description
|
|
|
|
|