Use tint colors to colorise links on individual pages
authorAlex Chan <a.chan@wellcome.ac.uk>
Sun, 11 Oct 2020 22:46:14 +0000 (23:46 +0100)
committerAlex Chan <a.chan@wellcome.ac.uk>
Sun, 11 Oct 2020 22:46:14 +0000 (23:46 +0100)
scripts/render_html.py
scripts/tint_colors.py [new file with mode: 0644]
src/tint_colors.json [new file with mode: 0644]
templates/review.html

index 9aee310..e4b9de0 100755 (executable)
@@ -17,6 +17,8 @@ from markdown.extensions.smarty import SmartyExtension
 from PIL import Image
 import smartypants
 
 from PIL import Image
 import smartypants
 
+from tint_colors import get_tint_colors, store_tint_color
+
 
 def rsync(dir1, dir2):
     subprocess.check_call(["rsync", "--recursive", "--delete", dir1, dir2])
 
 def rsync(dir1, dir2):
     subprocess.check_call(["rsync", "--recursive", "--delete", dir1, dir2])
@@ -161,10 +163,12 @@ def render_date(date_value):
         return date_obj.strftime("%B %Y")
 
 
         return date_obj.strftime("%B %Y")
 
 
-def render_individual_review(env, *, review_entry):
+def render_individual_review(env, *, review_entry, **kwargs):
     template = env.get_template("review.html")
     html = template.render(
     template = env.get_template("review.html")
     html = template.render(
-        review_entry=review_entry, title=f"My review of {review_entry.book.title}"
+        review_entry=review_entry,
+        title=f"My review of {review_entry.book.title}",
+        **kwargs
     )
 
     out_name = review_entry.out_path() / "index.html"
     )
 
     out_name = review_entry.out_path() / "index.html"
@@ -226,6 +230,8 @@ def create_thumbnails():
         elif src_path.stat().st_mtime > square_path.stat().st_mtime:
             _create_new_square(src_path, square_path)
 
         elif src_path.stat().st_mtime > square_path.stat().st_mtime:
             _create_new_square(src_path, square_path)
 
+        store_tint_color(dst_path)
+
 
 CSS_HASH = hashlib.md5(open('static/style.css', 'rb').read()).hexdigest()
 
 
 CSS_HASH = hashlib.md5(open('static/style.css', 'rb').read()).hexdigest()
 
@@ -248,6 +254,8 @@ def main():
 
     create_thumbnails()
 
 
     create_thumbnails()
 
+    tint_colors = get_tint_colors()
+
     rsync("src/covers/", "_html/covers/")
     rsync("static/", "_html/static/")
 
     rsync("src/covers/", "_html/covers/")
     rsync("static/", "_html/static/")
 
@@ -261,7 +269,11 @@ def main():
     )
 
     for review_entry in all_reviews:
     )
 
     for review_entry in all_reviews:
-        render_individual_review(env, review_entry=review_entry)
+        render_individual_review(
+            env,
+            review_entry=review_entry,
+            tint_colors=tint_colors
+        )
 
     template = env.get_template("list_reviews.html")
     html = template.render(
 
     template = env.get_template("list_reviews.html")
     html = template.render(
@@ -273,6 +285,7 @@ def main():
         ],
         title="books i’ve read",
         this_year=str(datetime.datetime.now().year),
         ],
         title="books i’ve read",
         this_year=str(datetime.datetime.now().year),
+        tint_colors=tint_colors,
     )
 
     out_path = pathlib.Path("_html") / "reviews/index.html"
     )
 
     out_path = pathlib.Path("_html") / "reviews/index.html"
@@ -287,7 +300,11 @@ def main():
     )
 
     template = env.get_template("list_reading.html")
     )
 
     template = env.get_template("list_reading.html")
-    html = template.render(all_reading=all_reading, title="books i’m currently reading")
+    html = template.render(
+        all_reading=all_reading,
+        title="books i’m currently reading",
+        tint_colors=tint_colors
+    )
 
     out_path = pathlib.Path("_html") / "reading/index.html"
     out_path.parent.mkdir(exist_ok=True, parents=True)
 
     out_path = pathlib.Path("_html") / "reading/index.html"
     out_path.parent.mkdir(exist_ok=True, parents=True)
@@ -302,7 +319,11 @@ def main():
     all_plans = sorted(all_plans, key=lambda plan: plan.plan.date_added, reverse=True)
 
     template = env.get_template("list_plans.html")
     all_plans = sorted(all_plans, key=lambda plan: plan.plan.date_added, reverse=True)
 
     template = env.get_template("list_plans.html")
-    html = template.render(all_plans=all_plans, title="books i want to read")
+    html = template.render(
+        all_plans=all_plans,
+        title="books i want to read",
+        tint_colors=tint_colors,
+    )
 
     out_path = pathlib.Path("_html") / "to-read/index.html"
     out_path.parent.mkdir(exist_ok=True, parents=True)
 
     out_path = pathlib.Path("_html") / "to-read/index.html"
     out_path.parent.mkdir(exist_ok=True, parents=True)
@@ -320,7 +341,9 @@ def main():
 
     template = env.get_template("list_will_never_read.html")
     html = template.render(
 
     template = env.get_template("list_will_never_read.html")
     html = template.render(
-        all_retired=all_retired, title="books i&rsquo;m never going to read"
+        all_retired=all_retired,
+        title="books i&rsquo;m never going to read",
+        tint_colors=tint_colors
     )
 
     out_path = pathlib.Path("_html") / "will-never-read/index.html"
     )
 
     out_path = pathlib.Path("_html") / "will-never-read/index.html"
@@ -332,7 +355,8 @@ def main():
     index_template = env.get_template("index.html")
     html = index_template.render(
         text=open("src/index.md").read(),
     index_template = env.get_template("index.html")
     html = index_template.render(
         text=open("src/index.md").read(),
-        reviews=all_reviews[:5]
+        reviews=all_reviews[:5],
+        tint_colors=tint_colors
     )
 
     index_path = pathlib.Path("_html") / "index.html"
     )
 
     index_path = pathlib.Path("_html") / "index.html"
