kk - Personal diary/journaling/logging tool

kk is an ultra-lightweight tool for journaling, logging and diarykeeping. It stores the journal in a sqlite3 database, ensuring fast updates and searches even when the logging gets fairly involved. kk is inspired by the awesome jrnl app, and my intention is to make kk "scratch my own itch" for specialty needs like templates, reporting and logging.

If all you need is a command-line journal app with tagging and simple searching, you probably want to use jrnl.

Feature Comparison with jrnl

This is a list of the key features of kk and jrnl so that I can implement them using this document as a sort of "README-driven development" guide.

Feature kk jrnl
Exists at all, in any way, shape or form YES yes
Add entries from the command line YES yes
Add journal entry with title and date NO yes
Show all journal entries NO yes
Convenient dates and times NO yes
Export to json NO yes
Search by date or date range NO yes
List tags NO yes
Add tags with @ from CLI NO yes
Search by tag NO yes
256-bit AES Encryption NO yes
Portable by saving file to dropbox NO yes
Init config, db on startup if no db NO yes
Web server NO NO
CLI->browser capture for longer entries NO NO
Template support NO NO
Extra document data NO NO
Search by document data NO NO

Simple Usage Examples

kk Lunch. Went to favorite place.
# title: "Lunch", body: "Went to favorite place.", date: today, time: now

kk 3pm: Late lunch
# title: "Late lunch", body: nil, date: today, time: 3pm

kk yesterday: Tracked time for @work.
# title: "Tracked time for @work", body: nil, time: none, date: yesterday

Template Support

I'm not sure yet how I want to tackle this, even to demonstrate how I'd use it (a la "design by wishful thinking"), but in short I want to create templates that let me quickly add new entries later. These examples will likely change!

kk --templates
# -> list templates

kk %fbg title: "Fasting Blood Glucose", body: %1, glucose:i: %1, fasting:b: true
# -> Creates/updates template "fbg" so that it takes one value
# from the command-line and writes it as the body of the journal
# entry. It sets the title to "Fasting Blood Glucose", and then
# adds extra data fields "glucose" (integer, set to .to_i of %1)
# and "fasting" (bool, set to true). TOTALLY UNSURE if I even want
# to support special types here or if everything should just be
# strings. On one hand I could see it all going into a json blob
# in the database anyway; on the other hand if create objects that
# the database can understand I can sift and sort by those types
# rather than by strings, so 10 would come after 9 instead of
# between 1 and 2, etc.

kk %fbg 86
# -> Records a glucose entry of 86.

Extra Document Data

An extension of the extra fields idea raised by the templating thought. Ideally I want to all some kind of NoSQL-like data storage and searching, like "Show me all the glucose logs that are fasting: false" or "Show me the highest glucose entry in this date range". For now I'm thinking of either a JSON blob field, or if I move to postgres I can consider using an hstore column.

TODO: Give some serious thought to rolling the whole thing with PStore or, even better, YAML::Store instead of sqlite. This would essentially make the whole store a NoSQL-style database. It essentially returns a giant hash so its use as a Key/Value store would be obvious. Expanding it to a "proper" document database might require more tooling than it's worth, dunno. Definitely have to store off my own indices, but I might be able to aggregate the values into document objects if the YAML store has a clean marshaling mechanism.

Do I want to go to the trouble of separating the data access from the application? That means rolling a lot of the data access myself, sigh, but would it be worth it to be able to switch from sqlite to yaml/store?

(Another consideration is speed: I know sqlite3 is pretty fast, but yaml store could end up implicitly creating objects for the entire file every time I loaded it. The result would be a huge performance hit once the journal got large. Food for thought. One option would be to roll my own store somehow, but right now I think perhaps the best option might be to use sqlite3 and roll as much of a key/value store on top of it as I need, but only as I need it.)