Add filters for text-matching on issue summary or details.
This commit is contained in:
parent
2404f6a3d1
commit
a924d7b649
24
src/pit.nim
24
src/pit.nim
@ -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)
|
||||||
|
@ -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 =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user