From: Carl Worth Date: Sat, 9 Jan 2021 10:57:07 +0000 (-0800) Subject: Sort all meta puzzles before all non-meta puzzle in puzzle lists X-Git-Url: https://git.cworth.org/git?p=turbot;a=commitdiff_plain;h=e62996312b2f0372a0ac6683affbddf1275fda4c Sort all meta puzzles before all non-meta puzzle in puzzle lists This is in the output of /hunt, /round, and the Turbot home view. --- diff --git a/turbot/interaction.py b/turbot/interaction.py index c0aff61..9e92242 100644 --- a/turbot/interaction.py +++ b/turbot/interaction.py @@ -5,10 +5,11 @@ from turbot.blocks import ( from turbot.hunt import find_hunt_for_hunt_id, hunt_blocks from turbot.puzzle import ( find_puzzle_for_url, - find_puzzle_for_puzzle_id, + find_puzzle_for_sort_key, puzzle_update_channel_and_sheet, puzzle_id_from_name, - puzzle_blocks + puzzle_blocks, + puzzle_sort_key ) import turbot.rot import turbot.sheets @@ -100,9 +101,9 @@ def edit_puzzle_button(turb, payload): response_url = payload['response_url'] trigger_id = payload['trigger_id'] - (hunt_id, puzzle_id) = action_id.split('-', 1) + (hunt_id, sort_key) = action_id.split('-', 1) - puzzle = find_puzzle_for_puzzle_id(turb, hunt_id, puzzle_id) + puzzle = find_puzzle_for_sort_key(turb, hunt_id, sort_key) if not puzzle: requests.post(response_url, @@ -265,9 +266,27 @@ def edit_puzzle_submission(turb, payload, metadata): ) # Get old puzzle from the database (to determine what's changed) - old_puzzle = find_puzzle_for_puzzle_id(turb, - puzzle['hunt_id'], - puzzle['puzzle_id']) + old_puzzle = find_puzzle_for_sort_key(turb, + puzzle['hunt_id'], + puzzle['SK']) + + # If we are changing puzzle type (meta -> plain or plain -> meta) + # the the sort key has to change, so compute the new one and delete + # the old item from the database. + # + # XXX: We should really be using a transaction here to combine the + # delete_item and the put_item into a single transaction, but + # the boto interface is annoying in that transactions are only on + # the "Client" object which has a totally different interface than + # the "Table" object I've been using so I haven't figured out how + # to do that yet. + + if puzzle['type'] != old_puzzle.get('type', 'plain'): + puzzle['SK'] = puzzle_sort_key(puzzle) + turb.table.delete_item(Key={ + 'hunt_id': old_puzzle['hunt_id'], + 'SK': old_puzzle['SK'] + }) # Update the puzzle in the database turb.table.put_item(Item=puzzle) @@ -779,7 +798,6 @@ def new_puzzle_submission(turb, payload, metadata): # Construct a puzzle dict puzzle = { "hunt_id": hunt_id, - "SK": "puzzle-{}".format(puzzle_id), "puzzle_id": puzzle_id, "channel_id": channel_id, "solution": [], @@ -792,6 +810,9 @@ def new_puzzle_submission(turb, payload, metadata): if rounds: puzzle['rounds'] = rounds + # Finally, compute the appropriate sort key + puzzle["SK"] = puzzle_sort_key(puzzle) + # Insert the newly-created puzzle into the database turb.table.put_item(Item=puzzle) diff --git a/turbot/puzzle.py b/turbot/puzzle.py index 8137de7..037dd5e 100644 --- a/turbot/puzzle.py +++ b/turbot/puzzle.py @@ -6,7 +6,7 @@ from boto3.dynamodb.conditions import Key import turbot.sheets import re -def find_puzzle_for_puzzle_id(turb, hunt_id, puzzle_id): +def find_puzzle_for_sort_key(turb, hunt_id, sort_key): """Given a hunt_id and puzzle_id, return that puzzle Returns None if no puzzle with the given hunt_id and puzzle_id @@ -17,7 +17,7 @@ def find_puzzle_for_puzzle_id(turb, hunt_id, puzzle_id): response = turb.table.get_item( Key={ 'hunt_id': hunt_id, - 'SK': 'puzzle-{}'.format(puzzle_id) + 'SK': sort_key, }) if 'Item' in response: @@ -104,14 +104,14 @@ def puzzle_blocks(puzzle, include_rounds=False): ) # Combining hunt ID and puzzle ID together here is safe because - # both IDs are restricted to not contain a hyphen, (see + # hunt_id is restricted to not contain a hyphen, (see # valid_id_re in interaction.py) - hunt_and_puzzle = "{}-{}".format(puzzle['hunt_id'], puzzle['puzzle_id']) + hunt_and_sort_key = "{}-{}".format(puzzle['hunt_id'], puzzle['SK']) return [ accessory_block( section_block(text_block(puzzle_text)), - button_block("✏", "edit_puzzle", hunt_and_puzzle) + button_block("✏", "edit_puzzle", hunt_and_sort_key) ) ] @@ -167,6 +167,24 @@ def puzzle_matches_all(puzzle, patterns): def puzzle_id_from_name(name): return re.sub(r'[^a-zA-Z0-9_]', '', name).lower() +def puzzle_sort_key(puzzle): + """Return an appropriate sort key for a puzzle in the database + + The sort key must start with "puzzle-" to distinguish puzzle items + in the database from all non-puzzle items. After that, though, the + only requirements are that each puzzle have a unique key and they + give us the ordering we want. And for ordering, we want meta puzzles + before non-meta puzzles and then alphabetical order by name within + each of those groups. + + So puting a "-meta-" prefix in front of the puzzle ID does the trick. + """ + + return "puzzle-{}{}".format( + "-meta-" if puzzle['type'] == "meta" else "", + puzzle['puzzle_id'] + ) + def puzzle_channel_topic(puzzle): """Compute the channel topic for a puzzle"""