diff --git a/scripts/tint_colors.py b/scripts/tint_colors.py
new file mode 100644 (file)
index 0000000..6421b2e
--- /dev/null
@@ -0,0 +1,139 @@
+import collections
+import colorsys
+import json
+import math
+import os
+
+from PIL import Image, UnidentifiedImageError
+from sklearn.cluster import KMeans
+import wcag_contrast_ratio as contrast
+
+
+def _get_colors_from_im(im):
+    # Resizing means less pixels to handle, so the *k*-means clustering converges
+    # faster.  Small details are lost, but the main details will be preserved.
+    if im.size > (100, 100):
+        resize_ratio = min([100 / im.width, 100 / im.height])
+
+        new_width = int(im.width * resize_ratio)
+        new_height = int(im.height * resize_ratio)
+
+        im = im.resize((new_width, new_height))
+
+    # Ensure the image is RGB for consistency.
+    im = im.convert("RGB")
+
+    return list(im.getdata())
+
+
+def get_colors_from(path):
+    """
+    Returns a list of the colors in the image at ``path``.
+    """
+    im = Image.open(str(path))
+
+    if getattr(im, "is_animated", False):
+        result = []
+
+        frame_count = im.n_frames
+
+        # Don't get all the frames from an animated GIF; if it has hundreds of
+        # frames this massively increases computation required for little gain.
+        # Take a sample and work from that.
+        for frame in range(0, frame_count, int(math.ceil(frame_count / 25))):
+            im.seek(frame)
+            result.extend(_get_colors_from_im(im))
+        return result
+    else:
+        return _get_colors_from_im(im)
+
+
+def choose_tint_color_from_dominant_colors(dominant_colors, background_color):
+    """
+    Given a set of dominant colors (say, from a k-means algorithm) and the
+    background against which they'll be displayed, choose a tint color.
+
+    Both ``dominant_colors`` and ``background_color`` should be tuples in [0,1].
+    """
+    # Clamp colours to the range 0.0 - 1.0; occasionally sklearn has spat out
+    # numbers outside this range.
+    dominant_colors = [
+        (min(max(col[0], 0), 1), min(max(col[1], 0), 1), min(max(col[2], 0), 1))
+        for col in dominant_colors
+    ]
+
+    # The minimum contrast ratio for text and background to meet WCAG AA
+    # is 4.5:1, so discard any dominant colours with a lower contrast.
+    sufficient_contrast_colors = [
+        col for col in dominant_colors if contrast.rgb(col, background_color) >= 4.5
+    ]
+
+    # If none of the dominant colours meet WCAG AA with the background,
+    # try again with black and white -- every colour in the RGB space
+    # has a contrast ratio of 4.5:1 with at least one of these, so we'll
+    # get a tint colour, even if it's not a good one.
+    #
+    # Note: you could modify the dominant colours until one of them
+    # has sufficient contrast, but that's omitted here because it adds
+    # a lot of complexity for a relatively unusual case.
+    if not sufficient_contrast_colors:
+        return choose_tint_color_from_dominant_colors(
+            dominant_colors=dominant_colors + [(0, 0, 0), (1, 1, 1)],
+            background_color=background_color,
+        )
+
+    # Of the colors with sufficient contrast, pick the one with the
+    # highest saturation.  This is meant to optimise for colors that are
+    # more colourful/interesting than simple greys and browns.
+    hsv_candidates = {
+        tuple(rgb_col): colorsys.rgb_to_hsv(*rgb_col)
+        for rgb_col in sufficient_contrast_colors
+    }
+
+    return max(hsv_candidates, key=lambda rgb_col: hsv_candidates[rgb_col][2])
+
+
+def choose_tint_color(p, *, background_color):
+    try:
+        background_color = {"black": (0, 0, 0), "white": (1, 1, 1)}[background_color]
+    except KeyError:  # pragma: no cover
+        raise ValueError(f"Unrecognised background color: {background_color!r}")
+
+    colors = get_colors_from(p)
+
+    # Normalise to [0, 1]
+    colors = [(r / 255, g / 255, b / 255) for (r, g, b) in colors]
+
+    pixel_tally = collections.Counter(colors)
+    most_common, most_common_count = pixel_tally.most_common(1)[0]
+    if (
+        most_common_count >= len(colors) * 0.15
+        and contrast.rgb(most_common, background_color) >= 4.5
+    ):
+        return most_common
+
+    dominant_colors = KMeans(n_clusters=12).fit(colors).cluster_centers_
+
+    return choose_tint_color_from_dominant_colors(
+        dominant_colors=dominant_colors, background_color=background_color
+    )
+
+
+def get_tint_colors():
+    try:
+        return json.load(open(os.path.join("src", "tint_colors.json")))
+    except FileNotFoundError:
+        return {}
+
+
+def store_tint_color(cover_path):
+    tint_colors = get_tint_colors()
+
+    if os.path.basename(cover_path) in tint_colors:
+        return
+
+    cover_color = choose_tint_color(cover_path, background_color="white")
+    tint_colors[os.path.basename(cover_path)] = cover_color
+
+    with open(os.path.join("src", "tint_colors.json"), "w") as outfile:
+        outfile.write(json.dumps(tint_colors, indent=2, sort_keys=True))
diff --git a/src/tint_colors.json b/src/tint_colors.json
new file mode 100644 (file)
index 0000000..19ef95c
--- /dev/null
@@ -0,0 +1,1527 @@
+{
+  "100-ways-to-improve-your-writing.jpg": [
+    0.17248490676924744,
+    0.3439012662285626,
+    0.5534327082331574
+  ],
+  "1421-the-year-china-discovered-the-world.jpg": [
+    0.8091607843137255,
+    0.06828235294117635,
+    0.1433254901960782
+  ],
+  "200-twenty-minute-meals.jpg": [
+    0.5595505617977526,
+    0.37142983035911004,
+    0.21708746419916275
+  ],
+  "500-lines-or-less.png": [
+    0.45388024941777455,
+    0.46481857110660363,
+    0.5011344001202014
+  ],
+  "a-brief-history-of-infinity.jpg": [
+    0.446961694894676,
+    0.41393795908456266,
+    0.37140775814969973
+  ],
+  "a-brief-history-of-wales.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "a-closed-and-common-orbit.jpg": [
+    0.7612690019828153,
+    0.07693324520819564,
+    0.07569949328045825
+  ],
+  "a-laymans-guide-to-greek-heroes.jpg": [
+    0.6175381263616558,
+    0.32401182695300335,
+    0.2928415810768753
+  ],
+  "a-little-book-about-the-runes.jpg": [
+    0.44073444406664386,
+    0.2893800294684345,
+    0.1858324832823301
+  ],
+  "a-little-book-of-language.jpg": [
+    0.46856698626587207,
+    0.46198497019953366,
+    0.432823702168092
+  ],
+  "a-philosophy-of-walking.jpg": [
+    0.42502367673366304,
+    0.4840296046862395,
+    0.30761513907888705
+  ],
+  "a-poem-for-every-day-of-the-year.jpg": [
+    0.8912994952185269,
+    0.028569616721203284,
+    0.24977804834741157
+  ],
+  "a-poem-for-every-night-of-the-year.jpg": [
+    0.5166353519294694,
+    0.4239919063448475,
+    0.3588861588861589
+  ],
+  "a-spellers-companion.jpg": [
+    0,
+    0,
+    0
+  ],
+  "abandoned-america-the-age-of-consequences.jpg": [
+    0.45141612200435743,
+    0.45585411119180186,
+    0.4508512870168643
+  ],
+  "abc-signalling-in-the-age-of-steam.jpg": [
+    0.5084625774300221,
+    0.4494434353313772,
+    0.42467514321643146
+  ],
+  "adventures-with-the-wife-in-space.jpg": [
+    0.361937716262976,
+    0.34997875311115173,
+    0.4409640016997514
+  ],
+  "agent-zigzag.jpg": [
+    0.5946157922628512,
+    0.27480657127715946,
+    0.26588235294117657
+  ],
+  "airman.jpg": [
+    0.3270207661123521,
+    0.4955985769835358,
+    0.4528832630098452
+  ],
+  "algebra-and-geometry.jpg": [
+    0.0,
+    0.00392156862745098,
+    0.40784313725490196
+  ],
+  "all-systems-red.jpg": [
+    0.45536881419234365,
+    0.1768440709617179,
+    0.11615312791783361
+  ],
+  "all-the-birds-in-the-sky.jpg": [
+    0.34777607004797156,
+    0.40912393460947327,
+    0.4709748032229519
+  ],
+  "am-i-overthinking-this.jpg": [
+    0.32624970936991415,
+    0.2721847632333565,
+    0.24363326358211268
+  ],
+  "an-astronauts-guide-to-life-on-earth.jpg": [
+    0.19016559991872398,
+    0.3773443055978872,
+    0.5370923498933249
+  ],
+  "an-atlas-of-extinct-countries.jpg": [
+    0.04701638231049954,
+    0.49210593328240393,
+    0.4451744334097277
+  ],
+  "an-unsuitable-job-for-a-woman.jpg": [
+    0.8005667006687067,
+    0.09531905247648217,
+    0.12070724243454621
+  ],
+  "ancillary-justice.jpg": [
+    0.7170703575547865,
+    0.0880046136101501,
+    0.17681660899653978
+  ],
+  "ancillary-mercy.jpg": [
+    0.596732026143791,
+    0.10409586056644937,
+    0.11115468409586088
+  ],
+  "ancillary-sword.jpg": [
+    0.866414402018117,
+    0.19220272904483432,
+    0.1677330581355348
+  ],
+  "and-another-thing.jpg": [
+    0.5623172229043418,
+    0.18950775756222776,
+    0.1457379915913233
+  ],
+  "antimatter.jpg": [
+    0.682240896358543,
+    0.12425770308123249,
+    0.16907563025210087
+  ],
+  "artemis-fowl-and-the-lost-colony.jpg": [
+    0.5758703481392557,
+    0.22116846738695478,
+    0.2230492196878755
+  ],
+  "artemis-fowl-and-the-opal-deception.jpg": [
+    0.023529411764705882,
+    0.2235294117647059,
+    0.47843137254901963
+  ],
+  "artemis-fowl-and-the-time-paradox.gif": [
+    0.44691472389480724,
+    0.36361998867788564,
+    0.14071843960681382
+  ],
+  "artemis-fowl-eternity-cube.jpg": [
+    0.7091897966965703,
+    0.16922953418829154,
+    0.118887350231802
+  ],
+  "artemis-fowl-the-arctic-incident.jpg": [
+    0.4382167961524236,
+    0.4460599334073253,
+    0.43720557405352056
+  ],
+  "artemis-fowl.jpg": [
+    0.5423080123138367,
+    0.38734226363812857,
+    0.040377735252517144
+  ],
+  "artificial-condition-murderbot-2.jpg": [
+    0.594471396806145,
+    0.425207196280574,
+    0.274580553871033
+  ],
+  "as-you-wish-inconceivable-tales-from-the-making-of-the-princess-bride.jpg": [
+    0.4905290418054013,
+    0.44794672586015555,
+    0.4039400665926748
+  ],
+  "atomic-accidents.jpg": [
+    0.2826434277414671,
+    0.2713725490196077,
+    0.5485984023238927
+  ],
+  "atomic-habits.jpg": [
+    0.36834733893557403,
+    0.36802054154995356,
+    0.3493464052287581
+  ],
+  "atomic.jpg": [
+    0.3084288260758851,
+    0.40800229182582126,
+    0.5581168831168828
+  ],
+  "automating-inequality.jpg": [
+    0.4129292504121506,
+    0.37865937072503403,
+    0.35512995896032845
+  ],
+  "await-your-reply.jpg": [
+    0.3713450292397661,
+    0.38402706111684437,
+    0.4314986813438826
+  ],
+  "beautiful-lego.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "becoming-sex-second-chances-and-figuring-out-who-the-hell-i-am.jpg": [
+    0,
+    0,
+    0
+  ],
+  "behind-the-scenes-at-scrapheap-challenge.jpg": [
+    0.25701357466063357,
+    0.310352392705334,
+    0.5149961149961152
+  ],
+  "berlitz-essential-russian.jpg": [
+    0.24134658429581896,
+    0.39586355089981207,
+    0.438221864088101
+  ],
+  "beyond-burnout.jpg": [
+    0.361026657854153,
+    0.4614452522582063,
+    0.4927517074245429
+  ],
+  "black-and-british-a-forgotten-history.jpg": [
+    0.5086698463169051,
+    0.4342554319024907,
+    0.26504504504504506
+  ],
+  "bletchley-park-demystifying-the-bombe.jpg": [
+    0.6571957084720683,
+    0.22796892341842395,
+    0.2219509187322728
+  ],
+  "bletchley-park-home-of-the-codebreakers.jpg": [
+    0.6042657488527325,
+    0.28888715060492276,
+    0.12124530663329143
+  ],
+  "blind-mans-bluff.jpg": [
+    0.32630019262112875,
+    0.41288092063021686,
+    0.40563046377241074
+  ],
+  "brag-better.jpg": [
+    0.7611348449994522,
+    0.24280863183262108,
+    0.3919158724942491
+  ],
+  "british-rail-designed-1948-1997.jpg": [
+    0.8138570062171208,
+    0.25598397895743663,
+    0.1926410808225727
+  ],
+  "building-git.jpg": [
+    0.6095294683529979,
+    0.4280168633109811,
+    0.19653679653679684
+  ],
+  "built.jpg": [
+    0.45027652086475606,
+    0.4572272498743086,
+    0.4538335847159378
+  ],
+  "burnout.jpg": [
+    0.7990132827324481,
+    0.11463630613535741,
+    0.34087286527514243
+  ],
+  "calendrical-calculations.png": [
+    0.5119961570374252,
+    0.4279138827023014,
+    0.31679112625005446
+  ],
+  "captain-corellis-mandolin.jpg": [
+    0.8130499258526935,
+    0.2687757455923547,
+    0.16945130993573904
+  ],
+  "carmilla.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "carrying-the-fire.jpg": [
+    0.6937862468931233,
+    0.26915216790941743,
+    0.27743717205191953
+  ],
+  "chernobyl-history-of-a-tragedy.jpg": [
+    0.48245285375296615,
+    0.3440239790183589,
+    0.16967653303359534
+  ],
+  "chernobyl-prayer-a-chronicle-of-the-future.jpg": [
+    0.4208233519652205,
+    0.42264217904356305,
+    0.42929198828852844
+  ],
+  "chinese-characters-their-origin-etymology-history-classification-and-signification.jpg": [
+    0.7594349567784103,
+    0.30685220324689017,
+    0.19666877503689648
+  ],
+  "clouds-cannot-cover-us.jpg": [
+    0.79860529986053,
+    0.12941176470588256,
+    0.19776847977684803
+  ],
+  "codebreakers-the-inside-story-of-bletchley-park.jpg": [
+    0.7349293205654355,
+    0.039063687490499946,
+    0.038258093935248406
+  ],
+  "codes-an-introduction-to-information-communication-and-cryptography.jpg": [
+    0.14991087344028498,
+    0.3214795008912654,
+    0.48244206773618536
+  ],
+  "colossus-bletchley-parks-greatest-secret.jpg": [
+    0.8156355098673775,
+    0.1723967256805632,
+    0.3051716479472051
+  ],
+  "creative-selection.png": [
+    0.0570980392156859,
+    0.40674509803921577,
+    0.696156862745098
+  ],
+  "creativity-inc.jpg": [
+    0.7919174531568443,
+    0.13864387329099148,
+    0.1643517144496545
+  ],
+  "cryptography-a-very-short-introduction.jpg": [
+    0.5843017694882832,
+    0.3495217599234814,
+    0.2639646102343371
+  ],
+  "death-dynamite-and-disaster.jpg": [
+    0.5485346826902804,
+    0.25861269238878326,
+    0.10354206198608495
+  ],
+  "deaths-end.jpg": [
+    0.5443340972752738,
+    0.2128342245989305,
+    0.18419489007724307
+  ],
+  "defying-hitler.jpg": [
+    0.4276130286825472,
+    0.34960298168854326,
+    0.23633122670555817
+  ],
+  "do-not-alight-here-walking-londons-lost-underground-and-railway-stations.jpg": [
+    0.503777511004402,
+    0.23005202080832343,
+    0.2509643857543021
+  ],
+  "do-you-feel-lucky.jpg": [
+    0.2669918699186993,
+    0.4616738402678148,
+    0.5235198469631754
+  ],
+  "doctor-who-timeless.jpg": [
+    0.1200869488066717,
+    0.354990684056428,
+    0.5816520273267678
+  ],
+  "double-cross.jpg": [
+    0.560240566814961,
+    0.4494480144999173,
+    0.18281430219146472
+  ],
+  "effective-python.jpg": [
+    0.13333333333333333,
+    0.12549019607843137,
+    0.12941176470588237
+  ],
+  "eleanor-oliphant-is-completely-fine.jpg": [
+    0.4341290139244105,
+    0.4216822961068483,
+    0.4129582267689682
+  ],
+  "electric-universe.jpg": [
+    0.7843627128695001,
+    0.09641747560299524,
+    0.1133052300473108
+  ],
+  "engineering-a-safer-world.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "engines-of-war.jpg": [
+    0.7081183350533196,
+    0.18789129686962514,
+    0.23250773993808038
+  ],
+  "enigma-the-battle-for-the-code.jpg": [
+    0.7267213862289104,
+    0.1792369661042713,
+    0.2381972944216444
+  ],
+  "equity-exclusion-and-everyday-science-learning.jpg": [
+    0.5816352201257862,
+    0.14847206807251112,
+    0.2236625971143173
+  ],
+  "exhalation.jpg": [
+    0.37487645464689917,
+    0.37487645464689917,
+    0.37487645464689917
+  ],
+  "exit-strategy.jpg": [
+    0.4749095754806778,
+    0.45627260612983056,
+    0.4588044926708548
+  ],
+  "felix-ever-after.jpg": [
+    0.5949090479565319,
+    0.3577368296716277,
+    0.23492794708244735
+  ],
+  "fire-season.jpg": [
+    0.5201402837557789,
+    0.31088793240873586,
+    0.1482225410489404
+  ],
+  "five-equations-that-changed-the-world.jpg": [
+    0.5605558728345708,
+    0.35849990481629535,
+    0.3149057681324959
+  ],
+  "five-go-on-a-strategy-away-day.jpg": [
+    0.7206476530005942,
+    0.22299465240641675,
+    0.1836601307189546
+  ],
+  "flavour.jpg": [
+    0.2176382660687594,
+    0.4990943462586827,
+    0.4599490020223334
+  ],
+  "focus.png": [
+    0.41669467787114844,
+    0.4136694677871151,
+    0.4188235294117648
+  ],
+  "focusing.jpg": [
+    0.6011335784313728,
+    0.3741421568627451,
+    0.17405024509803899
+  ],
+  "forgotten-sacrifice-the-arctic-convoys-of-world-war-ii.jpg": [
+    0.35464592448549637,
+    0.424469291848971,
+    0.4964057689191379
+  ],
+  "from-ace-to-ze.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "from-here-to-eternity.jpg": [
+    0,
+    0,
+    0
+  ],
+  "from-prejudice-to-pride.jpg": [
+    0.7125980392156862,
+    0.34462418300653563,
+    0.2676552287581698
+  ],
+  "fun-home.jpg": [
+    0.36878276062900384,
+    0.46072607260726084,
+    0.4297676826506181
+  ],
+  "gaming-the-vote.jpg": [
+    0.349463559008509,
+    0.3016648168701441,
+    0.3019607843137254
+  ],
+  "gchq.jpg": [
+    0.38614935335836453,
+    0.38498122653316635,
+    0.33813934084272
+  ],
+  "gender-outlaw.jpg": [
+    0.2324908088235299,
+    0.391069240196078,
+    0.587239583333333
+  ],
+  "getting-things-done.jpg": [
+    0.48323013415892674,
+    0.3416150670794634,
+    0.363235294117647
+  ],
+  "going-postal.jpg": [
+    0.773275604658107,
+    0.2168508012341993,
+    0.17373345277197177
+  ],
+  "good-omens.jpg": [
+    0.4246528881822998,
+    0.42469528351881325,
+    0.42160042395336517
+  ],
+  "grace-hopper-and-the-invention-of-the-information-age.jpg": [
+    0.24811300864431793,
+    0.28770820156019394,
+    0.43938435589289493
+  ],
+  "granuaile-grace-omalley-irelands-pirate-queen.jpg": [
+    0.37635200948629993,
+    0.380485325879812,
+    0.4194723245669758
+  ],
+  "h2o.jpg": [
+    0.1654478007419189,
+    0.5002583465818758,
+    0.5764043455219925
+  ],
+  "have-i-got-views-for-you.jpg": [
+    0.71487047373694,
+    0.2411049091169314,
+    0.2798626019750968
+  ],
+  "hello-world-how-to-be-human-in-the-age-of-the-machine.jpg": [
+    0,
+    0,
+    0
+  ],
+  "here-the-world-entire.jpg": [
+    0.31423149905123343,
+    0.35104364326375725,
+    0.35751332791180984
+  ],
+  "hidden-figures-the-untold-story-of-the-african-american-women-who-helped-win-the-space-race.jpg": [
+    0.2717168818747008,
+    0.307049258727881,
+    0.4650789096126254
+  ],
+  "high-performance-python-practical-performant-programming-for-humans.jpg": [
+    0.704872840225199,
+    0.24016809985496768,
+    0.5790103577831825
+  ],
+  "history-of-the-world-in-500-railway-journeys.jpg": [
+    0.428825114951641,
+    0.29783838063527296,
+    0.191300671211881
+  ],
+  "how-many-friends-does-one-person-need.jpg": [
+    0.6145067401960784,
+    0.2901041666666665,
+    0.3207261029411764
+  ],
+  "how-many-lightbulbs-does-it-take-to-change-a-planet.jpg": [
+    0.6276229790161678,
+    0.23415892672858651,
+    0.21547987616099074
+  ],
+  "how-to-be-alone.jpg": [
+    0.33299719887955165,
+    0.265546218487395,
+    0.22162464985994412
+  ],
+  "how-to-fold-it.jpg": [
+    0.4326109391124871,
+    0.3518059855521158,
+    0.5018575851393189
+  ],
+  "how-to-take-smart-notes.jpg": [
+    0.8820953198797767,
+    0.10974667239158464,
+    0.16690997566910004
+  ],
+  "how-to-understand-your-gender.jpg": [
+    0.5697530037071775,
+    0.3745589351913886,
+    0.5058644870248782
+  ],
+  "how-to-win-friends-and-influence-people.jpg": [
+    0.7969717127561848,
+    0.2622319363932972,
+    0.3612848047134847
+  ],
+  "how-to.jpg": [
+    0.09803921568627451,
+    0.12549019607843137,
+    0.16470588235294117
+  ],
+  "how-you-stand-how-you-move-how-you-live-learning-the-alexander-technique-to-explore-your-mind-body-connection-and-achieve-self-mastery.jpg": [
+    0.5045599635202918,
+    0.4426508587931298,
+    0.4047423620611035
+  ],
+  "human-resources-management.jpg": [
+    0.3590497737556561,
+    0.42126696832579186,
+    0.3401206636500754
+  ],
+  "hyperbole-and-a-half.jpg": [
+    0.3285345717234263,
+    0.3106983144134845,
+    0.23811489508083922
+  ],
+  "i-hate-everyone-but-you.jpg": [
+    0.4736945304437565,
+    0.2493498452012384,
+    0.27572755417956646
+  ],
+  "i-was-there-on-pq17-the-convoy-to-hell.jpg": [
+    0.6830616583982989,
+    0.30857547838412447,
+    0.18857784077486361
+  ],
+  "ibm-and-the-holocaust.jpg": [
+    0.46474945533769063,
+    0.387705156136529,
+    0.34265068990559155
+  ],
+  "ida.jpg": [
+    0.18052578829697016,
+    0.47866265772363975,
+    0.5272685220877341
+  ],
+  "inside-steves-brain.jpg": [
+    0.37687790091882173,
+    0.3816519844652838,
+    0.39041394335511964
+  ],
+  "intimate-friendships.jpg": [
+    0.5380334486735869,
+    0.17159746251441751,
+    0.13370818915801613
+  ],
+  "introduction-to-algebra.jpg": [
+    0.3964381436027645,
+    0.47175906333756523,
+    0.3388912399492171
+  ],
+  "introduction-to-graph-theory.jpg": [
+    0.2,
+    0.17647058823529413,
+    0.36470588235294116
+  ],
+  "just-my-type.jpg": [
+    0.23767437323187982,
+    0.4815920398009951,
+    0.539810750170715
+  ],
+  "last-chance-to-see.jpg": [
+    0.8168938492971065,
+    0.11524410697405016,
+    0.10307077730003322
+  ],
+  "laymans-guide-to-the-greek-gods.jpg": [
+    0.5581661891117478,
+    0.3676723411427608,
+    0.35544693522108034
+  ],
+  "lego-architecture-the-visual-guide.jpg": [
+    0.09411764705882353,
+    0.09411764705882353,
+    0.09411764705882353
+  ],
+  "lewis-carroll-in-numberland-his-fantastical-mathematical-logical-life.jpg": [
+    0.4612612612612611,
+    0.4434340222575519,
+    0.3898887122416536
+  ],
+  "liars-and-outliers.jpg": [
+    0.61359477124183,
+    0.08431372549019633,
+    0.11934640522875806
+  ],
+  "life-isnt-binary.jpg": [
+    0,
+    0,
+    0
+  ],
+  "locomotives-of-the-great-western-railway-jarrold-railway-series-1.jpg": [
+    0.6543686274509803,
+    0.3714196078431372,
+    0.36850196078431363
+  ],
+  "london-underground-by-design.jpg": [
+    0.45142857142857146,
+    0.4043697478991596,
+    0.18789915966386533
+  ],
+  "londons-lost-rivers.jpg": [
+    0.42251306390526877,
+    0.43199098710388784,
+    0.3617671029291909
+  ],
+  "long-walk-to-freedom.jpg": [
+    0.5479696684165654,
+    0.33373266926908063,
+    0.15137972809261008
+  ],
+  "looking-for-group.jpg": [
+    0.4537410632122885,
+    0.3626955475330926,
+    0.2144262759255326
+  ],
+  "lorenz.jpg": [
+    0.6888035214085635,
+    0.3649459783913568,
+    0.20496198479391753
+  ],
+  "make-games-with-python.jpg": [
+    0.20480325463518734,
+    0.3988021875416844,
+    0.5888502067493665
+  ],
+  "mallard-how-the-blue-streak-broke-the-world-speed-record.jpg": [
+    0.34504871130842263,
+    0.43175484030090033,
+    0.4898507830805278
+  ],
+  "manx-fairy-tales.jpg": [
+    0.3132789749563195,
+    0.40737720830906615,
+    0.24979615608619687
+  ],
+  "march-hares-and-monkeys-uncles.jpg": [
+    0.7352629940865235,
+    0.13719265483971355,
+    0.23762838468720798
+  ],
+  "master-pieces.jpg": [
+    0.32651405389742133,
+    0.48526997005698824,
+    0.409311310731189
+  ],
+  "masters-of-the-post.jpg": [
+    0.1411764705882353,
+    0.2784313725490196,
+    0.35294117647058826
+  ],
+  "math-hysteria-fun-and-games-with-mathematics.jpg": [
+    0.3712297822354745,
+    0.38793711032800243,
+    0.40514141140327065
+  ],
+  "mathematics-and-politics-strategy-voting-power-and-proof.jpg": [
+    0.25997481561431957,
+    0.4423997121784489,
+    0.5516999460334593
+  ],
+  "mathematics-of-public-key-cryptography.jpg": [
+    0.4394194041252864,
+    0.43129615482556666,
+    0.573414820473644
+  ],
+  "messages-from-the-sea.jpg": [
+    0.23726089824308919,
+    0.46541544242569644,
+    0.48371609602046733
+  ],
+  "mind-the-gap-a-london-underground-miscellany.jpg": [
+    0.880944156591825,
+    0.02143655389617083,
+    0.09504554844390323
+  ],
+  "mindfulness-a-practical-guide-to-finding-peace-in-a-frantic-world.jpg": [
+    0.18262243285939955,
+    0.37128519654307235,
+    0.3369947030945083
+  ],
+  "mindfulness-based-cognitive-therapy.jpg": [
+    0.7044221944096789,
+    0.2100472813238775,
+    0.18484911695174627
+  ],
+  "mismatch.jpg": [
+    0.4426241524647243,
+    0.4426241524647241,
+    0.4426241524647243
+  ],
+  "montmorency.jpg": [
+    0.4419777526395175,
+    0.33764140271493215,
+    0.48545437405731534
+  ],
+  "nets-puzzles-and-postmen.jpg": [
+    0.647753451156247,
+    0.2848237052668602,
+    0.21292534951200223
+  ],
+  "nineteen-eighty-four.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "norse-mythology.jpg": [
+    0.45580065359477095,
+    0.40690359477124194,
+    0.3802083333333332
+  ],
+  "not-forgetting-the-whale.jpg": [
+    0.0406627826849586,
+    0.45576019545198176,
+    0.7181544822401803
+  ],
+  "not-quite-human-bug-in-the-system.jpg": [
+    0.5573696795791487,
+    0.4249545671927307,
+    0.2724246771879486
+  ],
+  "number-9-the-search-for-the-sigma-code.jpg": [
+    0.5502601040416168,
+    0.38559423769507833,
+    0.23361344537815154
+  ],
+  "numbers-rule-the-vexing-mathematics-of-democracy-from-plato-to-the-present.jpg": [
+    0.43983873923401146,
+    0.46835257467472996,
+    0.48502840388491847
+  ],
+  "numbers.jpg": [
+    0.4528098320667979,
+    0.4153485317572005,
+    0.5018763486255746
+  ],
+  "oh-no.jpg": [
+    0.34175084175084197,
+    0.4293919588037233,
+    0.48892850069320637
+  ],
+  "on-the-beach.jpg": [
+    0.5994732221246708,
+    0.10535557506584736,
+    0.11583260169739529
+  ],
+  "operation-fortitude-the-greatest-hoax-of-the-second-world-war.jpg": [
+    0.8372593281104767,
+    0.029433895454344938,
+    0.06913645819501593
+  ],
+  "paper-towns.jpg": [
+    0.7107807744036241,
+    0.06440150067247141,
+    0.14911870885538325
+  ],
+  "paul-takes-the-form-of-a-mortal-girl.jpg": [
+    0.41973103377476645,
+    0.33376450056462353,
+    0.23124935838209607
+  ],
+  "places-i-stopped-on-the-way-home.jpg": [
+    0.37875816993464073,
+    0.4439950980392157,
+    0.43263888888888924
+  ],
+  "platos-podcasts.jpg": [
+    0.7624770414494912,
+    0.3002283445023579,
+    0.28295358649789054
+  ],
+  "pride-and-prejudice.jpg": [
+    0,
+    0,
+    0
+  ],
+  "programmed-inequality.jpg": [
+    0.3081548516842635,
+    0.3621317244846657,
+    0.5670789341377576
+  ],
+  "protecting-information-from-classical-error-correction-to-quantum-cryptography.jpg": [
+    0.4744697879151662,
+    0.3432439642523679,
+    0.5595704948646124
+  ],
+  "provenance.jpg": [
+    0.6646197991391681,
+    0.15875657580105215,
+    0.1979531324725012
+  ],
+  "psychiatric-tales.jpg": [
+    0.4073604826546004,
+    0.4073604826546004,
+    0.4073604826546004
+  ],
+  "quicksilver-ultraviolet-2.jpg": [
+    0.3932321315623023,
+    0.46007047980482524,
+    0.5734797144664318
+  ],
+  "r-in-a-nutshell.jpg": [
+    0.7912217194570136,
+    0.2091402714932128,
+    0.1407088989441927
+  ],
+  "ratio-the-simple-codes-behind-the-craft-of-everyday-cooking.png": [
+    0.8269799825935595,
+    0.04242051912148759,
+    0.4379869963651257
+  ],
+  "record-of-a-spaceborn-few.jpg": [
+    0.17251481988144113,
+    0.3163930688554491,
+    0.5784655722754218
+  ],
+  "remote-stations.jpg": [
+    0.40981240981240996,
+    0.4020711314828959,
+    0.3628639334521686
+  ],
+  "rewriting-the-rules.jpg": [
+    0.24048376107199632,
+    0.44296313119842534,
+    0.722564160799455
+  ],
+  "rogue-protocol.jpg": [
+    0.4030818915801617,
+    0.42046597462514446,
+    0.4401014994232985
+  ],
+  "seashaken-houses.jpg": [
+    0.3522238163558105,
+    0.4189861310377808,
+    0.4519966523194643
+  ],
+  "secrets-of-the-conqueror-the-untold-story-of-britains-most-famous-submarine.jpg": [
+    0.7218670076726342,
+    0.258823529411765,
+    0.281187837453822
+  ],
+  "set-theory-and-logic.jpg": [
+    0.1960205527994324,
+    0.4029577132057641,
+    0.7135695724072765
+  ],
+  "seven-languages-in-seven-weeks.jpg": [
+    0.6418466120625463,
+    0.338744105237032,
+    0.35378505832712864
+  ],
+  "seven-ways-we-lie.jpg": [
+    0.4636418908648728,
+    0.4374221006231948,
+    0.47238182094543235
+  ],
+  "sexuality-and-social-justice-in-africa.jpg": [
+    0.843929726422515,
+    0.07070477522361135,
+    0.08899571715759003
+  ],
+  "shady-characters-ampersands-interrobangs-and-other-typographical-curiosities.jpg": [
+    0.740532630962833,
+    0.12753877670471203,
+    0.16491074041556908
+  ],
+  "shouldnt-you-be-in-school.jpg": [
+    0.14009253139458122,
+    0.45657633840052875,
+    0.7214011896893586
+  ],
+  "small-robots.jpg": [
+    0.42190196078431397,
+    0.35219607843137246,
+    0.3860392156862745
+  ],
+  "smoke-gets-in-your-eyes.jpg": [
+    0.42785053644099147,
+    0.4037439881613022,
+    0.3711135775064742
+  ],
+  "space-stories-that-really-happened.jpg": [
+    0.2982431372549019,
+    0.3674352941176471,
+    0.39035294117647074
+  ],
+  "speeches-that-changed-the-world.jpg": [
+    0.00392156862745098,
+    0.00392156862745098,
+    0.00392156862745098
+  ],
+  "strange-planet.jpg": [
+    0.5053414469235971,
+    0.25147622267297726,
+    0.3054315979265271
+  ],
+  "taking-german-further.jpg": [
+    0.8782519948042309,
+    0.15242159955464762,
+    0.052143254778252324
+  ],
+  "target-tirpitz.jpg": [
+    0.8883217015619811,
+    0.060525091392488795,
+    0.055194416749750785
+  ],
+  "the-ancient-greek-computer-from-rhodes.jpg": [
+    0.43159879336349927,
+    0.4273755656108598,
+    0.3520613373554551
+  ],
+  "the-art-of-betrayal.jpg": [
+    0.7076373090672733,
+    0.28100964142563134,
+    0.3161737623226089
+  ],
+  "the-art-of-lecturing.jpg": [
+    0.00392156862745098,
+    0.06274509803921569,
+    0.08235294117647059
+  ],
+  "the-artist-and-the-mathematician.jpg": [
+    0.42609529477895725,
+    0.4367556128690937,
+    0.4719373078067647
+  ],
+  "the-box-how-the-shipping-container-made-the-world-smaller-and-the-world-economy-bigger.jpg": [
+    0.33825882352941194,
+    0.3832470588235295,
+    0.5780392156862744
+  ],
+  "the-bridge.jpg": [
+    0.14298846910037724,
+    0.5024071417184757,
+    0.6042297677878741
+  ],
+  "the-butterfly-isles.jpg": [
+    0.5621371029031768,
+    0.408451132390941,
+    0.18780969752242016
+  ],
+  "the-c-programming-language.jpg": [
+    0.34397048459976,
+    0.42560212245574786,
+    0.7607760228827258
+  ],
+  "the-chinese-typewriter-a-history.jpg": [
+    0.7283582089552239,
+    0.15557506584723407,
+    0.19119110330699468
+  ],
+  "the-chronicles-of-narnia-audio-collection.jpg": [
+    0.600862963569829,
+    0.43407067562401014,
+    0.13765907477197037
+  ],
+  "the-city-the-city.jpg": [
+    0.5448320545609547,
+    0.36750895140664946,
+    0.205394714407502
+  ],
+  "the-curious-incident-of-the-dog-in-the-night-time.jpg": [
+    0.7344771241830066,
+    0.25857843137254893,
+    0.181781045751634
+  ],
+  "the-dark.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "the-day-of-the-triffids.jpg": [
+    0.4476190476190476,
+    0.3714772865668008,
+    0.22391913287053952
+  ],
+  "the-design-of-everyday-things.jpg": [
+    0.27129424601264396,
+    0.41778505182760495,
+    0.35901286864991505
+  ],
+  "the-elements-of-style.jpg": [
+    0.2844817927170869,
+    0.4844817927170868,
+    0.5656022408963586
+  ],
+  "the-emotion-machine.jpg": [
+    0.12253393665158357,
+    0.1053996983408747,
+    0.5874509803921568
+  ],
+  "the-enchanted-castle.jpg": [
+    0.7960784313725487,
+    0.22001296386323116,
+    0.15873440285204993
+  ],
+  "the-examined-life-how-we-lose-and-find-ourselves.jpg": [
+    0.4305709023941065,
+    0.37584227060990144,
+    0.36862745098039246
+  ],
+  "the-gate-of-angels.jpg": [
+    0.5058823529411764,
+    0.34880345839123045,
+    0.2256291492975143
+  ],
+  "the-greatest-traitor-the-secret-lives-of-agent-george-blake.jpg": [
+    0.47617615736390384,
+    0.42577209797657056,
+    0.3841383198646874
+  ],
+  "the-healthy-programmer.jpg": [
+    0.15329768270944744,
+    0.4502079619726676,
+    0.6916815210932857
+  ],
+  "the-heart-of-the-buddhas-teaching.jpg": [
+    0.5779249838073369,
+    0.13053053053052988,
+    0.09280456927515762
+  ],
+  "the-hitchhikers-guide-to-python-best-practices-for-development.jpg": [
+    0.7176736157216838,
+    0.23425397246934926,
+    0.5871575266675517
+  ],
+  "the-house-plant-expert.jpg": [
+    0.413577150254875,
+    0.4027780025352644,
+    0.32885077001914914
+  ],
+  "the-indisputable-existence-of-santa-claus-the-mathematics-of-christmas.jpg": [
+    0.00392156862745098,
+    0.00392156862745098,
+    0.00392156862745098
+  ],
+  "the-insiders-guide-to-technical-writing.jpg": [
+    0.5725490196078431,
+    0.10196078431372549,
+    0.09803921568627451
+  ],
+  "the-invisible-orientation-an-introduction-to-asexuality.jpg": [
+    0,
+    0,
+    0
+  ],
+  "the-last-little-blue-envelope.jpg": [
+    0.8232221921026478,
+    0.170741845125146,
+    0.17463630613535774
+  ],
+  "the-life-changing-magic-of-tidying-up.jpg": [
+    0.742345018815607,
+    0.11154684095860612,
+    0.14278074866310053
+  ],
+  "the-little-book-of-lionel-blue.jpg": [
+    0.207020750047592,
+    0.48451551494384126,
+    0.669095754806777
+  ],
+  "the-lonely-city.jpg": [
+    0.7386881656215784,
+    0.25247337513685675,
+    0.20816164029063416
+  ],
+  "the-long-dark-tea-time-of-the-soul.jpg": [
+    0.5395665634674922,
+    0.19804437564499455,
+    0.11130546955624412
+  ],
+  "the-long-way-to-a-small-angry-planet.jpg": [
+    0.4143332186675842,
+    0.4221190230478159,
+    0.4988418759316591
+  ],
+  "the-magazine-the-book.jpg": [
+    0.4102032302282612,
+    0.4299690088801478,
+    0.2696555217831817
+  ],
+  "the-man-who-invented-the-daleks.jpg": [
+    0.5063452552513208,
+    0.2546224725970566,
+    0.20818376348312154
+  ],
+  "the-mathematics-of-logic.jpg": [
+    0.3874509803921572,
+    0.35831932773109243,
+    0.20913165266106437
+  ],
+  "the-maths-gene.jpg": [
+    0.5962034216076868,
+    0.2624794937895477,
+    0.3146629169596125
+  ],
+  "the-meaning-of-it-all.jpg": [
+    0.7150992372509083,
+    0.28034822890459626,
+    0.27020486402300203
+  ],
+  "the-never-ending-days-of-being-dead.jpg": [
+    0.6100421477002015,
+    0.339124060839289,
+    0.36866410115448056
+  ],
+  "the-night-circus.jpg": [
+    0.0196078431372549,
+    0.0,
+    0.01568627450980392
+  ],
+  "the-ones-who-walk-away-from-omelas.jpg": [
+    0.4531631520532738,
+    0.38305586385497625,
+    0.37502774694783525
+  ],
+  "the-order-of-the-pure-moon-reflected-in-water.png": [
+    0.10999323867478009,
+    0.4127924273157539,
+    0.4440838404327248
+  ],
+  "the-organized-mind.jpg": [
+    0.5333805811481216,
+    0.36810772501771793,
+    0.36262697850224423
+  ],
+  "the-origins-of-unfairness.jpg": [
+    0.21083778966131894,
+    0.33081402257872866,
+    0.21675579322638105
+  ],
+  "the-other-greek.jpg": [
+    0.3622260668973472,
+    0.37324106113033456,
+    0.30530565167243373
+  ],
+  "the-paper-menagerie-and-other-stories.jpg": [
+    0.6624262713215366,
+    0.3667463733460863,
+    0.25844093735054985
+  ],
+  "the-phantom-x.jpg": [
+    0.7886850152905197,
+    0.1457096600107935,
+    0.17699226479582658
+  ],
+  "the-phoenix-and-the-carpet.jpg": [
+    0.8091038784380079,
+    0.2658239607179592,
+    0.20640323811419664
+  ],
+  "the-pooh-cook-book.jpg": [
+    0.3931846529960584,
+    0.44520281395000766,
+    0.49832859352392356
+  ],
+  "the-princess-bride.jpg": [
+    0.3742888704777689,
+    0.38210439105219507,
+    0.3848200312989042
+  ],
+  "the-princess-saves-herself-in-this-one.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "the-railway-adventures.jpg": [
+    0.07188004613610154,
+    0.34654363706266844,
+    0.5778239138792772
+  ],
+  "the-riddle-of-the-labyrinth.jpg": [
+    0.6089147286821706,
+    0.26958504331965344,
+    0.16634746922024624
+  ],
+  "the-seabirds-cry.jpg": [
+    0.2619607843137255,
+    0.34470588235294125,
+    0.3474019607843136
+  ],
+  "the-secret-listeners.jpg": [
+    0.7653594771241832,
+    0.31909041394335513,
+    0.3110294117647059
+  ],
+  "the-sixth-form-at-st-clares.jpg": [
+    0.44147208942469807,
+    0.420446199681286,
+    0.6828818217510789
+  ],
+  "the-starless-sea.jpg": [
+    0.47415499533146593,
+    0.3892250233426703,
+    0.18061624649859945
+  ],
+  "the-students-introduction-to-mathematica-r.jpg": [
+    0.694083546462063,
+    0.23655583972719563,
+    0.49142369991474855
+  ],
+  "the-tales-of-beedle-the-bard.jpg": [
+    0.3283696695461399,
+    0.4271081859317156,
+    0.5382421500068562
+  ],
+  "the-thrilling-adventures-of-lovelace-and-babbage.jpg": [
+    0.4127374981000152,
+    0.4438871155697421,
+    0.43464558950195065
+  ],
+  "the-universe-next-door.jpg": [
+    0.0,
+    0.0,
+    0.0
+  ],
+  "the-unofficial-lego-technic-builders-guide.jpg": [
+    0.15522115823073301,
+    0.4455523466923368,
+    0.714130820286771
+  ],
+  "the-void.jpg": [
+    0.7692013390722146,
+    0.06344332855093238,
+    0.09519846963175549
+  ],
+  "the-wandering-wombles.jpg": [
+    0.822093837535014,
+    0.21087184873949538,
+    0.46020658263305303
+  ],
+  "the-world-without-us.jpg": [
+    0.24671340038606115,
+    0.4034745504419385,
+    0.49387381895763466
+  ],
+  "the-worst-case-scenario-survival-handbook-travel.jpg": [
+    0.4759912854030502,
+    0.46039215686274515,
+    0.45106753812636163
+  ],
+  "the-worst-case-scenario-survival-handbook.jpg": [
+    0.606776470588235,
+    0.3371921568627451,
+    0.1314196078431371
+  ],
+  "the-worst-witch-all-at-sea.jpg": [
+    0.19720215571608757,
+    0.46643733516798536,
+    0.31874785001719996
+  ],
+  "the-worst-witch.jpg": [
+    0.4053070329928379,
+    0.3406598567570741,
+    0.5157214981801104
+  ],
+  "the-wrong-kind-of-snow-how-the-weather-made-britain.jpg": [
+    0.37212299961767437,
+    0.44233983286908096,
+    0.4774264023158011
+  ],
+  "think-black.jpg": [
+    0.37935174069627836,
+    0.39568398788086684,
+    0.3801749271137025
+  ],
+  "to-be-taught-if-fortunate.jpg": [
+    0.7859065341954476,
+    0.2879146296588338,
+    0.10172422856542111
+  ],
+  "too-much-information-or-can-everyone-just-shut-up-for-a-moment-some-of-us-are-trying-to-think.jpg": [
+    0.39971911629665674,
+    0.4660076702857452,
+    0.28631772268135885
+  ],
+  "trans-britain.jpg": [
+    0.5700830550560836,
+    0.3343522561863175,
+    0.563430088192482
+  ],
+  "trans-like-me.jpg": [
+    0.8522144522144521,
+    0.15440833676127708,
+    0.221856574797751
+  ],
+  "transgender-health.jpg": [
+    0.45712970634263084,
+    0.228003314001657,
+    0.2823529411764706
+  ],
+  "turing-pioneer-of-the-information-age.jpg": [
+    0.5331973251728437,
+    0.21128867732063894,
+    0.21901847444179978
+  ],
+  "twos-company-three-is-complexity.jpg": [
+    0.26403085824493744,
+    0.3570556091288978,
+    0.5167898853530484
+  ],
+  "typography-for-lawyers.jpg": [
+    0.4234764175940649,
+    0.4234764175940649,
+    0.4234764175940649
+  ],
+  "ultraviolet-ultraviolet-1.jpg": [
+    0.383542447449894,
+    0.3541035250665365,
+    0.45327250013578835
+  ],
+  "uncle-silas.jpg": [
+    0.00784313725490196,
+    0.00784313725490196,
+    0.00784313725490196
+  ],
+  "understanding-asexuality.jpg": [
+    0.41718569780853515,
+    0.4137254901960784,
+    0.43863898500576715
+  ],
+  "unfamiliar-underground.jpg": [
+    0.00392156862745098,
+    0.00392156862745098,
+    0.00392156862745098
+  ],
+  "unicorn.png": [
+    0.4466571018651362,
+    0.3286657101865136,
+    0.13499760879961742
+  ],
+  "volks-electric-railway-a-visitors-guide.jpg": [
+    0.49262694278575375,
+    0.3995510947359505,
+    0.2960869012831914
+  ],
+  "volks-railways-brighton-an-illustrated-history.jpg": [
+    0.5891451708293525,
+    0.33186843182748715,
+    0.20710917004079277
+  ],
+  "we-go-forward.jpg": [
+    0.3009552538964303,
+    0.46410256410256423,
+    0.5613876319758673
+  ],
+  "we.png": [
+    0.41744813867576,
+    0.4308752486501847,
+    0.3914251207729469
+  ],
+  "what-if-serious-scientific-answers-to-absurd-hypothetical-questions.jpg": [
+    0.6778980392156864,
+    0.1833098039215686,
+    0.19824313725490206
+  ],
+  "when-did-you-see-her-last.jpg": [
+    0.4350370736529906,
+    0.3596440929312902,
+    0.4502158510463009
+  ],
+  "who-could-that-be-at-this-hour.jpg": [
+    0.7340490568851117,
+    0.3104078133154404,
+    0.10888690076791166
+  ],
+  "why-buildings-fall-down.jpg": [
+    0.44222087460413706,
+    0.41605013139276353,
+    0.5047503537497475
+  ],
+  "why-buildings-stand-up.jpg": [
+    0.4606138107416883,
+    0.2517476555839726,
+    0.26965046888320543
+  ],
+  "why-im-no-longer-talking-to-white-people-about-race.jpg": [
+    0.4040041279669763,
+    0.40489164086687335,
+    0.4046026831785344
+  ],
+  "why-is-this-night-different-from-all-other-nights.jpg": [
+    0.4965227470298464,
+    0.35518207282913133,
+    0.626079397276152
+  ],
+  "wish-you-were-here-the-official-biography-of-douglas-adams.jpg": [
+    0.5102698505144632,
+    0.440331974373908,
+    0.33995340710541644
+  ],
+  "write-to-the-point.jpg": [
+    0.22745098039215686,
+    0.2196078431372549,
+    0.2235294117647059
+  ],
+  "yellow-trains.png": [
+    0.49747281492530854,
+    0.45354865209589906,
+    0.3947294463540653
+  ],
+  "yes-you-are-trans-enough.jpg": [
+    0.27151023418771913,
+    0.46826479808224253,
+    0.573802938103141
+  ],
+  "you-say-potato.jpg": [
+    0.0,
+    0.43137254901960786,
+    0.7019607843137254
+  ]
+}
\ No newline at end of file
index 5c7f882..2926a5a 100644 (file)
 {% endblock %}
 
 {% block content %}
 {% endblock %}
 
 {% block content %}
