From 171adbb59d72c5f0f5dd0aaac194e6ee6500d2a5 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Tue, 21 Mar 2023 08:27:36 -0500 Subject: [PATCH] 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. --- pit.nimble | 5 +-- src/pit.nim | 4 +-- src/pitpkg/cliconstants.nim | 2 +- src/pitpkg/private/libpit.nim | 57 +++++++++++++++++++++-------------- 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/pit.nimble b/pit.nimble index 0e4b215..3db2896 100644 --- a/pit.nimble +++ b/pit.nimble @@ -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 diff --git a/src/pit.nim b/src/pit.nim index 7d95f13..b80d278 100644 --- a/src/pit.nim +++ b/src/pit.nim @@ -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 diff --git a/src/pitpkg/cliconstants.nim b/src/pitpkg/cliconstants.nim index e902a04..4d829ed 100644 --- a/src/pitpkg/cliconstants.nim +++ b/src/pitpkg/cliconstants.nim @@ -1,4 +1,4 @@ -const PIT_VERSION* = "4.22.2" +const PIT_VERSION* = "4.23.0" const USAGE* = """Usage: pit ( new | add) [] [options] diff --git a/src/pitpkg/private/libpit.nim b/src/pitpkg/private/libpit.nim index 6dfa9e6..2cfc3e6 100644 --- a/src/pitpkg/private/libpit.nim +++ b/src/pitpkg/private/libpit.nim @@ -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