Add the ability to order issues.

This commit is contained in:
Jonathan Bernard 2018-06-11 12:11:26 -05:00
parent a924d7b649
commit 08dfbde57f
4 changed files with 70 additions and 18 deletions

View File

@ -12,4 +12,4 @@ bin = @["pit", "pit_api"]
# Dependencies # Dependencies
requires @[ "nim >= 0.18.0", "cliutils 0.4.1", "docopt 0.6.5", "jester 0.2.0", requires @[ "nim >= 0.18.0", "cliutils 0.4.1", "docopt 0.6.5", "jester 0.2.0",
"timeutils 0.3.0", "uuids 0.1.9" ] "langutils >= 0.4.0", "timeutils 0.3.0", "uuids 0.1.9" ]

View File

@ -21,6 +21,11 @@ type
termWidth*: int termWidth*: int
triggerPtk*, verbose*: bool triggerPtk*, verbose*: bool
let EDITOR =
if existsEnv("EDITOR"): getEnv("EDITOR")
else: "vi"
proc initContext(args: Table[string, Value]): CliContext = proc initContext(args: Table[string, Value]): CliContext =
let pitCfg = loadConfig(args) let pitCfg = loadConfig(args)
@ -163,16 +168,18 @@ proc writeHeader(ctx: CliContext, header: string) =
stdout.writeLine('~'.repeat(ctx.termWidth)) stdout.writeLine('~'.repeat(ctx.termWidth))
stdout.resetAttributes stdout.resetAttributes
proc reorder(ctx: CliContext, state: IssueState) =
# load the issues to make sure the order file contains all issues in the state.
ctx.loadIssues(state)
discard os.execShellCmd(EDITOR & " '" & (ctx.tasksDir / $state / "order.txt") & "' </dev/tty >/dev/tty")
proc edit(issue: Issue) = proc edit(issue: Issue) =
# Write format comments (to help when editing) # Write format comments (to help when editing)
writeFile(issue.filepath, toStorageFormat(issue, true)) writeFile(issue.filepath, toStorageFormat(issue, true))
let editor = discard os.execShellCmd(EDITOR & " '" & issue.filepath & "' </dev/tty >/dev/tty")
if existsEnv("EDITOR"): getEnv("EDITOR")
else: "vi"
discard os.execShellCmd(editor & " " & issue.filepath & " </dev/tty >/dev/tty")
try: try:
# Try to parse the newly-edited issue to make sure it was successful. # Try to parse the newly-edited issue to make sure it was successful.
@ -234,6 +241,7 @@ Usage:
pit list [<listable>] [options] pit list [<listable>] [options]
pit ( start | done | pending | todo-today | todo | suspend ) <id>... [options] pit ( start | done | pending | todo-today | todo | suspend ) <id>... [options]
pit edit <ref>... pit edit <ref>...
pit reorder <state>
pit ( delete | rm ) <id>... pit ( delete | rm ) <id>...
Options: Options:
@ -327,6 +335,9 @@ Options:
stdout.writeLine ctx.formatIssue(issue) stdout.writeLine ctx.formatIssue(issue)
elif args["reorder"]:
ctx.reorder(parseEnum[IssueState]($args["<state>"]))
elif args["edit"]: elif args["edit"]:
for editRef in @(args["<ref>"]): for editRef in @(args["<ref>"]):

View File

@ -1,5 +1,5 @@
import cliutils, docopt, json, logging, options, os, ospaths, sequtils, import cliutils, docopt, json, logging, langutils, options, os, ospaths,
strutils, tables, times, timeutils, uuids sequtils, strutils, tables, times, timeutils, uuids
from nre import find, match, re, Regex from nre import find, match, re, Regex
@ -89,6 +89,14 @@ proc fullMatchFilter*(pattern: string): IssueFilter =
result = initFilter() result = initFilter()
result.fullMatch = some(re("(?i)" & pattern)) result.fullMatch = some(re("(?i)" & pattern))
proc groupBy*(issues: seq[Issue], propertyKey: string): TableRef[string, seq[Issue]] =
result = newTable[string, seq[Issue]]()
for i in issues:
let key = if i.hasProp(propertyKey): i[propertyKey] else: ""
if not result.hasKey(key): result[key] = newSeq[Issue]()
result[key].add(i)
## Parse and format issues ## Parse and format issues
proc fromStorageFormat*(id: string, issueTxt: string): Issue = proc fromStorageFormat*(id: string, issueTxt: string): Issue =
type ParseState = enum ReadingSummary, ReadingProps, ReadingDetails type ParseState = enum ReadingSummary, ReadingProps, ReadingDetails
@ -172,11 +180,51 @@ proc store*(tasksDir: string, issue: Issue, state: IssueState, withComments = fa
issue.store() issue.store()
proc storeOrder*(issues: seq[Issue], path: string) =
var orderLines = newSeq[string]()
for context, issues in issues.groupBy("context"):
orderLines.add("> " & context)
for issue in issues: orderLines.add($issue.id & " " & issue.summary)
orderLines.add("")
let orderFile = path / "order.txt"
orderFile.writeFile(orderLines.join("\n"))
proc loadIssues*(path: string): seq[Issue] = proc loadIssues*(path: string): seq[Issue] =
result = @[] let orderFile = path / "order.txt"
let orderedIds =
if fileExists(orderFile):
toSeq(orderFile.lines)
.mapIt(it.split(' ')[0])
.deduplicate
.filterIt(not it.startsWith("> ") and not it.isNilOrWhitespace)
else: newSeq[string]()
type TaggedIssue = tuple[issue: Issue, ordered: bool]
var unorderedIssues: seq[TaggedIssue] = @[]
for path in walkDirRec(path): for path in walkDirRec(path):
if extractFilename(path).match(ISSUE_FILE_PATTERN).isSome(): if extractFilename(path).match(ISSUE_FILE_PATTERN).isSome():
result.add(loadIssue(path)) unorderedIssues.add((loadIssue(path), false))
result = @[]
# Add all ordered issues in order
for id in orderedIds:
let idx = unorderedIssues.indexOf(($it.issue.id).startsWith(id))
if idx > 0:
result.add(unorderedIssues[idx].issue)
unorderedIssues[idx].ordered = true
# Add all remaining, unordered issues in the order they were loaded
for taggedIssue in unorderedIssues:
if taggedIssue.ordered: continue
result.add(taggedIssue.issue)
# Finally, save current order
result.storeOrder(path)
proc changeState*(issue: Issue, tasksDir: string, newState: IssueState) = proc changeState*(issue: Issue, tasksDir: string, newState: IssueState) =
let oldFilepath = issue.filepath let oldFilepath = issue.filepath
@ -187,13 +235,6 @@ proc changeState*(issue: Issue, tasksDir: string, newState: IssueState) =
proc delete*(issue: Issue) = removeFile(issue.filepath) 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]] =
result = newTable[string, seq[Issue]]()
for i in issues:
let key = if i.hasProp(propertyKey): i[propertyKey] else: ""
if not result.hasKey(key): result[key] = newSeq[Issue]()
result[key].add(i)
proc filter*(issues: seq[Issue], filter: IssueFilter): seq[Issue] = proc filter*(issues: seq[Issue], filter: IssueFilter): seq[Issue] =
result = issues result = issues

View File

@ -1 +1 @@
const PIT_VERSION = "4.3.0" const PIT_VERSION = "4.4.0"