Add filters for text-matching on issue summary or details.

This commit is contained in:
Jonathan Bernard 2018-06-11 10:19:10 -05:00
parent 2404f6a3d1
commit a924d7b649
2 changed files with 58 additions and 17 deletions

View File

@ -4,6 +4,7 @@
import cliutils, docopt, json, logging, options, os, ospaths, sequtils, import cliutils, docopt, json, logging, options, os, ospaths, sequtils,
tables, terminal, times, timeutils, unicode, uuids tables, terminal, times, timeutils, unicode, uuids
from nre import re
import strutils except capitalize, toUpper, toLower import strutils except capitalize, toUpper, toLower
import pitpkg/private/libpit import pitpkg/private/libpit
export libpit export libpit
@ -182,7 +183,7 @@ proc edit(issue: Issue) =
getCurrentExceptionMsg() getCurrentExceptionMsg()
issue.store() issue.store()
proc list(ctx: CliContext, filter: Option[IssueFilter], state: Option[IssueState], today, future, verbose: bool) = proc list(ctx: CliContext, filter: Option[IssueFilter], state: Option[IssueState], showToday, showFuture, verbose: bool) =
if state.isSome: if state.isSome:
ctx.loadIssues(state.get) ctx.loadIssues(state.get)
@ -193,6 +194,12 @@ proc list(ctx: CliContext, filter: Option[IssueFilter], state: Option[IssueState
ctx.loadAllIssues() ctx.loadAllIssues()
if filter.isSome: ctx.filterIssues(filter.get) if filter.isSome: ctx.filterIssues(filter.get)
let today = showToday and [Current, TodoToday].anyIt(
ctx.issues.hasKey(it) and ctx.issues[it].len > 0)
let future = showFuture and [Pending, Todo].anyIt(
ctx.issues.hasKey(it) and ctx.issues[it].len > 0)
let indent = if today and future: " " else: "" let indent = if today and future: " " else: ""
# Today's items # Today's items
@ -246,6 +253,12 @@ Options:
-F, --future Limit to future issues. -F, --future Limit to future issues.
-m, --match <pattern> Limit to issues whose summaries match the given
pattern (PCRE regex supported).
-M, --match-all <pat> Limit to the issues whose summaries or details
match the given pattern (PCRE regex supported).
-v, --verbose Show issue details when listing issues. -v, --verbose Show issue details when listing issues.
-q, --quiet Suppress verbose output. -q, --quiet Suppress verbose output.
@ -380,6 +393,15 @@ Options:
filter.properties = propertiesOption.get filter.properties = propertiesOption.get
filterOption = some(filter) filterOption = some(filter)
# If they supplied text matches, add that to the filter.
if args["--match"]:
filter.summaryMatch = some(re("(?i)" & $args["--match"]))
filterOption = some(filter)
if args["--match-all"]:
filter.fullMatch = some(re("(?i)" & $args["--match-all"]))
filterOption = some(filter)
# If no "context" property is given, use the default (if we have one) # If no "context" property is given, use the default (if we have one)
if ctx.defaultContext.isSome and not filter.properties.hasKey("context"): if ctx.defaultContext.isSome and not filter.properties.hasKey("context"):
stderr.writeLine("Limiting to default context: " & ctx.defaultContext.get) stderr.writeLine("Limiting to default context: " & ctx.defaultContext.get)

View File

@ -1,7 +1,8 @@
import cliutils, docopt, json, logging, options, os, ospaths, sequtils, import cliutils, docopt, json, logging, options, os, ospaths, sequtils,
strutils, tables, times, timeutils, uuids strutils, tables, times, timeutils, uuids
from nre import re, match from nre import find, match, re, Regex
type type
Issue* = ref object Issue* = ref object
id*: UUID id*: UUID
@ -19,8 +20,9 @@ type
Dormant = "dormant" Dormant = "dormant"
IssueFilter* = ref object IssueFilter* = ref object
completedRange*: Option[tuple[b, e: DateTime]]
fullMatch*, summaryMatch*: Option[Regex]
properties*: TableRef[string, string] properties*: TableRef[string, string]
completedRange*: tuple[b, e: DateTime]
PitConfig* = ref object PitConfig* = ref object
tasksDir*: string tasksDir*: string
@ -62,22 +64,30 @@ proc setDateTime*(issue: Issue, key: string, dt: DateTime) =
proc initFilter*(): IssueFilter = proc initFilter*(): IssueFilter =
result = IssueFilter( result = IssueFilter(
properties: newTable[string,string](), completedRange: none(tuple[b, e: DateTime]),
completedRange: (fromUnix(0).local, fromUnix(253400659199).local)) fullMatch: none(Regex),
summaryMatch: none(Regex),
properties: newTable[string, string]())
proc initFilter*(props: TableRef[string, string]): IssueFilter = proc propsFilter*(props: TableRef[string, string]): IssueFilter =
if isNil(props): if isNil(props):
raise newException(ValueError, raise newException(ValueError,
"cannot initialize property filter without properties") "cannot initialize property filter without properties")
result = IssueFilter( result = initFilter()
properties: props, result.properties = props
completedRange: (fromUnix(0).local, fromUnix(253400659199).local))
proc initFilter*(range: tuple[b, e: DateTime]): IssueFilter = proc dateFilter*(range: tuple[b, e: DateTime]): IssueFilter =
result = IssueFilter( result = initFilter()
properties: newTable[string, string](), result.completedRange = some(range)
completedRange: range)
proc summaryMatchFilter*(pattern: string): IssueFilter =
result = initFilter()
result.summaryMatch = some(re("(?i)" & pattern))
proc fullMatchFilter*(pattern: string): IssueFilter =
result = initFilter()
result.fullMatch = some(re("(?i)" & pattern))
## Parse and format issues ## Parse and format issues
proc fromStorageFormat*(id: string, issueTxt: string): Issue = proc fromStorageFormat*(id: string, issueTxt: string): Issue =
@ -190,10 +200,19 @@ proc filter*(issues: seq[Issue], filter: IssueFilter): seq[Issue] =
for k,v in filter.properties: for k,v in filter.properties:
result = result.filterIt(it.hasProp(k) and it[k] == v) result = result.filterIt(it.hasProp(k) and it[k] == v)
result = result.filterIt(not it.hasProp("completed") or if filter.completedRange.isSome:
it.getDateTime("completed").between( let range = filter.completedRange.get
filter.completedRange.b, result = result.filterIt(
filter.completedRange.e)) not it.hasProp("completed") or
it.getDateTime("completed").between(range.b, range.e))
if filter.summaryMatch.isSome:
let p = filter.summaryMatch.get
result = result.filterIt(it.summary.find(p).isSome)
if filter.fullMatch.isSome:
let p = filter.fullMatch.get
result = result.filterIt( it.summary.find(p).isSome or it.details.find(p).isSome)
### Configuration utilities ### Configuration utilities
proc loadConfig*(args: Table[string, Value] = initTable[string, Value]()): PitConfig = proc loadConfig*(args: Table[string, Value] = initTable[string, Value]()): PitConfig =