From 29c36d3057c5602c5a4b6fb0e48f454db01ddd9d Mon Sep 17 00:00:00 2001
From: Carl Worth <cworth@cworth.org>
Date: Sat, 9 Jan 2021 03:59:02 -0800
Subject: [PATCH] For /puzzle on meta puzzles print all titles/answers of
 feeder puzzles

Where feeder puzzles are specifically defined as puzzle belonging to the
same round.
---
 TODO                  |  3 ---
 turbot/hunt.py        | 20 +++++++++++++-------
 turbot/interaction.py | 25 ++++++++++++++++++++++++-
 turbot/round.py       | 30 ++++++++++++++++++++++++++++++
 4 files changed, 67 insertions(+), 11 deletions(-)

diff --git a/TODO b/TODO
index 4569821..1bc07a6 100644
--- a/TODO
+++ b/TODO
@@ -1,9 +1,6 @@
 Ordered punch-list (aiming to complete by 2021-01-08)
 -----------------------------------------------------
 
-• Make `/puzzle` for a meta puzzle also display the names/answers of
-  all puzzles in the round.
-
 • Add /tag to add/remove tags (will force to all caps and store as a
   prefix as part of the state string for now)
 
diff --git a/turbot/hunt.py b/turbot/hunt.py
index 1c2ee0e..c2e59e2 100644
--- a/turbot/hunt.py
+++ b/turbot/hunt.py
@@ -23,6 +23,18 @@ def find_hunt_for_hunt_id(turb, hunt_id):
     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 hunt_blocks(turb, hunt, puzzle_status='unsolved', search_terms=[],
                 limit_to_rounds=None):
     """Generate Slack blocks for a hunt
@@ -62,13 +74,7 @@ def hunt_blocks(turb, hunt, puzzle_status='unsolved', search_terms=[],
     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']
+    puzzles = hunt_puzzles_for_hunt_id(turb, hunt_id)
 
     # Filter the set of puzzles according the the requested puzzle_status
     if puzzle_status in ('solved', 'unsolved'):
diff --git a/turbot/interaction.py b/turbot/interaction.py
index 9e92242..8bff8bc 100644
--- a/turbot/interaction.py
+++ b/turbot/interaction.py
@@ -2,7 +2,11 @@ from slack.errors import SlackApiError
 from turbot.blocks import (
     input_block, section_block, text_block, multi_select_block, checkbox_block
 )
-from turbot.hunt import find_hunt_for_hunt_id, hunt_blocks
+from turbot.hunt import (
+    find_hunt_for_hunt_id,
+    hunt_blocks,
+    hunt_puzzles_for_hunt_id
+)
 from turbot.puzzle import (
     find_puzzle_for_url,
     find_puzzle_for_sort_key,
@@ -11,6 +15,7 @@ from turbot.puzzle import (
     puzzle_blocks,
     puzzle_sort_key
 )
+from turbot.round import round_quoted_puzzles_titles_answers
 import turbot.rot
 import turbot.sheets
 import turbot.slack
@@ -664,6 +669,24 @@ def puzzle(turb, body, args):
 
     blocks = puzzle_blocks(puzzle, include_rounds=True)
 
+    # For a meta puzzle, also display the titles and solutions for all
+    # puzzles in the same round.
+    if puzzle['type'] == 'meta':
+        puzzles = hunt_puzzles_for_hunt_id(turb, puzzle['hunt_id'])
+
+        # Drop this puzzle itself from the report
+        puzzles = [p for p in puzzles if p['puzzle_id'] != puzzle['puzzle_id']]
+
+        for round in puzzle.get('rounds', [None]):
+            answers = round_quoted_puzzles_titles_answers(round, puzzles)
+            blocks += [
+                section_block(text_block(
+                    "*Feeder solutions from round {}*".format(
+                        round if round else "<none>"
+                    ))),
+                section_block(text_block(answers))
+            ]
+
     requests.post(response_url,
                   json = {'blocks': blocks},
                   headers = {'Content-type': 'application/json'}
diff --git a/turbot/round.py b/turbot/round.py
index 3e12f5c..73f23d7 100644
--- a/turbot/round.py
+++ b/turbot/round.py
@@ -32,3 +32,33 @@ def round_blocks(round, puzzles, omit_header=False):
         blocks += puzzle_blocks(puzzle)
 
     return blocks
+
+def round_quoted_puzzles_titles_answers(round, puzzles):
+    answers = []
+    for puzzle in puzzles:
+        if round:
+            if 'rounds' not in puzzle:
+                continue
+            if round not in puzzle['rounds']:
+                continue
+        else:
+            if 'rounds' in puzzle and len(puzzle['rounds']):
+                continue
+        answer = {}
+        answer['name'] = puzzle['name']
+        if puzzle['status'] == 'solved' and 'solution' in puzzle:
+            answer['solution'] = ", ".join(puzzle['solution'])
+        else:
+            answer['solution'] = ""
+        answers.append(answer)
+
+    if not answers:
+        return ""
+
+    longest = max(len(ans['name']) for ans in answers)
+
+    format = "%{}s: %s".format(longest)
+
+    return "```" + "\n".join(
+        [format % (ans['name'], ans['solution']) for ans in answers]
+    ) + "```"
-- 
2.45.2