dlareau / puzzlehunt_server

Server for Puzzle hunts run by Puzzlehunt CMU, but can be repurposed for other hunts.
MIT License
19 stars 16 forks source link

Support for pre-puzzles #70

Closed TomWildenhain closed 5 years ago

TomWildenhain commented 5 years ago

We have started doing "pre-puzzles" for the theme announcement for all our hunts. Some examples: https://puzzlehunt.club.cc.cmu.edu/static/huntserver/f18prepuzzle/ https://puzzlehunt.club.cc.cmu.edu/static/huntserver/s18prepuzzle/ https://puzzlehunt.club.cc.cmu.edu/static/huntserver/f17prepuzzle/

It would be cool if there was an optional "prepuzzle" field on hunt objects that would contain the URL to the prepuzzle. Even better would be if we had a special "Prepuzzle" object that accepted a Django template and had code for validating answers. Currently we run the prepuzzles locally and use some hashing stuff to validate answers. If the user gets it correct, they get access to a Google form where we record their contact info.

So I see two reasonable options for improvement: 1) Add an optional prepuzzle URL to the Hunt object. If included, the prepuzzle should automatically be linked to from the main page in the "Our next hunt" section. 2) Make a Prepuzzle object that can accept a django template, the correct answer, and a google form URL. We can discuss the details more if you think this is reasonable.

dlareau commented 5 years ago

I've seen the prepuzzles and I've been thinking about how to possibly deal with them. After seeing your notes here, I think I've come to a solution: 1) Implement full HTML puzzles/folders from #68 2) Add an "is_prepuzzle" boolean to the puzzle object (or a pre-puzzle field to the hunt object) 3) The prepuzzle will be created like a normal puzzle in the system, assigned to the hunt like normal, and be specified with whichever above logic is picked, this allows simple PDF prepuzzles if desired. 4) The puzzle will be accessible before the hunt opens, will be accessible from a link on the "our next hunt" section, and with some simple logic in the puzzle page, will redirect users to the form when solved.

This solution is closer to your second option and as long as #68 is implemented properly, shouldn't sacrifice any functionality. Let me know if you see any downsides.

dlareau commented 5 years ago

I was wrong... Thinking about this a bit more, re-using the puzzle object for this does mean I could re-use code, but would also mean I need to then filter out the prepuzzles everywhere like on the progress page, main hunt page, and charts. So back to separate prepuzzle objects like you first suggested.

Even though we are back to separate objects, I can still re-use some code through the magic of duck-typing and I'm now wondering which is easier/better for you. A) The prepuzzle has a field for django template code like the hunt OR B) The prepuzzle has a resource link that downloads a folder and displays index.html like #68 just implemented.

TomWildenhain commented 5 years ago

Yes, I agree that it makes most sense for prepuzzles to be different objects, especially since they should be solvable without signing in. A few notes:

Prepuzzles should be fully visually customizable and mobile-friendly since we often link to them with a QR code.

I like the idea of using django template code (A) since we can then include the navigation bar at the top. Our current hunt pages do not scale perfectly on mobile (the navigation becomes tiny) but I think we can resolve that with the correct scaling tags in the head of the template.

Adding a prepuzzle object might enable us to move some of the interaction logic into Django. Currently, the prepuzzles check answers locally by comparing them against a (salted) hash. If the answer is correct, they use the answer to decrypt a Google Forms link which we use to collect emails for our mailing list. It would be awesome if we could move the answer checking logic into Django. It would be even better if we could view analytics on how many people have viewed, attempted, and solved the prepuzzle. But only if that isn't too difficult.

dlareau commented 5 years ago

I do like the point about the django tempate making it easy to include navigation at the top, the downside of the template proposal vs the resources folder proposal is that it doesn't allow for easy inclusion of photos, which means we'd still need some place on the server for those. It also means that any script files would have to be inlined in the template or still included separately like photos.

I'll sit on that for a bit and see if there is some nice way we can blend the two that allows easy integration and customization with the template system while making it easy to supply other files. If I fail and we can only get one set of benefits I think you are right and templates are the way to go.

The plan is definitely to move all of the solving into django, so that we can get rid of the default boilerplate solve code as well as possibly gather some data. As for data, so long as the template extends the base template, users will be tracked by the google analytics footer at the bottom of every page. Plus I include some simple count fields in the prepuzzle object to track solves and attempts.

Lastly, you mentioned the navigation looking weird on mobile. If you could post a screenshot and any other mobile issues you have over on #73 it would be appreciated.

dlareau commented 5 years ago

So now that I'm actually implementing this I've come into three minor questions that I'd like to hear your thoughts on @TomWildenhain . 1) The solution submission integration with the server requires sending specific AJAX to a specific endpoint and knowing how to interpret the result. Documentation will exist no matter what solution we pick, but I strive to make things easier on people. Given that we are letting the staff specify the template code, I think the easiest thing would just be a subtemplate that inserts a submission box into the page and does all the right javascript. That way the staff user can use it for easy creation or ignore it and do something custom if they wish. I'd like to know if you see any problems with this approach.

