diff --git a/CHANGELOG.md b/CHANGELOG.md index d4c2611..3a6b208 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - URL parameter to points.json to allow returning only the JSON for a single team by its team id (e.g., points.json?id=abc123). - A CONTRIBUTING.md to describe expectations when contributing to MOTH +### Fixed +- Handle cases where non-legacy puzzles don't have an `author` attribute +- Handle YAML-formatted file and script lists as expected +- YAML-formatted example puzzle actually works as expected + +## [3.4.3] - 2019-11-20 +### Fixed +- Made top-scoring teams full-width ## [3.4.2] - 2019-11-18 ### Fixed diff --git a/README.md b/README.md index 1268f45..bf443be 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ you can copy the example puzzles as a starting point: Then launch the development server: - $ python3 tools/devel-server.py + $ python3 devel/devel-server.py Point a web browser at http://localhost:8080/ and start hacking on things in your `puzzles` directory. diff --git a/VERSION b/VERSION index 4d9d11c..6cb9d3d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.4.2 +3.4.3 diff --git a/devel/devel-server.py b/devel/devel-server.py index 0d775dc..7d7b505 100755 --- a/devel/devel-server.py +++ b/devel/devel-server.py @@ -1,6 +1,5 @@ #!/usr/bin/python3 -import asyncio import cgitb import html import cgi @@ -43,6 +42,13 @@ class MothRequestHandler(http.server.SimpleHTTPRequestHandler): except TypeError: super().__init__(request, client_address, server) + # Why isn't this the default?! + def guess_type(self, path): + mtype, encoding = mimetypes.guess_type(path) + if encoding: + return "%s; encoding=%s" % (mtype, encoding) + else: + return mtype # Backport from Python 3.7 def translate_path(self, path): @@ -285,6 +291,8 @@ if __name__ == '__main__': logging.basicConfig(level=log_level) + mimetypes.add_type("application/javascript", ".mjs") + server = MothServer((addr, port), MothRequestHandler) server.args["base_url"] = args.base server.args["puzzles_dir"] = pathlib.Path(args.puzzles) diff --git a/devel/moth.py b/devel/moth.py index d228b72..3f4374c 100644 --- a/devel/moth.py +++ b/devel/moth.py @@ -233,11 +233,39 @@ class Puzzle: except IndexError: pass self.files[name] = PuzzleFile(stream, name, not hidden) + + elif key == 'files' and isinstance(val, dict): + for filename, options in val.items(): + if "source" in options: + source = options["source"] + else: + source = filename + + if "hidden" in options and options["hidden"]: + hidden = True + else: + hidden = False + + stream = open(source, "rb") + self.files[filename] = PuzzleFile(stream, filename, not hidden) + + elif key == 'files' and isinstance(val, list): + for filename in val: + stream = open(filename, "rb") + self.files[filename] = PuzzleFile(stream, filename) + elif key == 'script': stream = open(val, 'rb') # Make sure this shows up in the header block of the HTML output. self.files[val] = PuzzleFile(stream, val, visible=False) self.scripts.append(val) + + elif key == "scripts" and isinstance(val, list): + for script in val: + stream = open(script, "rb") + self.files[script] = PuzzleFile(stream, script, visible=False) + self.scripts.append(script) + elif key == "objective": self.objective = val elif key == "success": @@ -384,7 +412,12 @@ class Puzzle: self.body.write('') def get_authors(self): - return self.authors or [self.author] + if len(self.authors) > 0: + return self.authors + elif hasattr(self, "author"): + return [self.author] + else: + return [] def get_body(self): return self.body.getvalue() diff --git a/docs/devel-server.md b/docs/devel-server.md index bc05d3a..2ae34c8 100644 --- a/docs/devel-server.md +++ b/docs/devel-server.md @@ -28,6 +28,7 @@ If you can't use docker, try this: apt install python3 + pip3 install scapy pillow PyYAML git clone https://github.com/dirtbags/moth/ cd moth python3 devel/devel-server.py --puzzles example-puzzles diff --git a/theme/scoreboard.js b/theme/scoreboard.js index 8aa8e98..0c4d836 100644 --- a/theme/scoreboard.js +++ b/theme/scoreboard.js @@ -111,11 +111,15 @@ function scoreboardInit() { } winners.sort(teamCompare) winners.reverse() + + // Let's make some better names for things we've computed + let winningScore = winners[0].overallScore + let numCategories = Object.keys(highestCategoryScore).length // Clear out the element we're about to populate Array.from(element.childNodes).map(e => e.remove()) - let maxWidth = 100 / Object.keys(highestCategoryScore).length + let maxWidth = 100 / winningScore for (let team of winners) { let row = document.createElement("div") let ncat = 0 @@ -125,8 +129,6 @@ function scoreboardInit() { let catPct = catTeam / catHigh let width = maxWidth * catPct - console.log(catHigh, catTeam, catPct) - let bar = document.createElement("span") bar.classList.add("category") bar.classList.add("cat" + ncat)