Compare commits

..

1 Commits

Author SHA1 Message Date
ed3058a9c8 Initial ideas around a related-to special property. 2023-12-15 21:34:53 -06:00
7 changed files with 240 additions and 65 deletions

View File

@ -1 +0,0 @@
nim 2.2.0

174
nimble.lock Normal file
View File

@ -0,0 +1,174 @@
{
"version": 2,
"packages": {
"asynctools": {
"version": "0.1.1",
"vcsRevision": "0e6bdc3ed5bae8c7cc9e03cfbf66b7c882a908a7",
"url": "https://github.com/cheatfate/asynctools",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "54314dceabb06b20908ecb0f2c007e9ff3aaa054"
}
},
"isaac": {
"version": "0.1.3",
"vcsRevision": "45a5cbbd54ff59ba3ed94242620c818b9aad1b5b",
"url": "https://github.com/pragmagic/isaac/",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "05c3583a954715d84b0bf1be97f9a503249e9cdf"
}
},
"uuids": {
"version": "0.1.11",
"vcsRevision": "8cb8720b567c6bcb261bd1c0f7491bdb5209ad06",
"url": "https://github.com/pragmagic/uuids/",
"downloadMethod": "git",
"dependencies": [
"isaac"
],
"checksums": {
"sha1": "393f5fcefbc8ad3cf167e59760144208ff8f9f76"
}
},
"unicodedb": {
"version": "0.11.2",
"vcsRevision": "c70f8bc8c7373265670e0575bc5eda36fe3761b0",
"url": "https://github.com/nitely/nim-unicodedb",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "612c5955f91bd90a263ce914d1d5de74a33af3c6"
}
},
"regex": {
"version": "0.20.1",
"vcsRevision": "66f144f935cc73977c61185fab15a3147bf117ff",
"url": "https://github.com/nitely/nim-regex",
"downloadMethod": "git",
"dependencies": [
"unicodedb"
],
"checksums": {
"sha1": "ea9b6600443e73b1ea89a477c7a5d1fce742c9da"
}
},
"docopt": {
"version": "0.7.0",
"vcsRevision": "17803d1205f9e752cce03a66b0a29b710520398e",
"url": "https://github.com/docopt/docopt.nim",
"downloadMethod": "git",
"dependencies": [
"regex"
],
"checksums": {
"sha1": "21150284640b882fa91147181c52da3e5bb44df8"
}
},
"filetype": {
"version": "0.9.0",
"vcsRevision": "1fe1e7d988cd802abc26505efb5a91891bd6f53e",
"url": "https://github.com/jiro4989/filetype",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "d2242b94eeb0f6d3810a8c71af4664f28853e1be"
}
},
"zero_functional": {
"version": "1.3.0",
"vcsRevision": "edf3b7f59119f75706da435c2b7f080a0cf4960c",
"url": "https://github.com/zero-functional/zero-functional",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "2dc01ca0925ac1c2dcb46a0c6d9c93c57a9cddec"
}
},
"update_nim_package_version": {
"version": "0.2.0",
"vcsRevision": "5a78579fd7f88014263aed38c60327c85f6f8bcf",
"url": "https://git.jdb-software.com/jdb/update-nim-package-version",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "37052b7ce30d5493ef24253a82a6087350b4eabb"
}
},
"data_uri": {
"version": "1.0.2",
"vcsRevision": "f43ac66e44c37edd3cc7282d75d6fa2fa031b2ec",
"url": "",
"downloadMethod": "git",
"dependencies": [
"docopt",
"filetype",
"zero_functional",
"update_nim_package_version"
],
"checksums": {
"sha1": "949c11ffab4e85ff538b0bd3e5bb193f118b56d7"
}
},
"timeutils": {
"version": "0.5.4",
"vcsRevision": "a9308cbaf3c89496b5832ddd18404dc0debe66a2",
"url": "https://git.jdb-software.com/jdb/nim-time-utils.git",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "9ecd1020f5644bc59acb2f44ca9d30f4c7f066d3"
}
},
"cliutils": {
"version": "0.8.0",
"vcsRevision": "b1cc4fbe51d5e617789363efe716793ebe5bc5f1",
"url": "https://git.jdb-software.com/jdb/nim-cli-utils",
"downloadMethod": "git",
"dependencies": [
"docopt"
],
"checksums": {
"sha1": "5b114094c314007fa6f15e62852d62a58a3cbb62"
}
},
"langutils": {
"version": "0.4.0",
"vcsRevision": "8122660da3fc78132b823e76c9e990fd92802b0e",
"url": "https://git.jdb-software.com/jdb/nim-lang-utils.git",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "2da09deb0e0bfc186000f0d941d06dd974bf6e58"
}
},
"httpbeast": {
"version": "0.4.1",
"vcsRevision": "abc13d11c210b614960fe8760e581d44cfb2e3e9",
"url": "https://github.com/dom96/httpbeast",
"downloadMethod": "git",
"dependencies": [
"asynctools"
],
"checksums": {
"sha1": "b23e57a401057dcb9b7fae1fb8279a6a2ce1d0b8"
}
},
"jester": {
"version": "0.5.0",
"vcsRevision": "a21b36a02b7745d6cdcda32d4ab3fba328cda17a",
"url": "https://github.com/dom96/jester/",
"downloadMethod": "git",
"dependencies": [
"httpbeast",
"asynctools"
],
"checksums": {
"sha1": "a192ca25bfc05d5de5c9a5fafca3b0cee47d82d2"
}
}
},
"tasks": {}
}

