Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
339e88cddd | |||
0a2249018b | |||
ec3008937d | |||
10fcc34ea2 |
141
README.md
Normal file
141
README.md
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# Personal Issue Tracker
|
||||||
|
|
||||||
|
This is [Jonathan Bernard's](mailto:jonathan@jdbernard.com) personal issue
|
||||||
|
tracker. In it's current form it is essentially a way to keep an curated list of
|
||||||
|
TODO's, organizing them by workflow category (todo, todo-today, dormant, etc.)
|
||||||
|
and context (Personal, Work, etc.).
|
||||||
|
|
||||||
|
## Categories
|
||||||
|
|
||||||
|
`pit` organizes issues into the following workflow categories:
|
||||||
|
|
||||||
|
- `current` - actively in progress
|
||||||
|
- `todo` - to be addressed in the future
|
||||||
|
- `todo-today` - chosen to be addressed today
|
||||||
|
- `pending` - blocked by some third party
|
||||||
|
- `dormant` - long-term things I don't want to forget but don't need in front
|
||||||
|
of me every day.
|
||||||
|
- `done`
|
||||||
|
|
||||||
|
In my typical workflow the `todo` category serves as a collection point for
|
||||||
|
things I want to keep track of. Then on a a daily basis I review issues in the
|
||||||
|
`todo` category and move a selection to the `todo-today` category. I also try
|
||||||
|
to keep the total number of issues in the `todo` below about a dozen. If there
|
||||||
|
are more than a dozen things in my `todo` category I will identify the lowest
|
||||||
|
priority items and move them to the `dormant` category.
|
||||||
|
|
||||||
|
## Issue Properties
|
||||||
|
|
||||||
|
`pit` allows arbitrary properties to be attached to issues in the form of
|
||||||
|
key-value pairs. On the command line these can be provided via the `-p` or
|
||||||
|
`--properties` parameter in the form
|
||||||
|
`-p <prop1Name>:<prop1Value>;<prop2Name>:<prop2Value>[;...]`
|
||||||
|
|
||||||
|
There are a couple of properties that pit will recognize automatically:
|
||||||
|
|
||||||
|
- `context`: the context organization feature is implemented using issue
|
||||||
|
properties.
|
||||||
|
- `created`: `pit` uses this property to timestamp an issue when it is created.
|
||||||
|
- `completed`: `pit` uses this property to timestamp an issue when it is moved
|
||||||
|
to the `done` category.
|
||||||
|
- `pending`: `pit` looks to this property to provide extra information about
|
||||||
|
issues in the `pending` category. Typically I use this to note who or what is
|
||||||
|
blocking the issue and why.
|
||||||
|
|
||||||
|
Some other common properties I use are:
|
||||||
|
|
||||||
|
- `resolution`: for short notes about why an issue was moved to `done`,
|
||||||
|
especially if it the action wasn't taken or if it is not completely clear
|
||||||
|
that this issue was completed.
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
`pit` allows configuration via command-line options and via a configuration
|
||||||
|
file. There is some overlap between the two methods of configuring `pit`, but
|
||||||
|
it is not a complete mapping.
|
||||||
|
|
||||||
|
### Config File
|
||||||
|
|
||||||
|
`pit` looks for a JSON configuration file in the following places (in order):
|
||||||
|
|
||||||
|
1. From a file path passed on the command line via the `--config <cfgFile>` parameter,
|
||||||
|
2. `./.pitrc`, in the current working directory,
|
||||||
|
3. From a file path set in the `PITRC` environment variable.
|
||||||
|
4. `$HOME/.pitrc`, in the user's home directory.
|
||||||
|
|
||||||
|
|
||||||
|
#### Sample Config File
|
||||||
|
|
||||||
|
This example illustrates all of the possible configuration options.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"api": {
|
||||||
|
"apiKeys": [
|
||||||
|
"50cdcb660554e2d50fd88bd40b6579717bf00643f6ff57f108baf16c8c083f77",
|
||||||
|
"e4fc1aac49fc1f2f7f4ca6b1f04d41a4ccdd58e13bb53b41da97703d47267ceb",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"defaultContext": "personal",
|
||||||
|
"verbose": false,
|
||||||
|
"termWidth": 120,
|
||||||
|
"triggerPtk": true
|
||||||
|
},
|
||||||
|
"contexts": {
|
||||||
|
"nla-music": "New Life Music",
|
||||||
|
"nla-youth-band": "New Life Youth Band",
|
||||||
|
"acn": "Accenture",
|
||||||
|
"hff": "Hope Family Fellowship"
|
||||||
|
},
|
||||||
|
"tasksDir": "/mnt/c/Users/Jonathan Bernard/synced/tasks"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Explanation of configurable options.
|
||||||
|
|
||||||
|
In general, options supplied on the CLI directly will override options supplied
|
||||||
|
in the configuration file. All options are optional unless stated otherwise.
|
||||||
|
|
||||||
|
* `api`: configuration options specific to the API service.
|
||||||
|
|
||||||
|
- `apiKeys`: a list of Bearer tokens accepted by the API for the purpose of
|
||||||
|
authenticating API requests.
|
||||||
|
|
||||||
|
* `cli`: configuration options specific to the CLI.
|
||||||
|
|
||||||
|
- `defaultContext`: if present all invokations to the CLI will
|
||||||
|
be in this context. This is like adding a `--context <defaultContext>`
|
||||||
|
parameter to every CLI invocation. Any actual `--context` parameter will
|
||||||
|
override this value.
|
||||||
|
|
||||||
|
- `verbose`: Show issue details when listing issues (same as
|
||||||
|
`--verbose` flag).
|
||||||
|
|
||||||
|
- `termWidth`: Set the expected width of the terminal (for wrapping text).
|
||||||
|
|
||||||
|
- `triggerPtk`: If set to `true`, invoke the `ptk` command to start and stop
|
||||||
|
timers when issues move to the `current` and `done` categories
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
* `contexts`: `pit` allows issues to be organized into different contexts via
|
||||||
|
a `context` property on the issue. The CLI groups issues according to
|
||||||
|
context. When printing contexts the CLI will take the value from the issues'
|
||||||
|
`context` properties and capatalize it. In some cases you may wish to have a
|
||||||
|
different display value for a context. I like to use abbreviations for long
|
||||||
|
context names to reduce the need to type, `hff` for "Hope Family Fellowship",
|
||||||
|
for example. The `contexts` config option allows you to provide a map of
|
||||||
|
context values to context display names See the sample file below for an
|
||||||
|
example.
|
||||||
|
|
||||||
|
Note that this mapping does not have to have entries for all contexts, only
|
||||||
|
those you wish to provide with an alternate display form. For example, in the
|
||||||
|
configuration sample above the default context is `personal`, a value not
|
||||||
|
present in the `contexts` configuration. `personal` will be displayed as
|
||||||
|
"Personal"; it does not need an alternate display name.
|
||||||
|
|
||||||
|
* `tasksDir` **required**: a file path to the root directory for the issue
|
||||||
|
repository (same as `--tasks-dir` CLI parameter).
|
||||||
|
|
||||||
|
- CLI parameter: *cannot be specified via CLI*
|
||||||
|
- config file key: `contexts`
|
@ -1,6 +1,6 @@
|
|||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "4.7.0"
|
version = "4.9.0"
|
||||||
author = "Jonathan Bernard"
|
author = "Jonathan Bernard"
|
||||||
description = "Personal issue tracker."
|
description = "Personal issue tracker."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -15,7 +15,7 @@ requires @[
|
|||||||
"jester 0.4.1",
|
"jester 0.4.1",
|
||||||
"uuids 0.1.10",
|
"uuids 0.1.10",
|
||||||
|
|
||||||
"https://git.jdb-labs.com/jdb/nim-cli-utils.git >= 0.6.1",
|
"https://git.jdb-labs.com/jdb/nim-cli-utils.git >= 0.6.4",
|
||||||
"https://git.jdb-labs.com/jdb/nim-lang-utils.git >= 0.4.0",
|
"https://git.jdb-labs.com/jdb/nim-lang-utils.git >= 0.4.0",
|
||||||
"https://git.jdb-labs.com/jdb/nim-time-utils.git >= 0.4.0",
|
"https://git.jdb-labs.com/jdb/nim-time-utils.git >= 0.4.0",
|
||||||
"https://git.jdb-labs.com/jdb/update-nim-package-version"
|
"https://git.jdb-labs.com/jdb/update-nim-package-version"
|
||||||
|
17
src/pit.nim
17
src/pit.nim
@ -5,7 +5,7 @@ import cliutils, docopt, json, logging, options, os, sequtils,
|
|||||||
tables, terminal, times, timeutils, unicode, uuids
|
tables, terminal, times, timeutils, unicode, uuids
|
||||||
|
|
||||||
from nre import re
|
from nre import re
|
||||||
import strutils except capitalize, strip, toUpper, toLower
|
import strutils except alignLeft, capitalize, strip, toUpper, toLower
|
||||||
import pitpkg/private/libpit
|
import pitpkg/private/libpit
|
||||||
export libpit
|
export libpit
|
||||||
|
|
||||||
@ -36,6 +36,7 @@ proc initContext(args: Table[string, Value]): CliContext =
|
|||||||
let cliCfg = CombinedConfig(docopt: args, json: cliJson)
|
let cliCfg = CombinedConfig(docopt: args, json: cliJson)
|
||||||
|
|
||||||
result = CliContext(
|
result = CliContext(
|
||||||
|
cfg: pitCfg,
|
||||||
contexts: pitCfg.contexts,
|
contexts: pitCfg.contexts,
|
||||||
defaultContext:
|
defaultContext:
|
||||||
if not cliJson.hasKey("defaultContext"): none(string)
|
if not cliJson.hasKey("defaultContext"): none(string)
|
||||||
@ -43,7 +44,7 @@ proc initContext(args: Table[string, Value]): CliContext =
|
|||||||
verbose: parseBool(cliCfg.getVal("verbose", "false")) and not args["--quiet"],
|
verbose: parseBool(cliCfg.getVal("verbose", "false")) and not args["--quiet"],
|
||||||
issues: newTable[IssueState, seq[Issue]](),
|
issues: newTable[IssueState, seq[Issue]](),
|
||||||
tasksDir: pitCfg.tasksDir,
|
tasksDir: pitCfg.tasksDir,
|
||||||
termWidth: parseInt(cliCfg.getVal("term-width", "80")),
|
termWidth: parseInt(cliCfg.getVal("termWidth", "80")),
|
||||||
triggerPtk: cliJson.getOrDefault("triggerPtk").getBool(false))
|
triggerPtk: cliJson.getOrDefault("triggerPtk").getBool(false))
|
||||||
|
|
||||||
proc getIssueContextDisplayName(ctx: CliContext, context: string): string =
|
proc getIssueContextDisplayName(ctx: CliContext, context: string): string =
|
||||||
@ -287,6 +288,8 @@ Options:
|
|||||||
configured in the .pitrc file)
|
configured in the .pitrc file)
|
||||||
|
|
||||||
--term-width <width> Manually set the terminal width to use.
|
--term-width <width> Manually set the terminal width to use.
|
||||||
|
|
||||||
|
--ptk Enable PTK integration for this command.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logging.addHandler(newConsoleLogger())
|
logging.addHandler(newConsoleLogger())
|
||||||
@ -401,7 +404,7 @@ Options:
|
|||||||
if targetState == Done: issue["completed"] = getTime().local.formatIso8601
|
if targetState == Done: issue["completed"] = getTime().local.formatIso8601
|
||||||
issue.changeState(ctx.tasksDir, targetState)
|
issue.changeState(ctx.tasksDir, targetState)
|
||||||
|
|
||||||
if ctx.triggerPtk:
|
if ctx.triggerPtk or args["--ptk"]:
|
||||||
if targetState == Current:
|
if targetState == Current:
|
||||||
let issue = ctx.tasksDir.loadIssueById($(args["<id>"][0]))
|
let issue = ctx.tasksDir.loadIssueById($(args["<id>"][0]))
|
||||||
var cmd = "ptk start"
|
var cmd = "ptk start"
|
||||||
@ -487,7 +490,13 @@ Options:
|
|||||||
if issue.hasProp("context") and not uniqContexts.contains(issue["context"]):
|
if issue.hasProp("context") and not uniqContexts.contains(issue["context"]):
|
||||||
uniqContexts.add(issue["context"])
|
uniqContexts.add(issue["context"])
|
||||||
|
|
||||||
for c in uniqContexts: stdout.writeLine(c)
|
let maxLen = foldl(uniqContexts,
|
||||||
|
if a.len > b.len: a
|
||||||
|
else: b
|
||||||
|
).len
|
||||||
|
|
||||||
|
for c in uniqContexts:
|
||||||
|
stdout.writeLine(c.alignLeft(maxLen+2) & ctx.getIssueContextDisplayName(c))
|
||||||
|
|
||||||
# List a specific issue
|
# List a specific issue
|
||||||
elif issueIdOption.isSome:
|
elif issueIdOption.isSome:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import cliutils, docopt, json, logging, langutils, options, os, ospaths,
|
import cliutils, docopt, json, logging, langutils, options, os,
|
||||||
sequtils, 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
|
||||||
@ -49,6 +49,7 @@ proc `[]`*(issue: Issue, key: string): string =
|
|||||||
proc `[]=`*(issue: Issue, key: string, value: string) =
|
proc `[]=`*(issue: Issue, key: string, value: string) =
|
||||||
issue.properties[key] = value
|
issue.properties[key] = value
|
||||||
|
|
||||||
|
## Issue property accessors
|
||||||
proc hasProp*(issue: Issue, key: string): bool =
|
proc hasProp*(issue: Issue, key: string): bool =
|
||||||
return issue.properties.hasKey(key)
|
return issue.properties.hasKey(key)
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ proc getDateTime*(issue: Issue, key: string, default: DateTime): DateTime =
|
|||||||
proc setDateTime*(issue: Issue, key: string, dt: DateTime) =
|
proc setDateTime*(issue: Issue, key: string, dt: DateTime) =
|
||||||
issue.properties[key] = dt.formatIso8601
|
issue.properties[key] = dt.formatIso8601
|
||||||
|
|
||||||
|
## Issue filtering
|
||||||
proc initFilter*(): IssueFilter =
|
proc initFilter*(): IssueFilter =
|
||||||
result = IssueFilter(
|
result = IssueFilter(
|
||||||
completedRange: none(tuple[b, e: DateTime]),
|
completedRange: none(tuple[b, e: DateTime]),
|
||||||
|
@ -1 +1 @@
|
|||||||
const PIT_VERSION* = "4.7.0"
|
const PIT_VERSION* = "4.9.0"
|
Reference in New Issue
Block a user