Skip to content

Commit 264f1a8

Browse files
committed
v2.0 automatic colouring of nodes using capturing groups
1 parent e8340cc commit 264f1a8

2 files changed

Lines changed: 91 additions & 15 deletions

File tree

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ To a very limited extent the format is documented [here](https://www.toketaware.
1818
* [Actions](#actions)
1919
* [Colour Numbers](#colour-numbers)
2020
* [Colour RGB Values](#colour-rgb-values)
21+
* [Automatic Colouring](#automatic-colouring)
2122
* [Delete](#delete)
2223
* [Keep](#keep)
2324
* [Shapes](#shapes)
@@ -152,6 +153,7 @@ Actions you can take include:
152153

153154
* Specify a colour number
154155
* Specify a colour RGB value
156+
* Automate colouring nodes based on a regular expression's capturing group
155157
* `delete`
156158
* `keep`
157159
* Specify a shape
@@ -175,6 +177,23 @@ You can also specify `nextcolour`, `nextcolor` or even `nc` and filterCSV will s
175177

176178
This is a hexadecimal 6-character representation of the colour, in Red-Green-Blue (RGB) format. For example `FFAAFF`.
177179

180+
181+
#### Automatic Colouring
182+
183+
Rather than either using [Colour Numbers](#colour-numbers) or [Colour RGB Values](#colour-rgb-values) you might be able to automate colouring nodes.
184+
185+
Automatic node colouring requires you to code a capturing group inside a regular expression.
186+
187+
Here is an example:
188+
189+
filterCSV '-(.?)-' autocolour < test1.csv > test2.csv
190+
191+
The capturing group is the portion of the regular expression inside the round brackets. When filterCSV processes this command it keeps track of the values that match the capturing group and uses them to consistently colour the nodes.
192+
193+
**Notes**
194+
195+
1. You can specify `autocolour`, `autocolor`, or even `ac`.
196+
1. You can use multiple capturing groups, for example `RC: (.*) SC: (.*)`. filterCSV uses all the groups to form a key; When **any** of the capturing groups' values changes a new colour is selected.
178197
#### Delete
179198

180199
`delete` deletes the matching node and all its children.

filterCSV

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ from functools import reduce
3636

3737
# from CSVTree import CSVTree
3838

39-
filterCSV_level = "1.12"
40-
filterCSV_date = "28 December, 2020"
39+
filterCSV_level = "2.0"
40+
filterCSV_date = "17 April, 2022"
4141

4242
inputIndentCharacters = ""
4343

@@ -1485,18 +1485,23 @@ class CSVTree:
14851485

14861486
# Don't propagate
14871487
propagateToChildren = False
1488+
14881489
elif action == "asbullet":
14891490
self.makeAsBulletOfParent()
1491+
14901492
elif action == "reverse":
14911493
self.reverseChildren()
1494+
14921495
elif action == "sort":
14931496
self.sortChildren()
1497+
14941498
elif action[0] == "{":
14951499
# position specified
14961500
self.data["position"] = action
14971501

14981502
# Don't propagate
14991503
propagateToChildren = False
1504+
15001505
elif action == "note":
15011506
# Document the match in the note field
15021507
if isinstance(criterion, str):
@@ -1508,24 +1513,31 @@ class CSVTree:
15081513
self.data["note"] += "\nMatched " + criterionString
15091514
else:
15101515
self.data["note"] = "Matched " + criterionString
1516+
15111517
elif action == "noshape":
15121518
# Remove any shape specification from the matched node
15131519
self.data["shape"] = ""
1520+
15141521
elif action == "nonote":
15151522
# Remove any note specification from the matched node
15161523
self.data["note"] = ""
1524+
15171525
elif action == "noposition":
15181526
# Remove any position specification from the matched node
15191527
self.data["position"] = ""
1528+
15201529
elif action == "nocolour":
15211530
# Remove any colour specification from the matched node
15221531
self.data["colour"] = ""
1532+
15231533
elif len(action) == 6 and all(c in hexdigits for c in action):
15241534
# 6-digit hexadecimal so is colour RGB value
15251535
self.data["colour"] = action
1536+
15261537
elif action in iThoughtsShapes:
15271538
# Is a shape
15281539
self.data["shape"] = action
1540+
15291541
elif action[0:9] == "priority:":
15301542
if action[9:].isdigit():
15311543
self.data["priority"] = action[9:]
@@ -1534,6 +1546,7 @@ class CSVTree:
15341546
f"Erroneous priority value {action[9:]} "
15351547
f"(Pattern was: '{criterion.pattern}')." + "\n"
15361548
)
1549+
15371550
elif action[0:5] == "prio:":
15381551
if action[5].isdigit():
15391552
self.data["priority"] = action[5]
@@ -1542,8 +1555,10 @@ class CSVTree:
15421555
f"Erroneous priority value {action[5]} "
15431556
f"(Pattern was: '{criterion.pattern}')." + "\n"
15441557
)
1558+
15451559
elif action in ["noprio", "nopriority"]:
15461560
self.data["priority"] = ""
1561+
15471562
elif action[0:9] == "progress:":
15481563
if action[9:].isdigit():
15491564
self.data["progress"] = action[9:]
@@ -1552,6 +1567,7 @@ class CSVTree:
15521567
f"Erroneous progress value {action[9:]} "
15531568
f"(Pattern was: '{criterion.pattern}')." + "\n"
15541569
)
1570+
15551571
elif action[0:5] == "prog:":
15561572
if action[5].isdigit():
15571573
self.data["progress"] = action[5]
@@ -1562,16 +1578,20 @@ class CSVTree:
15621578
)
15631579
elif action in ["noprog", "noprogress"]:
15641580
self.data["progress"] = ""
1581+
15651582
elif action in iThoughtsIcons:
15661583
# Is an icons
15671584
if self.data["icons"] == "":
15681585
self.data["icons"] = action
15691586
else:
15701587
self.data["icons"] += "," + action
1588+
15711589
elif action == "noicons":
15721590
self.data["icons"] = ""
1591+
15731592
elif action[0:4] == "sub:":
1574-
self.data["cell"] = re.sub(criterion, action[4:], self.data["cell"])
1593+
self.data["cell"] = re.sub(criterion, action[4:], self.data["cell"])
1594+
15751595
elif action.isdigit():
15761596
# Attempt to parse as from the colour palette
15771597
colourNumber = int(action)
@@ -1586,6 +1606,7 @@ class CSVTree:
15861606

15871607
else:
15881608
self.data["colour"] = iThoughtsColours.getColour(colourNumber)
1609+
15891610
else:
15901611
sys.stderr.write(
15911612
f"Erroneous action value {action} "
@@ -2374,6 +2395,44 @@ class CSVTree:
23742395
# Delete any unmatched nodes
23752396
self._deleteUnmarked()
23762397

2398+
def _processKeep(self, matchCriterion):
2399+
if self.isMatch(matchCriterion) is True:
2400+
# mark self and all the ancestors matched
2401+
self._markAncestorsMatched()
2402+
2403+
# mark self and its subtree matched
2404+
self._markSubtreeMatched()
2405+
else:
2406+
# Maybe children etc are matches
2407+
for childNode in self.childNodes:
2408+
childNode._processKeep(matchCriterion)
2409+
2410+
def processAutocolour(self, matchCriterion):
2411+
self._processAutocolour(matchCriterion, [])
2412+
2413+
def _processAutocolour(self, matchCriterion, matchValues):
2414+
if self.isMatch(matchCriterion) is True:
2415+
# Process node as a match
2416+
searchResult = matchCriterion.search(self.data["cell"])
2417+
2418+
# Get key - tuple with match values for each group
2419+
matchValue = searchResult.groups()
2420+
2421+
# Handle whether key is a new key or existing
2422+
if matchValue not in matchValues:
2423+
# New colour
2424+
matchValues.append(matchValue)
2425+
2426+
colourNumber = len(matchValues)
2427+
else:
2428+
# Existing colour
2429+
colourNumber = matchValues.index(matchValue) + 1
2430+
self.data["colour"] = iThoughtsColours.getColour(colourNumber)
2431+
2432+
# Maybe children etc are matches
2433+
for childNode in self.childNodes:
2434+
childNode._processAutocolour(matchCriterion, matchValues)
2435+
23772436
def _markUnmatched(self):
23782437
self.matched = False
23792438

@@ -2390,18 +2449,6 @@ class CSVTree:
23902449
for childNode in self.childNodes:
23912450
childNode._markSubtreeMatched()
23922451

2393-
def _processKeep(self, matchCriterion):
2394-
if self.isMatch(matchCriterion) is True:
2395-
# mark self and all the ancestors matched
2396-
self._markAncestorsMatched()
2397-
2398-
# mark self and its subtree matched
2399-
self._markSubtreeMatched()
2400-
else:
2401-
# Maybe children etc are matches
2402-
for childNode in self.childNodes:
2403-
childNode._processKeep(matchCriterion)
2404-
24052452
def _deleteUnmarked(self):
24062453
for childNode in self.childNodes:
24072454
childNode._deleteUnmarked()
@@ -2856,6 +2903,7 @@ manual.
28562903
"""
28572904
)
28582905
sys.exit()
2906+
28592907
iThoughtsColours = iThoughtsColours()
28602908

28612909
iThoughtsShapes = iThoughtsShapes()
@@ -2984,12 +3032,19 @@ manual.
29843032
actionsList = actionsLists[parmPair]
29853033
if (isinstance(matchCriterion, str)) and (matchCriterion.startswith("@")):
29863034
csvTree.applyActions(matchCriterion, actionsList)
3035+
29873036
elif matchCriterion.pattern.lower() == "dump":
29883037
sys.stderr.write(csvTree.dump(actionsList))
3038+
29893039
elif actionsList[0] == "keep":
29903040
csvTree.processKeep(matchCriterion)
3041+
3042+
elif actionsList[0] in ["autocolour", "autocolor", "ac"]:
3043+
csvTree.processAutocolour(matchCriterion)
3044+
29913045
elif matchCriterion.pattern.lower() == "properties":
29923046
csvTree.printTreeProperties()
3047+
29933048
else:
29943049
func = {
29953050
"check": csvTree.checkHierarchy,
@@ -3004,12 +3059,14 @@ manual.
30043059
"xml": csvTree.exportToXML,
30053060
"taskpaper": csvTree.exportToTaskpaper,
30063061
}.get(matchCriterion.pattern.lower())
3062+
30073063
if func:
30083064
output = func(actionsList)
30093065
if output:
30103066
print("\n".join(output))
30113067
sys.exit()
30123068
# commands that generate no output fall through
3069+
30133070
else:
30143071
csvTree.applyActions(matchCriterion, actionsList)
30153072

0 commit comments

Comments
 (0)