htmlcmp 1.1.1__tar.gz → 1.2.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: htmlcmp
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: Compare HTML files by rendered output
5
5
  Author: Andreas Stefl
6
6
  Maintainer-email: Andreas Stefl <stefl.andreas@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "htmlcmp"
3
- version = "1.1.1"
3
+ version = "1.2.0"
4
4
  description = "Compare HTML files by rendered output"
5
5
  classifiers = []
6
6
  authors = [
@@ -253,6 +253,28 @@ class Comparator:
253
253
  app = Flask("compare")
254
254
 
255
255
 
256
+ @app.route("/script.js")
257
+ def script_js():
258
+ logger.debug("Serving script.js")
259
+
260
+ return """
261
+ function updateRef(path) {
262
+ fetch(`/update_ref/${path}`)
263
+ .then(response => {
264
+ if (response.ok) {
265
+ alert(`Reference updated for ${path}`);
266
+ location.reload();
267
+ } else {
268
+ alert(`Failed to update reference for ${path}: ${response.statusText}`);
269
+ }
270
+ })
271
+ .catch(error => {
272
+ alert(`Error updating reference for ${path}: ${error}`);
273
+ });
274
+ }
275
+ """
276
+
277
+
256
278
  @app.route("/")
257
279
  def root():
258
280
  logger.debug("Generating root directory listing")
@@ -444,6 +466,7 @@ tr {
444
466
  padding-left: calc(1.0rem * var(--depth));
445
467
  }
446
468
  </style>
469
+ <script src="/script.js"></script>
447
470
  </head>
448
471
  <body>
449
472
  """
@@ -493,6 +516,11 @@ function toggleChildren(parentId, show) {
493
516
  document.querySelectorAll(`tr[data-parent-id="${parentId}"]`)
494
517
  .forEach(child => {
495
518
  child.hidden = !show;
519
+ if (!show) {
520
+ toggleChildren(child.dataset.entryId, false);
521
+ const toggle = child.querySelector(".toggle");
522
+ if (toggle) toggle.textContent = "▶";
523
+ }
496
524
  });
497
525
  }
498
526
 
@@ -507,21 +535,6 @@ function toggleAll(show) {
507
535
  }
508
536
  });
509
537
  }
510
-
511
- function updateRef(path) {
512
- fetch(`/update_ref/${path}`)
513
- .then(response => {
514
- if (response.ok) {
515
- alert(`Reference updated for ${path}`);
516
- location.reload();
517
- } else {
518
- alert(`Failed to update reference for ${path}: ${response.statusText}`);
519
- }
520
- })
521
- .catch(error => {
522
- alert(`Error updating reference for ${path}: ${error}`);
523
- });
524
- }
525
538
  </script>
526
539
  </body>
527
540
  </html>
@@ -553,6 +566,7 @@ def compare(path: str):
553
566
  <style>
554
567
  html,body {{height:100%;margin:0;}}
555
568
  </style>
569
+ <script src="/script.js"></script>
556
570
  </head>
557
571
  <body style="display:flex;flex-flow:row;">
558
572
  <div style="display:flex;flex:1;flex-flow:column;margin:5px;">
@@ -561,6 +575,7 @@ html,body {{height:100%;margin:0;}}
561
575
  </div>
562
576
  <div style="display:flex;flex:0 0 50px;flex-flow:column;">
563
577
  <a href="/image_diff/{path}" target="_blank">diff</a>
578
+ <button onclick="updateRef('{path}')">▶</button>
564
579
  <img src="/image_diff/{path}" width="50" height="0" style="flex:1;">
565
580
  </div>
566
581
  <div style="display:flex;flex:1;flex-flow:column;margin:5px;">
@@ -616,6 +631,27 @@ def file(variant: str, path: str):
616
631
  return send_from_directory(variant_root, path)
617
632
 
618
633
 
634
+ @app.route("/update_ref/<path:path>")
635
+ def update_ref(path: str):
636
+ logger.debug(f"Updating reference for path: {path}")
637
+
638
+ if not isinstance(path, str):
639
+ raise TypeError("Path must be a string")
640
+
641
+ src = Config.path_b / path
642
+ dst = Config.path_a / path
643
+
644
+ if not src.exists():
645
+ return f"Source file does not exist: {src}", 404
646
+
647
+ if src.is_file():
648
+ shutil.copy2(src, dst)
649
+ else:
650
+ shutil.copytree(src, dst, dirs_exist_ok=True)
651
+
652
+ return "Reference updated", 200
653
+
654
+
619
655
  def verbosity_to_level(verbosity: int) -> int:
620
656
  if verbosity >= 3:
621
657
  return logging.DEBUG
@@ -659,27 +695,6 @@ def setup_logging(
659
695
  root_logger.addHandler(file_handler)
660
696
 
661
697
 
662
- @app.route("/update_ref/<path:path>")
663
- def update_ref(path: str):
664
- logger.debug(f"Updating reference for path: {path}")
665
-
666
- if not isinstance(path, str):
667
- raise TypeError("Path must be a string")
668
-
669
- src = Config.path_b / path
670
- dst = Config.path_a / path
671
-
672
- if not src.exists():
673
- return f"Source file does not exist: {src}", 404
674
-
675
- if src.is_file():
676
- shutil.copy2(src, dst)
677
- else:
678
- shutil.copytree(src, dst, dirs_exist_ok=True)
679
-
680
- return "Reference updated", 200
681
-
682
-
683
698
  def main():
684
699
  parser = argparse.ArgumentParser()
685
700
  parser.add_argument("ref", type=Path, help="Path to the reference directory")
@@ -10,7 +10,7 @@ from pathlib import Path
10
10
  from htmlcmp.common import bcolors
11
11
 
12
12
 
13
- def tidy_json(path: Path) -> int:
13
+ def tidy_json(path: Path, verbose: bool = False) -> int:
14
14
  if not isinstance(path, Path):
15
15
  raise TypeError("path must be a Path object")
16
16
  if not path.is_file():
@@ -21,10 +21,11 @@ def tidy_json(path: Path) -> int:
21
21
  json.load(f)
22
22
  return 0
23
23
  except ValueError:
24
+ print(f"{bcolors.FAIL}Error: {path} is not a valid JSON file{bcolors.ENDC}")
24
25
  return 1
25
26
 
26
27
 
27
- def tidy_html(path: Path, html_tidy_config: Path = None) -> int:
28
+ def tidy_html(path: Path, html_tidy_config: Path = None, verbose: bool = False) -> int:
28
29
  if not isinstance(path, Path):
29
30
  raise TypeError("path must be a Path object")
30
31
  if not path.is_file():
@@ -41,6 +42,15 @@ def tidy_html(path: Path, html_tidy_config: Path = None) -> int:
41
42
  result = subprocess.run(
42
43
  cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
43
44
  )
45
+ if result.stdout:
46
+ if verbose and result.returncode == 0:
47
+ print(result.stdout)
48
+ elif verbose and result.returncode == 1:
49
+ print(f"{bcolors.WARNING}Warning: {path} has warnings{bcolors.ENDC}")
50
+ print(f"{bcolors.WARNING}{result.stdout}{bcolors.ENDC}")
51
+ elif verbose or result.returncode > 1:
52
+ print(f"{bcolors.FAIL}Error: {path} has errors{bcolors.ENDC}")
53
+ print(f"{bcolors.FAIL}{result.stdout}{bcolors.ENDC}")
44
54
  if result.returncode == 1:
45
55
  return 1
46
56
  if result.returncode > 1:
@@ -48,16 +58,16 @@ def tidy_html(path: Path, html_tidy_config: Path = None) -> int:
48
58
  return 0
49
59
 
50
60
 
51
- def tidy_file(path: Path, html_tidy_config: Path = None) -> int:
61
+ def tidy_file(path: Path, html_tidy_config: Path = None, verbose: bool = False) -> int:
52
62
  if not isinstance(path, Path):
53
63
  raise TypeError("path must be a Path object")
54
64
  if not path.is_file():
55
65
  raise FileNotFoundError(f"{path} is not a file")
56
66
 
57
67
  if path.suffix == ".json":
58
- return tidy_json(path)
68
+ return tidy_json(path, verbose=verbose)
59
69
  elif path.suffix == ".html":
60
- return tidy_html(path, html_tidy_config=html_tidy_config)
70
+ return tidy_html(path, html_tidy_config=html_tidy_config, verbose=verbose)
61
71
 
62
72
 
63
73
  def tidyable_file(path: Path) -> bool:
@@ -74,7 +84,11 @@ def tidyable_file(path: Path) -> bool:
74
84
 
75
85
 
76
86
  def tidy_dir(
77
- path: Path, level: int = 0, prefix: str = "", html_tidy_config: Path = None
87
+ path: Path,
88
+ level: int = 0,
89
+ prefix: str = "",
90
+ html_tidy_config: Path = None,
91
+ verbose: bool = False,
78
92
  ) -> dict[str, list[Path]]:
79
93
  if not isinstance(path, Path):
80
94
  raise TypeError("path must be a Path object")
@@ -104,7 +118,7 @@ def tidy_dir(
104
118
 
105
119
  for filename in [path.name for path in files]:
106
120
  filepath = path / filename
107
- tidy = tidy_file(filepath, html_tidy_config=html_tidy_config)
121
+ tidy = tidy_file(filepath, html_tidy_config=html_tidy_config, verbose=verbose)
108
122
  if tidy == 0:
109
123
  print(f"{prefix_file}{bcolors.OKGREEN}{filename} ✓{bcolors.ENDC}")
110
124
  elif tidy == 1:
@@ -121,6 +135,7 @@ def tidy_dir(
121
135
  level=level + 1,
122
136
  prefix=prefix + "│ ",
123
137
  html_tidy_config=html_tidy_config,
138
+ verbose=verbose,
124
139
  )
125
140
  result["warning"].extend(subresult["warning"])
126
141
  result["error"].extend(subresult["error"])
@@ -134,9 +149,16 @@ def main():
134
149
  parser.add_argument(
135
150
  "--html-tidy-config", type=Path, help="Path to tidy config file"
136
151
  )
152
+ parser.add_argument(
153
+ "--verbose",
154
+ action="store_true",
155
+ help="Print verbose output (warnings and errors)",
156
+ )
137
157
  args = parser.parse_args()
138
158
 
139
- result = tidy_dir(args.path, html_tidy_config=args.html_tidy_config)
159
+ result = tidy_dir(
160
+ args.path, html_tidy_config=args.html_tidy_config, verbose=args.verbose
161
+ )
140
162
  if result["error"]:
141
163
  return 1
142
164
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: htmlcmp
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: Compare HTML files by rendered output
5
5
  Author: Andreas Stefl
6
6
  Maintainer-email: Andreas Stefl <stefl.andreas@gmail.com>
File without changes
File without changes
File without changes
File without changes