X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=html_generator.py;h=43d33702f81105f996ad6161174b0a1e47b216f0;hb=f2d4648c75d7070dc9663d1acfe1a787805d7883;hp=e75f14836921015be8247fa1ea5d25a153d12b71;hpb=5eb6c0e95cb218e6aa7b7c61613e9a73d526549b;p=turbot-web diff --git a/html_generator.py b/html_generator.py index e75f148..43d3370 100644 --- a/html_generator.py +++ b/html_generator.py @@ -14,24 +14,41 @@ that would be great Requires sorttable.js, which should be included """ +import boto3 from boto3.dynamodb.conditions import Key +import os +import re + +WEBROOT = "/srv/halibut.cworth.org/www" website = "https://halibut.cworth.org/" #change this if we're using AWS or some other subdomain instead +def hunt_file(hunt, name): + """Return a path file 'name' within the given hunt. + + This will be withing WEBROOT and in a hunt-specific path.""" + + return "{}/{}/{}".format(WEBROOT, hunt['channel_id'], name) + +def filename_from_name(name): + """Returns a string derived from name, but with all spaces and slashes + replaced with underscores, (for making a clean filename)""" + return re.sub(r'[ /]', '_', name) + def channel_url(channel_id): """Given a channel ID, return the URL for that channel.""" return "https://halibutthatbass.slack.com/archives/{}".format(channel_id) -def find_hunt_for_hunt_id(turb, hunt_id): +def find_hunt_for_hunt_id(table, hunt_id): """Given a hunt ID find the database item for that hunt Returns None if hunt ID is not found, otherwise a dictionary with all fields from the hunt's row in the table, (channel_id, active, hunt_id, name, url, sheet_url, etc.). """ - response = turb.table.get_item( + response = table.get_item( Key={ 'hunt_id': hunt_id, 'SK': 'hunt-{}'.format(hunt_id) @@ -42,10 +59,10 @@ def find_hunt_for_hunt_id(turb, hunt_id): else: return None -def hunt_puzzles_for_hunt_id(turb, hunt_id): +def hunt_puzzles_for_hunt_id(table, hunt_id): """Return all puzzles that belong to the given hunt_id""" - response = turb.table.query( + response = table.query( KeyConditionExpression=( Key('hunt_id').eq(hunt_id) & Key('SK').begins_with('puzzle-') @@ -63,15 +80,17 @@ def link(lin, text): #internal links, doesn't open new tab return '{}'.format(lin, text) -def hunt_info(turb, hunt): +def hunt_info(table, hunt_id): """ Retrieves list of rounds, puzzles for the given hunt """ + + hunt = find_hunt_for_hunt_id(table, hunt_id) + name = hunt["name"] - hunt_id = hunt["hunt_id"] channel_id = hunt["channel_id"] - puzzles = hunt_puzzles_for_hunt_id(turb, hunt_id) + puzzles = hunt_puzzles_for_hunt_id(table, hunt_id) rounds = set() for puzzle in puzzles: @@ -82,7 +101,7 @@ def hunt_info(turb, hunt): rounds = list(rounds) rounds.sort() - return puzzles, rounds + return hunt, puzzles, rounds def round_stat(rnd, puzzles): #Counts puzzles, solved, list of puzzles for a given round @@ -114,7 +133,7 @@ def round_stat(rnd, puzzles): -def overview(puzzles, rounds): +def overview(hunt, puzzles, rounds): #big board, main page. saves as index.html start = ['\n', '\n', @@ -122,7 +141,7 @@ def overview(puzzles, rounds): ' \n', ' \n', '\n', - ' \n', + ' \n', ' \n', '\n', ' Hunt Overview\n', - ' \n' + ' \n' '\n', '
\n' ' Hunt Overview' @@ -163,7 +182,7 @@ def overview(puzzles, rounds): expanding += [ ' \n') end = ['\n', '\n'] html = start + expanding + columns + end - file = "index.html" + file = hunt_file(hunt, "index.html") f = open(file, "w") for line in html: f.write(line) f.close() return None -def round_overview(rnd, puzzles): +def round_overview(hunt, rnd, puzzles): #inputs: round name, puzzles #round overview page #saves as (round name)_round.html, in case meta/round share names. @@ -210,9 +229,9 @@ def round_overview(rnd, puzzles): status = 'unsolved' start = ['\n', ' \n', - ' \n', + ' \n', ' Mystery Hunt 2022\n', - ' \n', + ' \n', ' \n', ' \n'.format(status), '

