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:
Simon Michael 2023-05-02 19:08:03 -10:00
parent 50349f81f7
commit bbecd611f1
4 changed files with 105 additions and 135 deletions

View File

@ -1,22 +1,16 @@
2016/2/1
fos:haskell ....
biz:research .
inc:client1 .... .... .... .... .... ....
# sample.timedot
# This is a comment line
; Also a comment line
* Org headings before the first date are also comment lines
2016/2/2
biz:research .
inc:client1 .... .... ..
2023-01-01 transaction description
biz:research ....
inc:client1 .... ..
2016/2/3
biz:research .
fos:hledger .... .... ...
biz:it .... ..
inc:client1 .... .... .... .... ....
2016/2/4
biz:research .... ..
fos:hledger .... .... ....
fos:ledger 0.25
fos:haskell .5
inc:client1 2
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

View File

@ -43,7 +43,6 @@ import Control.Monad
import Control.Monad.Except (ExceptT, liftEither)
import Control.Monad.State.Strict
import Data.Char (isSpace)
import Data.List (foldl')
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time (Day)
@ -113,7 +112,7 @@ preamblep = do
many $ notFollowedBy datelinep >> (lift $ emptyorcommentlinep "#;*")
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
-- fos.haskell .... ..
@ -123,14 +122,22 @@ preamblep = do
dayp :: JournalParser m ()
dayp = label "timedot day entry" $ do
lift $ traceparse "dayp"
pos <- getSourcePos
(date,desc,comment,tags) <- datelinep
commentlinesp
ts <- many $ entryp <* commentlinesp
modify' $ addTransactions $ map (\t -> t{tdate=date, tdescription=desc, tcomment=comment, ttags=tags}) ts
lift $ traceparse' "dayp"
where
addTransactions :: [Transaction] -> Journal -> Journal
addTransactions ts j = foldl' (flip ($)) j (map addTransaction ts)
ps <- many $ timedotentryp <* commentlinesp
endpos <- getSourcePos
-- lift $ traceparse' "dayp end"
let t = txnTieKnot $ nulltransaction{
tsourcepos = (pos, endpos),
tdate = date,
tstatus = Cleared,
tdescription = desc,
tcomment = comment,
ttags = tags,
tpostings = ps
}
modify' $ addTransaction t
datelinep :: JournalParser m (Day,Text,Text,[Tag])
datelinep = do
@ -139,7 +146,7 @@ datelinep = do
date <- datep
desc <- T.strip <$> lift descriptionp
(comment, tags) <- lift transactioncommentp
lift $ traceparse' "datelinep"
-- lift $ traceparse' "datelinep end"
return (date, desc, comment, tags)
-- | Zero or more empty lines or hash/semicolon comment lines
@ -165,10 +172,9 @@ orgheadingprefixp = do
-- @
-- fos.haskell .... ..
-- @
entryp :: JournalParser m Transaction
entryp = do
lift $ traceparse "entryp"
pos <- getSourcePos
timedotentryp :: JournalParser m Posting
timedotentryp = do
lift $ traceparse "timedotentryp"
notFollowedBy datelinep
lift $ optional $ choice [orgheadingprefixp, skipNonNewlineSpaces1]
a <- modifiedaccountnamep
@ -188,21 +194,13 @@ entryp = do
(c,s) = case mcs of
Just (defc,defs) -> (defc, defs{asprecision=max (asprecision defs) (Precision 2)})
_ -> ("", amountstyle{asprecision=Precision 2})
t = nulltransaction{
tsourcepos = (pos, pos),
tstatus = Cleared,
tpostings = [
nullposting{paccount=a
-- lift $ traceparse' "timedotentryp end"
return $ nullposting{paccount=a
,pamount=mixedAmount $ nullamt{acommodity=c, aquantity=hours, astyle=s}
,ptype=VirtualPosting
,pcomment=comment
,ptags=tags
,ptransaction=Just t
}
]
}
lift $ traceparse' "entryp"
return t
type Hours = Quantity

View File

@ -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:
```timedot
2021-08-04
hom:errands .... ....
fos:hledger:timedot .. ; docs
per:admin:finance
2023-05-01
hom:errands .... .... ; two hours
fos:hledger:timedot .. ; half an hour
per:admin:finance
```
hledger reads this as three time transactions on this day,
with each dot representing a quarter-hour spent:
hledger reads this as a transaction on this day with three (unbalanced) postings,
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
$ hledger -f a.timedot print # .timedot file extension activates the timedot reader
2021-08-04 *
(hom:errands) 2.00
2021-08-04 *
(fos:hledger:timedot) 0.50 ; docs
2021-08-04 *
(per:admin:finance) 0
$ hledger -f a.timedot print # .timedot file extension (or timedot: prefix) is required
2023-05-01 *
(hom:errands) 2.00 ; two hours
(fos:hledger:timedot) 0.50 ; half an hour
(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
- 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).
- an **account name** - any hledger-style [account name](#account-names), optionally hierarchical, optionally indented.
- **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 **posting comment** for this transaction, following with semicolon.
- an optional **timedot amount** - dots representing quarter hours, or a number representing hours, optionally with a unit suffix.
- 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.
Eg: `.... ..`
- a **number**, representing hours. Eg: `1.5`
- or a **number**. Eg: `1.5`
- a **number immediately followed by a unit symbol**
`s`, `m`, `h`, `d`, `w`, `mo`, or `y`,
representing seconds, minutes, hours, days weeks, months or years.
Eg `1.5h` or `90m`.
The following equivalencies are assumed:\
`60s` = `1m`,
`60m` = `1h`,
`24h` = `1d`,
`7d` = `1w`,
`30d` = `1mo`,
- or a **number immediately followed by a unit symbol**
`s`, `m`, `h`, `d`, `w`, `mo`, or `y`.
These are interpreted as seconds, minutes, hours, days weeks, months or years, and converted to hours, assuming:\
`60s` = `1m`,
`60m` = `1h`,
`24h` = `1d`,
`7d` = `1w`,
`30d` = `1mo`,
`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
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.
- 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.
This means the time log can be kept under an Org headline,
and date lines or time transaction lines can be Org headlines.
- Lines not ending with a double-space and amount are
parsed as transactions with zero amount.
(Most hledger reports hide these by default; add -E to see them.)
- Lines not ending with a double-space and amount are parsed as postings with zero amount.
Note hledger's register reports hide these by default (add -E to see them).
More examples:

View File

@ -1,58 +1,44 @@
# 1. basic timedot entry
# timedot format
<
# file comment
; another file comment
# sample.timedot
# This is a comment line
; Also a comment line
* Org headings before the first date are also comment lines
2020-01-01
a:aa 1
b:bb 2
2023-01-01 transaction description
biz:research ....
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
2020-01-01 *
(a:aa) 1.00
2023-01-01 * transaction description
(biz:research) 1.00
(inc:client1) 1.50
2020-01-01 *
(b:bb) 2.00
2023-01-01 * different transaction, same day ; with a comment and transaction-tag:
(fos:haskell) 1.00 ; a posting comment and posting-tag:
(per:admin) 1.00
>=0
# 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:
2023-01-02 * ; dates are allowed to be org headings
>=
# 5. Transaction descriptions, comments and tags are parsed properly.
$ hledger -ftimedot:- descriptions tag:day-tag
day description
# 2. And this register.
$ hledger -ftimedot:- reg
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.
$ hledger -ftimedot:- reg tag:posting-tag
2023-01-01 day description (b) 1.00 1.00
# 3. Tags are recognised. Account aliases are applied.
$ hledger -ftimedot:- reg tag:posting-tag --alias fos:haskell=λ
2023-01-01 different transac.. (λ) 1.00 1.00