View File

@ -1,30 +1,30 @@
# Package # Package
version = "4.26.0" version = "4.24.0"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Personal issue tracker." description = "Personal issue tracker."
license = "MIT" license = "MIT"
srcDir = "src" srcDir = "src"
installExt = @["nim"] installExt = @["nim"]
bin = @["pit"] bin = @["pit", "pit_api"]
# Dependencies # Dependencies
requires @[ requires @[
"nim >= 1.4.0", "nim >= 1.4.0",
"docopt >= 0.7.1", "docopt >= 0.6.8",
"jester >= 0.6.0", "jester >= 0.5.0",
"uuids >= 0.1.10", "uuids >= 0.1.10",
"zero_functional" "zero_functional"
] ]
# Dependencies from git.jdb-software.com/jdb/nim-packages # Dependencies from git.jdb-software.com/nim-jdb/packages
requires @[ requires @[
"cliutils >= 0.9.1", "cliutils >= 0.8.1",
"langutils >= 0.4.0", "langutils >= 0.4.0",
"timeutils >= 0.5.4", "timeutils >= 0.5.4",
"data_uri > 1.0.0", "data_uri > 1.0.0",
"update_nim_package_version >= 0.2.0" "https://git.jdb-software.com/jdb/update-nim-package-version >= 0.2.0"
] ]
task updateVersion, "Update the version of this package.": task updateVersion, "Update the version of this package.":

View File