{}

\n'.format(rnd), @@ -242,22 +261,22 @@ def round_overview(rnd, puzzles): if puzzle['status'] == 'solved': puzzle_list += [ ' \n', ' {}\n'.format(elink(slack_url, puzzle['name']+meta)), - ' {}\n'.format(elink(puzzle['url'], 'Puzzle')), + ' {}\n'.format(elink(puzzle.get('url',''), 'Puzzle')), ' {}\n'.format(elink(puzzle['sheet_url'], 'Sheet')), - ' {}\n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), + ' {}\n'.format(link(website + filename_from_name(puzzle['name']) + '.html', 'Overview')), ' {}\n'.format(puzzle['solution']), # ' \n', - ' {}\n'.format("".join(puzzle['tags'])), + ' {}\n'.format("".join(puzzle.get('tags',[]))), ' \n'] else: puzzle_list += [ ' \n', ' {}\n'.format(elink(slack_url, puzzle['name']+meta)), - ' {}\n'.format(elink(puzzle['url'], 'Puzzle')), + ' {}\n'.format(elink(puzzle.get('url',''), 'Puzzle')), ' {}\n'.format(elink(puzzle['sheet_url'], 'Sheet')), - ' {}\n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), + ' {}\n'.format(link(website + filename_from_name(puzzle['name']) + '.html', 'Overview')), ' \n', # ' \n', - ' {}\n'.format(" ".join(puzzle['tags'])), + ' {}\n'.format(" ".join(puzzle.get('tags',[]))), ' \n'] end = [' \n', ' \n', @@ -265,14 +284,14 @@ def round_overview(rnd, puzzles): ' \n', '\n'] html = start + puzzle_list + end - file = "{}_round.html".format('_'.join(rnd.split())) + file = hunt_file(hunt, "{}_round.html".format(filename_from_name(rnd))) f = open(file, "w") for line in html: f.write(line) f.close() return None -def puzzle_overview(puzzle): +def puzzle_overview(hunt, puzzle): #overview page for individual puzzles. saves as (name of puzzle).html, #with underscores rather than spaces name = puzzle['name'] @@ -281,7 +300,10 @@ def puzzle_overview(puzzle): else: meta = '' slack_url = channel_url(puzzle['channel_id']) - round_url = [link(website + "_".join(rnd.split()) + "_round.html", rnd) for rnd in puzzle['rounds']] + if 'rounds' in puzzle: + round_url = [link(website + filename_from_name(rnd) + "_round.html", rnd) for rnd in puzzle['rounds']] + else: + round_url = '' if puzzle['status'] == 'solved': solution = puzzle['solution'] status = 'solved' @@ -293,7 +315,7 @@ def puzzle_overview(puzzle): '\n', ' \n', ' \n', - ' \n', + ' \n', ' {}\n'.format(name+meta), '

{}

