interactive module to generate code/aliases to save things I do often
Project description
TL;DR: This converts a file like this (config file at ~/.config/ttally.py):
# https://github.com/seanbreckenridge/ttally
from datetime import datetime
from typing import NamedTuple, Optional
class Weight(NamedTuple):
when: datetime
pounds: float
class Food(NamedTuple):
when: datetime
calories: int
food: str
quantity: float
water: int # how much ml of water was in this
@staticmethod
def attr_validators() -> dict:
# https://sean.fish/d/ttally_types.py?redirect
from my.config.seanb.ttally_types import prompt_float_default # type: ignore
# if I don't supply a quantity, default to 1
return {"quantity": lambda: prompt_float_default("quantity")}
class Event(NamedTuple):
"""e.g. a concert or something"""
event_type: str
when: datetime
description: str
score: Optional[int]
comments: Optional[str]
@staticmethod
def attr_validators() -> dict:
from my.config.seanb.ttally_types import edit_in_vim # type: ignore
return {"comments": edit_in_vim}
import os
from enum import Enum
with open(os.path.join(os.environ["HPIDATA"], "self_types.txt")) as f:
SelfTypes = Enum("SelfTypes", [s.rstrip().upper() for s in f])
class Self(NamedTuple):
when: datetime
what: SelfTypes # type: ignore
to (shell aliases)...
alias event='python3 -m ttally prompt event'
alias event-now='python3 -m ttally prompt-now event'
alias event-recent='python3 -m ttally recent event'
alias food='python3 -m ttally prompt food'
alias food-now='python3 -m ttally prompt-now food'
alias food-recent='python3 -m ttally recent food'
alias self='python3 -m ttally prompt self'
alias self-now='python3 -m ttally prompt-now self'
alias self-recent='python3 -m ttally recent self'
alias weight='python3 -m ttally prompt weight'
alias weight-now='python3 -m ttally prompt-now weight'
alias weight-recent='python3 -m ttally recent weight'
Whenever I run any of those aliases, it inspects the model in the config file, and on-the-fly creates and runs an interactive interface like this:
... which saves what I enter to a file:
- when: 1598856786,
glasses": 2.0
ttally
ttally is an interactive module using autotui to save things I do often to YAML/JSON
Currently, I use this to store info like whenever I eat something/drink water/my current weight/thoughts on concerts
Given a NamedTuple defined in ~/.config/ttally.py, this creates interactive interfaces which validates my input and saves it to a file
The {tuple}-now aliases set the any datetime values for the prompted tuple to now
This also gives me {tuple}-recent aliases, which print recent items I've logged. For example:
$ water-recent 5
2021-03-20 18:23:24 2.0
2021-03-20 01:28:27 1.0
2021-03-19 23:34:12 1.0
2021-03-19 22:49:05 1.5
2021-03-19 16:05:34 1.0
The -recent aliases can accept all to print all items, or a duration like 1d or 6h to print data from the last few hours/days.
Why/How
Goals
- validates my user input to basic types
- stores it as a user-editable format (YAML)
- can be loaded into python as typed objects
- minimal boilerplate to add a new model
- can be synced across multiple machines without conflicts
- allow completely custom types or prompts - see autotui docs, my custom prompts
This intentionally uses YAML and doesn't store the info into a single "merged" database. That way:
- you can just open the YAML file and quickly change/edit some item, no need to re-invent a CRUD interface (though
ttally edit-recentdoes exist) - files can be synced across machines and to my phone using syncthing without file conflicts
- prevents issues with trying to merge multiple databases from different machines together (I've tried)
The YAML files are versioned with the date/OS/platform, so I'm able to add items on my linux, mac, or android (using termux) and sync them across all my devices using SyncThing. Each device creates its own file it adds items to, like:
food-darwin-seans-mbp.localdomain-2021-03.yaml
food-linux-bastion-2021-03.yaml
food-linux-localhost-2021-04.yaml
... which can then be combined back into python, like:
>>> from more_itertools import take # just to grab a few items
>>> from ttally.__main__ import ext
>>> from ttally.config import Food
>>> take(3, ext.glob_namedtuple(Food))
[Food(when=datetime.datetime(2020, 9, 27, 6, 49, 34, tzinfo=datetime.timezone.utc), calories=440, food='ramen, egg'),
Food(when=datetime.datetime(2020, 9, 27, 6, 52, 16, tzinfo=datetime.timezone.utc), calories=160, food='2 eggs'),
Food(when=datetime.datetime(2020, 9, 27, 6, 53, 44, tzinfo=datetime.timezone.utc), calories=50, food='ginger chai')]
... or into JSON using ttally export food
The from-json command can be used to send this JSON which matches a model, i.e. providing a non-interactive interface to add items, in case I want to call this from a script
hpi query from HPI can be used with the ttally.__main__ module, like:
# how many calories in the last day
$ hpi query ttally.__main__.food --recent 1d -s | jq -r '(.quantity)*(.calories)' | datamash sum 1
2252
If you'd prefer to use JSON files, you can set the TTALLY_EXT=json environment variable.
This can load data from YAML or JSON (or both at the same time), every couple months I'll combine all the versioned files to a single merged file using the merge command:
ttally merge food
Installation
pip install ttally
Usage: ttally [OPTIONS] COMMAND [ARGS]...
Tally things that I do often!
Given a few namedtuples, this creates serializers/deserializers and an
interactive interface using 'autotui', and aliases to:
prompt using default autotui behavior, writing to the ttally datafile, same
as above, but if the model has a datetime, set it to now, query the 10 most
recent items for a model
Options:
--help Show this message and exit.
Commands:
datafile print the datafile location
edit edit the datafile
edit-recent fuzzy select/edit recent items
export export all data from a model
from-json add item by piping JSON
generate generate shell aliases
merge merge all data for a model into one file
models list models
prompt tally an item
prompt-now tally an item (now)
recent print recently tallied items
update-cache cache export data
Configuration
You need to setup a ~/.config/ttally.py file. You can use the block above as a starting point, or with mine:
curl -s 'https://sean.fish/d/ttally.py' > ~/.config/ttally.py
To setup aliases; You can do it each time you launch you terminal like:
eval "$(python3 -m ttally generate)"
Or, 'cache' the generated aliases by putting a block like this in your shell config:
TTALLY_ALIASES="${HOME}/.cache/ttally_aliases"
if [[ ! -e "${TTALLY_ALIASES}" ]]; then # alias file doesn't exist
python3 -m ttally generate >"${TTALLY_ALIASES}" # generate and save the aliases
fi
source "${TTALLY_ALIASES}" # make aliases available in your shell
i.e., it runs the first time I open a terminal, but then stays the same until I remove the file
You can set the TTALLY_DATA_DIR environment variable to the directory that ttally should save data to, defaults to ~/.local/share/ttally. If you want to use a different path for configuration, you can set the TTALLY_CFG to the absolute path to the file.
For shell completion to autocomplete options/model names:
eval "$(_TTALLY_COMPLETE=bash_source ttally)" # in ~/.bashrc
eval "$(_TTALLY_COMPLETE=zsh_source ttally)" # in ~/.zshrc
eval "$(_TTALLY_COMPLETE=fish_source ttally)" # in ~/.config/fish/config.fish
Caching
ttally update-cache can be used to speedup the export and recent commands:
Usage: ttally update-cache [OPTIONS]
Caches data for 'export' and 'recent' by saving the current data and an
index to ~/.cache/ttally
exit code 0 if cache was updated, 2 if it was already up to date
Options:
--print-hashes print current filehash debug info
--help Show this message and exit.
I personally run it once every 3 minutes in the background, so at least my first interaction with ttally is guaranteed to be fast
Default cache directory can be overwritten with the TTALLY_CACHE_DIR environment variable
Subclassing/Extension
The entire ttally library/CLI can also be subclassed/extended for custom usage, by using ttally.core.Extension class and wrap_cli to add additional click commands. For an example, see flipflop.py
Shell Scripts
cz lets me fuzzy select something I've eaten in the past using fzf, like:
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ttally-0.1.4.tar.gz.
File metadata
- Download URL: ttally-0.1.4.tar.gz
- Upload date:
- Size: 22.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96f945a4a8f36638d2eeefef0c0e9788916490766d90b6ce5e4c23a01d4036cf
|
|
| MD5 |
3b5b1c63edc142b0622cb29beffbacdb
|
|
| BLAKE2b-256 |
14fccf0ed2cf01d65d58edb05db9a56acbb5867e1b9b501ca3feca24a2a3e89d
|
File details
Details for the file ttally-0.1.4-py3-none-any.whl.
File metadata
- Download URL: ttally-0.1.4-py3-none-any.whl
- Upload date:
- Size: 18.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b0cd23c88e90e3fff1a8ec8607fe1b7e1151fcae0caf8171e94facc473826b6
|
|
| MD5 |
4c791ecf522f5f669df1ea0fc1f3ff24
|
|
| BLAKE2b-256 |
09d482729d81fb1721f0008affd40e46213a40b407e43b4691ed41776a8226de
|