@ -70,24 +70,6 @@ proc formatIssue*(issue: Issue): string =
result &= termReset result &= termReset
proc formatPlainIssueSummary*(issue: Issue): string =
result = "$#: $# $#" % [
$issue.state,
($issue.id)[0..<6],
issue.summary ]
if issue.hasProp("delegated-to") or issue.hasProp("pending"):
var parts = newSeq[string]()
if issue.hasProp("delegated-to"):
parts.add("delegated to " & issue["delegated-to"])
if issue.hasProp("pending"):
parts.add("pendin: " & issue["pending"])
result &= "($#)" % [ parts.join("; ") ]
proc formatSectionIssue*( proc formatSectionIssue*(
issue: Issue, issue: Issue,
width: int = 80, width: int = 80,
@ -269,15 +251,7 @@ proc list(
it.hasProp("completed") and it.hasProp("completed") and
sameDay(getTime().local, it.getDateTime("completed"))) sameDay(getTime().local, it.getDateTime("completed")))
if isatty(stdout): stdout.write ctx.formatSection(ctx.issues[state], state, "", verbose)
stdout.write ctx.formatSection(ctx.issues[state], state, "", verbose)
else:
stdout.writeLine ctx.issues[state]
.mapIt(formatPlainIssueSummary(it))
.join("\n")
trace "listing complete" trace "listing complete"
return return
@ -305,13 +279,7 @@ proc list(
not (it.hasProp("hide-until") and not (it.hasProp("hide-until") and
it.getDateTime("hide-until") > getTime().local)) it.getDateTime("hide-until") > getTime().local))
if isatty(stdout): stdout.write ctx.formatSection(visibleIssues, s, indent, verbose)
stdout.write ctx.formatSection(visibleIssues, s, indent, verbose)
else:
stdout.writeLine visibleIssues
.mapIt(formatPlainIssueSummary(it))
.join("\n")
# Future items # Future items
if future: if future:
@ -328,13 +296,7 @@ proc list(
not (it.hasProp("hide-until") and not (it.hasProp("hide-until") and
it.getDateTime("hide-until") > getTime().local)) it.getDateTime("hide-until") > getTime().local))
if isatty(stdout): stdout.write ctx.formatSection(visibleIssues, s, indent, verbose)
stdout.write ctx.formatSection(visibleIssues, s, indent, verbose)
else:
stdout.writeLine visibleIssues
.mapIt(formatPlainIssueSummary(it))
.join("\n")
trace "listing complete" trace "listing complete"
@ -501,7 +463,7 @@ when isMainModule:
) )
cmd &= " -g \"" & tags.join(",") & "\"" cmd &= " -g \"" & tags.join(",") & "\""
cmd &= " -n \"pit-id: " & $issue.id & "\"" cmd &= " -n \"pit-id: " & $issue.id & "\""
cmd &= " \"[" & ($issue.id)[0..<6] & "] " & issue.summary & "\"" cmd &= " \"" & issue.summary & "\""
discard execShellCmd(cmd) discard execShellCmd(cmd)
elif targetState == Done or targetState == Pending: elif targetState == Done or targetState == Pending:
discard execShellCmd("ptk stop") discard execShellCmd("ptk stop")

View File

@ -1,11 +1,5 @@
## Personal Issue Tracker API Interface ## Personal Issue Tracker API Interface
## ==================================== ## ====================================
#
# **NOTE** This is currently not being built as it no longer works under Nim
# 2.x due to the inability to call system calls (invoke pit via cli) in a
# gc-safe manner. It should be rewritten to use the functionality exposed by
# libpit directly rather than calling the pit cli executable. Unfortunately
# this would require a non-trivial rewrite.
import asyncdispatch, cliutils, docopt, jester, json, logging, options, sequtils, strutils import asyncdispatch, cliutils, docopt, jester, json, logging, options, sequtils, strutils
import nre except toSeq import nre except toSeq

View File

@ -1,4 +1,4 @@
const PIT_VERSION* = "4.26.0" const PIT_VERSION* = "4.24.0"
const USAGE* = """Usage: const USAGE* = """Usage:
pit ( new | add) <summary> [<state>] [options] pit ( new | add) <summary> [<state>] [options]
@ -181,8 +181,32 @@ Issue Properties:
after 12 hours after 12 hours
every 2 weeks, 10a544 every 2 weeks, 10a544
relations
Used to store information about relationships between issues. PIT treats
all relations as bi-directional and will update related tickets as
necessary to maintain the integrity of the links.
Relations are captured as <relation-type> <related-issue-id>. Multiple
relations can be separated by ",". The <related-issue-id> must be a unique
issue ID or ID prefix (in the case of multiple matching issues, the first
found will be used with no guarantee on ordering). Valid value pairs for
<relation-type> are:
- child-of / parent-of
- related-to (default)
- blocks / blocked-by
- follows / precedes
- caused / caused-by
Examples:
relation: child-of fb3e63, blocked-by 2b71c1
relation: 2b71c1, 8f2b4c, follows 184dc6
relation: relates-to 2b71c1
tags tags
If present, expected to be a comma-delimited list of text tags. The -g If present, expected to be a comma-delimited list of text tags. The -g
option is a short-hand for '-p tags:<tags-value>'. option is a short-hand for '-p tags:<tags-value>'.
""" """

View File