'.format(link(website + 'index.html', 'Hunt Overview')), '\n', @@ -304,7 +326,7 @@ def puzzle_overview(puzzle): ' \n', ' \n', ' \n'.format(elink(slack_url, 'Channel')), #slack channel - ' \n'.format(elink(puzzle['url'], 'Puzzle')), + ' \n'.format(elink(puzzle.get('url',''), 'Puzzle')), ' \n'.format(elink(puzzle['sheet_url'], 'Sheet')), ' \n', ' \n', @@ -312,7 +334,7 @@ def puzzle_overview(puzzle): '
{}{}{}{}Additional Resources
\n', ' \n', ' \n'.format(" ".join(round_url)), #round page on our site - ' \n'.format(" ".join(puzzle['tags'])), #add tags + ' \n'.format(" ".join(puzzle.get('tags',[]))), #add tags ' \n', ' \n', ' \n'.format(solution), @@ -323,14 +345,13 @@ def puzzle_overview(puzzle): '\n', '\n', '\n'] - underscored = "_".join(name.split()) - file = "{}.html".format(underscored) + file = hunt_file(hunt, "{}.html".format(filename_from_name(name))) f = open(file, "w") for line in html: f.write(line) return None -def puzzle_lists(puzzles, filt): +def puzzle_lists(hunt, puzzles, filt): #filt is one of "All", "Solved", "Unsolved" and spits out the appropriate list of puzzles #generates pages for all puzzles, solved puzzles, unsolved puzzles #saves as all/solved/unsolved.html, has a sidebar to link to each other and to the big board @@ -338,9 +359,9 @@ def puzzle_lists(puzzles, filt): unsolved_puzzles = [puzzle for puzzle in puzzles if puzzle['status'] != 'solved'] start = ['\n', ' \n', - ' \n', + ' \n', ' Mystery Hunt 2022\n', - ' \n', + ' \n', ' \n', '
\n' ' Hunt Overview' @@ -373,17 +394,20 @@ def puzzle_lists(puzzles, filt): else: meta = '' slack_url = channel_url(puzzle['channel_id']) - round_url = link(website + "_".join(rnd.split()) + "_round.html", puzzle['rounds'][0]) + if 'rounds' in puzzle: + round_url = link(website + filename_from_name(rnd) + "_round.html", puzzle['rounds'][0]) + else: + round_url = '' #assuming one round per puzzle for now solved_code += ['
\n', ' \n'.format(elink(slack_url, puzzle['name']+meta)), - ' \n'.format(elink(puzzle['url'], 'Puzzle')), + ' \n'.format(elink(puzzle.get('url',''), 'Puzzle')), ' \n'.format(elink(puzzle['sheet_url'], 'Sheet')), - ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), + ' \n'.format(link(website + filename_from_name(puzzle['name']) + '.html', 'Overview')), ' \n'.format(puzzle['solution']), ' \n'.format(round_url), - ' \n'.format("".join(puzzle['tags'])), + ' \n'.format("".join(puzzle.get('tags',[]))), ' \n'] for puzzle in unsolved_puzzles: if puzzle['type'] == 'meta': @@ -391,17 +415,20 @@ def puzzle_lists(puzzles, filt): else: meta = '' slack_url = channel_url(puzzle['channel_id']) - round_url = link(website + "_".join(rnd.split()) + "_round.html", puzzle['rounds'][0]) + if 'rounds' in puzzle: + round_url = link(website + filename_from_name(rnd) + "_round.html", puzzle['rounds'][0]) + else: + round_url = '' #assuming one round per puzzle for now unsolved_code += [' \n', ' \n'.format(elink(slack_url, puzzle['name']+meta)), - ' \n'.format(elink(puzzle['url'], 'Puzzle')), + ' \n'.format(elink(puzzle.get('url',''), 'Puzzle')), ' \n'.format(elink(puzzle['sheet_url'], 'Sheet')), - ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), + ' \n'.format(link(website + filename_from_name(puzzle['name']) + '.html', 'Overview')), ' \n', ' \n'.format(round_url), - ' \n'.format("".join(puzzle['tags'])), + ' \n'.format("".join(puzzle.get('tags',[]))), ' \n'] end = [' \n', '
Round(s): {}Tags: {}Tags: {}
Answer: {}
{}{}{}{}{}{}{}{}{}{}
{}{}{}{}{}{}{}{}{}
\n', @@ -409,34 +436,43 @@ def puzzle_lists(puzzles, filt): ' \n', '\n'] if filt == "All": - file1 = 'all.html' + file1 = hunt_file(hunt, 'all.html') f = open(file1, "w") for line in start + unsolved_code + solved_code + end: f.write(line) f.close() elif filt == "Solved": - file2 = 'solved.html' + file2 = hunt_file(hunt, 'solved.html') f = open(file2, 'w') for line in start + solved_code + end: f.write(line) f.close() elif filt == "Unsolved": - file3 = 'unsolved.html' + file3 = hunt_file(hunt, 'unsolved.html') f = open(file3, 'w') for line in start + unsolved_code + end: f.write(line) f.close() return None +# Initialize AWS resources to talk to database +db = boto3.resource('dynamodb') +table = db.Table("turbot") +hunt, puzzles, rounds = hunt_info(table, "mh2021") +# Create a directory for the hunt in the WEBROOT +root = hunt_file(hunt, "") +try: + os.mkdir(root) +except FileExistsError: + # We're happy as a clam if the directory already exists + pass -puzzles, rounds = hunt_info(turb, hunt) -#I am not sure where these come from -overview(puzzles, rounds) +overview(hunt, puzzles, rounds) for rnd in rounds: - round_overview(rnd, puzzles) + round_overview(hunt, rnd, puzzles) for puzzle in puzzles: - puzzle_overview(puzzle) -puzzle_lists(puzzles, "All") -puzzle_lists(puzzles, "Solved") -puzzle_lists(puzzles, "Unsolved") + puzzle_overview(hunt, puzzle) +puzzle_lists(hunt, puzzles, "All") +puzzle_lists(hunt, puzzles, "Solved") +puzzle_lists(hunt, puzzles, "Unsolved")