Make IssueState available as a field on Issue.
* Add `state` on `Issue` to be able to query the state of an issue even if you only have a reference to this issue and don't have a reference to the context or issues table. This does not change the persisted format of the issue. On disk the state of an issue is still represented by it's location in the file hierarchy. * Refactored libpit to use zero_functional instead of sequtils.
This commit is contained in:
parent
d01d6e37f4
commit
171adbb59d
@ -1,6 +1,6 @@
|
||||
# Package
|
||||
|
||||
version = "4.22.2"
|
||||
version = "4.23.0"
|
||||
author = "Jonathan Bernard"
|
||||
description = "Personal issue tracker."
|
||||
license = "MIT"
|
||||
@ -14,7 +14,8 @@ requires @[
|
||||
"nim >= 1.4.0",
|
||||
"docopt >= 0.6.8",
|
||||
"jester >= 0.5.0",
|
||||
"uuids >= 0.1.10"
|
||||
"uuids >= 0.1.10",
|
||||
"zero_functional"
|
||||
]
|
||||
|
||||
# Dependencies from git.jdb-software.com/nim-jdb/packages
|
||||
|
@ -1,8 +1,8 @@
|
||||
## Personal Issue Tracker CLI interface
|
||||
## ====================================
|
||||
|
||||
import std/algorithm, std/logging, std/options, std/os, std/sequtils,
|
||||
std/wordwrap, std/tables, std/terminal, std/times, std/unicode
|
||||
import std/[algorithm, logging, options, os, sequtils, wordwrap, tables,
|
||||
terminal, times, unicode]
|
||||
import cliutils, data_uri, docopt, json, timeutils, uuids
|
||||
|
||||
from nre import re
|
||||
|
@ -1,4 +1,4 @@
|
||||
const PIT_VERSION* = "4.22.2"
|
||||
const PIT_VERSION* = "4.23.0"
|
||||
|
||||
const USAGE* = """Usage:
|
||||
pit ( new | add) <summary> [<state>] [options]
|
||||
|
@ -1,8 +1,9 @@
|
||||
import std/json, std/logging, std/options, std/os, std/sequtils, std/strformat,
|
||||
std/strutils, std/tables, std/times
|
||||
import cliutils, docopt, langutils, timeutils, uuids
|
||||
import std/[json, logging, options, os, strformat, strutils, tables, times]
|
||||
import cliutils, docopt, langutils, uuids, zero_functional
|
||||
|
||||
import nre except toSeq
|
||||
import timeutils except `>`
|
||||
from sequtils import deduplicate, toSeq
|
||||
|
||||
type
|
||||
Issue* = ref object
|
||||
@ -11,6 +12,7 @@ type
|
||||
summary*, details*: string
|
||||
properties*: TableRef[string, string]
|
||||
tags*: seq[string]
|
||||
state*: IssueState
|
||||
|
||||
IssueState* = enum
|
||||
Current = "current",
|
||||
@ -201,12 +203,13 @@ proc fromStorageFormat*(id: string, issueTxt: string): Issue =
|
||||
continue
|
||||
|
||||
|
||||
let parts = line.split({':'}, 1).mapIt(it.strip())
|
||||
let parts = line.split({':'}, 1) --> map(it.strip())
|
||||
if parts.len != 2:
|
||||
raise newException(ValueError, "unable to parse property line: " & line)
|
||||
|
||||
# Take care of special properties: `tags`
|
||||
if parts[0] == "tags": result.tags = parts[1].split({','}).mapIt(it.strip())
|
||||
if parts[0] == "tags":
|
||||
result.tags = parts[1].split({','}) --> map(it.strip())
|
||||
else: result[parts[0]] = parts[1]
|
||||
|
||||
of ReadingDetails:
|
||||
@ -234,6 +237,11 @@ proc loadIssue*(filePath: string): Issue =
|
||||
result = fromStorageFormat(splitFile(filePath).name, readFile(filePath))
|
||||
result.filepath = filePath
|
||||
|
||||
let parentDirName = filePath.splitFile().dir.splitFile().name
|
||||
let issueState = IssueState.items.toSeq --> find($it == parentDirName)
|
||||
if issueState.isSome: result.state = issueState.get
|
||||
else: result.state = IssueState.Done
|
||||
|
||||
proc loadIssueById*(tasksDir, id: string): Issue =
|
||||
for path in walkDirRec(tasksDir):
|
||||
if path.splitFile.name.startsWith(id):
|
||||
@ -273,10 +281,10 @@ proc loadIssues*(path: string): seq[Issue] =
|
||||
|
||||
let orderedIds =
|
||||
if fileExists(orderFile):
|
||||
toSeq(orderFile.lines)
|
||||
.mapIt(it.split(' ')[0])
|
||||
.deduplicate
|
||||
.filterIt(not it.startsWith("> ") and not it.isEmptyOrWhitespace)
|
||||
(orderFile.lines.toSeq -->
|
||||
map(it.split(' ')[0]).
|
||||
filter(not it.startsWith("> ") and not it.isEmptyOrWhitespace)).
|
||||
deduplicate()
|
||||
else: newSeq[string]()
|
||||
|
||||
type TaggedIssue = tuple[issue: Issue, ordered: bool]
|
||||
@ -332,6 +340,7 @@ proc nextRecurrence*(tasksDir: string, rec: Recurrence, defaultIssue: Issue): Is
|
||||
|
||||
result = Issue(
|
||||
id: genUUID(),
|
||||
state: baseIssue.state,
|
||||
summary: baseIssue.summary,
|
||||
properties: newProps,
|
||||
tags: baseIssue.tags)
|
||||
@ -354,36 +363,38 @@ proc nextRecurrence*(tasksDir: string, rec: Recurrence, defaultIssue: Issue): Is
|
||||
|
||||
## Utilities for working with issue collections.
|
||||
proc filter*(issues: seq[Issue], filter: IssueFilter): seq[Issue] =
|
||||
result = issues
|
||||
var f: seq[Issue] = issues
|
||||
|
||||
for k,v in filter.properties:
|
||||
result = result.filterIt(it.hasProp(k) and it[k] == v)
|
||||
f = f --> filter(it.hasProp(k) and it[k] == v)
|
||||
|
||||
for k,v in filter.exclProperties:
|
||||
result = result.filter(proc (iss: Issue): bool =
|
||||
not iss.hasProp(k) or
|
||||
not v.anyIt(it == iss[k])
|
||||
)
|
||||
f = f --> filter(not (it.hasProp(k) and v.contains(it[k])))
|
||||
|
||||
if filter.completedRange.isSome:
|
||||
let range = filter.completedRange.get
|
||||
result = result.filterIt(
|
||||
f = f --> filter(
|
||||
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)
|
||||
f = f --> filter(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)
|
||||
f = f -->
|
||||
filter(it.summary.find(p).isSome or it.details.find(p).isSome)
|
||||
|
||||
for tag in filter.hasTags:
|
||||
result = result.filterIt(it.tags.find(tag) >= 0)
|
||||
for tagLent in filter.hasTags:
|
||||
let tag = tagLent
|
||||
f = f --> filter(it.tags.find(tag) >= 0)
|
||||
|
||||
for exclTag in filter.exclTags:
|
||||
result = result.filterIt(it.tags.find(exclTag) < 0)
|
||||
for exclTagLent in filter.exclTags:
|
||||
let exclTag = exclTagLent
|
||||
f = f --> filter(it.tags.find(exclTag) < 0)
|
||||
|
||||
return f # not using result because zero_functional doesn't play nice with it
|
||||
|
||||
proc find*(
|
||||
issues: TableRef[IssueState, seq[Issue]],
|
||||
@ -400,7 +411,7 @@ proc loadConfig*(args: Table[string, Value] = initTable[string, Value]()): PitCo
|
||||
".pitrc", $getEnv("PITRC"), $getEnv("HOME") & "/.pitrc"]
|
||||
|
||||
var pitrcFilename: string =
|
||||
foldl(pitrcLocations, if len(a) > 0: a elif fileExists(b): b else: "")
|
||||
pitrcLocations --> fold("", if fileExists(it): it else: a)
|
||||
|
||||
if not fileExists(pitrcFilename):
|
||||
warn "could not find .pitrc file: " & pitrcFilename
|
||||
|
Loading…
x
Reference in New Issue
Block a user