Merge pull request #9 from pflarr/pflarr_edits

New verbose logging and file service
This commit is contained in:
Neale Pickett 2016-10-19 15:33:18 -06:00 committed by GitHub
commit 1e15a2c19f
2 changed files with 102 additions and 22 deletions

View File

@ -102,35 +102,92 @@ you are a fool.
body = []
path = self.path.rstrip('/')
parts = path.split("/")
#raise ValueError(parts)
if len(parts) < 3:
# List all categories
body.append("# Puzzle Categories")
for i in glob.glob(os.path.join("puzzles", "*", "")):
body.append("* [{}](/{})".format(i, i))
elif len(parts) == 3:
self.serve_md('\n'.join(body))
return
fpath = os.path.join("puzzles", parts[2])
cat = puzzles.Category(fpath, seed)
if len(parts) == 3:
# List all point values in a category
body.append("# Puzzles in category `{}`".format(parts[2]))
fpath = os.path.join("puzzles", parts[2])
cat = puzzles.Category(fpath, seed)
for points in cat.pointvals:
body.append("* [puzzles/{cat}/{points}](/puzzles/{cat}/{points}/)".format(cat=parts[2], points=points))
elif len(parts) == 4:
self.serve_md('\n'.join(body))
return
pzl = cat.puzzle(int(parts[3]))
if len(parts) == 4:
body.append("# {} puzzle {}".format(parts[2], parts[3]))
fpath = os.path.join("puzzles", parts[2])
cat = puzzles.Category(fpath, seed)
p = cat.puzzle(int(parts[3]))
body.append("* Author: `{}`".format(p['author']))
body.append("* Summary: `{}`".format(p['summary']))
body.append("* Author: `{}`".format(pzl['author']))
body.append("* Summary: `{}`".format(pzl['summary']))
body.append('')
body.append("## Body")
body.append(p.body)
body.append(pzl.body)
body.append("## Answers")
for a in p['answers']:
for a in pzl['answer']:
body.append("* `{}`".format(a))
body.append("")
body.append("## Files")
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:
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))
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."""
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", "application/octet-stream")
self.send_header('Content-Disposition', 'attachment; filename="{}"'.format(file.name))
if file.path is not None:
fs = os.stat(file.path)
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
# We're using application/octet stream, so we can send the raw bytes.
self.end_headers()
chunk = file.handle.read(self.CHUNK_SIZE)
while chunk:
self.wfile.write(chunk)
chunk = file.handle.read(self.CHUNK_SIZE)
def serve_file(self):
if self.path.endswith(".md"):

View File

@ -88,12 +88,15 @@ 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 = ''
# This defaults to a dict, not a list like most things
self._dict['files'] = {}
# A list of temporary files we've created that will need to be deleted.
self._temp_files = []
if path is not None:
@ -111,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)
@ -136,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 = []
@ -171,18 +195,17 @@ class Puzzle:
# Make sure it actually exists.
if not os.path.exists(path):
raise ValueError("Included file {} does not exist.")
raise ValueError("Included file {} does not exist.".format(path))
file = open(path, 'rb')
return PuzzleFile(path=path, handle=file, name=name, visible=visible)
def make_temp_file(self, name=None, mode='rw+b', visible=True):
def make_temp_file(self, name=None, visible=True):
"""Get a file object for adding dynamically generated data to the puzzle. When you're
done with this file, flush it, but don't close it.
:param name: The name of the file for links within the puzzle. If this is None, a name
will be generated for you.
:param mode: The mode under which
:param visible: Whether or not the file will be visible to the user.
:return: A file object for writing
"""
@ -190,7 +213,7 @@ class Puzzle:
if name is None:
name = self.random_hash()
file = tempfile.NamedTemporaryFile(mode=mode, delete=False)
file = tempfile.NamedTemporaryFile(mode='w+b', delete=False)
file_read = open(file.name, 'rb')
self._dict['files'][name] = PuzzleFile(path=file.name, handle=file_read,
@ -218,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':
@ -227,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: