summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLauréline Guérin <zebuline@entrouvert.com>2021-06-17 13:33:39 (GMT)
committerLauréline Guérin <zebuline@entrouvert.com>2021-11-15 16:22:25 (GMT)
commit129c1ecd584676dbff0f1d1e0943ee3fe59d7a23 (patch)
tree1f39b54c48ecd70ac0b645824a7b1bbe37a822fe
parentbb5e118f1b6d423918945ee24683d305d4730c37 (diff)
downloadmisc-fred-wip/shot-and-diff.zip
misc-fred-wip/shot-and-diff.tar.gz
misc-fred-wip/shot-and-diff.tar.bz2
shot them all: compare shotswip/shot-and-diff
-rw-r--r--bin/compare_them_all.py120
-rw-r--r--bin/index_template.html26
-rw-r--r--bin/shot_them_all.py31
3 files changed, 167 insertions, 10 deletions
diff --git a/bin/compare_them_all.py b/bin/compare_them_all.py
new file mode 100644
index 0000000..ad0be63
--- /dev/null
+++ b/bin/compare_them_all.py
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+#
+# Compare desktop/mobile shots of all themes from publik-base-theme.
+#
+# Usage:
+# --input1-directory -- directory to find version 1 screenshots
+# --input2-directory -- directory to find version 2 screenshots
+# --output-directory DIRECTORY -- directory to store comparison results, defaults to results/
+# --overlay OVERLAY -- limit to given overlay ("none" to exclude overlays)
+# --theme THEME -- theme to shot (substring match)
+#
+# Example:
+# hobo-manage tenant_command runscript compare_them_all.py -d hobo.fred.local.0d.be
+#
+
+import argparse
+import os
+import sys
+from hobo.theme.utils import get_themes
+from django.template import Template, Context
+from PIL import Image, ImageDraw
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--input1-directory', dest='input1_directory', type=str, required=True)
+parser.add_argument('--input2-directory', dest='input2_directory', type=str, required=True)
+parser.add_argument('--output-directory', dest='output_directory', type=str, default='results')
+parser.add_argument('--overlay', dest='overlay', type=str, default='none')
+parser.add_argument('--theme', dest='theme', type=str)
+args = parser.parse_args()
+
+if not os.path.exists(args.output_directory):
+ os.mkdir(args.output_directory)
+
+
+result = []
+
+for theme in get_themes():
+ theme_id = theme['id']
+ if args.overlay == 'all':
+ pass
+ elif args.overlay == 'none' and not theme.get('overlay'):
+ pass
+ elif args.overlay == theme.get('overlay'):
+ pass
+ else:
+ continue
+ if args.theme and args.theme not in theme['id']:
+ continue
+ sys.stderr.write("%-25s" % theme_id)
+ shots = [
+ 'desktop',
+ 'mobile',
+ 'mobile-horizontal',
+ ]
+
+ def process_region(image, x, y, width, height):
+ region_total = 0
+ # This can be used as the sensitivity factor, the larger it is the less sensitive the comparison
+ factor = 100
+ for coordinate_y in range(y, y + height):
+ for coordinate_x in range(x, x + width):
+ try:
+ pixel = image.getpixel((coordinate_x, coordinate_y))
+ region_total += sum(pixel) / 4
+ except Exception:
+ return
+ return region_total / factor
+
+ theme_data = {
+ 'label': theme['label'],
+ 'shots': [],
+ }
+
+ for shot in shots:
+ filepath_1 = os.path.join(args.input1_directory, '%s-%s.png' % (theme_id, shot))
+ filepath_2 = os.path.join(args.input2_directory, '%s-%s.png' % (theme_id, shot))
+ filepath_output = os.path.join(args.output_directory, '%s-%s.png' % (theme_id, shot))
+ try:
+ screenshot_1 = Image.open(filepath_1)
+ screenshot_2 = Image.open(filepath_2)
+ except FileNotFoundError:
+ continue
+ width, height = screenshot_1.size
+
+ columns = 60
+ rows = 80
+ screen_width, screen_height = screenshot_1.size
+
+ block_width = ((screen_width - 1) // columns) + 1
+ block_height = ((screen_height - 1) // rows) + 1
+
+ for y in range(0, screen_height, block_height + 1):
+ for x in range(0, screen_width, block_width + 1):
+ region_1 = process_region(screenshot_1, x, y, block_width, block_height)
+ region_2 = process_region(screenshot_2, x, y, block_width, block_height)
+
+ if region_1 is not None and region_2 is not None and region_1 != region_2:
+ draw = ImageDraw.Draw(screenshot_1)
+ draw.rectangle((x, y, x + block_width, y + block_height), outline="red")
+
+ screenshot_1.save(filepath_output)
+ theme_data['shots'].append({
+ 'label': shot,
+ 'width': width,
+ 'height': height,
+ 'before': '../%s' % filepath_1,
+ 'after': '../%s' % filepath_2,
+ 'diff': '../%s' % filepath_output,
+ })
+ result.append(theme_data)
+
+sys.stderr.write(u'\n')
+
+with open('index_template.html', 'r') as f:
+ template = Template(f.read())
+ context = Context({'themes': result})
+ html = template.render(context)
+
+with open(os.path.join(args.output_directory, 'index.html'), 'w') as f:
+ f.write(html)
diff --git a/bin/index_template.html b/bin/index_template.html
new file mode 100644
index 0000000..7db64f6
--- /dev/null
+++ b/bin/index_template.html
@@ -0,0 +1,26 @@
+<html>
+<meta charset="UTF-8">
+<head>
+<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
+<script src="https://www.jqueryscript.net/demo/before-after-image-viewer/beforeafter.jquery-1.0.0.min.js"></script>
+</head>
+<body>
+ {% for theme in themes %}
+ <h3>{{ theme.label }}</h3>
+ {% for shot in theme.shots %}
+ <h4>{{ shot.label }}</h4>
+ <div class="beforeAfter" style="width: {{ shot.width }}px; height: {{ shot.height }}px">
+ <img src="{{ shot.before }}"/>
+ <img src="{{ shot.after }}"/>
+ </div>
+ <h5>Diff</h5>
+ <img src="{{ shot.diff }}" />
+ {% endfor %}
+ {% endfor %}
+<script>
+$(function(){
+ $('.beforeAfter').beforeAfter();
+});
+</script>
+</body>
+</html>
diff --git a/bin/shot_them_all.py b/bin/shot_them_all.py
index 8940799..84d3332 100644
--- a/bin/shot_them_all.py
+++ b/bin/shot_them_all.py
@@ -3,14 +3,15 @@
# Take desktop/mobile shots of all themes from publik-base-theme.
#
# Usage:
-# --directory DIRECTORY -- directory to store shots, defaults to shots/
-# --no-headless -- run in an actual browser window
-# --no-memcached -- do not restart memcached after theme changes
-# --overlay OVERLAY -- limit to given overlay ("none" to exclude overlays)
-# --reshot -- retake existing shots
-# --timeout TIMEOUT -- timeout between shots (time for hobo deploy)
-# --theme THEME -- theme to shot (substring match)
-# URL -- specific URL to shot
+# --directory DIRECTORY -- directory to store shots, defaults to shots/
+# --no-headless -- run in an actual browser window
+# --no-memcached -- do not restart memcached after theme changes
+# --overlay OVERLAY -- limit to given overlay ("none" to exclude overlays)
+# --reshot -- retake existing shots
+# --timeout TIMEOUT -- timeout between shots (time for hobo deploy)
+# --cell-timeout TIMEOUT -- timeout after url loading (time for ajax cells loading)
+# --theme THEME -- theme to shot (substring match)
+# URL -- specific URL to shot
#
# Example:
# hobo-manage tenant_command runscript shot_them_all.py -d hobo.fred.local.0d.be
@@ -31,9 +32,10 @@ parser.add_argument('--no-memcached', dest='no_memcached', action='store_true')
parser.add_argument('--overlay', dest='overlay', type=str, default='none')
parser.add_argument('--reshot', dest='reshot', action='store_true')
parser.add_argument('--timeout', dest='timeout', type=int, default=20) # seconds
+parser.add_argument('--cell-timeout', dest='cell_timeout', type=int, default=0) # seconds
parser.add_argument('--theme', dest='theme', type=str)
parser.add_argument('url', metavar='URL', type=str, nargs='?',
- default='https://auquo.fred.local.0d.be/contactez-nous/inscription-sur-les-listes/')
+ default='https://auquo.fred.local.0d.be/contactez-nous/inscription-sur-les-listes/')
args = parser.parse_args()
if not os.path.exists(args.directory):
@@ -54,7 +56,7 @@ for theme in get_themes():
pass
else:
continue
- if args.theme and not args.theme in theme['id']:
+ if args.theme and args.theme not in theme['id']:
continue
sys.stderr.write("%-25s" % theme_id)
shots = [
@@ -75,9 +77,18 @@ for theme in get_themes():
if not args.no_memcached:
os.system('sudo service memcached restart')
browser.get(args.url)
+ for i in range(args.cell_timeout):
+ sys.stderr.write('.')
+ time.sleep(1)
+
+ def size(value):
+ return browser.execute_script('return document.body.parentNode.scroll' + value)
+
for shot in shots:
browser.set_window_size(shot[0], shot[1])
+ browser.set_window_size(shot[0], size('Height'))
browser.save_screenshot(os.path.join(args.directory, '%s-%s.png' % (theme_id, shot[2])))
+
sys.stderr.write(u' 📸 \n')
browser.close()