From 0076b5c890b0a6cfb61dac56f94e71566c669bc2 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Mon, 4 Jan 2021 18:41:07 -0800 Subject: [PATCH] Farm blocks-creation code out to various supporting files The code will be easier to manage with separate hunt, round, puzzle, and channel.py files each responsible for maintaining their own functionality. We don't yet have any classes in any of these files, (just functions that look things up in the database and that generate Slack blocks), but we may end up doing classes later, (particularly if we start wanting methods to update a single attribute in the database for a given item, etc.). --- turbot/channel.py | 4 ++ turbot/events.py | 105 ++-------------------------------------------- turbot/hunt.py | 57 +++++++++++++++++++++++++ turbot/puzzle.py | 47 +++++++++++++++++++++ turbot/round.py | 31 ++++++++++++++ 5 files changed, 142 insertions(+), 102 deletions(-) create mode 100644 turbot/channel.py create mode 100644 turbot/round.py diff --git a/turbot/channel.py b/turbot/channel.py new file mode 100644 index 0000000..1c9d412 --- /dev/null +++ b/turbot/channel.py @@ -0,0 +1,4 @@ +def channel_url(channel_id): + """Given a channel ID, return the URL for that channel.""" + + return "https://halibutthatbass.slack.com/archives/{}".format(channel_id) diff --git a/turbot/events.py b/turbot/events.py index 70f18a6..bf4154f 100644 --- a/turbot/events.py +++ b/turbot/events.py @@ -1,9 +1,10 @@ from turbot.blocks import ( section_block, text_block, button_block, actions_block, divider_block ) +from turbot.hunt import hunt_blocks, find_hunt_for_hunt_id from turbot.sheets import sheets_create, sheets_create_for_puzzle from turbot.slack import slack_send_message, slack_channel_members -from turbot.hunt import find_hunt_for_hunt_id +from turbot.channel import channel_url from boto3.dynamodb.conditions import Key TURBOT_USER_ID = 'U01B9QM4P9R' @@ -13,106 +14,6 @@ events = {} lambda_success = {'statusCode': 200} lambda_error = {'statusCode': 400} -def channel_url(channel_id): - return "https://halibutthatbass.slack.com/archives/{}".format(channel_id) - -def puzzle_block(puzzle): - name = puzzle['name'] - status = puzzle['status'] - solution = puzzle['solution'] - channel_id = puzzle['channel_id'] - url = puzzle.get('url', None) - sheet_url = puzzle.get('sheet_url', None) - state = puzzle.get('state', None) - status_emoji = '' - solution_str = '' - - if status == 'solved': - status_emoji = ":ballot_box_with_check:" - else: - status_emoji = ":white_square:" - - if len(solution): - solution_str = "*`" + '`, `'.join(solution) + "`*" - - links = [] - if url: - links.append("<{}|Puzzle>".format(url)) - if sheet_url: - links.append("<{}|Sheet>".format(sheet_url)) - - state_str = '' - if state: - state_str = "\n{}".format(state) - - puzzle_text = "{}{} <{}|{}> ({}){}".format( - status_emoji, solution_str, - channel_url(channel_id), name, - ', '.join(links), state_str - ) - - return section_block(text_block(puzzle_text)) - -def round_blocks(round, puzzles): - - round_text = "*Round: {}*".format(round) - - blocks = [ - section_block(text_block(round_text)), - ] - - for puzzle in puzzles: - if 'rounds' not in puzzle: - continue - if round not in puzzle['rounds']: - continue - blocks.append(puzzle_block(puzzle)) - - return blocks - -def hunt_details_blocks(turb, hunt): - name = hunt['name'] - hunt_id = hunt['hunt_id'] - channel_id = hunt['channel_id'] - - response = turb.table.query( - KeyConditionExpression=( - Key('hunt_id').eq(hunt_id) & - Key('SK').begins_with('puzzle-') - ) - ) - puzzles = response['Items'] - - # Compute the set of rounds across all puzzles - rounds = set() - for puzzle in puzzles: - if 'rounds' not in puzzle: - continue - for round in puzzle['rounds']: - rounds.add(round) - - hunt_text = "*<{}|{}>*".format(channel_url(channel_id), name) - - blocks = [ - section_block(text_block(hunt_text)), - ] - - # Construct blocks for each round - for round in rounds: - blocks += round_blocks(round, puzzles) - - # Also blocks for any puzzles not in any round - stray_puzzles = [puzzle for puzzle in puzzles if 'rounds' not in puzzle] - if len(stray_puzzles): - stray_text = "*Puzzles with no asigned round*" - blocks.append(section_block(text_block(stray_text))) - for puzzle in stray_puzzles: - blocks.append(puzzle_block(puzzle)) - - blocks.append(divider_block()) - - return blocks - def hunt_link_block(turb, hunt): name = hunt['name'] @@ -144,7 +45,7 @@ def home(turb, user_id): continue if user_id in slack_channel_members(turb.slack_client, hunt['channel_id']): - my_hunt_blocks += hunt_details_blocks(turb, hunt) + my_hunt_blocks += hunt_blocks(turb, hunt) else: available_hunt_blocks.append(hunt_link_block(turb, hunt)) diff --git a/turbot/hunt.py b/turbot/hunt.py index 87e47fa..77c1797 100644 --- a/turbot/hunt.py +++ b/turbot/hunt.py @@ -1,3 +1,9 @@ +from turbot.blocks import section_block, text_block, divider_block +from turbot.round import round_blocks +from turbot.puzzle import puzzle_block +from turbot.channel import channel_url +from boto3.dynamodb.conditions import Key + def find_hunt_for_hunt_id(turb, hunt_id): """Given a hunt ID find the database item for that hunt @@ -16,3 +22,54 @@ def find_hunt_for_hunt_id(turb, hunt_id): return response['Item'] else: return None + +def hunt_blocks(turb, hunt): + """Generate Slack blocks for a hunt + + The hunt argument should be a dictionary as returned from the + database. The return value can be used in a Slack command + expecting blocks to provide all the details of a hunt, (puzzles, + their state, solution, links to channels and sheets, etc.). + """ + + name = hunt['name'] + hunt_id = hunt['hunt_id'] + channel_id = hunt['channel_id'] + + response = turb.table.query( + KeyConditionExpression=( + Key('hunt_id').eq(hunt_id) & + Key('SK').begins_with('puzzle-') + ) + ) + puzzles = response['Items'] + + # Compute the set of rounds across all puzzles + rounds = set() + for puzzle in puzzles: + if 'rounds' not in puzzle: + continue + for round in puzzle['rounds']: + rounds.add(round) + + hunt_text = "*<{}|{}>*".format(channel_url(channel_id), name) + + blocks = [ + section_block(text_block(hunt_text)), + ] + + # Construct blocks for each round + for round in rounds: + blocks += round_blocks(round, puzzles) + + # Also blocks for any puzzles not in any round + stray_puzzles = [puzzle for puzzle in puzzles if 'rounds' not in puzzle] + if len(stray_puzzles): + stray_text = "*Puzzles with no asigned round*" + blocks.append(section_block(text_block(stray_text))) + for puzzle in stray_puzzles: + blocks.append(puzzle_block(puzzle)) + + blocks.append(divider_block()) + + return blocks diff --git a/turbot/puzzle.py b/turbot/puzzle.py index 9f9b1c6..810c97e 100644 --- a/turbot/puzzle.py +++ b/turbot/puzzle.py @@ -1,3 +1,5 @@ +from turbot.blocks import section_block, text_block +from turbot.channel import channel_url from boto3.dynamodb.conditions import Key def find_puzzle_for_url(turb, hunt_id, url): @@ -20,3 +22,48 @@ def find_puzzle_for_url(turb, hunt_id, url): return None return response['Items'][0] + +def puzzle_block(puzzle): + """Generate Slack blocks for a puzzle + + The puzzle argument should be a dictionary as returned from the + database. The return value can be used in a Slack command + expecting blocks to provide all the details of a puzzle, (its + state, solution, links to channel and sheet, etc.). + """ + + name = puzzle['name'] + status = puzzle['status'] + solution = puzzle['solution'] + channel_id = puzzle['channel_id'] + url = puzzle.get('url', None) + sheet_url = puzzle.get('sheet_url', None) + state = puzzle.get('state', None) + status_emoji = '' + solution_str = '' + + if status == 'solved': + status_emoji = ":ballot_box_with_check:" + else: + status_emoji = ":white_square:" + + if len(solution): + solution_str = "*`" + '`, `'.join(solution) + "`*" + + links = [] + if url: + links.append("<{}|Puzzle>".format(url)) + if sheet_url: + links.append("<{}|Sheet>".format(sheet_url)) + + state_str = '' + if state: + state_str = "\n{}".format(state) + + puzzle_text = "{}{} <{}|{}> ({}){}".format( + status_emoji, solution_str, + channel_url(channel_id), name, + ', '.join(links), state_str + ) + + return section_block(text_block(puzzle_text)) diff --git a/turbot/round.py b/turbot/round.py new file mode 100644 index 0000000..3c4042b --- /dev/null +++ b/turbot/round.py @@ -0,0 +1,31 @@ +from turbot.puzzle import puzzle_block +from turbot.blocks import section_block, text_block + +def round_blocks(round, puzzles): + """Generate Slack blocks for a round + + The 'round' argument should be the name of a round as it appears + in the datbase. The `puzzles` argument should be a list of + puzzles. The results will include only puzzles which have 'round' + in their 'rounds' attribute. + + The return value is a list of bocks that can be used in any Slack + command acceepting blocks. It will provide all the details of the + puzzles in the given round, (their state, solutions, links to + channels and sheets, etc.). + """ + + round_text = "*Round: {}*".format(round) + + blocks = [ + section_block(text_block(round_text)), + ] + + for puzzle in puzzles: + if 'rounds' not in puzzle: + continue + if round not in puzzle['rounds']: + continue + blocks.append(puzzle_block(puzzle)) + + return blocks -- 2.43.0