+  {% if tint_colors[review_entry.book.cover_image] %}
+    {% set r = tint_colors[review_entry.book.cover_image][0] * 255 | int %}
+    {% set g = tint_colors[review_entry.book.cover_image][1] * 255 | int %}
+    {% set b = tint_colors[review_entry.book.cover_image][2] * 255 | int %}
+    <style>
+      .review a {
+        color: rgb({{ r }}, {{ g }}, {{ b }});
+      }
+
+      .review a:hover {
+        background: rgb({{ r }}, {{ g }}, {{ b }}, 0.3);
+      }
+
+      .book-cover img {
+        box-shadow: 0px 5px 5px rgba({{ r }}, {{ g }}, {{ b }}, 0.25);
+      }
+    </style>
+  {% endif %}
+
   {% if review_entry.book.cover_image %}
     <div class="book-cover">
       {% set book = review_entry.book %}
   {% if review_entry.book.cover_image %}
     <div class="book-cover">
       {% set book = review_entry.book %}
@@ -61,7 +80,9 @@
     {% endif %}
   </table>
 
     {% endif %}
   </table>
 
-  {{ review_entry.review.text | render_markdown | safe }}
+  <div class="review">
+    {{ review_entry.review.text | render_markdown | safe }}
 
 
-  <p>(see <a href="/reviews">all reviews</a>)</p>
+    <p>(see <a href="/reviews">all reviews</a>)</p>
+  </div>
 {% endblock %}
 {% endblock %}