The devel server now serves puzzle files.

This commit is contained in:
Paul Ferrell 2016-10-18 20:11:20 -06:00
parent db78f43324
commit 10eaf13a4e
2 changed files with 53 additions and 17 deletions

View File

@ -102,36 +102,70 @@ you are a fool.
body = [] body = []
path = self.path.rstrip('/') path = self.path.rstrip('/')
parts = path.split("/") parts = path.split("/")
#raise ValueError(parts)
if len(parts) < 3: if len(parts) < 3:
# List all categories # List all categories
body.append("# Puzzle Categories") body.append("# Puzzle Categories")
for i in glob.glob(os.path.join("puzzles", "*", "")): for i in glob.glob(os.path.join("puzzles", "*", "")):
body.append("* [{}](/{})".format(i, i)) 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 # List all point values in a category
body.append("# Puzzles in category `{}`".format(parts[2])) 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: for points in cat.pointvals:
body.append("* [puzzles/{cat}/{points}](/puzzles/{cat}/{points}/)".format(cat=parts[2], points=points)) 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])) body.append("# {} puzzle {}".format(parts[2], parts[3]))
fpath = os.path.join("puzzles", parts[2]) body.append("* Author: `{}`".format(pzl['author']))
cat = puzzles.Category(fpath, seed) body.append("* Summary: `{}`".format(pzl['summary']))
p = cat.puzzle(int(parts[3]))
body.append("* Author: `{}`".format(p['author']))
body.append("* Summary: `{}`".format(p['summary']))
body.append('') body.append('')
body.append("## Body") body.append("## Body")
body.append(p.body) body.append(pzl.body)
body.append("## Answers") body.append("## Answers")
for a in p['answers']: for a in pzl['answer']:
body.append("* `{}`".format(a)) body.append("* `{}`".format(a))
body.append("") 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))
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
else: else:
body.append("# Not Implemented Yet") body.append("# Not Implemented Yet")
self.serve_md('\n'.join(body)) self.serve_md('\n'.join(body))
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): def serve_file(self):
if self.path.endswith(".md"): if self.path.endswith(".md"):
self.serve_md() self.serve_md()

View File

@ -94,6 +94,9 @@ class Puzzle:
self.message = bytes(random.choice(messageChars) for i in range(20)) self.message = bytes(random.choice(messageChars) for i in range(20))
self.body = '' 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. # A list of temporary files we've created that will need to be deleted.
self._temp_files = [] self._temp_files = []
if path is not None: if path is not None:
@ -171,18 +174,17 @@ class Puzzle:
# Make sure it actually exists. # Make sure it actually exists.
if not os.path.exists(path): 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') file = open(path, 'rb')
return PuzzleFile(path=path, handle=file, name=name, visible=visible) 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 """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. 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 :param name: The name of the file for links within the puzzle. If this is None, a name
will be generated for you. will be generated for you.
:param mode: The mode under which
:param visible: Whether or not the file will be visible to the user. :param visible: Whether or not the file will be visible to the user.
:return: A file object for writing :return: A file object for writing
""" """
@ -190,7 +192,7 @@ class Puzzle:
if name is None: if name is None:
name = self.random_hash() 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') file_read = open(file.name, 'rb')
self._dict['files'][name] = PuzzleFile(path=file.name, handle=file_read, self._dict['files'][name] = PuzzleFile(path=file.name, handle=file_read,
@ -313,7 +315,7 @@ class Category:
def puzzle(self, points): def puzzle(self, points):
path = os.path.join(self.path, str(points)) path = os.path.join(self.path, str(points))
return Puzzle(path, self.seed) return Puzzle(self.seed, path=path)
def puzzles(self): def puzzles(self):
for points in self.pointvals: for points in self.pointvals: