From 5722eb825e11c817209e0ce4b29e8acfcb8b1e3d Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 11 Jan 2022 23:54:46 -0800 Subject: [PATCH] Convert all files from DOS to Unix text file format Since otherwise I'm going to be strufflign when editing these files. --- html_generator.py | 876 ++++++++++++++++++++-------------------- individual.css | 132 +++---- overview.css | 188 ++++----- sorttable.js | 988 +++++++++++++++++++++++----------------------- 4 files changed, 1092 insertions(+), 1092 deletions(-) diff --git a/html_generator.py b/html_generator.py index 1ba3ac5..7d18e0a 100644 --- a/html_generator.py +++ b/html_generator.py @@ -1,438 +1,438 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Jan 6 23:35:23 2022 - -@author: Avram Gottschlich -""" -""" -I copied several functions from your code; -if it's easier to refer to them rather than copying them that's absolutely fine - -This rewrites the html each time it is called -If it's easy to call the puzzle/round functions each time they're updated, -that would be great - -Requires sorttable.js, which should be included -""" -from turbot.channel import channel_url -from boto3.dynamodb.conditions import Key - -website = "https://halibut.cworth.org/" -#change this if we're using AWS or some other subdomain instead - -def find_hunt_for_hunt_id(turb, 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( - Key={ - 'hunt_id': hunt_id, - 'SK': 'hunt-{}'.format(hunt_id) - }) - - if 'Item' in response: - return response['Item'] - else: - return None - -def hunt_puzzles_for_hunt_id(turb, hunt_id): - """Return all puzzles that belong to the given hunt_id""" - - response = turb.table.query( - KeyConditionExpression=( - Key('hunt_id').eq(hunt_id) & - Key('SK').begins_with('puzzle-') - ) - ) - - return response['Items'] - -def elink(lin, text): - #shortcutting function for creating html links - #opens in a new tab for external links - return '{}'.format(lin, text) - -def link(lin, text): - #internal links, doesn't open new tab - return '{}'.format(lin, text) - -def hunt_info(turb, hunt): - """ - Retrieves list of rounds, puzzles for the given hunt - """ - name = hunt["name"] - hunt_id = hunt["hunt_id"] - channel_id = hunt["channel_id"] - - puzzles = hunt_puzzles_for_hunt_id(turb, hunt_id) - - rounds = set() - for puzzle in puzzles: - if "rounds" not in puzzle: - continue - for rnd in puzzle["rounds"]: - rounds.add(rnd) - rounds = list(rounds) - rounds.sort() - - return puzzles, rounds - -def round_stat(rnd, puzzles): - #Counts puzzles, solved, list of puzzles for a given round - puzzle_count = 0 - solved_count = 0 - solved_puzzles = [] - unsolved_puzzles = [] - metas = [] - meta_solved = 0 - for puzzle in puzzles: - if "rounds" not in puzzle: - continue - if rnd in puzzle["rounds"]: - if puzzle['type'] == 'meta': - metas.append(puzzle) - if puzzle['status'] == 'solved': - meta_solved += 1 - else: - puzzle_count += 1 - if puzzle['status'] == 'solved': - solved_count += 1 - solved_puzzles.append(puzzle) - else: - unsolved_puzzles.append(puzzle) - solved_puzzles = sorted(solved_puzzles, key = lambda i: i['name']) - unsolved_puzzles = sorted(unsolved_puzzles, key = lambda i: i['name']) - rnd_puzzles = metas + unsolved_puzzles + solved_puzzles - return puzzle_count, solved_count, rnd_puzzles, meta_solved, len(metas) - - - -def overview(puzzles, rounds): - #big board, main page. saves as index.html - start = ['\n', - '\n', - '\n', - ' \n', - ' \n', - '\n', - ' \n', - ' \n', - '\n', - ' Hunt Overview\n', - ' \n' - '\n', - '
\n' - ' Hunt Overview' - ' All Puzzles\n' - ' Unsolved\n' - ' Solved\n' - '
\n' - '\n',] - columns = ['
\n'] - expanding = [] - i = 1 - for rnd in rounds: - puzzle_count, solved_count, rnd_puzzles, meta_solved, metas = round_stat(rnd, puzzles) - if metas == meta_solved and metas > 0: - status = 'solved' - else: - status = 'unsolved' - columns += ['
\n'.format(status, i), - '

{}

\n'.format(rnd), - '

Puzzles: {}/{}

\n'.format(solved_count, puzzle_count), - '

Metas: {}/{}

\n'.format(meta_solved, metas), - '
\n'] - - expanding += [ - ' \n') - i += 1 - columns.append('
\n') - end = ['\n', '\n'] - html = start + expanding + columns + end - file = "index.html" - f = open(file, "w") - for line in html: - f.write(line) - f.close() - return None - -def round_overview(rnd, puzzles): - #inputs: round name, puzzles - #round overview page - #saves as (round name)_round.html, in case meta/round share names. - #underscores replace spaces for links. - rnd_puzzles, meta_solved, metas = round_stat(rnd, puzzles)[2:] - if meta_solved == metas and metas > 0: - status = 'solved' - else: - status = 'unsolved' - start = ['\n', - ' \n', - ' \n', - ' Mystery Hunt 2022\n', - ' \n', - ' \n', - ' \n'.format(status), - '

{}

\n'.format(rnd), - '

{}

\n'.format(link(website + "index.html", 'Hunt Overview')), - '
\n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - #' \n', - ' \n', - ' \n', - ' \n', - ' \n'] - puzzle_list = [] - for puzzle in rnd_puzzles: - if puzzle['type'] == 'meta': - meta = ' [META]' - else: - meta = '' - slack_url = channel_url(puzzle['channel_id']) - - 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['sheet_url'], 'Sheet')), - ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), - ' \n'.format(puzzle['solution']), - # ' \n', - ' \n'.format("".join(puzzle['tags'])), - ' \n'] - else: - puzzle_list += [ ' \n', - ' \n'.format(elink(slack_url, puzzle['name']+meta)), - ' \n'.format(elink(puzzle['url'], 'Puzzle')), - ' \n'.format(elink(puzzle['sheet_url'], 'Sheet')), - ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), - ' \n', - # ' \n', - ' \n'.format(" ".join(puzzle['tags'])), - ' \n'] - end = [' \n', - '
Puzzle Title/SlackPuzzle LinkSheetOverviewAnswerExtra LinksTags
{}{}{}{}{}{}
{}{}{}{}{}
\n', - '
\n', - ' \n', - '\n'] - html = start + puzzle_list + end - file = "{}_round.html".format('_'.join(rnd.split())) - f = open(file, "w") - for line in html: - f.write(line) - f.close() - return None - -def puzzle_overview(puzzle): - #overview page for individual puzzles. saves as (name of puzzle).html, - #with underscores rather than spaces - name = puzzle['name'] - if puzzle['type'] == 'meta': - meta = ' [META]' - 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 puzzle['status'] == 'solved': - solution = puzzle['solution'] - status = 'solved' - else: - solution = "" - status = 'unsolved' - html = ['\n', - '\n', - '\n', - ' \n', - ' \n', - ' \n', - ' {}\n'.format(name+meta), - '

{}

'.format(link(website + 'index.html', 'Hunt Overview')), - '\n', - '\n', - '\n'.format(status), - '

{}

\n'.format(name+meta), - '
\n', - ' \n', - ' \n', - ' \n'.format(elink(slack_url, 'Channel')), #slack channel - ' \n'.format(elink(puzzle['url'], 'Puzzle')), - ' \n'.format(elink(puzzle['sheet_url'], 'Sheet')), - ' \n', - ' \n', - '
{}{}{}Additional Resources
\n', - ' \n', - ' \n', - ' \n'.format(" ".join(round_url)), #round page on our site - ' \n'.format(" ".join(puzzle['tags'])), #add tags - ' \n', - ' \n', - ' \n'.format(solution), - ' \n'.format(puzzle['status']), - ' \n', - '
Round(s): {}Tags: {}
Answer: {}State: {}
\n', - '
\n', - '\n', - '\n', - '\n'] - underscored = "_".join(name.split()) - file = "{}.html".format(underscored) - f = open(file, "w") - for line in html: - f.write(line) - return None - -def puzzle_lists(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 - solved_puzzles = [puzzle for puzzle in puzzles if puzzle['status'] == 'solved'] - unsolved_puzzles = [puzzle for puzzle in puzzles if puzzle['status'] != 'solved'] - start = ['\n', - ' \n', - ' \n', - ' Mystery Hunt 2022\n', - ' \n', - ' \n', - '
\n' - ' Hunt Overview' - ' All Puzzles\n' - ' Unsolved\n' - ' Solved\n' - '
\n' - ' \n', - '

{}

\n'.format('{} Puzzles').format(filt), - '

{}

\n'.format(link(website + "index.html", 'Hunt Overview')), - '
\n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n', - ' \n'] - solved_code = [] - unsolved_code = [] - for puzzle in solved_puzzles: - if puzzle['type'] == 'meta': - meta = ' [META]' - else: - meta = '' - slack_url = channel_url(puzzle['channel_id']) - round_url = link(website + "_".join(rnd.split()) + "_round.html", puzzle['rounds'][0]) - #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['sheet_url'], 'Sheet')), - ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), - ' \n'.format(puzzle['solution']), - ' \n'.format(round_url), - ' \n'.format("".join(puzzle['tags'])), - ' \n'] - for puzzle in unsolved_puzzles: - if puzzle['type'] == 'meta': - meta = ' [META]' - else: - meta = '' - slack_url = channel_url(puzzle['channel_id']) - round_url = link(website + "_".join(rnd.split()) + "_round.html", puzzle['rounds'][0]) - #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['sheet_url'], 'Sheet')), - ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), - ' \n', - ' \n'.format(round_url), - ' \n'.format("".join(puzzle['tags'])), - ' \n'] - end = [' \n', - '
Puzzle Title/SlackPuzzle LinkSheetOverviewAnswerRound(s)Tags
{}{}{}{}{}{}{}
{}{}{}{}{}{}
\n', - '
\n', - ' \n', - '\n'] - if filt == "All": - file1 = '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' - f = open(file2, 'w') - for line in start + solved_code + end: - f.write(line) - f.close() - elif filt == "Unsolved": - file3 = 'unsolved.html' - f = open(file3, 'w') - for line in start + unsolved_code + end: - f.write(line) - f.close() - return None - - - -puzzles, rounds = hunt_info(turb, hunt) -#I am not sure where these come from -overview(puzzles, rounds) -for rnd in rounds: - round_overview(rnd, puzzles) -for puzzle in puzzles: - puzzle_overview(puzzle) -puzzle_lists(puzzles, "All") -puzzle_lists(puzzles, "Solved") -puzzle_lists(puzzles, "Unsolved") \ No newline at end of file +# -*- coding: utf-8 -*- +""" +Created on Thu Jan 6 23:35:23 2022 + +@author: Avram Gottschlich +""" +""" +I copied several functions from your code; +if it's easier to refer to them rather than copying them that's absolutely fine + +This rewrites the html each time it is called +If it's easy to call the puzzle/round functions each time they're updated, +that would be great + +Requires sorttable.js, which should be included +""" +from turbot.channel import channel_url +from boto3.dynamodb.conditions import Key + +website = "https://halibut.cworth.org/" +#change this if we're using AWS or some other subdomain instead + +def find_hunt_for_hunt_id(turb, 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( + Key={ + 'hunt_id': hunt_id, + 'SK': 'hunt-{}'.format(hunt_id) + }) + + if 'Item' in response: + return response['Item'] + else: + return None + +def hunt_puzzles_for_hunt_id(turb, hunt_id): + """Return all puzzles that belong to the given hunt_id""" + + response = turb.table.query( + KeyConditionExpression=( + Key('hunt_id').eq(hunt_id) & + Key('SK').begins_with('puzzle-') + ) + ) + + return response['Items'] + +def elink(lin, text): + #shortcutting function for creating html links + #opens in a new tab for external links + return '{}'.format(lin, text) + +def link(lin, text): + #internal links, doesn't open new tab + return '{}'.format(lin, text) + +def hunt_info(turb, hunt): + """ + Retrieves list of rounds, puzzles for the given hunt + """ + name = hunt["name"] + hunt_id = hunt["hunt_id"] + channel_id = hunt["channel_id"] + + puzzles = hunt_puzzles_for_hunt_id(turb, hunt_id) + + rounds = set() + for puzzle in puzzles: + if "rounds" not in puzzle: + continue + for rnd in puzzle["rounds"]: + rounds.add(rnd) + rounds = list(rounds) + rounds.sort() + + return puzzles, rounds + +def round_stat(rnd, puzzles): + #Counts puzzles, solved, list of puzzles for a given round + puzzle_count = 0 + solved_count = 0 + solved_puzzles = [] + unsolved_puzzles = [] + metas = [] + meta_solved = 0 + for puzzle in puzzles: + if "rounds" not in puzzle: + continue + if rnd in puzzle["rounds"]: + if puzzle['type'] == 'meta': + metas.append(puzzle) + if puzzle['status'] == 'solved': + meta_solved += 1 + else: + puzzle_count += 1 + if puzzle['status'] == 'solved': + solved_count += 1 + solved_puzzles.append(puzzle) + else: + unsolved_puzzles.append(puzzle) + solved_puzzles = sorted(solved_puzzles, key = lambda i: i['name']) + unsolved_puzzles = sorted(unsolved_puzzles, key = lambda i: i['name']) + rnd_puzzles = metas + unsolved_puzzles + solved_puzzles + return puzzle_count, solved_count, rnd_puzzles, meta_solved, len(metas) + + + +def overview(puzzles, rounds): + #big board, main page. saves as index.html + start = ['\n', + '\n', + '\n', + ' \n', + ' \n', + '\n', + ' \n', + ' \n', + '\n', + ' Hunt Overview\n', + ' \n' + '\n', + '
\n' + ' Hunt Overview' + ' All Puzzles\n' + ' Unsolved\n' + ' Solved\n' + '
\n' + '\n',] + columns = ['
\n'] + expanding = [] + i = 1 + for rnd in rounds: + puzzle_count, solved_count, rnd_puzzles, meta_solved, metas = round_stat(rnd, puzzles) + if metas == meta_solved and metas > 0: + status = 'solved' + else: + status = 'unsolved' + columns += ['
\n'.format(status, i), + '

{}

\n'.format(rnd), + '

Puzzles: {}/{}

\n'.format(solved_count, puzzle_count), + '

Metas: {}/{}

\n'.format(meta_solved, metas), + '
\n'] + + expanding += [ + ' \n') + i += 1 + columns.append('
\n') + end = ['\n', '\n'] + html = start + expanding + columns + end + file = "index.html" + f = open(file, "w") + for line in html: + f.write(line) + f.close() + return None + +def round_overview(rnd, puzzles): + #inputs: round name, puzzles + #round overview page + #saves as (round name)_round.html, in case meta/round share names. + #underscores replace spaces for links. + rnd_puzzles, meta_solved, metas = round_stat(rnd, puzzles)[2:] + if meta_solved == metas and metas > 0: + status = 'solved' + else: + status = 'unsolved' + start = ['\n', + ' \n', + ' \n', + ' Mystery Hunt 2022\n', + ' \n', + ' \n', + ' \n'.format(status), + '

{}

\n'.format(rnd), + '

{}

\n'.format(link(website + "index.html", 'Hunt Overview')), + '
\n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + #' \n', + ' \n', + ' \n', + ' \n', + ' \n'] + puzzle_list = [] + for puzzle in rnd_puzzles: + if puzzle['type'] == 'meta': + meta = ' [META]' + else: + meta = '' + slack_url = channel_url(puzzle['channel_id']) + + 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['sheet_url'], 'Sheet')), + ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), + ' \n'.format(puzzle['solution']), + # ' \n', + ' \n'.format("".join(puzzle['tags'])), + ' \n'] + else: + puzzle_list += [ ' \n', + ' \n'.format(elink(slack_url, puzzle['name']+meta)), + ' \n'.format(elink(puzzle['url'], 'Puzzle')), + ' \n'.format(elink(puzzle['sheet_url'], 'Sheet')), + ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), + ' \n', + # ' \n', + ' \n'.format(" ".join(puzzle['tags'])), + ' \n'] + end = [' \n', + '
Puzzle Title/SlackPuzzle LinkSheetOverviewAnswerExtra LinksTags
{}{}{}{}{}{}
{}{}{}{}{}
\n', + '
\n', + ' \n', + '\n'] + html = start + puzzle_list + end + file = "{}_round.html".format('_'.join(rnd.split())) + f = open(file, "w") + for line in html: + f.write(line) + f.close() + return None + +def puzzle_overview(puzzle): + #overview page for individual puzzles. saves as (name of puzzle).html, + #with underscores rather than spaces + name = puzzle['name'] + if puzzle['type'] == 'meta': + meta = ' [META]' + 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 puzzle['status'] == 'solved': + solution = puzzle['solution'] + status = 'solved' + else: + solution = "" + status = 'unsolved' + html = ['\n', + '\n', + '\n', + ' \n', + ' \n', + ' \n', + ' {}\n'.format(name+meta), + '

{}

'.format(link(website + 'index.html', 'Hunt Overview')), + '\n', + '\n', + '\n'.format(status), + '

{}

\n'.format(name+meta), + '
\n', + ' \n', + ' \n', + ' \n'.format(elink(slack_url, 'Channel')), #slack channel + ' \n'.format(elink(puzzle['url'], 'Puzzle')), + ' \n'.format(elink(puzzle['sheet_url'], 'Sheet')), + ' \n', + ' \n', + '
{}{}{}Additional Resources
\n', + ' \n', + ' \n', + ' \n'.format(" ".join(round_url)), #round page on our site + ' \n'.format(" ".join(puzzle['tags'])), #add tags + ' \n', + ' \n', + ' \n'.format(solution), + ' \n'.format(puzzle['status']), + ' \n', + '
Round(s): {}Tags: {}
Answer: {}State: {}
\n', + '
\n', + '\n', + '\n', + '\n'] + underscored = "_".join(name.split()) + file = "{}.html".format(underscored) + f = open(file, "w") + for line in html: + f.write(line) + return None + +def puzzle_lists(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 + solved_puzzles = [puzzle for puzzle in puzzles if puzzle['status'] == 'solved'] + unsolved_puzzles = [puzzle for puzzle in puzzles if puzzle['status'] != 'solved'] + start = ['\n', + ' \n', + ' \n', + ' Mystery Hunt 2022\n', + ' \n', + ' \n', + '
\n' + ' Hunt Overview' + ' All Puzzles\n' + ' Unsolved\n' + ' Solved\n' + '
\n' + ' \n', + '

{}

\n'.format('{} Puzzles').format(filt), + '

{}

\n'.format(link(website + "index.html", 'Hunt Overview')), + '
\n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n', + ' \n'] + solved_code = [] + unsolved_code = [] + for puzzle in solved_puzzles: + if puzzle['type'] == 'meta': + meta = ' [META]' + else: + meta = '' + slack_url = channel_url(puzzle['channel_id']) + round_url = link(website + "_".join(rnd.split()) + "_round.html", puzzle['rounds'][0]) + #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['sheet_url'], 'Sheet')), + ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), + ' \n'.format(puzzle['solution']), + ' \n'.format(round_url), + ' \n'.format("".join(puzzle['tags'])), + ' \n'] + for puzzle in unsolved_puzzles: + if puzzle['type'] == 'meta': + meta = ' [META]' + else: + meta = '' + slack_url = channel_url(puzzle['channel_id']) + round_url = link(website + "_".join(rnd.split()) + "_round.html", puzzle['rounds'][0]) + #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['sheet_url'], 'Sheet')), + ' \n'.format(link(website + "_".join(puzzle['name'].split()) + '.html', 'Overview')), + ' \n', + ' \n'.format(round_url), + ' \n'.format("".join(puzzle['tags'])), + ' \n'] + end = [' \n', + '
Puzzle Title/SlackPuzzle LinkSheetOverviewAnswerRound(s)Tags
{}{}{}{}{}{}{}
{}{}{}{}{}{}
\n', + '
\n', + ' \n', + '\n'] + if filt == "All": + file1 = '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' + f = open(file2, 'w') + for line in start + solved_code + end: + f.write(line) + f.close() + elif filt == "Unsolved": + file3 = 'unsolved.html' + f = open(file3, 'w') + for line in start + unsolved_code + end: + f.write(line) + f.close() + return None + + + +puzzles, rounds = hunt_info(turb, hunt) +#I am not sure where these come from +overview(puzzles, rounds) +for rnd in rounds: + round_overview(rnd, puzzles) +for puzzle in puzzles: + puzzle_overview(puzzle) +puzzle_lists(puzzles, "All") +puzzle_lists(puzzles, "Solved") +puzzle_lists(puzzles, "Unsolved") diff --git a/individual.css b/individual.css index b525932..f9ef4d2 100644 --- a/individual.css +++ b/individual.css @@ -1,66 +1,66 @@ -.solved { - background-color: chartreuse; -} - -.unsolved { - background-color: orange; -} - -table { - border-collapse: collapse; - margin: 30px; - text-align: center; - - -} - -table td { - border: 1px solid black; - padding: 20px; - font-size: 150%; -} - - -h1 { - text-align: center; - font-size: 300%; -} - -.center { - margin-left: auto; - margin-right: auto; -} - -.row { - margin-left: -5px; - margin-right: -5px; -} - -.column{ - float: left; - width: 50%; - padding: 5px; -} - -/* Clearfix (clear floats) */ -.row::after { - content: ""; - clear: both; - display: table; -} - -.grid-container { - display: grid; - grid-template-columns: auto auto auto; - grid-gap: 10px; - background-color: #2196F3; - padding: 10px; -} - -.grid-container > div { - background-color: rgba(255, 255, 255, 0.8); - text-align: center; - padding: 20px 0; - font-size: 30px; -} - +.solved { + background-color: chartreuse; +} + +.unsolved { + background-color: orange; +} + +table { + border-collapse: collapse; + margin: 30px; + text-align: center; + + +} + +table td { + border: 1px solid black; + padding: 20px; + font-size: 150%; +} + + +h1 { + text-align: center; + font-size: 300%; +} + +.center { + margin-left: auto; + margin-right: auto; +} + +.row { + margin-left: -5px; + margin-right: -5px; +} + +.column{ + float: left; + width: 50%; + padding: 5px; +} + +/* Clearfix (clear floats) */ +.row::after { + content: ""; + clear: both; + display: table; +} + +.grid-container { + display: grid; + grid-template-columns: auto auto auto; + grid-gap: 10px; + background-color: #2196F3; + padding: 10px; +} + +.grid-container > div { + background-color: rgba(255, 255, 255, 0.8); + text-align: center; + padding: 20px 0; + font-size: 30px; +} + diff --git a/overview.css b/overview.css index b3061da..8426fb2 100644 --- a/overview.css +++ b/overview.css @@ -1,94 +1,94 @@ -/* The grid: Three equal columns that floats next to each other */ -.column { - float: left; - width: 15%; - padding: 25px; - text-align: center; - font-size: 20px; - cursor: pointer; - color: black; - border-radius: 25px; - margin: 25px; -} - -.containerTab { - padding: 20px; - color: black; - border-radius: 25px; - margin: auto; - width: 20%; - - -} - -/* Clear floats after the columns */ -.row:after { - content: ""; - display: table; - clear: both; -} - -/* Closable button inside the image */ -.closebtn { - float: right; - color: black; - font-size: 35px; - cursor: pointer; -} - -table { - color: black; - padding: 5px; -} - -th, td { - padding: 5px; -} - -.solved { - background-color: chartreuse; -} - -.unsolved { - background-color: orange; -} - -/* The sidebar menu */ -.sidenav { - height: 100%; /* Full-height: remove this if you want "auto" height */ - width: 160px; /* Set the width of the sidebar */ - position: fixed; /* Fixed Sidebar (stay in place on scroll) */ - z-index: 1; /* Stay on top */ - top: 0; /* Stay at the top */ - left: 0; - background-color: #111; /* Black */ - overflow-x: hidden; /* Disable horizontal scroll */ - padding-top: 20px; -} - -/* The navigation menu links */ -.sidenav a { - padding: 6px 8px 6px 16px; - text-decoration: none; - font-size: 25px; - color: #818181; - display: block; -} - -/* When you mouse over the navigation links, change their color */ -.sidenav a:hover { - color: #f1f1f1; -} - -/* Style page content */ -body { - margin-left: 160px; /* Same as the width of the sidebar */ - padding: 0px 10px; - background-color: #BBB; -} - -/* On smaller screens, where height is less than 450px, change the style of the sidebar (less padding and a smaller font size) */ -@media screen and (max-height: 450px) { - .sidenav {padding-top: 15px;} - .sidenav a {font-size: 18px;} -} +/* The grid: Three equal columns that floats next to each other */ +.column { + float: left; + width: 15%; + padding: 25px; + text-align: center; + font-size: 20px; + cursor: pointer; + color: black; + border-radius: 25px; + margin: 25px; +} + +.containerTab { + padding: 20px; + color: black; + border-radius: 25px; + margin: auto; + width: 20%; + + +} + +/* Clear floats after the columns */ +.row:after { + content: ""; + display: table; + clear: both; +} + +/* Closable button inside the image */ +.closebtn { + float: right; + color: black; + font-size: 35px; + cursor: pointer; +} + +table { + color: black; + padding: 5px; +} + +th, td { + padding: 5px; +} + +.solved { + background-color: chartreuse; +} + +.unsolved { + background-color: orange; +} + +/* The sidebar menu */ +.sidenav { + height: 100%; /* Full-height: remove this if you want "auto" height */ + width: 160px; /* Set the width of the sidebar */ + position: fixed; /* Fixed Sidebar (stay in place on scroll) */ + z-index: 1; /* Stay on top */ + top: 0; /* Stay at the top */ + left: 0; + background-color: #111; /* Black */ + overflow-x: hidden; /* Disable horizontal scroll */ + padding-top: 20px; +} + +/* The navigation menu links */ +.sidenav a { + padding: 6px 8px 6px 16px; + text-decoration: none; + font-size: 25px; + color: #818181; + display: block; +} + +/* When you mouse over the navigation links, change their color */ +.sidenav a:hover { + color: #f1f1f1; +} + +/* Style page content */ +body { + margin-left: 160px; /* Same as the width of the sidebar */ + padding: 0px 10px; + background-color: #BBB; +} + +/* On smaller screens, where height is less than 450px, change the style of the sidebar (less padding and a smaller font size) */ +@media screen and (max-height: 450px) { + .sidenav {padding-top: 15px;} + .sidenav a {font-size: 18px;} +} diff --git a/sorttable.js b/sorttable.js index d75609f..029ebfe 100644 --- a/sorttable.js +++ b/sorttable.js @@ -1,494 +1,494 @@ -/* - SortTable - version 2 - 7th April 2007 - Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ - - Instructions: - Download this file - Add to your HTML - Add class="sortable" to any table you'd like to make sortable - Click on the headers to sort - - Thanks to many, many people for contributions and suggestions. - Licenced as X11: http://www.kryogenix.org/code/browser/licence.html - This basically means: do what you want with it. -*/ - - -var stIsIE = /*@cc_on!@*/false; - -sorttable = { - init: function() { - // quit if this function has already been called - if (arguments.callee.done) return; - // flag this function so we don't do the same thing twice - arguments.callee.done = true; - // kill the timer - if (_timer) clearInterval(_timer); - - if (!document.createElement || !document.getElementsByTagName) return; - - sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; - - forEach(document.getElementsByTagName('table'), function(table) { - if (table.className.search(/\bsortable\b/) != -1) { - sorttable.makeSortable(table); - } - }); - - }, - - makeSortable: function(table) { - if (table.getElementsByTagName('thead').length == 0) { - // table doesn't have a tHead. Since it should have, create one and - // put the first table row in it. - the = document.createElement('thead'); - the.appendChild(table.rows[0]); - table.insertBefore(the,table.firstChild); - } - // Safari doesn't support table.tHead, sigh - if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; - - if (table.tHead.rows.length != 1) return; // can't cope with two header rows - - // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as - // "total" rows, for example). This is B&R, since what you're supposed - // to do is put them in a tfoot. So, if there are sortbottom rows, - // for backwards compatibility, move them to tfoot (creating it if needed). - sortbottomrows = []; - for (var i=0; i5' : ' ▴'; - this.appendChild(sortrevind); - return; - } - if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { - // if we're already sorted by this column in reverse, just - // re-reverse the table, which is quicker - sorttable.reverse(this.sorttable_tbody); - this.className = this.className.replace('sorttable_sorted_reverse', - 'sorttable_sorted'); - this.removeChild(document.getElementById('sorttable_sortrevind')); - sortfwdind = document.createElement('span'); - sortfwdind.id = "sorttable_sortfwdind"; - sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; - this.appendChild(sortfwdind); - return; - } - - // remove sorttable_sorted classes - theadrow = this.parentNode; - forEach(theadrow.childNodes, function(cell) { - if (cell.nodeType == 1) { // an element - cell.className = cell.className.replace('sorttable_sorted_reverse',''); - cell.className = cell.className.replace('sorttable_sorted',''); - } - }); - sortfwdind = document.getElementById('sorttable_sortfwdind'); - if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } - sortrevind = document.getElementById('sorttable_sortrevind'); - if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } - - this.className += ' sorttable_sorted'; - sortfwdind = document.createElement('span'); - sortfwdind.id = "sorttable_sortfwdind"; - sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; - this.appendChild(sortfwdind); - - // build an array to sort. This is a Schwartzian transform thing, - // i.e., we "decorate" each row with the actual sort key, - // sort based on the sort keys, and then put the rows back in order - // which is a lot faster because you only do getInnerText once per row - row_array = []; - col = this.sorttable_columnindex; - rows = this.sorttable_tbody.rows; - for (var j=0; j 12) { - // definitely dd/mm - return sorttable.sort_ddmm; - } else if (second > 12) { - return sorttable.sort_mmdd; - } else { - // looks like a date, but we can't tell which, so assume - // that it's dd/mm (English imperialism!) and keep looking - sortfn = sorttable.sort_ddmm; - } - } - } - } - return sortfn; - }, - - getInnerText: function(node) { - // gets the text we want to use for sorting for a cell. - // strips leading and trailing whitespace. - // this is *not* a generic getInnerText function; it's special to sorttable. - // for example, you can override the cell text with a customkey attribute. - // it also gets .value for fields. - - if (!node) return ""; - - hasInputs = (typeof node.getElementsByTagName == 'function') && - node.getElementsByTagName('input').length; - - if (node.getAttribute("sorttable_customkey") != null) { - return node.getAttribute("sorttable_customkey"); - } - else if (typeof node.textContent != 'undefined' && !hasInputs) { - return node.textContent.replace(/^\s+|\s+$/g, ''); - } - else if (typeof node.innerText != 'undefined' && !hasInputs) { - return node.innerText.replace(/^\s+|\s+$/g, ''); - } - else if (typeof node.text != 'undefined' && !hasInputs) { - return node.text.replace(/^\s+|\s+$/g, ''); - } - else { - switch (node.nodeType) { - case 3: - if (node.nodeName.toLowerCase() == 'input') { - return node.value.replace(/^\s+|\s+$/g, ''); - } - case 4: - return node.nodeValue.replace(/^\s+|\s+$/g, ''); - break; - case 1: - case 11: - var innerText = ''; - for (var i = 0; i < node.childNodes.length; i++) { - innerText += sorttable.getInnerText(node.childNodes[i]); - } - return innerText.replace(/^\s+|\s+$/g, ''); - break; - default: - return ''; - } - } - }, - - reverse: function(tbody) { - // reverse the rows in a tbody - newrows = []; - for (var i=0; i=0; i--) { - tbody.appendChild(newrows[i]); - } - delete newrows; - }, - - /* sort functions - each sort function takes two parameters, a and b - you are comparing a[0] and b[0] */ - sort_numeric: function(a,b) { - aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); - if (isNaN(aa)) aa = 0; - bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); - if (isNaN(bb)) bb = 0; - return aa-bb; - }, - sort_alpha: function(a,b) { - if (a[0]==b[0]) return 0; - if (a[0] 0 ) { - var q = list[i]; list[i] = list[i+1]; list[i+1] = q; - swap = true; - } - } // for - t--; - - if (!swap) break; - - for(var i = t; i > b; --i) { - if ( comp_func(list[i], list[i-1]) < 0 ) { - var q = list[i]; list[i] = list[i-1]; list[i-1] = q; - swap = true; - } - } // for - b++; - - } // while(swap) - } -} - -/* ****************************************************************** - Supporting functions: bundled here to avoid depending on a library - ****************************************************************** */ - -// Dean Edwards/Matthias Miller/John Resig - -/* for Mozilla/Opera9 */ -if (document.addEventListener) { - document.addEventListener("DOMContentLoaded", sorttable.init, false); -} - -/* for Internet Explorer */ -/*@cc_on @*/ -/*@if (@_win32) - document.write(" to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backwards compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i5' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=0; j 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for fields. + + if (!node) return ""; + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i=0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0] 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write("