elog -> evlog

This commit is contained in:
Jeffrey Serio 2025-04-19 11:03:47 -05:00
parent 154280cec9
commit 8e23b695bb
3 changed files with 100 additions and 96 deletions

View File

@ -1,31 +1,31 @@
# daily-event-logger # evlog
This is a little utility I use for logging my daily activities and events. It is written in Python. This is a little utility I use for logging my daily activities and events. It is written in Python.
## Install ## Install
```bash ```bash
python3 -m pip install daily-event-logger pipx install evlog
``` ```
## Usage ## Usage
To change the directory where elogs are stored, set a shell environment variable ELOG_DIR. To make this change permament, set the following in your shell configuration: To change the directory where evlogs are stored, set a shell environment variable EVLOG_DIR. To make this change permament, set the following in your shell configuration:
```bash ```bash
export ELOG_DIR="/path/to/elog/dir" export EVLOG_DIR="/path/to/evlog/dir"
``` ```
Otherwise, the default elog directory will be `~/elogs`. Otherwise, the default evlog directory will be `~/evlogs`.
To get started, add your first elog entry! This will create a JSON file under your elog directory for the day and ensure the elog directory exists. E.g.: To get started, add your first evlog entry! This will create a JSON file under your evlog directory for the day and ensure the evlog directory exists. E.g.:
```bash ```bash
elog add -m "Started new elog. Yay!" evlog add -m "Started new evlog. Yay!"
``` ```
```bash ```bash
usage: elog [-h] [-v] {add,edit,rm,ls,lsfiles,search} ... usage: evlog [-h] [-v] {add,edit,rm,ls,lsfiles,search} ...
positional arguments: positional arguments:
{add,edit,rm,ls,lsfiles,search} {add,edit,rm,ls,lsfiles,search}

174
evlog.py
View File

