Skip to content

Instantly share code, notes, and snippets.

@oseiskar
Created July 18, 2014 12:26
Show Gist options
  • Save oseiskar/66c3b4b0f3e3b3095085 to your computer and use it in GitHub Desktop.
Save oseiskar/66c3b4b0f3e3b3095085 to your computer and use it in GitHub Desktop.
Festival schedule generation script
#!/usr/bin/python
#
# A script for generating a printable HTML festival schedule from CSV input.
#
# Usage: ./this-file.py < schedule.txt > schedule.html
# see the readSchedule function for input format description
#
import sys
def toFloatMinutes(s):
"""
Convert hh:mm representation to floating point 'minutes since midnight
format. If the time is before 06:00 AM, it is interpreted as a time on
the next day.
"""
hourmin = [float(f) for f in s.split(':')]
t = hourmin[0] + hourmin[1] / 60
MORNING = 6
if t < MORNING: t += 24
return t
class Band:
def __init__(self,day,start,end,name,genre,stage):
self.day = day
self.start = start
self.end = end
self.stage = stage
self.genre = genre
self.name = name
# Return float or integer type time and stage notation
def startF(self):
return toFloatMinutes(self.start)
def endF(self):
return toFloatMinutes(self.end)
def readSchedule(inputFile):
"""
Each line in the input CSV (tab separated) represents a band gig and should
contain the following fields (see Band constructor above)
* date
* starting time (hh:mm)
* end time (hh:mm)
* band name
* comment/genre (currently ignored)
* stage
Example:
14.8. Wed 01:45 02:30 KADAVAR - PARTY
14.8. Wed 00:15 01:15 DESTRUCTION - PARTY
14.8. Wed 22:45 23:45 EXODUS - PARTY
14.8. Wed 21:15 22:15 VADER - MAIN
14.8. Wed 20:00 20:45 BURY TOMORROW - MAIN
"""
bands = []
for line in inputFile:
line = line.strip()
if line != '':
vec = line.split('\t')
bands.append(Band(*vec))
return bands
bands = readSchedule(sys.stdin)
outfile = sys.stdout
days = []
stages = []
bandsPerStagePerDay = {}
earliestBegin = 24
latestEnd = 0
for b in bands:
begin = b.startF()
end = b.endF()
if begin < earliestBegin:
earliestBegin = begin
if end > latestEnd:
latestEnd = end
day = b.day
if day not in days:
days.append(day)
bandsPerStagePerDay[day] = {}
if b.stage not in bandsPerStagePerDay[day]:
bandsPerStagePerDay[day][b.stage] = []
if b.stage not in stages:
stages.append(b.stage)
bandsPerStagePerDay[day][b.stage].append(b)
# Tweakable layout settings
PAGE_TITLE = "Festival schedule"
scale = 70 # pixels per hour
dayBegin = earliestBegin
dayEnd = latestEnd
dayPaddingTopPx = 20
dayPaddingBottomPx = 20
dayHeightPx = int(scale*(dayEnd-dayBegin)+dayPaddingTopPx+dayPaddingBottomPx)
dayMarginBottomPx = 20
dayHWMargin = dayHeightPx + dayMarginBottomPx
dayPaddingRightPx = 10
columnWidthPx = 190
SHORT_SHOW = 30/60.0 # 30 minutes
outfile.write("""<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>%s</title>
<style>
.boksi {
position:absolute;
border-top: 1px solid black;
border-bottom: 1px dotted black;
background: #ccffcc;
width: %dpx;
-webkit-print-color-adjust: economy;
}""" % (PAGE_TITLE, columnWidthPx))
outfile.write("""
.lava {
border-top: 1px solid black;
border-bottom: 2px solid black;
border-right: 1px solid black;
border-left: 1px solid black;
float: left;
position: relative;
width: %dpx;
height: %dpx;
background: #eeeeee;
-webkit-print-color-adjust: exact;
}""" % (columnWidthPx, dayHeightPx))
outfile.write("""
.paiva {
float: left;
width: %dpx;
height: %dpx;
page-break-after: always;
page-break-inside: avoid;
}""" % (columnWidthPx*len(stages)+dayPaddingRightPx, dayHWMargin))
outfile.write("""
.lavajatyhjaaalla {
float: left;
position: relative;
height: %dpx;
}""" % dayHWMargin)
outfile.write("""
.sisus {
padding: 5px;
font-size: 11pt;
}
.tiivis {
font-size: 9pt;
line-height: 107%;
}
.paivanjalavanotsikko {
padding-left: 5px;
font-size: 10pt;
color: gray;
}
small {
color: gray;
font-size: 8pt;
}
</style>
</head>
<body>""")
for day in days:
bandsPerStage = bandsPerStagePerDay[day]
outfile.write("""
<div class="paiva">""")
for stage in stages:
outfile.write("""
<div class="lavajatyhjaaalla">
<div class="lava">
<div class="paivanjalavanotsikko">%s / %s</div>""" % (day,stage))
if stage in bandsPerStage:
bands = bandsPerStage[stage]
for b in bands:
begin = b.startF()
duration = b.endF()-begin
style = "sisus"
#long_text = False
short_show = duration <= SHORT_SHOW
if short_show:
long_text = len(b.name) > 20
else:
long_text = len(b.name) > 20
if short_show or long_text:
style += " tiivis"
if short_show and not long_text:
br = " "
else:
br = "<br/>"
top_px = int((begin-dayBegin)*scale + dayPaddingTopPx)
height_px = int(duration*scale)
text = "<b>%s</b> %s %s&nbsp;-&nbsp;%s" % (b.name, br, b.start, b.end)
outfile.write("""
<div class="boksi" style="top: %dpx; height: %dpx;">
<div class="%s">%s</div>
</div>""" % (top_px, height_px, style, text))
outfile.write("""
</div>
</div>""")
outfile.write("""
</div>""")
outfile.write("""
</body>
</html>
""")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment