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
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]]
const STOP_MSG = "STOP"
@ -16,12 +16,12 @@ const STOP_MSG = "STOP"
let NO_MARK: Mark = (
id: parseUUID("00000000-0000-0000-0000-000000000000"),
time: getLocalTime(getTime()),
summary: "", notes: "")
summary: "", notes: "", tags: @[])
const ISO_TIME_FORMAT = "yyyy:MM:dd'T'HH:mm:ss"
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"]
#proc `$`*(mark: Mark): string =
@ -43,7 +43,8 @@ template `%`(mark: Mark): JsonNode =
"id": $(mark.id),
"time": mark.time.format(ISO_TIME_FORMAT),
"summary": mark.summary,
"notes": mark.notes
"notes": mark.notes,
"tags": mark.tags
}
template `%`(timeline: Timeline): JsonNode =
@ -63,7 +64,8 @@ proc loadTimeline(filename: string): Timeline =
id: parseUUID(markJson["id"].getStr()),
time: parse(markJson["time"].getStr(), ISO_TIME_FORMAT),
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
@ -121,7 +123,14 @@ proc writeMarks(marks: seq[Mark], includeNotes = false): void =
setForegroundColor(stdout, fgCyan)
write(stdout, " (" & duration & ")")
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:
writeLine(stdout, spaces(longestPrefix) & mark.notes)
@ -170,6 +179,8 @@ proc doInit(timelineLocation: string): void =
timelineFile.write($timeline.pretty)
finally: close(timelineFile)
type ExpectedMarkPart = enum Time, Summary, Tags, Notes
proc edit(mark: var Mark): void =
var
tempFile: File
@ -178,10 +189,11 @@ proc edit(mark: var Mark): void =
try:
(tempFile, tempFileName) = mkstemp("timestamp-mark-", ".txt", "", fmWrite)
tempFile.writeLine(
"""# Edit the time, mark, and notes below. Any lines starting with '#' will be
# ignored. When done, save the file and close the editor.""")
"""# Edit the time, mark, tags, and notes below. Any lines starting with '#' will
# be ignored. When done, save the file and close the editor.""")
tempFile.writeLine(mark.time.format(ISO_TIME_FORMAT))
tempFile.writeLine(mark.summary)
tempFile.writeLine(mark.tags.join(","))
tempFile.writeLine(
"""# Everything from the line below to the end of the file will be considered
# notes for this timeline mark.""")
@ -190,15 +202,16 @@ proc edit(mark: var Mark): void =
discard os.execShellCmd "$EDITOR " & tempFileName & " </dev/tty >/dev/tty"
var
readTime = false
readSummary = false
var markPart = Time
for line in lines tempFileName:
if strip(line)[0] == '#': continue
elif not readTime: mark.time = parseTime(line); readTime = true
elif not readSummary: mark.summary = line; readSummary = true
else: mark.notes &= line
elif markPart == Time: mark.time = parseTime(line); markPart = Summary
elif markPart == Summary: mark.summary = line; markPart = Tags
elif markPart == Tags:
mark.tags = line.split({',', ';'});
markPart = Notes
else: mark.notes &= line & "\x0D\x0A"
finally: close(tempFile)
@ -209,11 +222,11 @@ Usage:
ptk init [options]
ptk add [options]
ptk add [options] <summary>
ptk ammend [options] <id> [<summary>]
ptk amend [options] <id> [<summary>]
ptk stop [options]
ptk continue
ptk delete <id>
ptk list [options]
ptk (list | ls) [options]
ptk sum-time --ids <ids>...
ptk sum-time [options] [<firstId>] [<lastId>]
ptk (-V | --version)
@ -221,17 +234,19 @@ Usage:
Options:
-f --file <file> Use the given timeline file.
-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.
-E --echo-args Echo the program's understanding of it's arguments.
-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>.
-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.
-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.
-E --echo-args Echo the program's understanding of it's arguments.
"""
# TODO: add ptk delete [options]
@ -239,7 +254,7 @@ Options:
logging.addHandler(newConsoleLogger())
# 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
@ -295,13 +310,14 @@ Options:
if args["stop"]:
let newMark = (
let newMark: Mark = (
id: genUUID(),
time:
if args["--time"]: parseTime($args["--time"])
else: getLocalTime(getTime()),
summary: STOP_MSG,
notes: args["--notes"] ?: "")
notes: args["--notes"] ?: "",
tags: (args["--tags"] ?: "").split({',', ';'}))
timeline.marks.add(newMark)
writeMarks(
@ -327,7 +343,8 @@ Options:
if args["--time"]: parseTime($args["--time"])
else: getLocalTime(getTime()),
summary: prevMark.summary,
notes: prevMark.notes)
notes: prevMark.notes,
tags: prevMark.tags)
timeline.marks.add(newMark)
writeMarks(marks = @[newMark], includeNotes = args["--verbose"])
@ -342,7 +359,8 @@ Options:
if args["--time"]: parseTime($args["--time"])
else: getLocalTime(getTime()),
summary: args["<summary>"] ?: "",
notes: args["--notes"] ?: "")
notes: args["--notes"] ?: "",
tags: (args["--tags"] ?: "").split({',', ';'}))
if args["--edit"]: edit(newMark)
@ -351,7 +369,7 @@ Options:
saveTimeline(timeline, timelineLocation)
if args["ammend"]:
if args["amend"]:
# Note, this returns a copy, not a reference to the mark in the seq.
let markIdx = timeline.marks.findById($args["<id>"])
@ -359,6 +377,13 @@ Options:
if args["<summary>"]: mark.summary = $args["<summary>"]
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"]:
try: mark.time = parseTime($args["--time"])
except: raise newException(ValueError,
@ -366,10 +391,7 @@ Options:
if args["--edit"]: edit(mark)
echo formatMark(
mark = mark,
timeFormat = "HH:mm",
includeNotes = args["--verbose"])
writeMarks(marks = @[mark], includeNotes = args["--verbose"])
timeline.marks.delete(markIdx)
timeline.marks.insert(mark, markIdx)
@ -381,7 +403,7 @@ Options:
timeline.marks.delete(markIdx)
saveTimeline(timeline, timelineLocation)
if args["list"]:
if args["list"] or args["ls"]:
var marks = timeline.marks

View File

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