@ -17,50 +17,50 @@ from rich.traceback import install
install(show_locals=True) install(show_locals=True)
VERSION = "0.1.5" VERSION = "0.1.6"
default_date = dt.date.today().strftime("%Y-%m-%d") default_date = dt.date.today().strftime("%Y-%m-%d")
ELOG_DIR = os.getenv("ELOG_DIR") EVLOG_DIR = os.getenv("EVLOG_DIR")
if ELOG_DIR is None: if EVLOG_DIR is None:
elog_dir = Path("~/elogs").expanduser() evlog_dir = Path("~/evlogs").expanduser()
else: else:
elog_dir = Path(ELOG_DIR) evlog_dir = Path(EVLOG_DIR)
def elog_init(filename): def evlog_init(filename):
"""Initialize elog file and directory, if necessary. """Initialize evlog file and directory, if necessary.
elog_dir is taken from the current shell's ELOG_DIR environment variable, or else it defaults to evlog_dir is taken from the current shell's EVLOG_DIR environment variable, or else it defaults to
~/elogs. ~/evlogs.
""" """
elog_file = elog_dir.joinpath(filename).with_suffix(".json") evlog_file = evlog_dir.joinpath(filename).with_suffix(".json")
elog_dir.mkdir(exist_ok=True) evlog_dir.mkdir(exist_ok=True)
elog_file.touch() evlog_file.touch()
json_array = [] json_array = []
with open(elog_file, "w") as ef: with open(evlog_file, "w") as ef:
json.dump(json_array, ef) json.dump(json_array, ef)
def elog_list(args): def evlog_list(args):
"""List elog entries. """List evlog entries.
Lists elog entries for provided timestamp range and/or elog file Lists evlog entries for provided timestamp range and/or evlog file
""" """
if args.file: if args.file:
selected_elog_file = elog_dir.joinpath(args.file) selected_evlog_file = evlog_dir.joinpath(args.file)
else: else:
selected_elog_file = elog_dir.joinpath(default_date + "_elog").with_suffix( selected_evlog_file = evlog_dir.joinpath(default_date + "_evlog").with_suffix(
".json" ".json"
) )
if not selected_elog_file.exists(): if not selected_evlog_file.exists():
exit("elog file %s not found. Are you sure it exists?" % selected_elog_file) exit("evlog file %s not found. Are you sure it exists?" % selected_evlog_file)
if not args.start: if not args.start:
ts_from = selected_elog_file.stem[:10] + " 00:00:00" ts_from = selected_evlog_file.stem[:10] + " 00:00:00"
else: else:
dt.datetime.strptime(args.start, "%Y-%m-%d %H:%M:%S") dt.datetime.strptime(args.start, "%Y-%m-%d %H:%M:%S")
ts_from = args.start ts_from = args.start
@ -71,7 +71,7 @@ def elog_list(args):
dt.datetime.strptime(args.end, "%Y-%m-%d %H:%M:%S") dt.datetime.strptime(args.end, "%Y-%m-%d %H:%M:%S")
ts_to = args.end ts_to = args.end
with open(selected_elog_file, "r") as ef: with open(selected_evlog_file, "r") as ef:
json_data = json.load(ef) json_data = json.load(ef)
table = Table(style="#5f00ff", header_style="bold", box=box.ROUNDED) table = Table(style="#5f00ff", header_style="bold", box=box.ROUNDED)
@ -87,12 +87,12 @@ def elog_list(args):
console.print(table) console.print(table)
def elog_list_files(args): def evlog_list_files(args):
"""List all elog files. """List all evlog files.
Lists all elog files currently present in elog directory. Lists all evlog files currently present in evlog directory.
""" """
for file in sorted(elog_dir.iterdir()): for file in sorted(evlog_dir.iterdir()):
if file.is_file(): if file.is_file():
if args.absolute: if args.absolute:
print(file) print(file)
@ -100,18 +100,18 @@ def elog_list_files(args):
print(file.name) print(file.name)
def elog_search(args): def evlog_search(args):
"""Search for a string. """Search for a string.
Searches all elog files and prints found matches. Searches all evlog files and prints found matches.
""" """
found_entries = list() found_entries = list()
elog_list = [file.name for file in elog_dir.iterdir()] evlog_list = [file.name for file in evlog_dir.iterdir()]
console = Console() console = Console()
for file in elog_list: for file in evlog_list:
with open(elog_dir.joinpath(file), "r") as ef: with open(evlog_dir.joinpath(file), "r") as ef:
json_data = json.load(ef) json_data = json.load(ef)
for entry in json_data: for entry in json_data:
if args.word in entry["message"]: if args.word in entry["message"]:
@ -126,14 +126,14 @@ def elog_search(args):
) )
else: else:
console.print( console.print(
"[bold yellow]{0}[/bold yellow] was not found in any of the elog files".format( "[bold yellow]{0}[/bold yellow] was not found in any of the evlog files".format(
args.word args.word
) )
) )
def elog_sort(file): def evlog_sort(file):
"""Sort elog entries. """Sort evlog entries.
Entries are sorted by provided timestamp. Entries are sorted by provided timestamp.
""" """
@ -154,7 +154,7 @@ def validate_json(file):
Call jsonschema.validate on `file`. Call jsonschema.validate on `file`.
""" """
elog_schema = { evlog_schema = {
"type": "array", "type": "array",
"properties": { "properties": {
"timestamp": {"type": "string"}, "timestamp": {"type": "string"},
@ -166,17 +166,17 @@ def validate_json(file):
json_data = json.load(ef) json_data = json.load(ef)
try: try:
validate(instance=json_data, schema=elog_schema) validate(instance=json_data, schema=evlog_schema)
except jsonschema.ValidationError as err: except jsonschema.ValidationError as err:
print("Invalid JSON detected on %s" % file) print("Invalid JSON detected on %s" % file)
print(err) print(err)
def elog_append(args): def evlog_append(args):
""" """
Append a new elog entry to the elog file. Append a new evlog entry to the evlog file.
Use elog file indicated by provided timestamp, or else use the elog file for current day. Use evlog file indicated by provided timestamp, or else use the evlog file for current day.
""" """
if not args.timestamp: if not args.timestamp:
ts = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S") ts = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@ -184,64 +184,68 @@ def elog_append(args):
dt.datetime.strptime(args.timestamp, "%Y-%m-%d %H:%M:%S") dt.datetime.strptime(args.timestamp, "%Y-%m-%d %H:%M:%S")
ts = args.timestamp ts = args.timestamp
elog_filename = ts[:10] + "_elog" evlog_filename = ts[:10] + "_evlog"
elog_file = elog_dir.joinpath(elog_filename).with_suffix(".json") evlog_file = evlog_dir.joinpath(evlog_filename).with_suffix(".json")
if not elog_file.exists(): if not evlog_file.exists():
elog_init(elog_file) evlog_init(evlog_file)
entry = {"timestamp": ts, "message": args.message} entry = {"timestamp": ts, "message": args.message}
with open(elog_file, "r+") as ef: with open(evlog_file, "r+") as ef:
json_data = json.load(ef) json_data = json.load(ef)
json_data.append(entry) json_data.append(entry)
ef.seek(0) ef.seek(0)
json.dump(json_data, ef, indent=4) json.dump(json_data, ef, indent=4)
elog_sort(elog_file) evlog_sort(evlog_file)
validate_json(elog_file) validate_json(evlog_file)
def elog_edit(args): def evlog_edit(args):
"""Edit elog entry at provided index argument.""" """Edit evlog entry at provided index argument."""
if args.file: if args.file:
elog_file = elog_dir.joinpath(args.file) evlog_file = evlog_dir.joinpath(args.file)
else: else:
elog_file = elog_dir.joinpath(default_date + "_elog").with_suffix(".json") evlog_file = evlog_dir.joinpath(default_date + "_evlog").with_suffix(".json")
if not elog_file.exists(): if not evlog_file.exists():
exit("elog file not found. Please run 'elog append' to start a new elog file.") exit(
"evlog file not found. Please run 'evlog append' to start a new evlog file."
)
with open(elog_file, "r+") as ef: with open(evlog_file, "r+") as ef:
json_data = json.load(ef) json_data = json.load(ef)
json_data[args.index]["message"] = args.message json_data[args.index]["message"] = args.message
ef.seek(0) ef.seek(0)
json.dump(json_data, ef, indent=4) json.dump(json_data, ef, indent=4)
validate_json(elog_file) validate_json(evlog_file)
def elog_remove(args): def evlog_remove(args):
"""Remove an elog entry at provided index argument.""" """Remove an evlog entry at provided index argument."""
if args.file: if args.file:
elog_file = elog_dir.joinpath(args.file) evlog_file = evlog_dir.joinpath(args.file)
else: else:
elog_file = elog_dir.joinpath(default_date + "_elog").with_suffix(".json") evlog_file = evlog_dir.joinpath(default_date + "_evlog").with_suffix(".json")
if not elog_file.exists(): if not evlog_file.exists():
exit("elog file not found. Please run 'elog append' to start a new elog file.") exit(
"evlog file not found. Please run 'evlog append' to start a new evlog file."
)
with open(elog_file, "r") as ef: with open(evlog_file, "r") as ef:
json_data = json.load(ef) json_data = json.load(ef)
json_data.pop(args.index) json_data.pop(args.index)
with open(elog_file, "w") as ef: with open(evlog_file, "w") as ef:
json.dump(json_data, ef, indent=4) json.dump(json_data, ef, indent=4)
validate_json(elog_file) validate_json(evlog_file)
parser = argparse.ArgumentParser(prog="elog") parser = argparse.ArgumentParser(prog="evlog")
parser.add_argument( parser.add_argument(
"-v", "-v",
"--version", "--version",
@ -251,14 +255,14 @@ parser.add_argument(
) )
subparsers = parser.add_subparsers() subparsers = parser.add_subparsers()
add_parser = subparsers.add_parser("add", description="Add an elog entry") add_parser = subparsers.add_parser("add", description="Add an evlog entry")
add_parser.add_argument( add_parser.add_argument(
"-t", "-t",
"--timestamp", "--timestamp",
required=False, required=False,
type=str, type=str,
action="store", action="store",
help="Timestamp for elog entry: str", help="Timestamp for evlog entry: str",
) )
add_parser.add_argument( add_parser.add_argument(
"-m", "-m",
@ -266,18 +270,18 @@ add_parser.add_argument(
required=True, required=True,
type=str, type=str,
action="store", action="store",
help="Message for elog entry: str", help="Message for evlog entry: str",
) )
add_parser.set_defaults(func=elog_append) add_parser.set_defaults(func=evlog_append)
edit_parser = subparsers.add_parser("edit", description="Edit an elog entry") edit_parser = subparsers.add_parser("edit", description="Edit an evlog entry")
edit_parser.add_argument( edit_parser.add_argument(
"-i", "-i",
"--index", "--index",
required=True, required=True,
type=int, type=int,
action="store", action="store",
help="Index of elog entry: int", help="Index of evlog entry: int",
) )
edit_parser.add_argument( edit_parser.add_argument(
"-m", "-m",
@ -285,7 +289,7 @@ edit_parser.add_argument(
required=True, required=True,
type=str, type=str,
action="store", action="store",
help="New message for elog entry: str", help="New message for evlog entry: str",
) )
edit_parser.add_argument( edit_parser.add_argument(
"-f", "-f",
@ -293,18 +297,18 @@ edit_parser.add_argument(
required=False, required=False,
type=str, type=str,
action="store", action="store",
help="elog file to edit. Ex: 2022-10-02_elog.json", help="evlog file to edit. Ex: 2022-10-02_evlog.json",
) )
edit_parser.set_defaults(func=elog_edit) edit_parser.set_defaults(func=evlog_edit)
rm_parser = subparsers.add_parser("rm", description="Remove an elog entry") rm_parser = subparsers.add_parser("rm", description="Remove an evlog entry")
rm_parser.add_argument( rm_parser.add_argument(
"-i", "-i",
"--index", "--index",
required=True, required=True,
type=int, type=int,
action="store", action="store",
help="Index of elog entry: int", help="Index of evlog entry: int",
) )
rm_parser.add_argument( rm_parser.add_argument(
"-f", "-f",
@ -312,11 +316,11 @@ rm_parser.add_argument(
required=False, required=False,
type=str, type=str,
action="store", action="store",
help="elog file to remove from. Ex: 2022-10-02_elog.json", help="evlog file to remove from. Ex: 2022-10-02_evlog.json",
) )
rm_parser.set_defaults(func=elog_remove) rm_parser.set_defaults(func=evlog_remove)
ls_parser = subparsers.add_parser("ls", description="List elog entries") ls_parser = subparsers.add_parser("ls", description="List evlog entries")
ls_parser.add_argument( ls_parser.add_argument(
"-s", "-s",
"--start", "--start",
@ -341,27 +345,27 @@ ls_parser.add_argument(
required=False, required=False,
type=str, type=str,
action="store", action="store",
help="elog file to view. Ex: 2022-10-02_elog.json", help="evlog file to view. Ex: 2022-10-02_evlog.json",
) )
ls_parser.set_defaults(func=elog_list) ls_parser.set_defaults(func=evlog_list)
ls_files_parser = subparsers.add_parser("lsfiles", description="List all elog files") ls_files_parser = subparsers.add_parser("lsfiles", description="List all evlog files")
ls_files_parser.add_argument( ls_files_parser.add_argument(
"-a", "-a",
"--absolute", "--absolute",
required=False, required=False,
action="store_true", action="store_true",
help="List the absolute paths of the elog files", help="List the absolute paths of the evlog files",
) )
ls_files_parser.set_defaults(func=elog_list_files) ls_files_parser.set_defaults(func=evlog_list_files)
search_parser = subparsers.add_parser( search_parser = subparsers.add_parser(
"search", description="Search for keywords in elog files" "search", description="Search for keywords in evlog files"
) )
search_parser.add_argument( search_parser.add_argument(
"-w", "--word", required=True, type=str, action="store", help="Word to search for" "-w", "--word", required=True, type=str, action="store", help="Word to search for"
) )
search_parser.set_defaults(func=elog_search) search_parser.set_defaults(func=evlog_search)
def main(): def main():

View File

@ -1,6 +1,6 @@
[project] [project]
name = "evlog" name = "evlog"
version = "0.1.5" version = "0.1.6"
authors = [ authors = [
{ name="Jeffrey Serio", email="hyperreal@moonshadow.dev" }, { name="Jeffrey Serio", email="hyperreal@moonshadow.dev" },
] ]
@ -18,8 +18,8 @@ dependencies = ["jsonschema>=4.17.0", "rich>=12.6.0"]
elog = "evlog:main" elog = "evlog:main"
[project.urls] [project.urls]
Homepage = "https://git.hyperreal.coffee/hyperreal/daily-event-logger" Homepage = "https://git.hyperreal.coffee/hyperreal/evlog"
Issues = "https://git.hyperreal.coffee/hyperreal/daily-event-logger/issues" Issues = "https://git.hyperreal.coffee/hyperreal/evlog/issues"
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling"]