6 Commits

5 changed files with 56 additions and 24 deletions

View File

@ -1,7 +1,7 @@
## Personal Time Keeping API Interface ## Personal Time Keeping API Interface
## =================================== ## ===================================
import asyncdispatch, base64, bcrypt, cliutils, docopt, jester, json, logging, import asyncdispatch, base64, bcrypt, cliutils, docopt, httpcore, jester, json, logging,
sequtils, strutils, os, tables, times, uuids sequtils, strutils, os, tables, times, uuids
import nre except toSeq import nre except toSeq
@ -61,10 +61,10 @@ template checkAuth(cfg: PtkApiCfg) =
var user {.inject.}: PtkUser = PtkUser() var user {.inject.}: PtkUser = PtkUser()
try: try:
if not request.headers.hasKey("Authorization"): if not headers(request).hasKey("Authorization"):
raiseEx "No auth token." raiseEx "No auth token."
let headerVal = request.headers["Authorization"] let headerVal = headers(request)["Authorization"]
if not headerVal.startsWith("Basic "): if not headerVal.startsWith("Basic "):
raiseEx "Invalid Authorization type (only 'Basic' is supported)." raiseEx "Invalid Authorization type (only 'Basic' is supported)."

View File

@ -9,6 +9,8 @@ type
Timeline* = tuple[name: string, marks: seq[Mark]] Timeline* = tuple[name: string, marks: seq[Mark]]
## Representation of a timeline: a name and sequence of Marks. ## Representation of a timeline: a name and sequence of Marks.
OffsetFrom = enum Year, Month, Day, None
const STOP_MSG* = "STOP" const STOP_MSG* = "STOP"
let NO_MARK*: Mark = ( let NO_MARK*: Mark = (
@ -20,11 +22,26 @@ const ISO_TIME_FORMAT* = "yyyy-MM-dd'T'HH:mm:ss"
## The canonical time format used by PTK. ## The canonical time format used by PTK.
const TIME_FORMATS* = @[ const TIME_FORMATS* = @[
"yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd HH:mm:ss", (fmtStr: "yyyy-MM-dd'T'HH:mm:sszzz", offsetFrom: OffsetFrom.None),
"yyyy-MM-dd'T'HH:mm", "yyyy-MM-dd HH:mm", (fmtStr: "yyyy-MM-dd HH:mm:sszzz", offsetFrom: OffsetFrom.None),
"MM-dd'T'HH:mm:ss", "MM-dd HH:mm:ss", (fmtStr: "yyyy-MM-dd'T'HH:mm:sszz", offsetFrom: OffsetFrom.None),
"MM-dd'T'HH:mm", "MM-dd HH:mm", (fmtStr: "yyyy-MM-dd HH:mm:sszz", offsetFrom: OffsetFrom.None),
"HH:mm:ss", "H:mm:ss", "H:mm", "HH:mm" ] (fmtStr: "yyyy-MM-dd'T'HH:mm:ssz", offsetFrom: OffsetFrom.None),
(fmtStr: "yyyy-MM-dd HH:mm:ssz", offsetFrom: OffsetFrom.None),
(fmtStr: "yyyy-MM-dd'T'HH:mm:ss", offsetFrom: OffsetFrom.None),
(fmtStr: "yyyy-MM-dd HH:mm:ss", offsetFrom: OffsetFrom.None),
(fmtStr: "yyyy-MM-dd'T'HH:mm", offsetFrom: OffsetFrom.None),
(fmtStr: "yyyy-MM-dd HH:mm", offsetFrom: OffsetFrom.None),
(fmtStr: "yyyy-MM-dd", offsetFrom: OffsetFrom.None),
(fmtStr: "yyyy-MM", offsetFrom: OffsetFrom.None),
(fmtStr: "MM-dd'T'HH:mm:ss", offsetFrom: OffsetFrom.Year),
(fmtStr: "MM-dd HH:mm:ss", offsetFrom: OffsetFrom.Year),
(fmtStr: "MM-dd'T'HH:mm", offsetFrom: OffsetFrom.Year),
(fmtStr: "MM-dd HH:mm", offsetFrom: OffsetFrom.Year),
(fmtStr: "HH:mm:ss", offsetFrom: OffsetFrom.Day),
(fmtStr: "H:mm:ss", offsetFrom: OffsetFrom.Day),
(fmtStr: "H:mm", offsetFrom: OffsetFrom.Day),
(fmtStr: "HH:mm", offsetFrom: OffsetFrom.Day) ]
## Other time formats that PTK will accept as input. ## Other time formats that PTK will accept as input.
proc getOrFail*(n: JsonNode, key: string, objName: string = ""): JsonNode = proc getOrFail*(n: JsonNode, key: string, objName: string = ""): JsonNode =
@ -39,8 +56,25 @@ proc getIfExists*(n: JsonNode, key: string): JsonNode =
proc parseTime*(timeStr: string): DateTime = proc parseTime*(timeStr: string): DateTime =
## Helper to parse time strings trying multiple known formats. ## Helper to parse time strings trying multiple known formats.
let now = now()
for fmt in TIME_FORMATS: for fmt in TIME_FORMATS:
try: return parse(timeStr, fmt) try:
let parsed = parse(timeStr, fmt.fmtStr)
case fmt.offsetFrom:
of OffsetFrom.None:
return parsed
of OffsetFrom.Year:
return dateTime(now.year, parsed.month, parsed.monthday,
parsed.hour, parsed.minute, parsed.second, parsed.nanosecond,
now.timezone)
of OffsetFrom.Month:
return initDateTime(parsed.monthday, now.month, now.year,
parsed.hour, parsed.minute, parsed.second, parsed.nanosecond,
now.timezone)
of OffsetFrom.Day:
return initDateTime(now.monthday, now.month, now.year, parsed.hour,
parsed.minute, parsed.second, parsed.nanosecond, now.timezone)
except: discard nil except: discard nil
raise newException(Exception, "unable to interpret as a date: " & timeStr) raise newException(Exception, "unable to interpret as a date: " & timeStr)
@ -112,5 +146,3 @@ proc getLastIndex*(marks: seq[Mark]): int =
while idx >= 0 and marks[idx].summary == STOP_MSG: idx -= 1 while idx >= 0 and marks[idx].summary == STOP_MSG: idx -= 1
if idx < 0: result = -1 if idx < 0: result = -1
else: result = idx else: result = idx

View File

@ -1 +1 @@
const PTK_VERSION* = "1.0.7" const PTK_VERSION* = "1.0.12"

14
ptk.nim
View File

@ -4,7 +4,7 @@
## Simple time keeping CLI ## Simple time keeping CLI
import algorithm, docopt, json, langutils, logging, os, nre, std/wordwrap, import algorithm, docopt, json, langutils, logging, os, nre, std/wordwrap,
sequtils, sets, strutils, tempfile, terminal, times, uuids sequtils, sets, strutils, sugar, tempfile, terminal, times, uuids
import timeutils except `-` import timeutils except `-`
@ -43,8 +43,7 @@ proc writeMarks(timeline: Timeline, indices: seq[int], includeNotes = false): vo
writeLine(stdout, "No marks match the given criteria.") writeLine(stdout, "No marks match the given criteria.")
return return
var idxs = indices.sorted( var idxs = indices.sorted((a, b) => cmp(marks[a].time, marks[b].time))
proc(a, b: int): int = cmp(marks[a].time, marks[b].time))
let largestInterval = now - marks[idxs.first].time let largestInterval = now - marks[idxs.first].time
let timeFormat = let timeFormat =
@ -149,7 +148,8 @@ proc edit(mark: Mark): Mark =
close(tempFile) close(tempFile)
tempFile = nil tempFile = nil
discard os.execShellCmd "$EDITOR " & tempFileName & " </dev/tty >/dev/tty" let editor = getEnv("EDITOR", "vim")
discard os.execShellCmd editor & " " & tempFileName & " </dev/tty >/dev/tty"
var markPart = Time var markPart = Time
var notes: seq[string] = @[] var notes: seq[string] = @[]
@ -278,8 +278,9 @@ Options:
-e --edit Open the mark in an editor. -e --edit Open the mark in an editor.
-f --file <file> Use the given timeline file. -f --file <file> Use the given timeline file.
-g --tags <tags> Add the given tags (comma-separated) to the selected marks. -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. -G --remove-tags <tags> Remove the given tag from the selected marks.
-h --help Print this usage information. -h --help Print this usage information.
-m --matching <pattern> Restric the selection to marks matching <pattern>. -m --matching <pattern> Restric the selection to marks matching <pattern>.
-n --notes <notes> For add and amend, set the notes for a time mark. -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. -t --time <time> For add and amend, use this time instead of the current time.
@ -490,8 +491,7 @@ Options:
mark.tags = mark.tags.deduplicate mark.tags = mark.tags.deduplicate
if args["--remove-tags"]: if args["--remove-tags"]:
let tagsToRemove = (args["--remove-tags"] ?: "").split({',', ';'}) let tagsToRemove = (args["--remove-tags"] ?: "").split({',', ';'})
mark.tags = mark.tags.filter(proc (t: string): bool = mark.tags = mark.tags.filter((t) => not anyIt(tagsToRemove, it == t))
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,

View File

@ -1,6 +1,6 @@
# Package # Package
version = "1.0.7" version = "1.0.12"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Personal Time Keeper" description = "Personal Time Keeper"
license = "MIT" license = "MIT"
@ -16,10 +16,10 @@ requires @[
"isaac >= 0.1.3", "isaac >= 0.1.3",
"bcrypt", "bcrypt",
"jester 0.5.0", "jester 0.5.0",
"https://git.jdb-labs.com/jdb/nim-lang-utils.git", "https://git.jdb-software.com/jdb/nim-lang-utils.git",
"https://git.jdb-labs.com/jdb/nim-cli-utils.git >= 0.6.5", "https://git.jdb-software.com/jdb/nim-cli-utils.git >= 0.6.5",
"https://git.jdb-labs.com/jdb/nim-time-utils.git >= 0.5.2", "https://git.jdb-software.com/jdb/nim-time-utils.git >= 0.5.2",
"https://git.jdb-labs.com/jdb/update-nim-package-version" "https://git.jdb-software.com/jdb/update-nim-package-version"
] ]
task updateVersion, "Update the version of this package.": task updateVersion, "Update the version of this package.":