Skip to content

Commit ea62a63

Browse files
committed
log file; update ref
1 parent 65d0006 commit ea62a63

File tree

1 file changed

+111
-21
lines changed

1 file changed

+111
-21
lines changed

src/htmlcmp/compare_output_server.py

Lines changed: 111 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import io
55
import sys
6+
import shutil
67
import argparse
78
import logging
89
import threading
@@ -29,6 +30,7 @@ class Config:
2930
comparator = None
3031
browser = None
3132
thread_local = threading.local()
33+
log_file: Path = None
3234

3335

3436
def result_symbol(result: str) -> str | None:
@@ -293,7 +295,7 @@ def generate_entry(
293295
result += "<td></td>"
294296

295297
if is_comparable:
296-
main = f'<a href="/compare/{path}">{name}</a>'
298+
main = f'<a href="/compare/{path}" target="_blank">{name}</a>'
297299
else:
298300
main = f"{name}"
299301
if cmp_result is not None:
@@ -306,6 +308,10 @@ def generate_entry(
306308
result += f'<td class="main" style="{style}">{main}</td>'
307309
result += f"<td>{message}</td>"
308310

311+
result += (
312+
f"<td><button onclick=\"updateRef('{path}')\">update ref</button></td>"
313+
)
314+
309315
result += "</tr>"
310316

311317
return result
@@ -444,18 +450,25 @@ def generate_tree(
444450

445451
result += "<p>"
446452
result += "comparing<br>"
447-
result += f"A: {Config.path_a}<br>"
448-
result += f"B: {Config.path_b}"
453+
result += f"Reference: {Config.path_a}<br>"
454+
result += f"Monitored: {Config.path_b}"
449455
result += "</p>"
450456

457+
if Config.log_file is not None:
458+
result += "<p>"
459+
result += (
460+
f'<a href="/logfile" target="_blank">View log file: {Config.log_file}</a>'
461+
)
462+
result += "</p>"
463+
451464
result += "<p>"
452465
result += '<button onclick="toggleAll(true)">Expand All</button>'
453466
result += '<button onclick="toggleAll(false)">Collapse All</button>'
454467
result += "</p>"
455468

456469
result += "<table>"
457470
result += "<thead>"
458-
result += "<tr><td></td><td></td><td>Name</td><td>Message</td></tr>"
471+
result += "<tr><td></td><td></td><td>Name</td><td>Message</td><td>Actions</td></tr>"
459472
result += "</thead>"
460473
result += "<tbody>"
461474
result += generate_tree(Config.path_a, Config.path_b, "", None, 0)
@@ -494,6 +507,21 @@ def generate_tree(
494507
}
495508
});
496509
}
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+
}
497525
</script>
498526
</body>
499527
</html>
@@ -502,6 +530,16 @@ def generate_tree(
502530
return result
503531

504532

533+
@app.route("/logfile")
534+
def logfile():
535+
logger.debug("Serving log file")
536+
537+
if Config.log_file is None:
538+
return "No log file configured", 404
539+
540+
return send_from_directory(Config.log_file.parent, Config.log_file.name)
541+
542+
505543
@app.route("/compare/<path:path>")
506544
def compare(path: str):
507545
logger.debug(f"Generating comparison page for path: {path}")
@@ -518,15 +556,15 @@ def compare(path: str):
518556
</head>
519557
<body style="display:flex;flex-flow:row;">
520558
<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
521-
<a href="/file/a/{path}">{Config.path_a / path}</a>
559+
<a href="/file/a/{path}" target="_blank">{Config.path_a / path}</a>
522560
<iframe id="a" src="/file/a/{path}" title="a" frameborder="0" align="left" style="flex:1;"></iframe>
523561
</div>
524562
<div style="display:flex;flex:0 0 50px;flex-flow:column;">
525-
<a href="/image_diff/{path}">diff</a>
563+
<a href="/image_diff/{path}" target="_blank">diff</a>
526564
<img src="/image_diff/{path}" width="50" height="0" style="flex:1;">
527565
</div>
528566
<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
529-
<a href="/file/b/{path}">{Config.path_b / path}</a>
567+
<a href="/file/b/{path}" target="_blank">{Config.path_b / path}</a>
530568
<iframe id="b" src="/file/b/{path}" title="b" frameborder="0" align="right" style="flex:1;"></iframe>
531569
</div>
532570
<script>
@@ -551,6 +589,9 @@ def image_diff(path: str):
551589
if not isinstance(path, str):
552590
raise TypeError("Path must be a string")
553591

592+
if Config.driver is None:
593+
return "Image diff not available without browser driver", 404
594+
554595
diff, _ = html_render_diff(
555596
Config.path_a / path,
556597
Config.path_b / path,
@@ -575,34 +616,74 @@ def file(variant: str, path: str):
575616
return send_from_directory(variant_root, path)
576617

577618

578-
def setup_logging(verbosity: int):
619+
def verbosity_to_level(verbosity: int) -> int:
579620
if verbosity >= 3:
580-
level = logging.DEBUG
621+
return logging.DEBUG
581622
elif verbosity == 2:
582-
level = logging.INFO
623+
return logging.INFO
583624
elif verbosity == 1:
584-
level = logging.WARNING
625+
return logging.WARNING
585626
else:
586-
level = logging.ERROR
627+
return logging.ERROR
628+
629+
630+
def setup_logging(
631+
verbosity: int, log_file: Path = None, log_file_verbosity: int = None
632+
) -> None:
633+
level = verbosity_to_level(verbosity)
634+
635+
root_logger = logging.getLogger()
636+
root_logger.setLevel(level)
637+
root_logger.handlers.clear()
587638

588639
formatter = logging.Formatter(
589640
fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
590641
datefmt="%Y-%m-%d %H:%M:%S",
591642
)
592643

593644
console_handler = logging.StreamHandler(sys.stderr)
645+
console_handler.setLevel(level)
594646
console_handler.setFormatter(formatter)
595-
596-
root_logger = logging.getLogger()
597-
root_logger.setLevel(level)
598-
root_logger.handlers.clear()
599647
root_logger.addHandler(console_handler)
600648

649+
if log_file is not None:
650+
file_level = (
651+
verbosity_to_level(log_file_verbosity)
652+
if log_file_verbosity is not None
653+
else level
654+
)
655+
656+
file_handler = logging.FileHandler(log_file, mode="a", encoding="utf-8")
657+
file_handler.setLevel(file_level)
658+
file_handler.setFormatter(formatter)
659+
root_logger.addHandler(file_handler)
660+
661+
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+
601682

602683
def main():
603684
parser = argparse.ArgumentParser()
604-
parser.add_argument("a", type=Path, help="Path to the first directory")
605-
parser.add_argument("b", type=Path, help="Path to the second directory")
685+
parser.add_argument("ref", type=Path, help="Path to the reference directory")
686+
parser.add_argument("mon", type=Path, help="Path to the monitored directory")
606687
parser.add_argument("--driver", choices=["chrome", "firefox", "phantomjs"])
607688
parser.add_argument("--max-workers", type=int, default=1)
608689
parser.add_argument("--compare", action="store_true")
@@ -614,16 +695,25 @@ def main():
614695
default=0,
615696
help="Increase verbosity (-v, -vv, -vvv)",
616697
)
698+
parser.add_argument(
699+
"--log-file",
700+
type=Path,
701+
help="Path to log file",
702+
)
703+
parser.add_argument(
704+
"--log-file-verbosity", type=int, help="Log file verbosity level"
705+
)
617706
args = parser.parse_args()
618707

619-
setup_logging(args.verbose)
708+
setup_logging(args.verbose, args.log_file, args.log_file_verbosity)
620709

621-
Config.path_a = args.a
622-
Config.path_b = args.b
710+
Config.path_a = args.ref
711+
Config.path_b = args.mon
623712
Config.driver = args.driver
624713
Config.browser = (
625714
get_browser(driver=args.driver) if args.driver is not None else None
626715
)
716+
Config.log_file = args.log_file
627717

628718
if args.compare:
629719
Config.comparator = Comparator(max_workers=args.max_workers)

0 commit comments

Comments
 (0)