mirror of https://github.com/dirtbags/moth.git
The devel server now serves puzzle files.
This commit is contained in:
parent
db78f43324
commit
10eaf13a4e
|
@ -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()
|
||||||
|
|
12
puzzles.py
12
puzzles.py
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue