pyDiffTools 0.1.8__tar.gz → 0.1.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pydifftools-0.1.8/pyDiffTools.egg-info → pydifftools-0.1.11}/PKG-INFO +1 -1
- {pydifftools-0.1.8 → pydifftools-0.1.11/pyDiffTools.egg-info}/PKG-INFO +1 -1
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/command_line.py +68 -1
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/flowchart/graph.py +66 -2
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/flowchart/watch_graph.py +91 -24
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pyproject.toml +1 -1
- {pydifftools-0.1.8 → pydifftools-0.1.11}/LICENSE.md +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/MANIFEST.in +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/README.rst +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/_quarto.yml +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/__version__.txt +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/1a724af72b16f5a9e607e12b1c721645/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/1b28fc9daac9081847e5161b2c546f8a/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/231f64eee282fa225d1104935cf80a24/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/26e56f6b0ff54851a45145157f2f0dc4/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/311fabd7029ffd050d056e2f316eb50f/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/57da2021e5b156ac3adf01398201c723/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/62b24ea7da75011d92b0f8924faa208d/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/7f1b20d69d889514ab5d1cc92e3cb14f/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/86f74c8c54a87ff892d9b15dd714e8f0/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/88893b4234eac2945d9d6cb2e277f186/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/9a40046ada6f582ee34af00fbdbfb417/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/a1bf4d270d0641ff41faf1d7cce3439a/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/a3789f7d9585a781f2a1c60ce95ff10d/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/be46ecba858d39ad5f0c46902ddf1c02/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/d0cbc57a12f2ccce710a5afd04cc05e7/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/d3e12d320b14228f701231ae32ddd7dd/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/eb434f61555438d020a6970a5dbf9ee8/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/f6b0e73aa7fa029134665d4dde57e096/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/f719a6e4ff09873cb0ffb06ec9d232f9/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/feeee244a7ce3d60e1a227eb604df823/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/global.db +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/example.qmd +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/example.tex +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/index.qmd +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/.jupyter_cache/__version__.txt +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/.jupyter_cache/executed/ca90e4df5f4f0583df6554156a68dc7f/base.ipynb +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/.jupyter_cache/global.db +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/end_vs_he_sketch.jpg +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/independent.qmd +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/index.qmd +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/tasks.qmd +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/test_include.qmd +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/tryforerror.qmd +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/test_include.qmd +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/SOURCES.txt +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/dependency_links.txt +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/entry_points.txt +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/requires.txt +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/top_level.txt +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/__init__.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/check_numbers.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/command_registry.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/comment_functions.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/continuous.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/copy_files.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/diff-doc.js +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/doc_contents.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/flowchart/__init__.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/flowchart/dot_to_yaml.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/html_comments.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/html_uncomments.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/match_spaces.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/notebook/__init__.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/notebook/fast_build.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/notebook/tex_to_qmd.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/onewordify.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/onewordify_undo.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/outline.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/rearrange_tex.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/searchacro.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/separate_comments.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/split_conflict.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/unseparate_comments.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/update_check.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/wrap_sentences.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/xml2xlsx.vbs +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/setup.cfg +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/tests/test_rrng.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/tests/test_tex_to_qmd.py +0 -0
- {pydifftools-0.1.8 → pydifftools-0.1.11}/tests/test_update_check.py +0 -0
|
@@ -10,6 +10,7 @@ import re
|
|
|
10
10
|
import nbformat
|
|
11
11
|
import difflib
|
|
12
12
|
import shutil
|
|
13
|
+
import importlib.util
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from . import (
|
|
15
16
|
match_spaces,
|
|
@@ -26,11 +27,31 @@ from .copy_files import copy_image_files
|
|
|
26
27
|
from .searchacro import replace_acros
|
|
27
28
|
from .rearrange_tex import run as rearrange_tex_run
|
|
28
29
|
from .flowchart.watch_graph import wgrph
|
|
30
|
+
from .flowchart.graph import load_graph_yaml
|
|
29
31
|
from .notebook.tex_to_qmd import tex2qmd
|
|
30
32
|
from .notebook.fast_build import qmdb, qmdinit
|
|
31
33
|
|
|
32
34
|
from .command_registry import _COMMAND_SPECS, register_command
|
|
33
35
|
|
|
36
|
+
_ARGCOMPLETE_SPEC = importlib.util.find_spec("argcomplete")
|
|
37
|
+
if (
|
|
38
|
+
_ARGCOMPLETE_SPEC is not None
|
|
39
|
+
and _ARGCOMPLETE_SPEC.submodule_search_locations is not None
|
|
40
|
+
):
|
|
41
|
+
_ARGCOMPLETE_COMPLETERS_SPEC = importlib.util.find_spec(
|
|
42
|
+
"argcomplete.completers"
|
|
43
|
+
)
|
|
44
|
+
else:
|
|
45
|
+
_ARGCOMPLETE_COMPLETERS_SPEC = None
|
|
46
|
+
if _ARGCOMPLETE_SPEC is not None:
|
|
47
|
+
import argcomplete
|
|
48
|
+
else:
|
|
49
|
+
argcomplete = None
|
|
50
|
+
if _ARGCOMPLETE_COMPLETERS_SPEC is not None:
|
|
51
|
+
from argcomplete.completers import FilesCompleter
|
|
52
|
+
else:
|
|
53
|
+
FilesCompleter = None
|
|
54
|
+
|
|
34
55
|
|
|
35
56
|
def printed_exec(cmd):
|
|
36
57
|
print("about to execute:\n", cmd)
|
|
@@ -57,6 +78,37 @@ def get_data(path):
|
|
|
57
78
|
return os.path.join(_ROOT, path)
|
|
58
79
|
|
|
59
80
|
|
|
81
|
+
def wgrph_task_completer(prefix, parsed_args, **kwargs):
|
|
82
|
+
# Provide case-insensitive task name completions for wgrph -t.
|
|
83
|
+
if parsed_args is None or not hasattr(parsed_args, "yaml"):
|
|
84
|
+
return []
|
|
85
|
+
yaml_path = Path(parsed_args.yaml)
|
|
86
|
+
if not yaml_path.exists():
|
|
87
|
+
return []
|
|
88
|
+
try:
|
|
89
|
+
data = load_graph_yaml(str(yaml_path))
|
|
90
|
+
except Exception:
|
|
91
|
+
return []
|
|
92
|
+
if "nodes" not in data:
|
|
93
|
+
return []
|
|
94
|
+
prefix_lower = prefix.lower()
|
|
95
|
+
matches = []
|
|
96
|
+
for name in data["nodes"]:
|
|
97
|
+
if (
|
|
98
|
+
"style" in data["nodes"][name]
|
|
99
|
+
and data["nodes"][name]["style"] == "completed"
|
|
100
|
+
):
|
|
101
|
+
continue
|
|
102
|
+
if name.lower().startswith(prefix_lower):
|
|
103
|
+
# Preserve case-insensitive matches even when the typed prefix
|
|
104
|
+
# doesn't match case so argcomplete still accepts the suggestion.
|
|
105
|
+
if name.startswith(prefix):
|
|
106
|
+
matches.append(name)
|
|
107
|
+
else:
|
|
108
|
+
matches.append(prefix + name[len(prefix) :])
|
|
109
|
+
return matches
|
|
110
|
+
|
|
111
|
+
|
|
60
112
|
def recursive_include_search(directory, basename, does_it_input):
|
|
61
113
|
with open(
|
|
62
114
|
os.path.join(directory, basename + ".tex"), "r", encoding="utf-8"
|
|
@@ -704,7 +756,19 @@ def build_parser():
|
|
|
704
756
|
for argument in arguments:
|
|
705
757
|
flags = argument["flags"]
|
|
706
758
|
kwargs = dict(argument["kwargs"])
|
|
707
|
-
subparser.add_argument(*flags, **kwargs)
|
|
759
|
+
action = subparser.add_argument(*flags, **kwargs)
|
|
760
|
+
if (
|
|
761
|
+
FilesCompleter is not None
|
|
762
|
+
and name == "wgrph"
|
|
763
|
+
and action.dest == "yaml"
|
|
764
|
+
):
|
|
765
|
+
# Provide YAML-only completions for the flowchart watcher.
|
|
766
|
+
action.completer = FilesCompleter(
|
|
767
|
+
allowednames=["*.yaml", "*.yml"]
|
|
768
|
+
)
|
|
769
|
+
if name == "wgrph" and action.dest == "t":
|
|
770
|
+
# Offer case-insensitive completions for incomplete task names.
|
|
771
|
+
action.completer = wgrph_task_completer
|
|
708
772
|
subparser.set_defaults(_handler=spec["handler"])
|
|
709
773
|
return parser
|
|
710
774
|
|
|
@@ -732,6 +796,9 @@ def main(argv=None):
|
|
|
732
796
|
file=sys.stderr,
|
|
733
797
|
)
|
|
734
798
|
parser = build_parser()
|
|
799
|
+
if argcomplete is not None:
|
|
800
|
+
# Enable argcomplete integration when the dependency is available.
|
|
801
|
+
argcomplete.autocomplete(parser)
|
|
735
802
|
if not argv:
|
|
736
803
|
parser.print_help()
|
|
737
804
|
return
|
|
@@ -324,7 +324,7 @@ def _node_text_with_due(node):
|
|
|
324
324
|
# same visual emphasis used for overdue dates.
|
|
325
325
|
if is_completed:
|
|
326
326
|
due_color = "green"
|
|
327
|
-
elif
|
|
327
|
+
elif (due_date - today_date).days <= 7:
|
|
328
328
|
due_color = "red"
|
|
329
329
|
else:
|
|
330
330
|
due_color = "orange"
|
|
@@ -565,6 +565,7 @@ def write_dot_from_yaml(
|
|
|
565
565
|
order_by_date=False,
|
|
566
566
|
old_data=None,
|
|
567
567
|
validate_due_dates=False,
|
|
568
|
+
filter_task=None,
|
|
568
569
|
):
|
|
569
570
|
data = load_graph_yaml(str(yaml_path), old_data=old_data)
|
|
570
571
|
_normalize_graph_dates(data)
|
|
@@ -611,8 +612,71 @@ def write_dot_from_yaml(
|
|
|
611
612
|
parents_to_check.append(
|
|
612
613
|
(grandparent, path + [grandparent])
|
|
613
614
|
)
|
|
615
|
+
data_for_dot = data
|
|
616
|
+
if filter_task is not None:
|
|
617
|
+
# Limit the rendered graph to incomplete ancestors of the target task.
|
|
618
|
+
if "nodes" not in data or filter_task not in data["nodes"]:
|
|
619
|
+
# Allow case-insensitive task lookup to align with CLI completion.
|
|
620
|
+
matches = [
|
|
621
|
+
name
|
|
622
|
+
for name in data["nodes"]
|
|
623
|
+
if name.lower() == filter_task.lower()
|
|
624
|
+
]
|
|
625
|
+
if len(matches) == 1:
|
|
626
|
+
filter_task = matches[0]
|
|
627
|
+
elif len(matches) > 1:
|
|
628
|
+
raise ValueError(
|
|
629
|
+
"Task name is ambiguous when compared case-insensitively: "
|
|
630
|
+
f"'{filter_task}' matches {matches}."
|
|
631
|
+
)
|
|
632
|
+
else:
|
|
633
|
+
raise ValueError(
|
|
634
|
+
f"Task '{filter_task}' not found in flowchart YAML."
|
|
635
|
+
)
|
|
636
|
+
# Include the target task alongside its ancestors in the filtered view.
|
|
637
|
+
ancestors = set([filter_task])
|
|
638
|
+
parents_to_check = list(data["nodes"][filter_task]["parents"])
|
|
639
|
+
while parents_to_check:
|
|
640
|
+
parent = parents_to_check.pop()
|
|
641
|
+
if parent in ancestors:
|
|
642
|
+
continue
|
|
643
|
+
ancestors.add(parent)
|
|
644
|
+
if (
|
|
645
|
+
parent in data["nodes"]
|
|
646
|
+
and "parents" in data["nodes"][parent]
|
|
647
|
+
):
|
|
648
|
+
for grandparent in data["nodes"][parent]["parents"]:
|
|
649
|
+
parents_to_check.append(grandparent)
|
|
650
|
+
incomplete_ancestors = set()
|
|
651
|
+
for name in ancestors:
|
|
652
|
+
if name not in data["nodes"]:
|
|
653
|
+
continue
|
|
654
|
+
if (
|
|
655
|
+
"style" in data["nodes"][name]
|
|
656
|
+
and data["nodes"][name]["style"] == "completed"
|
|
657
|
+
):
|
|
658
|
+
continue
|
|
659
|
+
incomplete_ancestors.add(name)
|
|
660
|
+
data_for_dot = {"nodes": {}, "styles": {}}
|
|
661
|
+
if "styles" in data:
|
|
662
|
+
data_for_dot["styles"] = data["styles"]
|
|
663
|
+
for name in incomplete_ancestors:
|
|
664
|
+
data_for_dot["nodes"][name] = dict(data["nodes"][name])
|
|
665
|
+
for name in data_for_dot["nodes"]:
|
|
666
|
+
if "children" in data_for_dot["nodes"][name]:
|
|
667
|
+
data_for_dot["nodes"][name]["children"] = [
|
|
668
|
+
child
|
|
669
|
+
for child in data_for_dot["nodes"][name]["children"]
|
|
670
|
+
if child in incomplete_ancestors
|
|
671
|
+
]
|
|
672
|
+
if "parents" in data_for_dot["nodes"][name]:
|
|
673
|
+
data_for_dot["nodes"][name]["parents"] = [
|
|
674
|
+
parent
|
|
675
|
+
for parent in data_for_dot["nodes"][name]["parents"]
|
|
676
|
+
if parent in incomplete_ancestors
|
|
677
|
+
]
|
|
614
678
|
dot_str = yaml_to_dot(
|
|
615
|
-
|
|
679
|
+
data_for_dot, wrap_width=wrap_width, order_by_date=order_by_date
|
|
616
680
|
)
|
|
617
681
|
Path(dot_path).write_text(dot_str)
|
|
618
682
|
if update_yaml:
|
|
@@ -26,6 +26,23 @@ def _reload_svg(driver, svg_file: Path) -> None:
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def start_chrome(webdriver, options, html_file):
|
|
30
|
+
# Launch Chrome and display the local SVG preview HTML file.
|
|
31
|
+
driver = webdriver.Chrome(options=options)
|
|
32
|
+
driver.get(html_file.resolve().as_uri())
|
|
33
|
+
return driver
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def close_chrome(driver):
|
|
37
|
+
# Close the Chrome window if it is still running.
|
|
38
|
+
if driver is None:
|
|
39
|
+
return
|
|
40
|
+
try:
|
|
41
|
+
driver.quit()
|
|
42
|
+
except Exception:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
29
46
|
def build_graph(
|
|
30
47
|
yaml_file,
|
|
31
48
|
dot_file,
|
|
@@ -33,6 +50,7 @@ def build_graph(
|
|
|
33
50
|
wrap_width,
|
|
34
51
|
order_by_date=False,
|
|
35
52
|
prev_data=None,
|
|
53
|
+
target_task=None,
|
|
36
54
|
):
|
|
37
55
|
# Graphviz is required for dot -> svg rendering.
|
|
38
56
|
if shutil.which("dot") is None:
|
|
@@ -47,6 +65,7 @@ def build_graph(
|
|
|
47
65
|
order_by_date=order_by_date,
|
|
48
66
|
old_data=prev_data,
|
|
49
67
|
validate_due_dates=True,
|
|
68
|
+
filter_task=target_task,
|
|
50
69
|
)
|
|
51
70
|
subprocess.run(
|
|
52
71
|
["dot", "-Tsvg", str(dot_file), "-o", str(svg_file)],
|
|
@@ -61,19 +80,27 @@ class GraphEventHandler(FileSystemEventHandler):
|
|
|
61
80
|
yaml_file,
|
|
62
81
|
dot_file,
|
|
63
82
|
svg_file,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
html_file=None,
|
|
84
|
+
driver=None,
|
|
85
|
+
options=None,
|
|
86
|
+
webdriver=None,
|
|
87
|
+
wrap_width=55,
|
|
88
|
+
data=None,
|
|
67
89
|
order_by_date=False,
|
|
90
|
+
target_task=None,
|
|
68
91
|
debounce=0.25,
|
|
69
92
|
):
|
|
70
93
|
self.yaml_file = Path(yaml_file)
|
|
71
94
|
self.dot_file = Path(dot_file)
|
|
72
95
|
self.svg_file = Path(svg_file)
|
|
96
|
+
self.html_file = None if html_file is None else Path(html_file)
|
|
73
97
|
self.driver = driver
|
|
98
|
+
self.options = options
|
|
99
|
+
self.webdriver = webdriver
|
|
74
100
|
self.wrap_width = wrap_width
|
|
75
101
|
self.data = data
|
|
76
102
|
self.order_by_date = order_by_date
|
|
103
|
+
self.target_task = target_task
|
|
77
104
|
self.debounce = debounce
|
|
78
105
|
self._last_handled = 0.0
|
|
79
106
|
self._last_mtime = None
|
|
@@ -87,15 +114,40 @@ class GraphEventHandler(FileSystemEventHandler):
|
|
|
87
114
|
if now - self._last_handled < self.debounce:
|
|
88
115
|
return
|
|
89
116
|
self._last_handled = now
|
|
90
|
-
|
|
91
|
-
self.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
117
|
+
try:
|
|
118
|
+
self.data = build_graph(
|
|
119
|
+
self.yaml_file,
|
|
120
|
+
self.dot_file,
|
|
121
|
+
self.svg_file,
|
|
122
|
+
self.wrap_width,
|
|
123
|
+
self.order_by_date,
|
|
124
|
+
self.data,
|
|
125
|
+
self.target_task,
|
|
126
|
+
)
|
|
127
|
+
except Exception:
|
|
128
|
+
# If the graph fails to build (e.g. invalid date), close the
|
|
129
|
+
# preview window until a clean rebuild occurs.
|
|
130
|
+
close_chrome(self.driver)
|
|
131
|
+
self.driver = None
|
|
132
|
+
self._last_mtime = self.yaml_file.stat().st_mtime
|
|
133
|
+
return
|
|
134
|
+
if self.driver is None:
|
|
135
|
+
# Restart the preview once the SVG successfully builds again.
|
|
136
|
+
if (
|
|
137
|
+
self.webdriver is not None
|
|
138
|
+
and self.options is not None
|
|
139
|
+
and self.html_file is not None
|
|
140
|
+
):
|
|
141
|
+
self.driver = start_chrome(
|
|
142
|
+
self.webdriver, self.options, self.html_file
|
|
143
|
+
)
|
|
144
|
+
else:
|
|
145
|
+
# Allow legacy/test usage without a live driver.
|
|
146
|
+
_reload_svg(self.driver, self.svg_file)
|
|
147
|
+
self._last_mtime = self.yaml_file.stat().st_mtime
|
|
148
|
+
return
|
|
149
|
+
else:
|
|
150
|
+
_reload_svg(self.driver, self.svg_file)
|
|
99
151
|
self._last_mtime = self.yaml_file.stat().st_mtime
|
|
100
152
|
|
|
101
153
|
|
|
@@ -106,9 +158,12 @@ class GraphEventHandler(FileSystemEventHandler):
|
|
|
106
158
|
"yaml": "Path to the flowchart YAML file",
|
|
107
159
|
"wrap_width": "Line wrap width used when generating node labels",
|
|
108
160
|
"d": "Render nodes by date without showing connections",
|
|
161
|
+
"t": (
|
|
162
|
+
"Task name to focus on (show incomplete ancestor tasks only)"
|
|
163
|
+
),
|
|
109
164
|
},
|
|
110
165
|
)
|
|
111
|
-
def wgrph(yaml, wrap_width=55, d=False):
|
|
166
|
+
def wgrph(yaml, wrap_width=55, d=False, t=None):
|
|
112
167
|
# Selenium is only required when actually launching the watcher, so it is
|
|
113
168
|
# imported here to avoid breaking the command-line tools when the optional
|
|
114
169
|
# dependency is not installed.
|
|
@@ -134,35 +189,47 @@ def wgrph(yaml, wrap_width=55, d=False):
|
|
|
134
189
|
html_file = yaml_file.with_suffix(".html")
|
|
135
190
|
|
|
136
191
|
# Use date ordering when requested so boxes appear in calendar order.
|
|
137
|
-
|
|
192
|
+
# Render the initial graph, optionally restricting to incomplete ancestors
|
|
193
|
+
# of a target task.
|
|
194
|
+
data = build_graph(yaml_file, dot_file, svg_file, wrap_width, d, None, t)
|
|
138
195
|
html_file.write_text(
|
|
139
196
|
"<html><body style='margin:0'><embed id='svg-view'"
|
|
140
197
|
" type='image/svg+xml'"
|
|
141
198
|
f" src='{svg_file.name}?t={time.time()}'/></body></html>"
|
|
142
199
|
)
|
|
143
200
|
options = Options()
|
|
144
|
-
driver = webdriver
|
|
145
|
-
driver.get(html_file.resolve().as_uri())
|
|
201
|
+
driver = start_chrome(webdriver, options, html_file)
|
|
146
202
|
event_handler = GraphEventHandler(
|
|
147
|
-
yaml_file,
|
|
203
|
+
yaml_file,
|
|
204
|
+
dot_file,
|
|
205
|
+
svg_file,
|
|
206
|
+
html_file,
|
|
207
|
+
driver,
|
|
208
|
+
options,
|
|
209
|
+
webdriver,
|
|
210
|
+
wrap_width,
|
|
211
|
+
data,
|
|
212
|
+
d,
|
|
213
|
+
t,
|
|
148
214
|
)
|
|
149
215
|
observer = Observer()
|
|
150
216
|
observer.schedule(event_handler, yaml_file.parent, recursive=False)
|
|
151
217
|
observer.start()
|
|
152
218
|
try:
|
|
153
219
|
while True:
|
|
220
|
+
if event_handler.driver is None:
|
|
221
|
+
time.sleep(1)
|
|
222
|
+
continue
|
|
154
223
|
try:
|
|
155
|
-
_ = driver.window_handles
|
|
156
|
-
driver.execute_script("return 1")
|
|
224
|
+
_ = event_handler.driver.window_handles
|
|
225
|
+
event_handler.driver.execute_script("return 1")
|
|
157
226
|
except (NoSuchWindowException, WebDriverException):
|
|
158
|
-
|
|
227
|
+
close_chrome(event_handler.driver)
|
|
228
|
+
event_handler.driver = None
|
|
159
229
|
time.sleep(1)
|
|
160
230
|
except KeyboardInterrupt:
|
|
161
231
|
pass
|
|
162
232
|
finally:
|
|
163
233
|
observer.stop()
|
|
164
234
|
observer.join()
|
|
165
|
-
|
|
166
|
-
driver.quit()
|
|
167
|
-
except Exception:
|
|
168
|
-
pass
|
|
235
|
+
close_chrome(event_handler.driver)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/__version__.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/end_vs_he_sketch.jpg
RENAMED
|
File without changes
|
{pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/independent.qmd
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/test_include.qmd
RENAMED
|
File without changes
|
{pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/tryforerror.qmd
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|