2 Commits
0.1.0 ... 0.2.1

2 changed files with 56 additions and 34 deletions

88
ptk.nim
View File

@ -8,7 +8,7 @@ import algorithm, docopt, json, langutils, logging, os, sequtils, strutils,
import ptkutil import ptkutil
type type
Mark* = tuple[id: UUID, time: TimeInfo, summary: string, notes: string] Mark* = tuple[id: UUID, time: TimeInfo, summary: string, notes: string, tags: seq[string]]
Timeline* = tuple[name: string, marks: seq[Mark]] Timeline* = tuple[name: string, marks: seq[Mark]]
const STOP_MSG = "STOP" const STOP_MSG = "STOP"
@ -16,12 +16,12 @@ const STOP_MSG = "STOP"
let NO_MARK: Mark = ( let NO_MARK: Mark = (
id: parseUUID("00000000-0000-0000-0000-000000000000"), id: parseUUID("00000000-0000-0000-0000-000000000000"),
time: getLocalTime(getTime()), time: getLocalTime(getTime()),
summary: "", notes: "") summary: "", notes: "", tags: @[])
const ISO_TIME_FORMAT = "yyyy:MM:dd'T'HH:mm:ss" const ISO_TIME_FORMAT = "yyyy:MM:dd'T'HH:mm:ss"
const TIME_FORMATS = @[ const TIME_FORMATS = @[
"HH:mm", "HH:mm:ss", "HH:mm:ss", "H:mm", "HH:mm", "H:mm:ss", "HH:mm:ss",
"yyyy:MM:dd'T'HH:mm:ss", "yyyy:MM:dd'T'HH:mm"] "yyyy:MM:dd'T'HH:mm:ss", "yyyy:MM:dd'T'HH:mm"]
#proc `$`*(mark: Mark): string = #proc `$`*(mark: Mark): string =
@ -43,7 +43,8 @@ template `%`(mark: Mark): JsonNode =
"id": $(mark.id), "id": $(mark.id),
"time": mark.time.format(ISO_TIME_FORMAT), "time": mark.time.format(ISO_TIME_FORMAT),
"summary": mark.summary, "summary": mark.summary,
"notes": mark.notes "notes": mark.notes,
"tags": mark.tags
} }
template `%`(timeline: Timeline): JsonNode = template `%`(timeline: Timeline): JsonNode =
@ -63,7 +64,8 @@ proc loadTimeline(filename: string): Timeline =
id: parseUUID(markJson["id"].getStr()), id: parseUUID(markJson["id"].getStr()),
time: parse(markJson["time"].getStr(), ISO_TIME_FORMAT), time: parse(markJson["time"].getStr(), ISO_TIME_FORMAT),
summary: markJson["summary"].getStr(), summary: markJson["summary"].getStr(),
notes: markJson["notes"].getStr())) notes: markJson["notes"].getStr(),
tags: markJson["tags"].getElems(@[]).map(proc (t: JsonNode): string = t.getStr())))
return timeline return timeline
@ -121,7 +123,14 @@ proc writeMarks(marks: seq[Mark], includeNotes = false): void =
setForegroundColor(stdout, fgCyan) setForegroundColor(stdout, fgCyan)
write(stdout, " (" & duration & ")") write(stdout, " (" & duration & ")")
resetAttributes(stdout) resetAttributes(stdout)
writeLine(stdout, spaces(longestPrefix - prefixLens[i]) & " -- " & mark.summary) write(stdout, spaces(longestPrefix - prefixLens[i]) & " -- " & mark.summary)
if mark.tags.len > 0:
setForegroundColor(stdout, fgGreen)
write(stdout, " (" & mark.tags.join(", ") & ")")
resetAttributes(stdout)
writeLine(stdout, "")
if includeNotes and len(mark.notes.strip) > 0: if includeNotes and len(mark.notes.strip) > 0:
writeLine(stdout, spaces(longestPrefix) & mark.notes) writeLine(stdout, spaces(longestPrefix) & mark.notes)
@ -170,6 +179,8 @@ proc doInit(timelineLocation: string): void =
timelineFile.write($timeline.pretty) timelineFile.write($timeline.pretty)
finally: close(timelineFile) finally: close(timelineFile)
type ExpectedMarkPart = enum Time, Summary, Tags, Notes
proc edit(mark: var Mark): void = proc edit(mark: var Mark): void =
var var
tempFile: File tempFile: File
@ -178,10 +189,11 @@ proc edit(mark: var Mark): void =
try: try:
(tempFile, tempFileName) = mkstemp("timestamp-mark-", ".txt", "", fmWrite) (tempFile, tempFileName) = mkstemp("timestamp-mark-", ".txt", "", fmWrite)
tempFile.writeLine( tempFile.writeLine(
"""# Edit the time, mark, and notes below. Any lines starting with '#' will be """# Edit the time, mark, tags, and notes below. Any lines starting with '#' will
# ignored. When done, save the file and close the editor.""") # be ignored. When done, save the file and close the editor.""")
tempFile.writeLine(mark.time.format(ISO_TIME_FORMAT)) tempFile.writeLine(mark.time.format(ISO_TIME_FORMAT))
tempFile.writeLine(mark.summary) tempFile.writeLine(mark.summary)
tempFile.writeLine(mark.tags.join(","))
tempFile.writeLine( tempFile.writeLine(
"""# Everything from the line below to the end of the file will be considered """# Everything from the line below to the end of the file will be considered
# notes for this timeline mark.""") # notes for this timeline mark.""")
@ -190,15 +202,16 @@ proc edit(mark: var Mark): void =
discard os.execShellCmd "$EDITOR " & tempFileName & " </dev/tty >/dev/tty" discard os.execShellCmd "$EDITOR " & tempFileName & " </dev/tty >/dev/tty"
var var markPart = Time
readTime = false
readSummary = false
for line in lines tempFileName: for line in lines tempFileName:
if strip(line)[0] == '#': continue if strip(line)[0] == '#': continue
elif not readTime: mark.time = parseTime(line); readTime = true elif markPart == Time: mark.time = parseTime(line); markPart = Summary
elif not readSummary: mark.summary = line; readSummary = true elif markPart == Summary: mark.summary = line; markPart = Tags
else: mark.notes &= line elif markPart == Tags:
mark.tags = line.split({',', ';'});
markPart = Notes
else: mark.notes &= line & "\x0D\x0A"
finally: close(tempFile) finally: close(tempFile)
@ -209,11 +222,11 @@ Usage:
ptk init [options] ptk init [options]
ptk add [options] ptk add [options]
ptk add [options] <summary> ptk add [options] <summary>
ptk ammend [options] <id> [<summary>] ptk amend [options] <id> [<summary>]
ptk stop [options] ptk stop [options]
ptk continue ptk continue
ptk delete <id> ptk delete <id>
ptk list [options] ptk (list | ls) [options]
ptk sum-time --ids <ids>... ptk sum-time --ids <ids>...
ptk sum-time [options] [<firstId>] [<lastId>] ptk sum-time [options] [<firstId>] [<lastId>]
ptk (-V | --version) ptk (-V | --version)
@ -221,17 +234,19 @@ Usage:
Options: Options:
-f --file <file> Use the given timeline file. -E --echo-args Echo the program's understanding of it's arguments.
-c --config <cfgFile> Use <cfgFile> as configuration for the CLI.
-t --time <time> For add and ammend, use this time instead of the current time.
-n --notes <notes> For add and ammend, set the notes for a time mark.
-V --version Print the tool's version information. -V --version Print the tool's version information.
-e --edit Open the mark in an editor.
-a --after <after> Restrict the selection to marks after <after>. -a --after <after> Restrict the selection to marks after <after>.
-b --before <before> Restrict the selection to marks after <before>. -b --before <before> Restrict the selection to marks after <before>.
-c --config <cfgFile> Use <cfgFile> as configuration for the CLI.
-e --edit Open the mark in an editor.
-f --file <file> Use the given timeline file.
-g --tags <tags> Add the given tags (comma-separated) to the selected marks.
-G --remove-tags <tagx> Remove the given tag from the selected marks.
-h --help Print this usage information. -h --help Print this usage information.
-n --notes <notes> For add and amend, set the notes for a time mark.
-t --time <time> For add and amend, use this time instead of the current time.
-v --verbose Include notes in timeline entry output. -v --verbose Include notes in timeline entry output.
-E --echo-args Echo the program's understanding of it's arguments.
""" """
# TODO: add ptk delete [options] # TODO: add ptk delete [options]
@ -239,7 +254,7 @@ Options:
logging.addHandler(newConsoleLogger()) logging.addHandler(newConsoleLogger())
# Parse arguments # Parse arguments
let args = docopt(doc, version = "ptk 0.1.0") let args = docopt(doc, version = "ptk 0.2.1")
if args["--echo-args"]: echo $args if args["--echo-args"]: echo $args
@ -295,13 +310,14 @@ Options:
if args["stop"]: if args["stop"]:
let newMark = ( let newMark: Mark = (
id: genUUID(), id: genUUID(),
time: time:
if args["--time"]: parseTime($args["--time"]) if args["--time"]: parseTime($args["--time"])
else: getLocalTime(getTime()), else: getLocalTime(getTime()),
summary: STOP_MSG, summary: STOP_MSG,
notes: args["--notes"] ?: "") notes: args["--notes"] ?: "",
tags: (args["--tags"] ?: "").split({',', ';'}))
timeline.marks.add(newMark) timeline.marks.add(newMark)
writeMarks( writeMarks(
@ -327,7 +343,8 @@ Options:
if args["--time"]: parseTime($args["--time"]) if args["--time"]: parseTime($args["--time"])
else: getLocalTime(getTime()), else: getLocalTime(getTime()),
summary: prevMark.summary, summary: prevMark.summary,
notes: prevMark.notes) notes: prevMark.notes,
tags: prevMark.tags)
timeline.marks.add(newMark) timeline.marks.add(newMark)
writeMarks(marks = @[newMark], includeNotes = args["--verbose"]) writeMarks(marks = @[newMark], includeNotes = args["--verbose"])
@ -342,7 +359,8 @@ Options:
if args["--time"]: parseTime($args["--time"]) if args["--time"]: parseTime($args["--time"])
else: getLocalTime(getTime()), else: getLocalTime(getTime()),
summary: args["<summary>"] ?: "", summary: args["<summary>"] ?: "",
notes: args["--notes"] ?: "") notes: args["--notes"] ?: "",
tags: (args["--tags"] ?: "").split({',', ';'}))
if args["--edit"]: edit(newMark) if args["--edit"]: edit(newMark)
@ -351,7 +369,7 @@ Options:
saveTimeline(timeline, timelineLocation) saveTimeline(timeline, timelineLocation)
if args["ammend"]: if args["amend"]:
# Note, this returns a copy, not a reference to the mark in the seq. # Note, this returns a copy, not a reference to the mark in the seq.
let markIdx = timeline.marks.findById($args["<id>"]) let markIdx = timeline.marks.findById($args["<id>"])
@ -359,6 +377,13 @@ Options:
if args["<summary>"]: mark.summary = $args["<summary>"] if args["<summary>"]: mark.summary = $args["<summary>"]
if args["--notes"]: mark.notes = $args["<notes>"] if args["--notes"]: mark.notes = $args["<notes>"]
if args["--tags"]:
mark.tags &= (args["--tags"] ?: "").split({',', ';'})
mark.tags = mark.tags.deduplicate
if args["--remove-tags"]:
let tagsToRemove = (args["--remove-tags"] ?: "").split({',', ';'})
mark.tags = mark.tags.filter(proc (t: string): bool =
anyIt(tagsToRemove, it == t))
if args["--time"]: if args["--time"]:
try: mark.time = parseTime($args["--time"]) try: mark.time = parseTime($args["--time"])
except: raise newException(ValueError, except: raise newException(ValueError,
@ -366,10 +391,7 @@ Options:
if args["--edit"]: edit(mark) if args["--edit"]: edit(mark)
echo formatMark( writeMarks(marks = @[mark], includeNotes = args["--verbose"])
mark = mark,
timeFormat = "HH:mm",
includeNotes = args["--verbose"])
timeline.marks.delete(markIdx) timeline.marks.delete(markIdx)
timeline.marks.insert(mark, markIdx) timeline.marks.insert(mark, markIdx)
@ -381,7 +403,7 @@ Options:
timeline.marks.delete(markIdx) timeline.marks.delete(markIdx)
saveTimeline(timeline, timelineLocation) saveTimeline(timeline, timelineLocation)
if args["list"]: if args["list"] or args["ls"]:
var marks = timeline.marks var marks = timeline.marks

View File

@ -1,6 +1,6 @@
# Package # Package
version = "0.1.0" version = "0.2.1"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Personal Time Keeper" description = "Personal Time Keeper"
license = "MIT" license = "MIT"