imp: timedot: make one multi-posting transaction per date line (#1754)
Descriptions and comments are now more straightforward and similar to journal format.
This commit is contained in:
parent
50349f81f7
commit
bbecd611f1
@ -1,22 +1,16 @@
|
|||||||
2016/2/1
|
# sample.timedot
|
||||||
fos:haskell ....
|
# This is a comment line
|
||||||
biz:research .
|
; Also a comment line
|
||||||
inc:client1 .... .... .... .... .... ....
|
* Org headings before the first date are also comment lines
|
||||||
|
|
||||||
2016/2/2
|
2023-01-01 transaction description
|
||||||
biz:research .
|
biz:research ....
|
||||||
inc:client1 .... .... ..
|
inc:client1 .... ..
|
||||||
|
|
||||||
2016/2/3
|
2023-01-01 different transaction, same day ; with a comment and transaction-tag:
|
||||||
biz:research .
|
; more transaction comment lines ? currently ignored
|
||||||
fos:hledger .... .... ...
|
fos:haskell .... ; a posting comment and posting-tag:
|
||||||
biz:it .... ..
|
; more posting comment lines ? currently ignored
|
||||||
inc:client1 .... .... .... .... ....
|
per:admin ....
|
||||||
|
|
||||||
2016/2/4
|
|
||||||
biz:research .... ..
|
|
||||||
fos:hledger .... .... ....
|
|
||||||
fos:ledger 0.25
|
|
||||||
fos:haskell .5
|
|
||||||
inc:client1 2
|
|
||||||
|
|
||||||
|
** 2023-01-02 ; dates are allowed to be org headings
|
||||||
|
|||||||
@ -43,7 +43,6 @@ import Control.Monad
|
|||||||
import Control.Monad.Except (ExceptT, liftEither)
|
import Control.Monad.Except (ExceptT, liftEither)
|
||||||
import Control.Monad.State.Strict
|
import Control.Monad.State.Strict
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace)
|
||||||
import Data.List (foldl')
|
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Time (Day)
|
import Data.Time (Day)
|
||||||
@ -113,7 +112,7 @@ preamblep = do
|
|||||||
many $ notFollowedBy datelinep >> (lift $ emptyorcommentlinep "#;*")
|
many $ notFollowedBy datelinep >> (lift $ emptyorcommentlinep "#;*")
|
||||||
lift $ traceparse' "preamblep"
|
lift $ traceparse' "preamblep"
|
||||||
|
|
||||||
-- | Parse timedot day entries to zero or more time transactions for that day.
|
-- | Parse timedot day entries to multi-posting time transactions for that day.
|
||||||
-- @
|
-- @
|
||||||
-- 2020/2/1 optional day description
|
-- 2020/2/1 optional day description
|
||||||
-- fos.haskell .... ..
|
-- fos.haskell .... ..
|
||||||
@ -123,14 +122,22 @@ preamblep = do
|
|||||||
dayp :: JournalParser m ()
|
dayp :: JournalParser m ()
|
||||||
dayp = label "timedot day entry" $ do
|
dayp = label "timedot day entry" $ do
|
||||||
lift $ traceparse "dayp"
|
lift $ traceparse "dayp"
|
||||||
|
pos <- getSourcePos
|
||||||
(date,desc,comment,tags) <- datelinep
|
(date,desc,comment,tags) <- datelinep
|
||||||
commentlinesp
|
commentlinesp
|
||||||
ts <- many $ entryp <* commentlinesp
|
ps <- many $ timedotentryp <* commentlinesp
|
||||||
modify' $ addTransactions $ map (\t -> t{tdate=date, tdescription=desc, tcomment=comment, ttags=tags}) ts
|
endpos <- getSourcePos
|
||||||
lift $ traceparse' "dayp"
|
-- lift $ traceparse' "dayp end"
|
||||||
where
|
let t = txnTieKnot $ nulltransaction{
|
||||||
addTransactions :: [Transaction] -> Journal -> Journal
|
tsourcepos = (pos, endpos),
|
||||||
addTransactions ts j = foldl' (flip ($)) j (map addTransaction ts)
|
tdate = date,
|
||||||
|
tstatus = Cleared,
|
||||||
|
tdescription = desc,
|
||||||
|
tcomment = comment,
|
||||||
|
ttags = tags,
|
||||||
|
tpostings = ps
|
||||||
|
}
|
||||||
|
modify' $ addTransaction t
|
||||||
|
|
||||||
datelinep :: JournalParser m (Day,Text,Text,[Tag])
|
datelinep :: JournalParser m (Day,Text,Text,[Tag])
|
||||||
datelinep = do
|
datelinep = do
|
||||||
@ -139,7 +146,7 @@ datelinep = do
|
|||||||
date <- datep
|
date <- datep
|
||||||
desc <- T.strip <$> lift descriptionp
|
desc <- T.strip <$> lift descriptionp
|
||||||
(comment, tags) <- lift transactioncommentp
|
(comment, tags) <- lift transactioncommentp
|
||||||
lift $ traceparse' "datelinep"
|
-- lift $ traceparse' "datelinep end"
|
||||||
return (date, desc, comment, tags)
|
return (date, desc, comment, tags)
|
||||||
|
|
||||||
-- | Zero or more empty lines or hash/semicolon comment lines
|
-- | Zero or more empty lines or hash/semicolon comment lines
|
||||||
@ -165,10 +172,9 @@ orgheadingprefixp = do
|
|||||||
-- @
|
-- @
|
||||||
-- fos.haskell .... ..
|
-- fos.haskell .... ..
|
||||||
-- @
|
-- @
|
||||||
entryp :: JournalParser m Transaction
|
timedotentryp :: JournalParser m Posting
|
||||||
entryp = do
|
timedotentryp = do
|
||||||
lift $ traceparse "entryp"
|
lift $ traceparse "timedotentryp"
|
||||||
pos <- getSourcePos
|
|
||||||
notFollowedBy datelinep
|
notFollowedBy datelinep
|
||||||
lift $ optional $ choice [orgheadingprefixp, skipNonNewlineSpaces1]
|
lift $ optional $ choice [orgheadingprefixp, skipNonNewlineSpaces1]
|
||||||
a <- modifiedaccountnamep
|
a <- modifiedaccountnamep
|
||||||
@ -188,21 +194,13 @@ entryp = do
|
|||||||
(c,s) = case mcs of
|
(c,s) = case mcs of
|
||||||
Just (defc,defs) -> (defc, defs{asprecision=max (asprecision defs) (Precision 2)})
|
Just (defc,defs) -> (defc, defs{asprecision=max (asprecision defs) (Precision 2)})
|
||||||
_ -> ("", amountstyle{asprecision=Precision 2})
|
_ -> ("", amountstyle{asprecision=Precision 2})
|
||||||
t = nulltransaction{
|
-- lift $ traceparse' "timedotentryp end"
|
||||||
tsourcepos = (pos, pos),
|
return $ nullposting{paccount=a
|
||||||
tstatus = Cleared,
|
|
||||||
tpostings = [
|
|
||||||
nullposting{paccount=a
|
|
||||||
,pamount=mixedAmount $ nullamt{acommodity=c, aquantity=hours, astyle=s}
|
,pamount=mixedAmount $ nullamt{acommodity=c, aquantity=hours, astyle=s}
|
||||||
,ptype=VirtualPosting
|
,ptype=VirtualPosting
|
||||||
,pcomment=comment
|
,pcomment=comment
|
||||||
,ptags=tags
|
,ptags=tags
|
||||||
,ptransaction=Just t
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
lift $ traceparse' "entryp"
|
|
||||||
return t
|
|
||||||
|
|
||||||
type Hours = Quantity
|
type Hours = Quantity
|
||||||
|
|
||||||
|
|||||||
@ -4174,63 +4174,55 @@ Compared to [`timeclock` format](#timeclock), it is
|
|||||||
A timedot file contains a series of day entries, which might look like this:
|
A timedot file contains a series of day entries, which might look like this:
|
||||||
|
|
||||||
```timedot
|
```timedot
|
||||||
2021-08-04
|
2023-05-01
|
||||||
hom:errands .... ....
|
hom:errands .... .... ; two hours
|
||||||
fos:hledger:timedot .. ; docs
|
fos:hledger:timedot .. ; half an hour
|
||||||
per:admin:finance
|
per:admin:finance
|
||||||
```
|
```
|
||||||
|
|
||||||
hledger reads this as three time transactions on this day,
|
hledger reads this as a transaction on this day with three (unbalanced) postings,
|
||||||
with each dot representing a quarter-hour spent:
|
where each dot represents "0.25". No commodity is assumed, but normally we interpret
|
||||||
|
it as hours, with each dot representing a quarter-hour. It's convenient, though
|
||||||
|
not required, to group the dots in fours for easy reading.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ hledger -f a.timedot print # .timedot file extension activates the timedot reader
|
$ hledger -f a.timedot print # .timedot file extension (or timedot: prefix) is required
|
||||||
2021-08-04 *
|
2023-05-01 *
|
||||||
(hom:errands) 2.00
|
(hom:errands) 2.00 ; two hours
|
||||||
|
(fos:hledger:timedot) 0.50 ; half an hour
|
||||||
2021-08-04 *
|
|
||||||
(fos:hledger:timedot) 0.50 ; docs
|
|
||||||
|
|
||||||
2021-08-04 *
|
|
||||||
(per:admin:finance) 0
|
(per:admin:finance) 0
|
||||||
```
|
```
|
||||||
|
|
||||||
A day entry begins with a date line:
|
A transaction begins with a non-indented **[simple date](#simple-dates)** (Y-M-D, Y/M/D, or Y.M.D).
|
||||||
|
It can optionally be preceded by one or more stars and a space, for Emacs org mode compatibility.
|
||||||
|
It can optionally be followed on the same line by a transaction description,
|
||||||
|
and/or a transaction comment following a semicolon.
|
||||||
|
|
||||||
- a non-indented **[simple date](#simple-dates)** (Y-M-D, Y/M/D, or Y.M.D).
|
After the date line are zero or more time postings, consisting of:
|
||||||
|
|
||||||
Optionally this can be followed on the same line by
|
- an **account name** - any hledger-style [account name](#account-names), optionally hierarchical, optionally indented.
|
||||||
|
|
||||||
- a **common description** for this day's transactions.
|
|
||||||
- a **common comment** for this day's transactions, following a semicolon (`;`).
|
|
||||||
|
|
||||||
After the date line are zero or more optionally-indented time transactions, consisting of:
|
|
||||||
|
|
||||||
- an **account name** - any word or phrase, usually a hledger-style [account name](#account-names).
|
|
||||||
- **two or more spaces** - a field separator, required if there is an amount (as in journal format).
|
- **two or more spaces** - a field separator, required if there is an amount (as in journal format).
|
||||||
- a **timedot amount** - dots representing quarter hours, or a number representing hours, optionally with a unit suffix.
|
- an optional **timedot amount** - dots representing quarter hours, or a number representing hours, optionally with a unit suffix.
|
||||||
- an **posting comment** for this transaction, following with semicolon.
|
- an optional **posting comment** following a semicolon.
|
||||||
|
|
||||||
In more detail, timedot amounts can be:
|
Timedot amounts can be:
|
||||||
|
|
||||||
- **dots**: zero or more period characters, each representing one quarter-hour.
|
- **dots**: zero or more period characters (`.`), each representing 0.25.
|
||||||
Spaces are ignored and can be used for grouping.
|
Spaces are ignored and can be used for grouping.
|
||||||
Eg: `.... ..`
|
Eg: `.... ..`
|
||||||
|
|
||||||
- a **number**, representing hours. Eg: `1.5`
|
- or a **number**. Eg: `1.5`
|
||||||
|
|
||||||
- a **number immediately followed by a unit symbol**
|
- or a **number immediately followed by a unit symbol**
|
||||||
`s`, `m`, `h`, `d`, `w`, `mo`, or `y`,
|
`s`, `m`, `h`, `d`, `w`, `mo`, or `y`.
|
||||||
representing seconds, minutes, hours, days weeks, months or years.
|
These are interpreted as seconds, minutes, hours, days weeks, months or years, and converted to hours, assuming:\
|
||||||
Eg `1.5h` or `90m`.
|
|
||||||
The following equivalencies are assumed:\
|
|
||||||
`60s` = `1m`,
|
`60s` = `1m`,
|
||||||
`60m` = `1h`,
|
`60m` = `1h`,
|
||||||
`24h` = `1d`,
|
`24h` = `1d`,
|
||||||
`7d` = `1w`,
|
`7d` = `1w`,
|
||||||
`30d` = `1mo`,
|
`30d` = `1mo`,
|
||||||
`365d` = `1y`.
|
`365d` = `1y`.
|
||||||
(This unit will not be visible in the generated transaction amount, which is always in hours.)
|
Eg `90m` is parsed as `1.5`.
|
||||||
|
|
||||||
There is some added flexibility to help with keeping time log data
|
There is some added flexibility to help with keeping time log data
|
||||||
in the same file as your notes, todo lists, etc.:
|
in the same file as your notes, todo lists, etc.:
|
||||||
@ -4238,14 +4230,14 @@ in the same file as your notes, todo lists, etc.:
|
|||||||
- Blank lines and lines beginning with `#` or `;` are ignored.
|
- Blank lines and lines beginning with `#` or `;` are ignored.
|
||||||
|
|
||||||
- Before the first date line, lines beginning with `*` are ignored.
|
- Before the first date line, lines beginning with `*` are ignored.
|
||||||
From the first date line onward, a sequence of `*`'s followed by a space
|
|
||||||
|
- From the first date line onward, one or more `*`'s followed by a space
|
||||||
at beginning of lines (ie, the headline prefix used by Emacs Org mode) is ignored.
|
at beginning of lines (ie, the headline prefix used by Emacs Org mode) is ignored.
|
||||||
This means the time log can be kept under an Org headline,
|
This means the time log can be kept under an Org headline,
|
||||||
and date lines or time transaction lines can be Org headlines.
|
and date lines or time transaction lines can be Org headlines.
|
||||||
|
|
||||||
- Lines not ending with a double-space and amount are
|
- Lines not ending with a double-space and amount are parsed as postings with zero amount.
|
||||||
parsed as transactions with zero amount.
|
Note hledger's register reports hide these by default (add -E to see them).
|
||||||
(Most hledger reports hide these by default; add -E to see them.)
|
|
||||||
|
|
||||||
More examples:
|
More examples:
|
||||||
|
|
||||||
|
|||||||
@ -1,58 +1,44 @@
|
|||||||
# 1. basic timedot entry
|
# timedot format
|
||||||
|
|
||||||
<
|
<
|
||||||
# file comment
|
# sample.timedot
|
||||||
; another file comment
|
# This is a comment line
|
||||||
|
; Also a comment line
|
||||||
|
* Org headings before the first date are also comment lines
|
||||||
|
|
||||||
2020-01-01
|
2023-01-01 transaction description
|
||||||
a:aa 1
|
biz:research ....
|
||||||
b:bb 2
|
inc:client1 .... ..
|
||||||
|
|
||||||
|
2023-01-01 different transaction, same day ; with a comment and transaction-tag:
|
||||||
|
; more transaction comment lines ? currently ignored
|
||||||
|
fos:haskell .... ; a posting comment and posting-tag:
|
||||||
|
; more posting comment lines ? currently ignored
|
||||||
|
per:admin ....
|
||||||
|
|
||||||
|
** 2023-01-02 ; dates are allowed to be org headings
|
||||||
|
|
||||||
|
# 1. The above timedot is converted to these transactions.
|
||||||
$ hledger -ftimedot:- print
|
$ hledger -ftimedot:- print
|
||||||
2020-01-01 *
|
2023-01-01 * transaction description
|
||||||
(a:aa) 1.00
|
(biz:research) 1.00
|
||||||
|
(inc:client1) 1.50
|
||||||
|
|
||||||
2020-01-01 *
|
2023-01-01 * different transaction, same day ; with a comment and transaction-tag:
|
||||||
(b:bb) 2.00
|
(fos:haskell) 1.00 ; a posting comment and posting-tag:
|
||||||
|
(per:admin) 1.00
|
||||||
|
|
||||||
>=0
|
2023-01-02 * ; dates are allowed to be org headings
|
||||||
|
|
||||||
# 2. Org mode headline prefixes are ignored.
|
|
||||||
<
|
|
||||||
* 2020-01-01
|
|
||||||
** a:aa 1
|
|
||||||
|
|
||||||
$ hledger -ftimedot:- print
|
|
||||||
2020-01-01 *
|
|
||||||
(a:aa) 1.00
|
|
||||||
|
|
||||||
>=0
|
|
||||||
|
|
||||||
# 3. Command-line account aliases are applied.
|
|
||||||
$ hledger -ftimedot:- print --alias a=b
|
|
||||||
2020-01-01 *
|
|
||||||
(b:aa) 1.00
|
|
||||||
|
|
||||||
>=0
|
|
||||||
|
|
||||||
# 4. A common day description and comment, and posting comments are supported.
|
|
||||||
<
|
|
||||||
2023-01-01 day description ; day comment, day-tag:
|
|
||||||
a ....
|
|
||||||
b .... ; posting comment, posting-tag:
|
|
||||||
|
|
||||||
$ hledger -ftimedot:- print
|
|
||||||
2023-01-01 * day description ; day comment, day-tag:
|
|
||||||
(a) 1.00
|
|
||||||
|
|
||||||
2023-01-01 * day description ; day comment, day-tag:
|
|
||||||
(b) 1.00 ; posting comment, posting-tag:
|
|
||||||
|
|
||||||
>=
|
>=
|
||||||
|
|
||||||
# 5. Transaction descriptions, comments and tags are parsed properly.
|
# 2. And this register.
|
||||||
$ hledger -ftimedot:- descriptions tag:day-tag
|
$ hledger -ftimedot:- reg
|
||||||
day description
|
2023-01-01 transaction descr.. (biz:research) 1.00 1.00
|
||||||
|
(inc:client1) 1.50 2.50
|
||||||
|
2023-01-01 different transac.. (fos:haskell) 1.00 3.50
|
||||||
|
(per:admin) 1.00 4.50
|
||||||
|
|
||||||
# 6. Posting comments and tags are parsed properly.
|
# 3. Tags are recognised. Account aliases are applied.
|
||||||
$ hledger -ftimedot:- reg tag:posting-tag
|
$ hledger -ftimedot:- reg tag:posting-tag --alias fos:haskell=λ
|
||||||
2023-01-01 day description (b) 1.00 1.00
|
2023-01-01 different transac.. (λ) 1.00 1.00
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user