Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
28569a643e | |||
97eb286e32 | |||
fcab7a4cc6 |
@ -1,6 +1,6 @@
|
|||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "4.0.0"
|
version = "4.0.3"
|
||||||
author = "Jonathan Bernard"
|
author = "Jonathan Bernard"
|
||||||
description = "Personal issue tracker."
|
description = "Personal issue tracker."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
37
src/pit.nim
37
src/pit.nim
@ -5,8 +5,8 @@
|
|||||||
import cliutils, docopt, json, logging, options, os, ospaths, sequtils,
|
import cliutils, docopt, json, logging, options, os, ospaths, sequtils,
|
||||||
tables, terminal, times, unicode, uuids
|
tables, terminal, times, unicode, uuids
|
||||||
|
|
||||||
import strutils except capitalize
|
import strutils except capitalize, toUpper, toLower
|
||||||
import pit/private/libpit
|
import pitpkg/private/libpit
|
||||||
export libpit
|
export libpit
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -225,10 +225,11 @@ when isMainModule:
|
|||||||
try:
|
try:
|
||||||
let doc = """
|
let doc = """
|
||||||
Usage:
|
Usage:
|
||||||
pit new <summary> [<state>] [options]
|
pit ( new | add) <summary> [<state>] [options]
|
||||||
pit list [<state>] [options]
|
pit list [<state>] [options]
|
||||||
pit ( start | done | pending | do-today | todo ) <id>...
|
pit ( start | done | pending | do-today | todo | suspend ) <id>...
|
||||||
pit edit <id>
|
pit edit <id>
|
||||||
|
pit delete <id>...
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
@ -245,6 +246,8 @@ Options:
|
|||||||
|
|
||||||
-F, --future Limit to future issues.
|
-F, --future Limit to future issues.
|
||||||
|
|
||||||
|
-y, --yes Automatically answer "yes" to any prompts.
|
||||||
|
|
||||||
-C, --config <cfgFile> Location of the config file (defaults to $HOME/.pitrc)
|
-C, --config <cfgFile> Location of the config file (defaults to $HOME/.pitrc)
|
||||||
|
|
||||||
-E, --echo-args Echo arguments (for debug purposes).
|
-E, --echo-args Echo arguments (for debug purposes).
|
||||||
@ -258,7 +261,7 @@ Options:
|
|||||||
logging.addHandler(newConsoleLogger())
|
logging.addHandler(newConsoleLogger())
|
||||||
|
|
||||||
# Parse arguments
|
# Parse arguments
|
||||||
let args = docopt(doc, version = "pit 4.0.0")
|
let args = docopt(doc, version = "pit 4.0.3")
|
||||||
|
|
||||||
if args["--echo-args"]: stderr.writeLine($args)
|
if args["--echo-args"]: stderr.writeLine($args)
|
||||||
|
|
||||||
@ -268,8 +271,13 @@ Options:
|
|||||||
|
|
||||||
let ctx = initContext(args)
|
let ctx = initContext(args)
|
||||||
|
|
||||||
|
# Create our tasks directory structure if needed
|
||||||
|
for s in IssueState:
|
||||||
|
if not existsDir(ctx.tasksDir / $s):
|
||||||
|
(ctx.tasksDir / $s).createDir
|
||||||
|
|
||||||
## Actual command runners
|
## Actual command runners
|
||||||
if args["new"]:
|
if args["new"] or args["add"]:
|
||||||
let state =
|
let state =
|
||||||
if args["<state>"]: parseEnum[IssueState]($args["<state>"])
|
if args["<state>"]: parseEnum[IssueState]($args["<state>"])
|
||||||
else: TodoToday
|
else: TodoToday
|
||||||
@ -292,7 +300,7 @@ Options:
|
|||||||
edit(ctx.tasksDir.loadIssueById(issueId))
|
edit(ctx.tasksDir.loadIssueById(issueId))
|
||||||
|
|
||||||
elif args["start"] or args["do-today"] or args["done"] or
|
elif args["start"] or args["do-today"] or args["done"] or
|
||||||
args["pending"] or args["todo"]:
|
args["pending"] or args["todo"] or args["suspend"]:
|
||||||
|
|
||||||
var targetState: IssueState
|
var targetState: IssueState
|
||||||
if args["done"]: targetState = Done
|
if args["done"]: targetState = Done
|
||||||
@ -300,9 +308,10 @@ Options:
|
|||||||
elif args["pending"]: targetState = Todo
|
elif args["pending"]: targetState = Todo
|
||||||
elif args["start"]: targetState = Current
|
elif args["start"]: targetState = Current
|
||||||
elif args["todo"]: targetState = Todo
|
elif args["todo"]: targetState = Todo
|
||||||
|
elif args["suspend"]: targetState = Dormant
|
||||||
|
|
||||||
for id in @(args["<id>"]):
|
for id in @(args["<id>"]):
|
||||||
ctx.tasksDir.moveIssue(ctx.tasksDir.loadIssueById(id), targetState)
|
ctx.tasksDir.loadIssueById(id).changeState(ctx.tasksDir, targetState)
|
||||||
|
|
||||||
if ctx.triggerPtk:
|
if ctx.triggerPtk:
|
||||||
if targetState == Current:
|
if targetState == Current:
|
||||||
@ -313,6 +322,18 @@ Options:
|
|||||||
discard execShellCmd(cmd)
|
discard execShellCmd(cmd)
|
||||||
elif targetState == Done: discard execShellCmd("ptk stop")
|
elif targetState == Done: discard execShellCmd("ptk stop")
|
||||||
|
|
||||||
|
elif args["delete"]:
|
||||||
|
for id in @(args["<id>"]):
|
||||||
|
|
||||||
|
let issue = ctx.tasksDir.loadIssueById(id)
|
||||||
|
|
||||||
|
if not args["--yes"]:
|
||||||
|
stderr.write("Delete '" & issue.summary & "' (y/n)? ")
|
||||||
|
if not "yes".startsWith(stdin.readLine.toLower):
|
||||||
|
continue
|
||||||
|
|
||||||
|
issue.delete
|
||||||
|
|
||||||
elif args["list"]:
|
elif args["list"]:
|
||||||
|
|
||||||
let filter = initFilter()
|
let filter = initFilter()
|
||||||
|
@ -15,6 +15,7 @@ type
|
|||||||
Pending = "pending",
|
Pending = "pending",
|
||||||
Done = "done",
|
Done = "done",
|
||||||
Todo = "todo"
|
Todo = "todo"
|
||||||
|
Dormant = "dormant"
|
||||||
|
|
||||||
IssueFilter* = ref object
|
IssueFilter* = ref object
|
||||||
properties*: TableRef[string, string]
|
properties*: TableRef[string, string]
|
||||||
@ -27,8 +28,9 @@ let ISSUE_FILE_PATTERN = re"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f
|
|||||||
proc displayName*(s: IssueState): string =
|
proc displayName*(s: IssueState): string =
|
||||||
case s
|
case s
|
||||||
of Current: result = "Current"
|
of Current: result = "Current"
|
||||||
of Pending: result = "Pending"
|
|
||||||
of Done: result = "Done"
|
of Done: result = "Done"
|
||||||
|
of Dormant: result = "Dormant"
|
||||||
|
of Pending: result = "Pending"
|
||||||
of Todo: result = "Todo"
|
of Todo: result = "Todo"
|
||||||
of TodoToday: result = "Todo"
|
of TodoToday: result = "Todo"
|
||||||
|
|
||||||
@ -160,10 +162,13 @@ proc loadIssues*(path: string): seq[Issue] =
|
|||||||
if extractFilename(path).match(ISSUE_FILE_PATTERN).isSome():
|
if extractFilename(path).match(ISSUE_FILE_PATTERN).isSome():
|
||||||
result.add(loadIssue(path))
|
result.add(loadIssue(path))
|
||||||
|
|
||||||
proc moveIssue*(tasksDir: string, issue: Issue, newState: IssueState) =
|
proc changeState*(issue: Issue, tasksDir: string, newState: IssueState) =
|
||||||
removeFile(issue.filepath)
|
let oldFilepath = issue.filepath
|
||||||
if newState == Done: issue.setDateTime("completed", getTime().local)
|
if newState == Done: issue.setDateTime("completed", getTime().local)
|
||||||
tasksDir.store(issue, newState)
|
tasksDir.store(issue, newState)
|
||||||
|
removeFile(oldFilepath)
|
||||||
|
|
||||||
|
proc delete*(issue: Issue) = removeFile(issue.filepath)
|
||||||
|
|
||||||
## Utilities for working with issue collections.
|
## Utilities for working with issue collections.
|
||||||
proc groupBy*(issues: seq[Issue], propertyKey: string): TableRef[string, seq[Issue]] =
|
proc groupBy*(issues: seq[Issue], propertyKey: string): TableRef[string, seq[Issue]] =
|
Reference in New Issue
Block a user