]> git.cworth.org Git - turbot/blob - turbot/hunt.py
Correct typo in hunt description verbiage
[turbot] / turbot / hunt.py
1 from turbot.blocks import section_block, text_block, divider_block
2 from turbot.round import round_blocks
3 from turbot.puzzle import puzzle_block
4 from turbot.channel import channel_url
5 from boto3.dynamodb.conditions import Key
6
7 def find_hunt_for_hunt_id(turb, hunt_id):
8     """Given a hunt ID find the database item for that hunt
9
10     Returns None if hunt ID is not found, otherwise a
11     dictionary with all fields from the hunt's row in the table,
12     (channel_id, active, hunt_id, name, url, sheet_url, etc.).
13     """
14
15     response = turb.table.get_item(
16         Key={
17             'hunt_id': hunt_id,
18             'SK': 'hunt-{}'.format(hunt_id)
19         })
20
21     if 'Item' in response:
22         return response['Item']
23     else:
24         return None
25
26 def hunt_blocks(turb, hunt, puzzle_status='unsolved'):
27     """Generate Slack blocks for a hunt
28
29     The hunt argument should be a dictionary as returned from the
30     database.
31
32     Option 'puzzle_status' indicates which puzzles to include. If
33     either 'solved' or 'unsolved' only puzzles with that status from
34     the hunt will be included in the result. If any other value, all
35     puzzles in the hunt will be included.
36
37     The return value can be used in a Slack command expecting blocks to
38     provide all the details of a hunt, (puzzles, their state,
39     solution, links to channels and sheets, etc.).
40
41     """
42
43     name = hunt['name']
44     hunt_id = hunt['hunt_id']
45     channel_id = hunt['channel_id']
46
47     response = turb.table.query(
48         KeyConditionExpression=(
49             Key('hunt_id').eq(hunt_id) &
50             Key('SK').begins_with('puzzle-')
51         )
52     )
53     puzzles = response['Items']
54
55     # Filter the set of puzzles according the the requested puzzle_status
56     if puzzle_status in ('solved', 'unsolved'):
57         puzzles = [p for p in puzzles if p['status'] == puzzle_status]
58
59     # Compute the set of rounds across all puzzles
60     rounds = set()
61     for puzzle in puzzles:
62         if 'rounds' not in puzzle:
63             continue
64         for round in puzzle['rounds']:
65             rounds.add(round)
66
67     hunt_text = "*<{}|{}>*".format(channel_url(channel_id), name)
68
69     blocks = [
70         section_block(text_block(hunt_text)),
71     ]
72
73     # Construct blocks for each round
74     for round in rounds:
75         blocks += round_blocks(round, puzzles)
76
77     # Also blocks for any puzzles not in any round
78     stray_puzzles = [puzzle for puzzle in puzzles if 'rounds' not in puzzle]
79     if len(stray_puzzles):
80         stray_text = "*Puzzles with no assigned round*"
81         blocks.append(section_block(text_block(stray_text)))
82         for puzzle in stray_puzzles:
83             blocks.append(puzzle_block(puzzle))
84
85     blocks.append(divider_block())
86
87     return blocks