2011-11-18 23:42:05 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
|
|
|
Convert questions for the famous Penta News Game Show from
|
|
|
|
some yaml format to json.
|
|
|
|
|
|
|
|
It's just a helper to write the questions in a more human
|
|
|
|
readeable format assuming yaml is more human readable
|
|
|
|
|
2011-11-21 12:45:42 +01:00
|
|
|
Note:
|
|
|
|
- Media files are looked for relative to the questions file
|
|
|
|
- Media files are expected relative to the generated json files
|
2011-11-18 23:42:05 +01:00
|
|
|
|
|
|
|
TODO:
|
|
|
|
* Add own constructor to Question() / nice to have
|
2011-11-21 12:45:42 +01:00
|
|
|
* Use import logging for debug logging
|
2011-11-18 23:42:05 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
__author__ = "Frank Becker <fb@alien8.de>"
|
2011-11-21 10:33:13 +01:00
|
|
|
__version__ = "0.0.2"
|
2011-11-18 23:42:05 +01:00
|
|
|
__date__ = "Fri 18 Nov 2011 18:10:44 CET"
|
|
|
|
__copyright__ = "Copyright (c) 2011 Frank Becker"
|
|
|
|
__license__ = "Python"
|
|
|
|
|
2011-11-21 10:33:13 +01:00
|
|
|
import os
|
2011-11-18 23:42:05 +01:00
|
|
|
import sys
|
2011-12-18 14:24:32 +01:00
|
|
|
import random
|
2011-11-18 23:42:05 +01:00
|
|
|
import json
|
2011-12-27 15:09:00 +01:00
|
|
|
from reportlab.lib.pagesizes import A5, LETTER, landscape, portrait
|
|
|
|
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image
|
|
|
|
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
|
|
|
from reportlab.lib.units import inch, cm
|
|
|
|
from reportlab.platypus.flowables import PageBreak
|
2011-11-18 23:42:05 +01:00
|
|
|
from optparse import OptionParser
|
|
|
|
|
|
|
|
try:
|
|
|
|
import yaml
|
|
|
|
except ImportError:
|
|
|
|
print 'You need to install PyYAML first. E. g. "pip install pyyaml"'
|
|
|
|
|
|
|
|
|
|
|
|
class Question(yaml.YAMLObject):
|
|
|
|
"""Represents a question
|
|
|
|
"""
|
|
|
|
yaml_tag = u"!Question"
|
2011-12-13 23:13:00 +01:00
|
|
|
web_root = "data"
|
2011-11-18 23:42:05 +01:00
|
|
|
|
2011-12-18 14:24:32 +01:00
|
|
|
# Generate random points in range of points_per_round
|
|
|
|
gen_random_points = True
|
|
|
|
points_per_round = {
|
2011-12-27 17:45:45 +01:00
|
|
|
1: (1, 100),
|
|
|
|
2: (100, 1000),
|
|
|
|
3: (10000, 100000),
|
|
|
|
4: (5, 42),
|
|
|
|
5: (13, 80),
|
2011-12-18 13:58:32 +01:00
|
|
|
}
|
|
|
|
|
2011-12-18 14:24:32 +01:00
|
|
|
# {round_no1: [tier1, tier2, ...], round_no2: [tier1, ...]}
|
|
|
|
registered_questions = {}
|
2011-11-18 23:42:05 +01:00
|
|
|
|
|
|
|
def __init__(self, question=u"", tier=0, answers=[], game_round=0,
|
2011-12-18 13:58:32 +01:00
|
|
|
media=("", "", ""), media_path="data", web_root="data"):
|
2011-11-18 23:42:05 +01:00
|
|
|
"""docstring for __init__
|
|
|
|
@question - the Question
|
|
|
|
@rank - number of the question in the game
|
|
|
|
@game_round - number of the round in the game
|
|
|
|
@answers - list of answers, assumed are 4
|
|
|
|
@media - (media show at question time, media shown at answer time,
|
|
|
|
media shown at resolution time)
|
2011-12-18 13:58:32 +01:00
|
|
|
@media_path - path to the media files
|
2011-11-18 23:42:05 +01:00
|
|
|
"""
|
|
|
|
self.question = question
|
|
|
|
self.answers = answers
|
|
|
|
self.tier = tier
|
|
|
|
self.game_round = game_round
|
|
|
|
self.media = media
|
2011-11-21 10:33:13 +01:00
|
|
|
self.media_path = media_path
|
2011-12-13 23:13:00 +01:00
|
|
|
self.web_root = web_root
|
2011-11-21 10:33:13 +01:00
|
|
|
|
|
|
|
def __type_by_extension(self, media_file):
|
|
|
|
"""returns the media type looked up by it's extension
|
|
|
|
FIXME (a8): maybe use file magic in the future
|
|
|
|
|
|
|
|
@media_file - path to the media file
|
|
|
|
"""
|
|
|
|
media_types = dict(
|
|
|
|
video = ('webm'),
|
|
|
|
image = ('png', 'jpg', 'gif'),
|
|
|
|
)
|
|
|
|
if not os.path.isfile(media_file):
|
|
|
|
raise IOError("The file {0} does not exist.".format(media_file,))
|
|
|
|
ext = media_file.rsplit('.', 1)[1]
|
|
|
|
for k, v in media_types.items():
|
|
|
|
if ext in v:
|
|
|
|
return k
|
|
|
|
raise KeyError("Media type for {0} not found".format(media_file,))
|
2011-11-18 23:42:05 +01:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
"""docstring for __repr__"""
|
|
|
|
return "%s(%r)" % (self.__class__.__name__, self.question)
|
|
|
|
|
2011-12-18 14:24:32 +01:00
|
|
|
def _get_points(self, game_round, tier):
|
|
|
|
"""returns the points by game_round/tier combo"""
|
|
|
|
|
|
|
|
points_fixed = {
|
|
|
|
1: 100,
|
|
|
|
2: 150,
|
|
|
|
3: 225,
|
|
|
|
4: 337,
|
|
|
|
5: 506,
|
|
|
|
6: 759,
|
|
|
|
7: 1139,
|
|
|
|
8: 1709,
|
|
|
|
9: 2563,
|
|
|
|
10: 3844,
|
|
|
|
11: 5555,
|
|
|
|
12: 7531,
|
|
|
|
}
|
|
|
|
|
|
|
|
if not self.gen_random_points:
|
|
|
|
return points_fixed.get(tier, 0)
|
|
|
|
|
|
|
|
range = self.points_per_round.get(game_round)
|
|
|
|
points = random.randint(*range)
|
|
|
|
return points
|
|
|
|
|
2011-11-18 23:42:05 +01:00
|
|
|
@property
|
|
|
|
def as_dict(self):
|
|
|
|
"""dump data suiteable for json conversion"""
|
|
|
|
|
|
|
|
data = {}
|
|
|
|
data['text'] = self.question
|
2011-12-17 14:14:28 +01:00
|
|
|
try:
|
|
|
|
data['source'] = self.source
|
|
|
|
except AttributeError:
|
2011-12-18 13:58:32 +01:00
|
|
|
data['source'] = False
|
2011-11-18 23:42:05 +01:00
|
|
|
data['answers'] = [
|
|
|
|
{'text': answer[False]} if answer.has_key(False) \
|
|
|
|
else {'text': answer[True], 'right': True} \
|
|
|
|
for answer in self.answers
|
|
|
|
]
|
2011-11-21 10:33:13 +01:00
|
|
|
if hasattr(self, 'media'):
|
|
|
|
def gen_questions():
|
|
|
|
q_data = {}
|
|
|
|
for f in self.media['question']:
|
2011-12-18 13:58:32 +01:00
|
|
|
q_data[self.__type_by_extension(
|
|
|
|
os.path.sep.join(os.path.join([self.media_path, f]))
|
|
|
|
)] = os.sep.join([self.web_root, f])
|
2011-11-21 10:33:13 +01:00
|
|
|
return q_data
|
|
|
|
def gen_explanation():
|
2011-12-13 23:13:00 +01:00
|
|
|
return {'explanation': [os.sep.join([self.web_root, expl]) \
|
|
|
|
for expl in self.media['explanation']]}
|
2011-11-21 10:33:13 +01:00
|
|
|
def k_not_found():
|
|
|
|
raise KeyError("Media keyword not found")
|
|
|
|
|
|
|
|
for k in self.media.keys():
|
|
|
|
m_data = dict(
|
|
|
|
question = gen_questions,
|
|
|
|
explanation= gen_explanation,
|
|
|
|
k_not_found = "lambda x: pass",
|
|
|
|
).get(k, 'k_not_found')()
|
|
|
|
for key, value in m_data.items():
|
|
|
|
data[key] = value
|
2011-11-18 23:42:05 +01:00
|
|
|
return data
|
|
|
|
|
2011-12-27 15:09:00 +01:00
|
|
|
@property
|
|
|
|
def as_pdf_dict(self):
|
|
|
|
"""Return full data set. Includes comment field"""
|
|
|
|
data = self.as_dict
|
|
|
|
try:
|
|
|
|
data['comment'] = self.comment
|
|
|
|
except AttributeError:
|
|
|
|
data['comment'] = ""
|
|
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
2011-11-18 23:42:05 +01:00
|
|
|
@classmethod
|
|
|
|
def get_points(cls):
|
|
|
|
"""docstring for get_points"""
|
|
|
|
for key in sorted(cls.points.keys()):
|
|
|
|
yield cls.points[key]
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def register_question(obj):
|
|
|
|
"""register object in class so no question with the
|
|
|
|
same tier/round combo can exist"""
|
|
|
|
if Question.registered_questions.has_key(obj.game_round) and \
|
|
|
|
obj.tier in Question.registered_questions[obj.game_round]:
|
|
|
|
raise IndexError("Slot for Question {0} is alredy taken".format(
|
|
|
|
obj.question,))
|
|
|
|
elif Question.registered_questions.has_key(obj.game_round):
|
|
|
|
Question.registered_questions[obj.game_round].append(obj.tier)
|
|
|
|
else:
|
|
|
|
Question.registered_questions[obj.game_round] = [obj.tier]
|
|
|
|
|
|
|
|
|
|
|
|
def init_parser():
|
|
|
|
"""Read command line options
|
|
|
|
|
|
|
|
returns:
|
|
|
|
options:dict -- config options
|
|
|
|
"""
|
|
|
|
parser = OptionParser()
|
|
|
|
|
|
|
|
parser.add_option(
|
|
|
|
"-d",
|
|
|
|
"--debug",
|
|
|
|
dest="debug",
|
|
|
|
help="Toggle debugging",
|
|
|
|
action="store_true",
|
|
|
|
default=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
parser.add_option(
|
|
|
|
"-f",
|
|
|
|
"--questions-file",
|
|
|
|
dest="file",
|
|
|
|
help=("Use this file instead of the default "
|
|
|
|
"questions.yaml"),
|
|
|
|
metavar="FILE",
|
|
|
|
)
|
|
|
|
|
2011-12-27 15:09:00 +01:00
|
|
|
parser.add_option(
|
|
|
|
"-p",
|
|
|
|
"--generate-pdf",
|
|
|
|
dest="pdf",
|
|
|
|
help=("Generate the speaker PDF"),
|
|
|
|
action="store_true",
|
|
|
|
default=False,
|
|
|
|
)
|
|
|
|
|
2011-11-18 23:42:05 +01:00
|
|
|
parser.add_option(
|
|
|
|
"-v",
|
|
|
|
"--version",
|
|
|
|
dest="version",
|
|
|
|
help="Show program version",
|
|
|
|
action="store_true",
|
|
|
|
default=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
options = parser.parse_args()[0]
|
|
|
|
return options
|
|
|
|
|
2011-11-19 19:58:50 +01:00
|
|
|
def questions_per_round(questions, game_round=None):
|
|
|
|
"""docstring for questions_per_round"""
|
|
|
|
return [q for q in questions if q.game_round == game_round]
|
|
|
|
|
2011-11-21 12:45:42 +01:00
|
|
|
def write_json_file(questions):
|
|
|
|
"""docstring for write_json_file"""
|
|
|
|
game_round = questions[0].game_round
|
|
|
|
file_name = 'round_{0}.json'.format(game_round)
|
|
|
|
fh = open(file_name, 'w')
|
|
|
|
fh.writelines(json.dumps([q.as_dict for q in questions], indent=2))
|
|
|
|
|
2011-12-27 15:09:00 +01:00
|
|
|
def gen_pdf(questions, game_rounds):
|
|
|
|
"""generate speaker PDF"""
|
|
|
|
|
|
|
|
styles = getSampleStyleSheet()
|
|
|
|
doc = SimpleDocTemplate("pngs-speaker.pdf")
|
|
|
|
doc.pagesize = landscape(A5)
|
|
|
|
style = styles["Normal"]
|
|
|
|
page_elements = []
|
|
|
|
for round in game_rounds:
|
2011-12-27 17:29:36 +01:00
|
|
|
for num, question in enumerate(
|
|
|
|
questions_per_round(questions, game_round=round)):
|
2011-12-27 15:09:00 +01:00
|
|
|
q_data = question.as_pdf_dict
|
|
|
|
page_elements.append(
|
|
|
|
Paragraph("<em>Game Round</em>: {0}".format(round),
|
|
|
|
style)
|
|
|
|
)
|
|
|
|
page_elements.append(Spacer(0, 0.1*cm))
|
|
|
|
page_elements.append(
|
|
|
|
Paragraph(
|
|
|
|
"<font size=12><em>Question {0}:</em> <bold>{1}</bold>"
|
|
|
|
"</font>".format(num + 1, q_data['text'].encode('utf-8')),
|
|
|
|
style)
|
|
|
|
)
|
|
|
|
page_elements.append(Spacer(0, 0.2*cm))
|
|
|
|
page_elements.append(
|
|
|
|
Paragraph("<em>Comment</em>: {0}".format(q_data.get('comment').encode('utf-8')),
|
|
|
|
style)
|
|
|
|
)
|
|
|
|
page_elements.append(Spacer(0, 0.2*cm))
|
|
|
|
page_elements.append(
|
|
|
|
Paragraph("<em>Answers</em>:",
|
|
|
|
style)
|
|
|
|
)
|
|
|
|
page_elements.append(
|
|
|
|
Paragraph("* " + "<br />* ".join([unicode(t['text']) for t in q_data['answers']]),
|
|
|
|
style)
|
|
|
|
)
|
|
|
|
page_elements.append(
|
|
|
|
Paragraph("<em>Points</em>: {0}".format(q_data.get('tier')),
|
|
|
|
style)
|
|
|
|
)
|
|
|
|
page_elements.append(PageBreak())
|
|
|
|
doc.build(page_elements)
|
|
|
|
return
|
|
|
|
|
|
|
|
Story = [Spacer(0, 1*cm)]
|
|
|
|
p = Paragraph("Blubber1", styles["Normal"])
|
|
|
|
Story.append(p)
|
|
|
|
p = Paragraph("Blubber2", styles["Normal"])
|
|
|
|
Story.append(p)
|
|
|
|
Story.append(Spacer(10, 5*cm))
|
|
|
|
p = Paragraph("Blubber3", styles["Normal"])
|
|
|
|
Story.append(p)
|
|
|
|
#doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages)
|
|
|
|
doc.build(Story)
|
|
|
|
|
2011-11-18 23:42:05 +01:00
|
|
|
def main():
|
|
|
|
"""docstring for main"""
|
|
|
|
|
|
|
|
options = init_parser()
|
|
|
|
if options.version:
|
|
|
|
print "Version: {0}".format(__version__,)
|
|
|
|
sys.exit()
|
|
|
|
|
|
|
|
if options.file:
|
|
|
|
questions_fh = open(options.file)
|
|
|
|
else:
|
|
|
|
questions_fh = open('questions.yaml')
|
|
|
|
|
|
|
|
questions = []
|
|
|
|
for q in yaml.load_all(questions_fh.read()):
|
|
|
|
#FIXME (fb@alien8.de) 11-11-18 23:16:34 use yaml constructor
|
|
|
|
# yaml.add_constructor
|
|
|
|
Question.register_question(q)
|
2011-11-21 10:33:13 +01:00
|
|
|
if options.file:
|
|
|
|
q.media_path = os.path.abspath(os.path.dirname(options.file))
|
2011-11-18 23:42:05 +01:00
|
|
|
questions.append(q)
|
2011-11-19 00:01:04 +01:00
|
|
|
if options.debug:
|
|
|
|
print Question.registered_questions
|
2011-11-21 10:33:13 +01:00
|
|
|
print [q.media for q in questions]
|
2011-11-19 19:58:50 +01:00
|
|
|
|
|
|
|
game_rounds = sorted(Question.registered_questions.keys())
|
|
|
|
for r in game_rounds:
|
2011-11-21 12:45:42 +01:00
|
|
|
write_json_file(questions_per_round(questions, game_round=r))
|
|
|
|
if options.debug:
|
|
|
|
print "Written file for game round: {0}".format(r)
|
2011-11-19 19:58:50 +01:00
|
|
|
|
2011-12-27 15:09:00 +01:00
|
|
|
if options.pdf:
|
|
|
|
gen_pdf(questions, game_rounds)
|
|
|
|
|
2011-11-18 23:42:05 +01:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|
|
|
|
|
|
|
|
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 :
|
|
|
|
|