2) There is a similar issue for associated media. I have the ability for extra resources to be downloaded from a linked zip folder, but the user needs some known consistent way to access those. Is it worth creating some template tag specifically to ease the use of static file inclusion in prepuzzles? Or is just having the prefix string (e.g. /static/prepuzzles/p1/ ) be documented enough?

3) Do you imagine any sort of access control to pre puzzles? (Something like a "don't let non-staff see the page before a certain date")

TomWildenhain commented 5 years ago
  1. It would be nice to have a subtemplate, but we often change the position of the inputs/buttons, so it would probably be best to just have the subtemplate load a JS file that has all the shared boilerplate. We can add the inputs and link them up to the JS functions. If we could edit that shared file JS file, then I can put in the shared code that shows the Google form, etc.

  2. A custom tag would be best. Basically an equivalent to {{ STATIC_URL }} except it populates the whole prefix. This way we don't have to worry about updating it.

  3. I think access control is a good idea. Just make a checkbox called "Released" and have it staff-only until it is checked. Timed release is not needed.

We have also made some puzzles using the prepuzzle-type setup that are not associated with any hunt. (For example: https://puzzlehunt.club.cc.cmu.edu/static/huntserver/f18recruiting/tshirts.html) Ideally the "prepuzzles" would be their own type of object and could exist without a hunt. Then a hunt would have an optional prepuzzle field.

The AJAX request should validate the answer and return the form URL if it is correct.

dlareau commented 5 years ago

Okay, this is up on the dev server now. If possible, play around with it and let me know of any changes that need to be made. You can use the admin test account, the details of which were sent out previously on 9/19/18 to staff.

Some notes:

If all looks good, this will round out V3.3 and it will be out on production soon.

TomWildenhain commented 5 years ago

Thanks so much for working on this!

TomWildenhain commented 5 years ago

To clarify, the prepuzzle I was testing just has a "click here" link that goes to an index.html. I realize this is not how the prepuzzles would actually work but just wanted to test the resource loading.

dlareau commented 5 years ago

Thanks for the feedback, I'll just reply in order:

  • I might be doing it wrong, but I can't get the assets for the prepuzzle to load. I made a prepuzzle here with this resource link but index.html gives a 404 here.

This was just a set of bad folder permissions on the dev server, the apache user didn't have the permissions download the file into the prepuzzles folder, so there was no file there when you went to access it. Should be fixed now (you still have to go redownload the puzzle resources from the management page).

  • It would be great if there was a button to copy the prepuzzle URL to the clipboard since we often make QR codes for them.
  • I like that the prepuzzles have the nav bar at the top, but it seems like the navigation for current hunt, previous hunts, hunt info, and contact us are missing. Can those be included?
  • In the description of the hunt field, maybe say that it can be left blank to make the prepuzzle not have a hunt
  • I got an Access Denied error when using the admin test account to view a puzzle that was not released. Will admins be able to see unreleased hunts?

Those four are now implemented.

  • I would like to be able to edit the JS boilerplate so I can put in the standard functions for opening the form, etc. Where should I do that?

If you want to add any helper functions, make a pull request against the development branch to edit the JS in the prepuzzle template. I would avoid putting anything besides helper functions in there though, as putting in any code that runs by default means that changing this file later down the line could break any prepuzzles we write now. One thing I've seen even just over the life of this server is that new puzzlehunt staff members change what "every puzzle will definitely definitely have in it" so stuff like "when the puzzle is solved, pull up this google form" should be left to helper functions waiting to be called or the template for the individual prepuzzle.

dlareau commented 5 years ago

Also, I'm going to push out V3.3 to production tomorrow after I clean up the documentation, but if you do put in a pull request, I'll push out a new V3.3.1 to production within a day or so of the request so you can use it.

TomWildenhain commented 5 years ago

Just tested it again. I'm actually happy with just the check_answer function, so I'll leave the boilerplate as is. One thing I ran in to: is there a way I can add tags in the head tag for a prepuzzle? Usually I use {% block base_includes %} but that wipes out the boilerplate code.

dlareau commented 5 years ago

Each page on the server is built off of a "base template", hunt pages extend "hunt_base.html", info pages extend "info_base.html", etc... the includes blocks are structured so that any includes stuff (css/js) that that base template provides is in the "base_includes" block. The prepuzzle template puts its helper functions in there. Normal includes for the page should actually go in just {% block includes %}, which is left for the child page to populate. You've probably been working mostly with hunt pages, where even if you used the "includes" block, you'd end up wanting to overwrite the "base_includes" block anyway just to get rid of the default hunt CSS that older hunts rely on, which means there's no harm in using "base_includes" as "includes".

Tl;dr: Use {% block includes %}

dlareau commented 5 years ago

Now merged into master, open a new issue for any further bugs regarding prepuzzles.