Skip to content

Commit

Permalink
Merge pull request #173 from dlareau/development
Browse files Browse the repository at this point in the history
Merge Development
  • Loading branch information
dlareau committed Dec 13, 2023
2 parents 44f87cc + c414dbb commit 648f1fd
Show file tree
Hide file tree
Showing 43 changed files with 927 additions and 239 deletions.
19 changes: 11 additions & 8 deletions docker-compose.yml
Expand Up @@ -20,10 +20,6 @@ services:
app:
build:
context: .
args:
http_proxy:
https_proxy:
no_proxy:
image: django_puzzlehunt # Name image for use in huey
restart: always
volumes:
Expand All @@ -33,11 +29,16 @@ services:
- ./docker/volumes/logs:/var/log/external
environment:
- DOMAIN
- SITE_TITLE
- DJANGO_SECRET_KEY
- DJANGO_ENABLE_DEBUG
- DJANGO_EMAIL_USER
- DJANGO_EMAIL_PASSWORD
- DJANGO_EMAIL_HOST
- DJANGO_EMAIL_PORT
- DJANGO_EMAIL_FROM
- DJANGO_USE_SHIBBOLETH
- PUZZLEHUNT_CHAT_ENABLED
- DJANGO_SETTINGS_MODULE=puzzlehunt_server.settings.env_settings
- DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db/${DB_NAME}
- ENABLE_DEBUG_TOOLBAR
Expand All @@ -60,6 +61,11 @@ services:
- DJANGO_SETTINGS_MODULE=puzzlehunt_server.settings.env_settings
- DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db/${DB_NAME}
- SENTRY_DSN
- DJANGO_EMAIL_USER
- DJANGO_EMAIL_PASSWORD
- DJANGO_EMAIL_HOST
- DJANGO_EMAIL_PORT
- DJANGO_EMAIL_FROM
depends_on:
- app

Expand All @@ -69,9 +75,6 @@ services:
context: ./docker/
dockerfile: apacheDockerfile
args:
http_proxy:
https_proxy:
no_proxy:
DOMAIN: ${DOMAIN}
depends_on:
- app
Expand All @@ -85,4 +88,4 @@ services:

volumes:
static:
media:
media:
13 changes: 12 additions & 1 deletion docs/setup.rst
Expand Up @@ -16,7 +16,18 @@ following steps to get a sample server up and going
uncommented lines. Other lines can be safely ignored as they only provide
additional functionality.
5. Run ``docker-compose up`` (possibly using ``sudo`` if needed)
6. You should now have the server running on a newly created VM, accessible via
6. Once up, you'll need to run the following commands to collect all the static
files (to be run any time after you alter the static files), to load in an
initial hunt to pacify some of the display logic (to be run only once), and
to create a new admin user (follow the prompts).

.. code-block:: bash
docker-compose exec app python3 /code/manage.py collectstatic --noinput
docker-compose exec app python3 /code/manage.py loaddata initial_hunt
docker-compose exec app python3 /code/manage.py createsuperuser
7. You should now have the server running on a newly created VM, accessible via
(http://localhost). The repository you cloned has been
linked into the VM by docker, so any changes made to the repository on the
host system should show up automatically. (A ``docker-compose restart`` may
Expand Down
46 changes: 26 additions & 20 deletions huntserver/admin.py
Expand Up @@ -11,6 +11,7 @@
from django.contrib.flatpages.models import FlatPage
from django.contrib.flatpages.forms import FlatpageForm
import re
from .utils import get_validation_error, get_puzzle_answer_regex

# Register your models here.
from . import models
Expand Down Expand Up @@ -62,6 +63,7 @@ class Meta:
class HuntAdmin(admin.ModelAdmin):
form = HuntAdminForm
inlines = (HintUnlockPLanInline,)
ordering = ['-hunt_number']
fieldsets = (
('Basic Info', {'fields': ('hunt_name', 'hunt_number', 'team_size', 'location',
('start_date', 'display_start_date'), ('end_date', 'display_end_date'),
Expand Down Expand Up @@ -102,16 +104,16 @@ def user_username(self, person):
class PrepuzzleAdminForm(forms.ModelForm):
class Meta:
model = models.Prepuzzle
fields = ['puzzle_name', 'released', 'hunt', 'answer', 'resource_file', 'template',
'response_string']
fields = ['puzzle_name', 'released', 'hunt', 'answer', 'answer_validation_type',
'resource_file', 'template', 'response_string']
widgets = {
'template': HtmlEditor(attrs={'style': 'width: 90%; height: 400px;'}),
}


class PrepuzzleAdmin(admin.ModelAdmin):
form = PrepuzzleAdminForm
list_display = ['puzzle_name', 'hunt', 'released']
list_display = ['puzzle_name', 'hunt', 'released', 'answer_validation_type']
readonly_fields = ('puzzle_url',)

# Needed to add request to modelAdmin
Expand Down Expand Up @@ -180,18 +182,23 @@ def save(self, *args, **kwargs):
instance.puzzle_set.add(*self.cleaned_data['reverse_unlocks'])
return instance

def clean_answer(self):
data = self.cleaned_data.get('answer')
if(re.fullmatch(r"[a-zA-Z]+", data.upper()) is None):
raise forms.ValidationError("Answer must only contain the characters A-Z.")
def clean(self):
data = self.cleaned_data
answer = data.get('answer')
validation_type = data.get('answer_validation_type')
if(validation_type == models.Puzzle.ANSWER_STRICT):
data['answer'] = answer.upper()
if(re.fullmatch(get_puzzle_answer_regex(validation_type), answer) is None):
self.add_error('answer', forms.ValidationError(get_validation_error(validation_type)))
return data

class Meta:
model = models.Puzzle
fields = ('hunt', 'puzzle_name', 'puzzle_number', 'puzzle_id', 'answer', 'is_meta',
'doesnt_count', 'puzzle_page_type', 'puzzle_file', 'resource_file',
'solution_file', 'extra_data', 'num_required_to_unlock', 'unlock_type',
'points_cost', 'points_value', 'solution_is_webpage', 'solution_resource_file')
fields = ('hunt', 'puzzle_name', 'puzzle_number', 'puzzle_id', 'answer',
'answer_validation_type', 'puzzle_type', 'puzzle_page_type', 'puzzle_file',
'resource_file', 'solution_file', 'extra_data', 'num_required_to_unlock',
'unlock_type', 'points_cost', 'points_value', 'solution_is_webpage',
'solution_resource_file')


class PuzzleAdmin(admin.ModelAdmin):
Expand All @@ -202,15 +209,15 @@ class Media:

list_filter = ('hunt',)
search_fields = ['puzzle_id', 'puzzle_name']
list_display = ['combined_id', 'puzzle_name', 'hunt', 'is_meta']
list_display = ['combined_id', 'puzzle_name', 'hunt', 'puzzle_type']
list_display_links = ['combined_id', 'puzzle_name']
ordering = ['-hunt', 'puzzle_number']
inlines = (ResponseInline,)
radio_fields = {"unlock_type": admin.VERTICAL}
fieldsets = (
(None, {
'fields': ('hunt', 'puzzle_name', 'answer', 'puzzle_number', 'puzzle_id', 'is_meta',
'doesnt_count', 'puzzle_page_type', 'puzzle_file', 'resource_file',
'fields': ('hunt', 'puzzle_name', 'answer', 'answer_validation_type', 'puzzle_number',
'puzzle_id', 'puzzle_type', 'puzzle_page_type', 'puzzle_file', 'resource_file',
'solution_is_webpage', 'solution_file', 'solution_resource_file',
'extra_data', 'unlock_type')
}),
Expand All @@ -233,7 +240,6 @@ def combined_id(self, puzzle):
class ResponseAdmin(admin.ModelAdmin):
list_display = ['__str__', 'puzzle_just_name']
search_fields = ['regex', 'text']
ordering = ['-puzzle']

def puzzle_just_name(self, response):
return response.puzzle.puzzle_name
Expand Down Expand Up @@ -265,13 +271,13 @@ class TeamAdminForm(forms.ModelForm):
)
)

num_unlock_points = forms.IntegerField(disabled=True)
num_unlock_points = forms.IntegerField(disabled=True, initial=0)

class Meta:
model = models.Team
fields = ['team_name', 'hunt', 'location', 'join_code', 'playtester', 'playtest_start_date',
'playtest_end_date', 'num_available_hints', 'num_unlock_points', 'unlockables',
'num_unlock_points']
fields = ['team_name', 'hunt', 'location', 'join_code', 'playtester', 'is_local',
'playtest_start_date', 'playtest_end_date', 'num_available_hints',
'num_unlock_points', 'unlockables']

def __init__(self, *args, **kwargs):
super(TeamAdminForm, self).__init__(*args, **kwargs)
Expand All @@ -295,7 +301,7 @@ def save(self, commit=True):
class TeamAdmin(admin.ModelAdmin):
form = TeamAdminForm
search_fields = ['team_name']
list_display = ['short_team_name', 'location', 'hunt', 'playtester']
list_display = ['short_team_name', 'hunt', 'is_local', 'playtester']
list_filter = ['hunt']

def short_team_name(self, team):
Expand Down
22 changes: 13 additions & 9 deletions huntserver/forms.py
@@ -1,8 +1,9 @@
from django import forms
from .models import Person
from .models import Person, Puzzle
from .utils import get_puzzle_answer_regex, get_validation_error, strip_puzzle_answer
from django.contrib.auth.models import User
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout
from crispy_forms.layout import Submit, Layout, Hidden
from crispy_forms.bootstrap import StrictButton, InlineField
from django.core.exceptions import ValidationError
import re
Expand All @@ -13,6 +14,7 @@ class AnswerForm(forms.Form):

def __init__(self, *args, **kwargs):
disable_form = kwargs.pop('disable_form', False)
self.validation_type = kwargs.pop('validation_type', Puzzle.ANSWER_STRICT)
super(AnswerForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-inline'
Expand All @@ -30,13 +32,15 @@ def __init__(self, *args, **kwargs):
StrictButton('Submit', value="submit", type="submit", css_class='btn btn-default')
)

def clean_answer(self):
# Currently the desire is to strip all non A-Z characters (Github issue #129)
new_cleaned_data = re.sub(r"[^A-Z]", "", self.cleaned_data.get('answer').upper())
if(new_cleaned_data == ""):
raise ValidationError("Submission was empty after stripping non A-Z characters",
code='all_spaces')
return new_cleaned_data
def clean(self):
data = self.cleaned_data
if(self.validation_type == Puzzle.ANSWER_STRICT):
data['answer'] = data.get('answer').upper()
data['answer'] = strip_puzzle_answer(data.get('answer'), self.validation_type)
if(re.fullmatch(get_puzzle_answer_regex(self.validation_type), data.get('answer')) is None):
print("error", get_puzzle_answer_regex(self.validation_type), data.get('answer'))
self.add_error('answer', forms.ValidationError(get_validation_error(self.validation_type)))
return data


class SubmissionForm(forms.Form):
Expand Down

0 comments on commit 648f1fd

Please sign in to comment.