Compare commits
84 Commits
059579e841
...
88aa54c082
Author | SHA1 | Date |
---|---|---|
contrapunctus | 88aa54c082 | |
contrapunctus | f25c586232 | |
contrapunctus | 30bde518a0 | |
contrapunctus | ea704f8c00 | |
contrapunctus | 8dbba77d12 | |
contrapunctus | 3a68a65156 | |
contrapunctus | 1465f5da33 | |
contrapunctus | 38e4997269 | |
contrapunctus | 0831ae82d5 | |
contrapunctus | b9417d6a25 | |
contrapunctus | f3f96a8066 | |
contrapunctus | b9ba7fe9b2 | |
contrapunctus | 90b68d6671 | |
contrapunctus | 80f08aabdf | |
contrapunctus | 1a7ebecb2e | |
contrapunctus | 8d65dd8a13 | |
contrapunctus | b70b5eac5c | |
contrapunctus | 0f0dab74bc | |
contrapunctus | 162f005507 | |
contrapunctus | 71d116480d | |
contrapunctus | 53f650678b | |
contrapunctus | 9df00858f4 | |
contrapunctus | 33ac4243f4 | |
contrapunctus | e21c85e543 | |
contrapunctus | aec8f82c60 | |
contrapunctus | 98f8bcd7b8 | |
contrapunctus | 88c6e11b9f | |
contrapunctus | 031f3b9640 | |
contrapunctus | 63f60ccde5 | |
contrapunctus | a04447f745 | |
contrapunctus | e4ff298ba4 | |
contrapunctus | a0107ccb91 | |
contrapunctus | 5a64f7ce93 | |
contrapunctus | 5d0910e4a0 | |
contrapunctus | 11ba9d14c9 | |
contrapunctus | f18c71453b | |
contrapunctus | ebce220bd5 | |
contrapunctus | 7a1cf0d66e | |
contrapunctus | e45064be44 | |
contrapunctus | 131e157239 | |
contrapunctus | fdc2b1a964 | |
contrapunctus | 3f50c1ba55 | |
contrapunctus | 0ea1dee847 | |
contrapunctus | ad6cf91a53 | |
contrapunctus | 8fc14e6686 | |
contrapunctus | 6e38c56f0e | |
contrapunctus | 493d7d34ba | |
contrapunctus | c4666ee770 | |
contrapunctus | b5d71bd4d5 | |
contrapunctus | c0c6c20e2a | |
contrapunctus | 48ea058ce3 | |
contrapunctus | a77eaac963 | |
contrapunctus | 0e52e04491 | |
contrapunctus | 1a7d70b8b7 | |
contrapunctus | 35d9e81c78 | |
contrapunctus | 10b8328474 | |
contrapunctus | cf80e12ce6 | |
contrapunctus | 125cba8fc3 | |
contrapunctus | 1e00f8e930 | |
contrapunctus | 0722b10a32 | |
contrapunctus | cfbe5a6209 | |
contrapunctus | 01deba0c45 | |
contrapunctus | 987e82cbb9 | |
contrapunctus | 605fc63b49 | |
contrapunctus | 04ede947b4 | |
contrapunctus | c47906f0bf | |
contrapunctus | 8ede73dbf5 | |
contrapunctus | 769495ca5e | |
contrapunctus | b5501afc9d | |
contrapunctus | 6de9e473fd | |
contrapunctus | 985969f867 | |
contrapunctus | b902da07ee | |
contrapunctus | 691c8c4089 | |
contrapunctus | e4a3cf4cfd | |
contrapunctus | fc7ed02df7 | |
contrapunctus | 236dbfda8f | |
contrapunctus | e50b2813b3 | |
contrapunctus | 9ff6dd7edf | |
contrapunctus | 0cc451fa03 | |
contrapunctus | cd657af021 | |
contrapunctus | d7da24b7cb | |
contrapunctus | 7c85867aa7 | |
contrapunctus | 6b30afb90e | |
contrapunctus | 0147bc3650 |
168
TODO.org
168
TODO.org
|
@ -8,10 +8,12 @@
|
|||
* v0.3.0 [100%]
|
||||
1. [X] +Release or+ bundle plist-pp.el
|
||||
2. [X] -report - error opening when clocked in
|
||||
|
||||
* MELPA, hosting [0%]
|
||||
1. [ ] merge =chronometrist-goal= into this repository
|
||||
2. [X] move project to codeberg.org or tildegit.org
|
||||
3. [X] ...and then, update the MELPA recipes.
|
||||
|
||||
* Chronometrist
|
||||
1. implement point restore for =chronometrist=/=chronometrist-report=/=chronometrist-statistics= in a kill-buffer-hook, so it works whenever the buffer is killed, not just when we call the command.
|
||||
|
||||
|
@ -34,11 +36,13 @@
|
|||
5. Delete =:stop= time and save
|
||||
6. Clock out again
|
||||
7. Accept the already-inserted key-value. Duplicate!
|
||||
|
||||
** Features [50%]
|
||||
1. [ ] Hook enhancement - can we supply the whole plist (including tags and key-values) to the task-start hooks, so users can have even smarter hook functions?
|
||||
* That would mean ensuring that -kv-read runs before other hooks.
|
||||
* Actually, it should be trivial to access the last expression in the file, so maybe this is unnecessary.
|
||||
2. [X] Revisit 'no reason' commands - maybe we should ask for tags and key-values with the regular commands, and skip them with the 'no reason' variants?
|
||||
|
||||
*** Key-values [66%]
|
||||
1. [X] bug - value-history appears in chronological rather than reverse chronological order
|
||||
2. [X] generate history hash table from chronometrist-file.
|
||||
|
@ -70,6 +74,7 @@
|
|||
15. [X] bug - tag history starts at the beginning (wtf?), value history is empty (wtf?)
|
||||
* Does not occur on master, only on dev
|
||||
* Does not occur if you disable lexical binding (introduced in 4e89836)
|
||||
|
||||
**** Values
|
||||
What forms can they take?
|
||||
1. Integers, floating point numbers - easy to identify via regexp
|
||||
|
@ -109,6 +114,7 @@
|
|||
** Maybe
|
||||
1. Add a new kind of plist - =(:name "NAME" :time "TIME" ...)=
|
||||
To record events for which the time interval is not relevant. These won't be shown in =chronometrist= - perhaps in a different buffer.
|
||||
|
||||
* Optimization
|
||||
** DONE Don't update task list from data when user has set their own
|
||||
:PROPERTIES:
|
||||
|
@ -135,6 +141,7 @@ Defer (tag/key/value) history generation from file-change-time to prompt-time, a
|
|||
+ Reduce memory use by allowing user to restrict number of s-expressions read.
|
||||
+ Per-task history generation will create problems - e.g. values for a given key for one task won't be suggested for values for the same key in another 🤦
|
||||
* Tags and keys are already task-sensitive; just don't make values task-sensitive.
|
||||
|
||||
** DONE Hash file contents to optimize for common changes
|
||||
Compare partial hashes of file to know what has changed - only update memory when necessary.
|
||||
|
||||
|
@ -143,20 +150,24 @@ Don't store entire file into memory; instead, split midnight-spanning intervals
|
|||
+ Will increase load time for each forward/backward command in =chronometrist-report= and =chronometrist-statistics=
|
||||
+ Will reduce memory used by =chronometrist-events=.
|
||||
* Further reductions can take place, if we automatically discard cache entries past a certain limit. (perhaps excluding data for the current day, week, or month)
|
||||
|
||||
** Split and save midnight-spanning intervals to disk
|
||||
It will remove the need for an in-memory version of data with split midnight-spanning intervals.
|
||||
+ Least memory use?
|
||||
+ Might make the file harder for a user to edit.
|
||||
|
||||
** Save timestamps as UNIX epoch time
|
||||
+ Will (probably) greatly speed up time parsing and interval splitting.
|
||||
+ Will greatly impede human editing of the file, too. 🤔
|
||||
* An editing UI could help - pretty sure every timestamp edit I've ever made has been for the last interval, or at most an interval in today's data.
|
||||
- The editing UI could have commands for next/previous interval; one could also have a command which, in the file, opens the plist at point for editing.
|
||||
|
||||
** Use an SQL database instead of a text file
|
||||
That is, assuming SQL can
|
||||
1. find the difference between ISO-8601 timestamps
|
||||
2. compare ISO-8601 timestamps, and
|
||||
3. do 1 and 2 faster than Elisp.
|
||||
|
||||
** Change data structure
|
||||
Instead of storing each plist as-is, split each into two, one with the =:start= and one with the =:end=. Now we have the elegance of the one-plist-is-a-complete-interval schema in the file, and the ease and speed of detection of midnight spanning intervals in memory.
|
||||
|
||||
|
@ -168,6 +179,7 @@ Instead of storing each plist as-is, split each into two, one with the =:start=
|
|||
(:stop "<timestamp>")
|
||||
...)
|
||||
#+END_SRC
|
||||
|
||||
** Change file timestamp format to =("<iso-date>" "<iso-time>")=
|
||||
** Change file schema
|
||||
Pros
|
||||
|
@ -225,6 +237,7 @@ Cons
|
|||
+ Anything requiring split intervals will first look in =chronometrist-events=, and if not found, will read from the file and update =chronometrist-events=.
|
||||
+ When the file changes, use the file byte length and hash strategy described below to know whether to keep the cache.
|
||||
+ Save cache to a file, so that event splitting is avoided by reading from that.
|
||||
|
||||
** (old) ideas to make -refresh-file faster
|
||||
1. Support multiple files, so we read and process lesser data when one of them changes.
|
||||
2. Make file writing async
|
||||
|
@ -236,12 +249,14 @@ Cons
|
|||
* Or even the first expression of the current date. That way, we just re-read the intervals for today. Because chronometrist-events uses dates as keys, it's easy to work on the basis of dates.
|
||||
6. [ ] Don't generate tag/keyword/value history from the entire log, just from the last N days (where N is user-customizable).
|
||||
7. [ ] Just why are we reading the whole file? ~chronometrist~ should not read more than a day; ~chronometrist-report~ should not read more than a week at a time, and so on. Make a branch which works on this logic, see if it is faster.
|
||||
|
||||
* Certain
|
||||
1. [ ] statistics UI for arbitrary queries
|
||||
* user provides a predicate
|
||||
* we show buffer with
|
||||
+ matched unique tag groups, and sparklines for time spent on each
|
||||
+ matched key-values, and sparklines for time spent on each
|
||||
|
||||
** plist-pp [66%]
|
||||
1. [X] plist-pp - work recursively for plist/alist values
|
||||
2. [ ] Fix alignment of alist dots
|
||||
|
@ -373,7 +388,7 @@ Cons
|
|||
4. [ ] Fix heading link to "midnight-spanning intervals" - jumps to the correct heading in HTML export, but jumps to its own self in Org mode.
|
||||
5. [ ] Figure out some way to hide package prefixes in identifiers in Org mode (without actually affecting the contents, a la nameless-mode)
|
||||
|
||||
** UX [30%]
|
||||
** UX [33%]
|
||||
1. [X] Optimization - (jonasw) store length and hash of previous file, see if the new file has the same hash until old-length bytes.
|
||||
* [X] Check for type of change to file
|
||||
+ [X] Handle last expression being removed
|
||||
|
@ -381,14 +396,13 @@ Cons
|
|||
* [X] BUG - if something was removed from the last expression (thereby decreasing the length of the file), =chronometrist-file-change-type= returns =t= instead of =:last=
|
||||
* [X] BUG - args out of range error when last plist is removed
|
||||
2. [X] Optimization - generate history before querying, not when the file changes.
|
||||
3. [ ] Don't suggest nil when asking for first project on first run
|
||||
4. [ ] When starting a project with time of "-" (i.e. not worked on today until now), immediately set time to 0 instead of waiting for the first timer refresh
|
||||
5. [ ] Mouse commands should work only on buttons.
|
||||
6. [X] Button actions should accept prefix arguments and behave exactly like their keyboard counterparts.
|
||||
7. [ ] mouse-3 should clock-out without asking for reason.
|
||||
8. [ ] Some way to ask for the reason just before starting a project. Even when clocking out, the reason is asked /before/ clocking out, which adds time to the project.
|
||||
9. [ ] Allow calling chronometrist-in/out from anywhere-within-Emacs (a la timeclock) as well as from the chronometrist buffer.
|
||||
10. [ ] =chronometrist-timer= - if =chronometrist-file= is being edited (buffer exists and modified), don't refresh - this will (hopefully) prevent Emacs from going crazy with errors in trying to parse malformed data.
|
||||
3. [ ] When starting a project with time of "-" (i.e. not worked on today until now), immediately set time to 0 instead of waiting for the first timer refresh
|
||||
4. [ ] Mouse commands should work only on buttons.
|
||||
5. [X] Button actions should accept prefix arguments and behave exactly like their keyboard counterparts.
|
||||
6. [ ] mouse-3 should clock-out without asking for reason.
|
||||
7. [ ] Some way to ask for the reason just before starting a project. Even when clocking out, the reason is asked /before/ clocking out, which adds time to the project.
|
||||
8. [ ] Allow calling chronometrist-in/out from anywhere-within-Emacs (a la timeclock) as well as from the chronometrist buffer.
|
||||
9. [ ] =chronometrist-timer= - if =chronometrist-file= is being edited (buffer exists and modified), don't refresh - this will (hopefully) prevent Emacs from going crazy with errors in trying to parse malformed data.
|
||||
|
||||
* Maybe
|
||||
** New features
|
||||
|
@ -458,6 +472,7 @@ Cons
|
|||
- untouched project with target defined - red
|
||||
- target ±5 minutes - green
|
||||
- target*2 and above - red
|
||||
|
||||
* viewing/editing frontends
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: viewing-editing-frontends
|
||||
|
@ -477,7 +492,6 @@ A way to represent s-expressions as Markdown, Org, etc, so the entire plist can
|
|||
* see [[info:elisp#Swapping Text][swapping text]]
|
||||
|
||||
*** A binding in the key-value buffer, which will insert the string at point in a buffer of a certain mode.
|
||||
|
||||
* documentation discoverability :doc:
|
||||
Ensure that the user manual is easily discoverable.
|
||||
|
||||
|
@ -687,10 +701,13 @@ list of tasks, one day, durations and graphs
|
|||
2. [ ] set day (blank input to reset to today's date)
|
||||
3. [ ] set duration format
|
||||
4. [ ] display sparkline for total time
|
||||
|
||||
*** report
|
||||
list of tasks, one week, durations only
|
||||
|
||||
*** statistics
|
||||
list of tasks, one week/month/year [fn:1]
|
||||
|
||||
*** details (intervals for a day) [16%]
|
||||
list of intervals, one day [fn:1]
|
||||
+ [-] commands [50%]
|
||||
|
@ -707,12 +724,14 @@ list of intervals, one day [fn:1]
|
|||
|
||||
Bugs
|
||||
1. [ ] day-crossing events have the wrong duration and time. e.g. ~1 Exercise walking 30 minutes from 23:37 to 00:07~
|
||||
|
||||
** New frontends I want
|
||||
*** task-key-values
|
||||
list of unique key-values for a task, one day [fn:1], durations and graphs
|
||||
+ commands [0%]
|
||||
1. [ ] set task
|
||||
2. [ ] set range
|
||||
|
||||
*** task-graph [0%]
|
||||
list of days, weeks, or months [fn:1], one task (with optional key-value filter [fn:2]), horizontal graph (and durations/stats?)
|
||||
+ columns
|
||||
|
@ -807,6 +826,7 @@ See docstring of =timeclock-log-data=
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: new-backends-klog
|
||||
:END:
|
||||
|
||||
** SQLite [0%]
|
||||
Chronometrist allows the user to add arbitrary key-values. There are a few ways to do this in SQL, but [[https://mariadb.com/kb/en/entity-attribute-value-implementation/][this solution]] seems well-suited to Chronometrist - put commonly-queried keys (name, start, stop) into SQL columns, and put user key-values in a JSON/s-exp TEXT column. The latter can be queried separately. SQLite has support for [[https://www.sqlite.org/json1.html][JSON columns]].
|
||||
|
||||
|
@ -851,7 +871,6 @@ Command to delete the interval currently being recorded. (bind to 'k')
|
|||
* pretty printer
|
||||
** fix handling of tagged alist group values :bug:
|
||||
** put each list element on its own line if length of list exceeds fill-column :feature:
|
||||
|
||||
* verify command [20%] :feature:
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: verify-command
|
||||
|
@ -936,10 +955,8 @@ The plist backend could theoretically be extended to support it, but I'd rather
|
|||
:PROPERTIES:
|
||||
:CREATED: 2022-01-08T23:32:37+0530
|
||||
:END:
|
||||
1. [ ] On first launch, offer list of supported backends to migrate from
|
||||
+ Some backends may not be installed - "see MELPA for more formats"
|
||||
2. [ ] Use active backend as the suggested input backend
|
||||
3. [ ] Conserve file local variables prop line for text backends
|
||||
1. [ ] Use active backend as the suggested input backend when migrating
|
||||
2. [ ] Conserve file local variables prop line for text backends
|
||||
|
||||
** make migrate command async [33%]
|
||||
:PROPERTIES:
|
||||
|
@ -1060,3 +1077,124 @@ Similar to [[#verify-command][verify command]], perhaps, but this is really abou
|
|||
:CREATED: 2022-03-31T22:44:34+0530
|
||||
:END:
|
||||
A task can be part of any number of categories. A category may also contain other categories. The categories, and the total time spent on each one, could be displayed in the main view, separate from the tasks.
|
||||
|
||||
* First run behaviour :ux:
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-02T17:28:33+0530
|
||||
:END:
|
||||
1. [ ] Look for any existing Chronometrist database in the default location. If one is found, use it to determine the default active backend. Otherwise, offer to import from one of the supported backends.
|
||||
+ Some backends may not be installed - "see MELPA for more formats"
|
||||
2. [ ] Don't suggest nil when asking for first project on first run
|
||||
|
||||
* Common Lisp port
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-03T09:36:53+0530
|
||||
:END:
|
||||
|
||||
** DONE Replace =add-variable-watcher=
|
||||
Common Lisp does not have Elisp's =add-variable-watcher=. The Elisp code used that to automatically update the =file= slot of the active backend object when the user changes the value of =chronometrist-file=. The new solution is to remove direct accesses to the =file= slot, define a =backend-file= method which returns the value of =file= if non-nil (it's usually nil), or derives a file path from =*user-data-file*= and the backend's extension.
|
||||
|
||||
It seems that the =file= slot is now unnecessary - =backend-file= can check =path= instead of =file=, and use either =path= or =*user-data-file*= to derive the full path.
|
||||
|
||||
1. [X] Remove the =file= slot
|
||||
|
||||
** DONE Use pathnames instead of namestrings
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-03T12:30:24+0530
|
||||
:END:
|
||||
|
||||
** CLIM UI design
|
||||
*** One-day task-duration table
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-10T08:09:18+0530
|
||||
:END:
|
||||
columns
|
||||
1. index (for numeric argument)
|
||||
2. task name
|
||||
3. task duration today
|
||||
4. task activity indicator
|
||||
* click to clock in/out (hover to show icon)
|
||||
5. graph of daily task durations
|
||||
* Click on a point in the graph to view that day in the Details View.
|
||||
|
||||
click on row to show taller graph and inline display of either key-value breakdown, or interval breakdown.
|
||||
|
||||
commands
|
||||
1. clock in, clock out, etc
|
||||
2. toggle details for row
|
||||
3. toggle details for all rows
|
||||
4. next/previous day
|
||||
5. set displayed day
|
||||
6. cycle graph heights (self/comparative)
|
||||
* the height of the graphs can be based on the range of each individual graph's own data ('self') or based on the range of all data ('comparative').
|
||||
|
||||
settings
|
||||
1. default graph height
|
||||
|
||||
*** One-day category-duration table
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-10T08:11:20+0530
|
||||
:END:
|
||||
1. category
|
||||
2. time duration today
|
||||
|
||||
commands
|
||||
1. toggle details for row (show tasks in each category)
|
||||
2. toggle details for all rows
|
||||
|
||||
*** One-day event-occurrence table
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-10T08:12:58+0530
|
||||
:END:
|
||||
("Useful for recording things like weight, medication, waist circumference, etc.")
|
||||
1. index (prefix argument)
|
||||
2. event name
|
||||
3. time of occurrence
|
||||
4. graph of past occurrences
|
||||
|
||||
commands
|
||||
1. add event
|
||||
2. record event
|
||||
|
||||
*** 1+ day editable log
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-08T18:23:42+0530
|
||||
:END:
|
||||
Displays tables of day properties, intervals, and events, grouped by day, in date/week/month/year range (default - today)
|
||||
|
||||
can select a day property/interval/event to edit its data
|
||||
|
||||
buttons to add new day properties, intervals, and events
|
||||
|
||||
When there are no intervals/events/day properties, show placeholder text - "No intervals recorded on this day." / "No events recorded on this day." / "No properties for this day."
|
||||
|
||||
Commands
|
||||
1. filter contents
|
||||
2. next/previous day (or range)
|
||||
3. set displayed day (or range)
|
||||
|
||||
*** Line chart views
|
||||
:PROPERTIES:
|
||||
:CREATED: [2022-04-10 Sun 14:35]
|
||||
:END:
|
||||
Line chart with one or more (adjustable) metrics -
|
||||
* duration for task
|
||||
* task-property combinations
|
||||
* numeric property value/function with numeric output
|
||||
...over a time range (adjustable), with points for days/weeks/months/years (adjustable)
|
||||
|
||||
User can switch between single graph vs separate graphs, different types of graphs, etc.
|
||||
|
||||
Commands
|
||||
1. Add metric, remove metric
|
||||
2. Increase/decrease/set range, next/previous range
|
||||
3. Increase/decrease/set granularity
|
||||
|
||||
*** Default view (overview for a day)
|
||||
Default view is a tiled composition of the one-day task-duration table, one-day grouped log, and the one-day category-duration table.
|
||||
|
||||
*** view/edit database as
|
||||
:PROPERTIES:
|
||||
:CREATED: 2022-04-08T19:56:38+0530
|
||||
:END:
|
||||
some way to view/edit the database in a different format, e.g. Lisp plists
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
(defsystem chronometrist-sqlite
|
||||
:version "0.0.1"
|
||||
:serial t
|
||||
:license "Unlicense"
|
||||
:author "contrapunctus <contrapunctus at disroot dot org>"
|
||||
:description "SQLite backend for Chronometrist"
|
||||
:defsystem-depends-on ("literate-lisp")
|
||||
:depends-on (:trivia :clsql-sqlite3)
|
||||
:components ((:org "chronometrist")))
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
(defsystem chronometrist
|
||||
:version "0.0.1"
|
||||
:serial t
|
||||
:license "Unlicense"
|
||||
:author "contrapunctus <contrapunctus at disroot dot org>"
|
||||
:description "Friendly and extensible personal time tracker - common library"
|
||||
:defsystem-depends-on ("literate-lisp")
|
||||
:depends-on (:trivia)
|
||||
:components ((:org "chronometrist")))
|
|
@ -0,0 +1,9 @@
|
|||
(defsystem chronometrist-clim
|
||||
:version "0.0.1"
|
||||
:serial t
|
||||
:license "Unlicense"
|
||||
:author "contrapunctus <contrapunctus at disroot dot org>"
|
||||
:description "Friendly and extensible personal time tracker - CLIM GUI"
|
||||
:defsystem-depends-on ("literate-lisp")
|
||||
:depends-on (:trivia :mcclim)
|
||||
:components ((:org "chronometrist")))
|
|
@ -115,25 +115,14 @@ Return the connection object from `emacsql-sqlite'."
|
|||
finally return db)))
|
||||
#+END_SRC
|
||||
|
||||
** to-file :method:
|
||||
#+BEGIN_SRC emacs-lisp :load no :tangle no
|
||||
(cl-defmethod chronometrist-to-file (hash-table (backend chronometrist-sqlite-backend) file)
|
||||
(cl-loop with db = (emacsql-sqlite file)
|
||||
with count = 0
|
||||
for records being the hash-values of hash-table do
|
||||
(cl-loop for record in records do
|
||||
(chronometrist-insert record db)
|
||||
(cl-incf count)
|
||||
(when (zerop (% count 5))
|
||||
(message "chronometrist-migrate - %s records converted" count)))
|
||||
finally return count do
|
||||
(message "chronometrist-migrate - finished converting %s events." count)))
|
||||
#+END_SRC
|
||||
|
||||
** iso-to-unix :function:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun chronometrist-iso-to-unix (timestamp)
|
||||
(truncate (float-time (parse-iso8601-time-string timestamp))))
|
||||
#+END_SRC
|
||||
|
||||
** to-file :method:
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(cl-defmethod chronometrist-to-file (hash-table (backend chronometrist-sqlite-backend) file)
|
||||
(with-slots (connection) backend
|
||||
(delete-file file)
|
||||
|
|
161
manual.org
161
manual.org
|
@ -1,5 +1,5 @@
|
|||
#+TITLE: Chronometrist - an extensible time tracker for Emacs
|
||||
#+SUBTITLE: Friendly and powerful personal time tracker and analyzer for Emacs
|
||||
#+TITLE: Chronometrist
|
||||
#+SUBTITLE: Friendly and powerful personal time tracker/analyzer with Emacs and CLIM frontends
|
||||
#+DESCRIPTION: User Manual
|
||||
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style.css" />
|
||||
|
||||
|
@ -13,12 +13,13 @@
|
|||
</a>
|
||||
#+END_EXPORT
|
||||
|
||||
Chronometrist is a friendly and powerful personal time tracker and analyzer for Emacs.
|
||||
* Explanation
|
||||
Chronometrist is a friendly and powerful personal time tracker and analyzer. It has frontends for Emacs and [[https://mcclim.common-lisp.dev/][CLIM]].
|
||||
|
||||
#+CAPTION: The main Chronometrist buffer, with the enabled extensions [[#time-goals][chronometrist-goal]] ("Targets" column + alerts) and chronometrist-spark ("Graph" column displaying the activity for the past 4 weeks).
|
||||
[[file:doc/2022-02-20 13-26-53.png]]
|
||||
|
||||
* Benefits
|
||||
** Benefits
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: benefits
|
||||
:END:
|
||||
|
@ -29,32 +30,30 @@ Chronometrist is a friendly and powerful personal time tracker and analyzer for
|
|||
5. Hooks to let you perform arbitrary actions when starting/stopping tasks
|
||||
6. Fancy graphs with the =chronometrist-spark= extension
|
||||
|
||||
* Limitations
|
||||
** Limitations
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: limitations
|
||||
:END:
|
||||
1. No support for concurrent tasks.
|
||||
|
||||
* Comparisons
|
||||
** Comparisons
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: comparisons
|
||||
:END:
|
||||
** timeclock.el
|
||||
*** timeclock.el (Emacs built-in)
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: timeclock.el
|
||||
:END:
|
||||
|
||||
Compared to timeclock.el, Chronometrist
|
||||
+ stores data in an s-expression format rather than a line-based one
|
||||
+ supports attaching tags and arbitrary key-values to time intervals
|
||||
+ has commands to shows useful summaries
|
||||
+ has more hooks
|
||||
|
||||
** Org time tracking
|
||||
*** Org time tracking
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: org-time-tracking
|
||||
:END:
|
||||
|
||||
Chronometrist and Org time tracking seem to be equivalent in terms of capabilities, approaching the same ends through different means.
|
||||
+ Chronometrist doesn't have a mode line indicator at the moment. (planned)
|
||||
+ Chronometrist doesn't have Org's sophisticated querying facilities. (an SQLite backend is planned)
|
||||
|
@ -62,23 +61,92 @@ Chronometrist and Org time tracking seem to be equivalent in terms of capabiliti
|
|||
+ Chronometrist's UI is cleaner, since the storage is separate from the display. It doesn't show tasks as trees like Org, but it uses tags and key-values to achieve that. Additionally, navigating a flat list takes fewer user operations than navigating a tree.
|
||||
+ Chronometrist data is just s-expressions (plists), and may be easier to parse than a complex text format with numerous use-cases.
|
||||
|
||||
* Installation
|
||||
** Common Lisp port
|
||||
In March 2022, work began on the long-awaited Common Lisp port of Chronometrist, which aims to create -
|
||||
1. a greater variety of backends (e.g. SQLite)
|
||||
2. a common reusable library for frontends to use,
|
||||
3. a greater variety of frontends, such as -
|
||||
* a command line interface (CLI), for UNIX scripting;
|
||||
* a terminal user inteface (TUI), for those so inclined;
|
||||
* a CLIM (Common Lisp Interface Manager) GUI [fn:1],
|
||||
* Qt and Android interfaces using [[https://gitlab.com/eql/lqml][LQML]],
|
||||
* web frontends (possibly via [[https://common-lisp.net/project/parenscript/][Parenscript]] or [[https://github.com/rabbibotton/clog][CLOG]]),
|
||||
* and perhaps even an interface for wearable devices!
|
||||
|
||||
The port was also driven by the desire to have access to Common Lisp's better performance, and features such as namespaces, a /de facto/ standard build system, multithreading, SQLite bindings, a more fully-featured implementation of CLOS and MOP, and type annotations, checking, and inference.
|
||||
|
||||
Currently, this port can -
|
||||
1. import from a plist-group file and export to an SQLite database
|
||||
#+BEGIN_SRC lisp
|
||||
(chronometrist:to-file (chronometrist:to-hash-table
|
||||
(make-instance 'chronometrist.plist-group:plist-group-backend
|
||||
:file "/path/to/file.plg"))
|
||||
(make-instance 'chronometrist.sqlite:sqlite-backend)
|
||||
"/path/to/file.sqlite")
|
||||
#+END_SRC
|
||||
2. display a (WIP) CLIM GUI - =(chronometrist.clim:run-chronometrist)=
|
||||
|
||||
[fn:1] McCLIM also has an incomplete ncurses backend - when completed, a CLIM frontend could provide a TUI "for free".
|
||||
|
||||
** Literate Program
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: explanation-literate-program
|
||||
:END:
|
||||
Chronometrist is a literate program, made using Org - the canonical source is the =chronometrist.org= file, which contains source blocks. These are provided to users after /tangling/ (extracting the source into an Emacs Lisp file).
|
||||
|
||||
The Org file can also be loaded directly using the [[https://github.com/jingtaozf/literate-elisp][literate-elisp]] package, so that all source links (e.g. =xref=, =describe-function=) lead to the Org file, within the context of the concerned documentation. See [[#how-to-literate-elisp][How to load the program using literate-elisp]].
|
||||
|
||||
=chronometrist.org= is also included in MELPA installs, although not used directly by default, since doing so would interfere with automatic generation of autoloads.
|
||||
|
||||
** License
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: license
|
||||
:END:
|
||||
I'd /like/ for all software to be liberated - transparent, trustable, and accessible for anyone to use, study, or improve.
|
||||
|
||||
I'd /like/ anyone using my software to credit me for the work.
|
||||
|
||||
I'd /like/ to receive financial support for my efforts, so I can spend all my time doing what I find meaningful.
|
||||
|
||||
But I don't want to make demands or threats (e.g. via legal conditions) to accomplish all that, nor restrict my services to only those who can pay.
|
||||
|
||||
Thus, Chronometrist is released under your choice of [[https://unlicense.org/][Unlicense]] or the [[http://www.wtfpl.net/][WTFPL]].
|
||||
|
||||
(See files [[file:UNLICENSE][UNLICENSE]] and [[file:WTFPL][WTFPL]]).
|
||||
|
||||
** Thanks
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: thanks
|
||||
:END:
|
||||
The main buffer and the report buffer are copied from the Android application, [[https://github.com/netmackan/ATimeTracker][A Time Tracker]]
|
||||
|
||||
wasamasa, bpalmer, aidalgol, pjb and the rest of #emacs for their tireless help and support
|
||||
|
||||
jwiegley for =timeclock.el=, which we used as a backend in earlier versions
|
||||
|
||||
blandest for helping me with the name
|
||||
|
||||
fiete and wu-lee for testing and bug reports
|
||||
|
||||
* Tutorials
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: usage
|
||||
:END:
|
||||
** Installation
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: installation
|
||||
:END:
|
||||
** from MELPA
|
||||
*** from MELPA
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: install-from-melpa
|
||||
:END:
|
||||
|
||||
1. Set up MELPA - https://melpa.org/#/getting-started
|
||||
2. =M-x package-install RET chronometrist RET=
|
||||
|
||||
** from Git
|
||||
*** from Git
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: install-from-git
|
||||
:END:
|
||||
|
||||
You can get =chronometrist= from https://tildegit.org/contrapunctus/chronometrist or https://codeberg.org/contrapunctus/chronometrist
|
||||
|
||||
=chronometrist= requires
|
||||
|
@ -86,18 +154,12 @@ You can get =chronometrist= from https://tildegit.org/contrapunctus/chronometris
|
|||
+ [[https://github.com/magnars/dash.el][dash.el]]
|
||||
+ [[https://github.com/alphapapa/ts.el][ts.el]]
|
||||
|
||||
Add the "elisp/" subdirectory to your load-path, and =(require 'chronometrist)=.
|
||||
|
||||
* Usage
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: usage
|
||||
:END:
|
||||
Add the ="elisp/"= subdirectory to your load-path, and =(require 'chronometrist)=.
|
||||
|
||||
** chronometrist
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: usage-chronometrist
|
||||
:END:
|
||||
|
||||
Run =M-x chronometrist= to see your projects, the time you spent on them today, which one is active, and the total time clocked today.
|
||||
|
||||
Click or hit =RET= (=chronometrist-toggle-task=) on a project to start tracking time for it. If it's already clocked in, it will be clocked out.
|
||||
|
@ -110,7 +172,6 @@ Press =r= to see a weekly report (see =chronometrist-report=)
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: usage-chronometrist-report
|
||||
:END:
|
||||
|
||||
Run =M-x chronometrist-report= (or =chronometrist= with a prefix argument of 1, or press =r= in the =chronometrist= buffer) to see a weekly report.
|
||||
|
||||
Press =b= to look at past weeks, and =f= for future weeks.
|
||||
|
@ -119,7 +180,6 @@ Press =b= to look at past weeks, and =f= for future weeks.
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: usage-chronometrist-statistics
|
||||
:END:
|
||||
|
||||
Run =M-x chronometrist-statistics= (or =chronometrist= with a prefix argument of 2) to view statistics.
|
||||
|
||||
Press =b= to look at past time ranges, and =f= for future ones.
|
||||
|
@ -143,18 +203,16 @@ All buffers keep themselves updated via an idle timer - no need to frequently pr
|
|||
|
||||
If you wish you could define time goals for some tasks, and have Chronometrist notify you when you're approaching the goal, completing it, or exceeding it, check out the extension [[https://github.com/contrapunctus-1/chronometrist-goal/][chronometrist-goal.el]].
|
||||
|
||||
* How-to
|
||||
* How-to Guides
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to
|
||||
:END:
|
||||
|
||||
See the Customize groups =chronometrist= and =chronometrist-report= for variables intended to be user-customizable.
|
||||
|
||||
** How to display a prompt when exiting with an active task
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-prompt-when-exiting-emacs
|
||||
:END:
|
||||
|
||||
Evaluate or add to your init.el the following -
|
||||
=(add-hook 'kill-emacs-query-functions 'chronometrist-query-stop)=
|
||||
|
||||
|
@ -162,7 +220,6 @@ Evaluate or add to your init.el the following -
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-literate-elisp
|
||||
:END:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(add-to-list 'load-path "<directory containing chronometrist.org>")
|
||||
|
||||
|
@ -174,7 +231,6 @@ Evaluate or add to your init.el the following -
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-tags
|
||||
:END:
|
||||
|
||||
1. Add =chronometrist-tags-add= to one or more of these hooks [fn:2] -
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
@ -192,7 +248,6 @@ Evaluate or add to your init.el the following -
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-key-value-pairs
|
||||
:END:
|
||||
|
||||
1. Add =chronometrist-kv-add= to one or more of these hooks [fn:2] -
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
@ -210,7 +265,6 @@ Use =M-RET= (=chronometrist-toggle-task-no-hooks=) to clock in/out.
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-open-files-on-task-start
|
||||
:END:
|
||||
|
||||
An idea from the author's own init -
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
@ -228,7 +282,6 @@ An idea from the author's own init -
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-warn-uncommitted-changes
|
||||
:END:
|
||||
|
||||
Another one, prompting the user if they have uncommitted changes in a git repository (assuming they use [[https://magit.vc/][Magit]]) -
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
@ -253,7 +306,6 @@ Return nil (and run `magit-status') if the user answers no."
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-activity-indicator
|
||||
:END:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun my-activity-indicator ()
|
||||
(--> (chronometrist-latest-record (chronometrist-active-backend))
|
||||
|
@ -271,7 +323,7 @@ Return nil (and run `magit-status') if the user answers no."
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: how-to-backup
|
||||
:END:
|
||||
I suggest backing up Chronometrist data on each save using the [[https://tildegit.org/contrapunctus/async-backup][async-backup]] package.[fn:1] Here's how you can do that.
|
||||
I suggest backing up Chronometrist data on each save using the [[https://tildegit.org/contrapunctus/async-backup][async-backup]] package.[fn:3] Here's how you can do that.
|
||||
|
||||
1. Add the following to your init.
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
@ -282,7 +334,7 @@ I suggest backing up Chronometrist data on each save using the [[https://tildegi
|
|||
: M-x add-file-local-variable-prop-line RET eval RET (add-hook 'after-save-hook #'async-backup nil t) RET
|
||||
3. Optionally, configure =backup-directory-alist= to set a specific directory for the backups.
|
||||
|
||||
[fn:1] It is possible to use Emacs' built-in backup system to do it, but since it is synchronous, doing so will greatly slow down saving of the Chronometrist file.
|
||||
[fn:3] It is possible to use Emacs' built-in backup system to do it, but since it is synchronous, doing so will greatly slow down saving of the Chronometrist file.
|
||||
|
||||
** How to configure Vertico for use with Chronometrist
|
||||
:PROPERTIES:
|
||||
|
@ -306,17 +358,6 @@ Or use =vertico-multiform= to disable sorting for only specific commands -
|
|||
(chronometrist-key-values-unified-prompt (vertico-sort-function . nil)))))
|
||||
#+END_SRC
|
||||
|
||||
* Explanation
|
||||
** Literate Program
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: explanation-literate-program
|
||||
:END:
|
||||
Chronometrist is a literate program, made using Org - the canonical source is the =chronometrist.org= file, which contains source blocks. These are provided to users after /tangling/ (extracting the source into an Emacs Lisp file).
|
||||
|
||||
The Org file can also be loaded directly using the [[https://github.com/jingtaozf/literate-elisp][literate-elisp]] package, so that all source links (e.g. =xref=, =describe-function=) lead to the Org file, within the context of the concerned documentation. See [[#how-to-literate-elisp][How to load the program using literate-elisp]].
|
||||
|
||||
=chronometrist.org= is also included in MELPA installs, although not used directly by default, since doing so would interfere with automatic generation of autoloads.
|
||||
|
||||
* User's reference
|
||||
All variables intended for user customization are listed here. They serve as the public API for this project for the purpose of semantic versioning. Any changes to these which require a user to modify their configuration are considered breaking changes.
|
||||
|
||||
|
@ -348,7 +389,6 @@ Hooks
|
|||
:PROPERTIES:
|
||||
:CUSTOM_ID: contributions-contact
|
||||
:END:
|
||||
|
||||
Feedback and MRs are very welcome. 🙂
|
||||
+ [[file:TODO.org]] has a long list of tasks
|
||||
+ [[file:elisp/chronometrist.org]] contains all developer-oriented documentation
|
||||
|
@ -357,33 +397,6 @@ If you have tried using Chronometrist, I'd love to hear your experiences! Get in
|
|||
|
||||
(For help in getting started with Jabber, [[https://xmpp.org/getting-started/][click here]])
|
||||
|
||||
* License
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: license
|
||||
:END:
|
||||
|
||||
I dream of a world where all software is liberated - transparent, trustable, and accessible for anyone to use or improve. But I don't want to make demands or threats (e.g. via legal conditions) to get there.
|
||||
|
||||
I'd rather make a request - please do everything you can to help that dream come true. Please Unlicense as much software as you can.
|
||||
|
||||
Chronometrist is released under your choice of [[https://unlicense.org/][Unlicense]] or the [[http://www.wtfpl.net/][WTFPL]].
|
||||
|
||||
(See files [[file:UNLICENSE][UNLICENSE]] and [[file:WTFPL][WTFPL]]).
|
||||
|
||||
* Thanks
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: thanks
|
||||
:END:
|
||||
The main buffer and the report buffer are copied from the Android application, [[https://github.com/netmackan/ATimeTracker][A Time Tracker]]
|
||||
|
||||
wasamasa, bpalmer, aidalgol, pjb and the rest of #emacs for their tireless help and support
|
||||
|
||||
jwiegley for =timeclock.el=, which we used as a backend in earlier versions
|
||||
|
||||
blandest for helping me with the name
|
||||
|
||||
fiete and wu-lee for testing and bug reports
|
||||
|
||||
* Local variables :noexport:
|
||||
# Local Variables:
|
||||
# my-org-src-default-lang: "emacs-lisp"
|
||||
|
|
Reference in New Issue