@ -11,6 +11,7 @@ type
filepath*: string filepath*: string
summary*, details*: string summary*, details*: string
properties*: TableRef[string, string] properties*: TableRef[string, string]
relations*: seq[Relation]
tags*: seq[string] tags*: seq[string]
state*: IssueState state*: IssueState
@ -40,6 +41,19 @@ type
interval*: TimeInterval interval*: TimeInterval
isFromCompletion*: bool isFromCompletion*: bool
Relation* = tuple[rel: RelationType, id: UUID]
RelationType* = enum
ParentOf = "parent-of",
ChildOf = "child-of",
RelatedTo = "related-to",
Blocks = "blocks",
BlockedBy = "blocked-by",
Follow = "follows",
FollowedBy = "followed-by",
Caused = "caused",
CausedBy = "caused-by"
const DONE_FOLDER_FORMAT* = "yyyy-MM" const DONE_FOLDER_FORMAT* = "yyyy-MM"
const ISO8601_MS = "yyyy-MM-dd'T'HH:mm:ss'.'fffzzz" const ISO8601_MS = "yyyy-MM-dd'T'HH:mm:ss'.'fffzzz"
@ -173,6 +187,11 @@ proc parseDate*(d: string): DateTime =
continue continue
raise newException(ValueError, "Unable to parse input as a date: " & d & errMsg) raise newException(ValueError, "Unable to parse input as a date: " & d & errMsg)
proc parseRelation*(relStr: string): Relation =
let parts = relStr.split({' '})
if parts.len == 1: result = (RelatedTo, parseUUID(parts[0]))
else: result = (parseEnum[RelationType](parts[0]), parseUUID(parts[1]))
## 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
@ -180,6 +199,7 @@ proc fromStorageFormat*(id: string, issueTxt: string): Issue =
result = Issue( result = Issue(
id: parseUUID(id), id: parseUUID(id),
properties: newTable[string,string](), properties: newTable[string,string](),
relations: @[],
tags: @[]) tags: @[])
var parseState = ReadingSummary var parseState = ReadingSummary
@ -203,14 +223,16 @@ proc fromStorageFormat*(id: string, issueTxt: string): Issue =
parseState = ReadingDetails parseState = ReadingDetails
continue continue
let parts = line.split({':'}, 1) --> map(it.strip()) let parts = line.split({':'}, 1) --> map(it.strip())
if parts.len != 2: if parts.len != 2:
raise newException(ValueError, "unable to parse property line: " & line) raise newException(ValueError, "unable to parse property line: " & line)
# Take care of special properties: `tags` # Take care of special properties: `tags` and `relations`
if parts[0] == "tags": if parts[0] == "tags":
result.tags = parts[1].split({','}) --> map(it.strip()) result.tags = parts[1].split({','}) --> map(it.strip())
elif parts[0] == "relations":
result.relations = parts[1].split({','}) -->
map(parseRelation(it.strip))
else: result[parts[0]] = parts[1] else: result[parts[0]] = parts[1]
of ReadingDetails: of ReadingDetails:
@ -231,6 +253,11 @@ proc toStorageFormat*(issue: Issue, withComments = false): string =
if issue.tags.len > 0: lines.add("tags: " & issue.tags.join(",")) if issue.tags.len > 0: lines.add("tags: " & issue.tags.join(","))
if issue.relations.len > 0:
lines.add("relations: " &
(issue.relations --> map(it.rel & " " & it.id)).
join(', '))
if not isEmptyOrWhitespace(issue.details) or withComments: if not isEmptyOrWhitespace(issue.details) or withComments:
if withComments: lines.add("# Details go below the \"--------\"") if withComments: lines.add("# Details go below the \"--------\"")
lines.add("--------") lines.add("--------")
@ -328,16 +355,11 @@ proc loadAllIssues*(tasksDir: string): TableRef[IssueState, seq[Issue]] =
for state in IssueState: result[state] = tasksDir.loadIssues(state) for state in IssueState: result[state] = tasksDir.loadIssues(state)
proc changeState*(issue: Issue, tasksDir: string, newState: IssueState) = proc changeState*(issue: Issue, tasksDir: string, newState: IssueState) =
var dbgInfo = "[$#] changing state: $#$#" %
[ ($issue.id)[0..<6], $issue.state, $newState ]
let oldFilepath = 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)
if oldFilePath != issue.filepath: removeFile(oldFilepath) if oldFilePath != issue.filepath: removeFile(oldFilepath)
dbgInfo &= "\n\told path: $#\n\tnew path: $#" % [oldFilePath, issue.filepath]
issue.state = newState issue.state = newState
debug dbgInfo
proc delete*(issue: Issue) = removeFile(issue.filepath) proc delete*(issue: Issue) = removeFile(issue.filepath)