Last active
February 24, 2020 18:05
-
-
Save bdunnette/51cc4127a5cd1e1877e58fbd649c1d19 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import os | |
from zipfile import ZipFile | |
from csv import DictReader | |
from pathlib import Path | |
from datetime import date | |
from jinja2 import Template | |
bootstrap_version = "3.4.1" | |
jquery_version = "3.4.1" | |
leaflet_version = "1.6.0" | |
leaflet_fullscreen_version = "1.6.0" | |
copyright_holder = "Regents of the University of Minnesota" | |
copyright_year = date.today().year | |
missing_image_url="//upload.wikimedia.org/wikipedia/commons/thumb/5/53/Font_Awesome_5_regular_clock.svg/240px-Font_Awesome_5_regular_clock.svg.png" | |
index_template = Template("<html><head>" | |
"<meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'>" | |
"<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/{{bootstrap_version}}/css/bootstrap.min.css' />" | |
"<script src='//cdnjs.cloudflare.com/ajax/libs/jquery/{{jquery_version}}/jquery.min.js' ></script>" | |
"<title>{{title}}</title></head>" | |
"<body><div class='container'>" | |
"<div class='page-header'><h1>{{title}}</h1></div>" | |
"<div class='row'>{% for slide in slides %}" | |
"<div class='col-md-3 col-sm-6 col-xs-12'>" | |
"<div class='panel panel-default' id='{{loop.index}}'>" | |
"<div class='panel-heading'>" | |
"<span class='panel-title'><strong>{{slide['Block ID']}} {{slide['Stain']}}</strong></span>" | |
"</div><div class='panel-body'>" | |
"<a href='{{slide['stem']}}.html'>" | |
"<img src='{{slide['stem']}}/0/0/0.jpg' class='img-thumbnail'></a>" | |
"</div></div></div>" | |
"{% endfor %}</div></div>" | |
"<script>$('img').on('error', function() {$(this).attr('src', '{{missing_image_url}}').parent().attr('href','#0');});</script>" | |
"</body></html>") | |
slide_template = Template("<!DOCTYPE html><html><head><meta charset='utf-8'>" | |
"<meta name='viewport' content='width=device-width'>" | |
"<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/leaflet/{{leaflet_version}}/leaflet.css' />" | |
"<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/leaflet.fullscreen/{{leaflet_fullscreen_version}}/Control.FullScreen.css' />" | |
"<script src='//cdnjs.cloudflare.com/ajax/libs/jquery/{{jquery_version}}/jquery.min.js' ></script>" | |
"<script src='//cdnjs.cloudflare.com/ajax/libs/leaflet/{{leaflet_version}}/leaflet.js' ></script>" | |
"<script src='//cdnjs.cloudflare.com/ajax/libs/leaflet.fullscreen/{{leaflet_fullscreen_version}}/Control.FullScreen.min.js'></script>" | |
"<style>.leaflet-container {background: #FFF;}</style>" | |
"<title>{{slide['Case Number']}} {{slide['Block ID']}} {{slide['Stain']}}</title></head>" | |
"<body style='margin:0;'><div id='map'></div>" | |
"<script>$(document).ready(function () {" | |
"$('#map').css({height: $(window).height() + 'px'});" | |
"var map = new L.map('map', {fullscreenControl: true, fullscreenControlOptions: {position: 'topleft'}});" | |
"var slideUrl = '{{slide['stem']}}/{z}/{y}/{x}.jpg';" | |
"slide = L.tileLayer(slideUrl, {minZoom: 0, maxZoom: {{max_zoom}}, noWrap: true, " | |
"errorTileUrl: '{{slide['stem']}}/blank.png', " | |
"attribution: 'Images © {{copyright_year}} {{copyright_holder}}'});" | |
"map.addLayer(slide);map.setView(new L.LatLng(0, 0), 2);map.attributionControl.setPrefix('');" | |
"$(window).resize(function () {$('#map').css({height: $(window).height() + 'px'});});" | |
"});</script>" | |
"</body></html>") | |
def main(): | |
files_to_parse = sys.argv[1:] | |
for f in files_to_parse: | |
print("Reading cases from %s" % f) | |
cases = [] | |
with open(f) as csvfile: | |
reader = DictReader(csvfile) | |
for row in reader: | |
case_dict = dict(row) | |
image_location = Path(case_dict['Image Location']) | |
# print(image_location) | |
# print(image_location.parts, image_location.name, | |
# image_location.suffix, image_location.stem) | |
# slide_id = case_dict['Image ID'] | |
slide_id = image_location.stem | |
case_dict['stem'] = image_location.stem | |
print(case_dict) | |
cases.append(case_dict) | |
zipfile_name = "{}.zip".format(slide_id) | |
if os.path.exists(zipfile_name): | |
try: | |
with ZipFile(zipfile_name, 'r') as z: | |
dirs = list(set([os.path.dirname(x) | |
for x in z.namelist()])) | |
# Sort the zipfile directories in reverse, so we get the highest-zoom directories first | |
last_dir = sorted(dirs, reverse=True)[0] | |
# Get the second part of the path, which is the zoom level - e.g. "12345/8/99" -> "8" | |
max_zoom = os.path.split( | |
os.path.split(last_dir)[0])[1] | |
except: | |
max_zoom = 8 | |
else: | |
max_zoom = 8 | |
print("max_zoom=%s" % max_zoom) | |
slide_html = slide_template.render( | |
slide=case_dict, max_zoom=max_zoom, copyright_year=copyright_year, copyright_holder=copyright_holder, | |
jquery_version=jquery_version, leaflet_version=leaflet_version, leaflet_fullscreen_version=leaflet_fullscreen_version) | |
# print(slide_html) | |
with open("./{}.html".format(slide_id), "w") as slide_file: | |
print("Writing {}.html...".format(slide_id)) | |
slide_file.write(slide_html) | |
index_html = index_template.render( | |
title=cases[0]['Case Number'], slides=cases, bootstrap_version=bootstrap_version, jquery_version=jquery_version, missing_image_url=missing_image_url) | |
# print(index_html) | |
with open("./index.html", "w") as index_file: | |
print("Writing index.html...") | |
index_file.write(index_html) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment