diff --git a/devel-server.py b/devel-server.py index 0f15dfd..10133e5 100755 --- a/devel-server.py +++ b/devel-server.py @@ -137,18 +137,41 @@ you are a fool. for pzl_file in pzl['files']: body.append("* [puzzles/{cat}/{points}/{filename}]({filename})" .format(cat=parts[2], points=pzl.points, filename=pzl_file)) + + if len(pzl.logs) > 0: + body.extend(["", "## Logs"]) + body.append("* [Full Log File](_logs)" + .format(cat=parts[2], points=pzl.points)) + body.extend(["", "### Logs Head"]) + for log in pzl.logs[:10]: + body.append("* `{}`".format(log)) + body.extend(["", "### Logs Tail"]) + for log in pzl.logs[-10:]: + body.append("* `{}`".format(log)) self.serve_md('\n'.join(body)) return elif len(parts) == 5: - try: - self.serve_puzzle_file(pzl['files'][parts[4]]) - except KeyError: - self.send_error(HTTPStatus.NOT_FOUND, "File not found") - return + if parts[4] == '_logs': + self.serve_puzzle_logs(pzl.logs) + else: + try: + self.serve_puzzle_file(pzl['files'][parts[4]]) + except KeyError: + self.send_error(HTTPStatus.NOT_FOUND, "File not found") + return else: body.append("# Not Implemented Yet") self.serve_md('\n'.join(body)) + def serve_puzzle_logs(self, logs): + """Serve a PuzzleFile object.""" + self.send_response(HTTPStatus.OK) + self.send_header("Content-type", "text/plain; charset=utf-8") + self.end_headers() + for log in logs: + self.wfile.write(log.encode('ascii')) + self.wfile.write(b"\n") + CHUNK_SIZE = 4096 def serve_puzzle_file(self, file): """Serve a PuzzleFile object.""" diff --git a/puzzles.py b/puzzles.py index df18c0c..2fd5ee7 100644 --- a/puzzles.py +++ b/puzzles.py @@ -88,9 +88,9 @@ class Puzzle: self._dict = defaultdict(lambda: []) if os.path.isdir(path): - self._puzzle_dir = path + self.puzzle_dir = path else: - self._puzzle_dir = None + self.puzzle_dir = None self.message = bytes(random.choice(messageChars) for i in range(20)) self.body = '' @@ -114,6 +114,8 @@ class Puzzle: self._seed = category_seed * self.points self.rand = random.Random(self._seed) + self._logs = [] + if path is not None: files = os.listdir(path) @@ -139,6 +141,25 @@ class Puzzle: except OSError: pass + def log(self, msg): + """Add a new log message to this puzzle.""" + self._logs.append(msg) + + @property + def logs(self): + """Get all the log messages, as strings.""" + + _logs = [] + for log in self._logs: + if type(log) is bytes: + log = log.decode('utf-8') + elif type(log) is not str: + log = str(log) + + _logs.append(log) + + return _logs + def _read_config(self, stream): """Read a configuration file (ISO 2822)""" body = [] @@ -220,7 +241,7 @@ class Puzzle: key = key.lower() - if key in ('file', 'resource', 'hidden') and self._puzzle_dir is None: + if key in ('file', 'resource', 'hidden') and self.puzzle_dir is None: raise KeyError("Cannot set a puzzle file for single file puzzles.") if key == 'answer': @@ -229,15 +250,15 @@ class Puzzle: self._dict['answer'].append(value) elif key == 'file': # Handle adding files to the puzzle - path = os.path.join(self._puzzle_dir, 'files', value) + path = os.path.join(self.puzzle_dir, 'files', value) self._dict['files'][value] = self._puzzle_file(path, value) elif key == 'resource': # Handle adding category files to the puzzle - path = os.path.join(self._puzzle_dir, '../res', value) + path = os.path.join(self.puzzle_dir, '../res', value) self._dict['files'].append(self._puzzle_file(path, value)) elif key == 'hidden': # Handle adding secret, 'hidden' files to the puzzle. - path = os.path.join(self._puzzle_dir, 'files', value) + path = os.path.join(self.puzzle_dir, 'files', value) name = self.random_hash() self._dict['files'].append(self._puzzle_file(path, name, visible=False)) elif key in self.SINGULAR_KEYS: