symposion_app/pinaxcon/settings.py
Joel Addison 539fa2dfdd Finalise registration
Add shirt types and sizes. Improve messaging about discounts.
Restyle ticket wizard and product category screens. Enable page titles
and messages. Update dashboard to hide raffle.
Enable inventory population for dev container.
2019-10-02 23:25:17 +10:00

559 lines
17 KiB
Python

from decimal import Decimal
import os
import sys
import django
import dj_database_url
import saml2
import saml2.saml
from datetime import date, datetime, timedelta
from dataclasses import dataclass
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
DJANGO_ROOT = os.path.abspath(os.path.dirname(django.__file__))
BASE_DIR = PACKAGE_ROOT
sys.path.append(os.path.join(PROJECT_ROOT, 'vendor'))
### USER SETTINGS
DEV_MODE = os.environ.get("SYMPOSION_DEV_MODE", None)
DEBUG = os.environ.get('SYMPOSION_APP_DEBUG', '0')
if isinstance(DEBUG, str):
try:
i = int(DEBUG)
if not i in [0, 1]:
raise ValueError("not 0 or 1")
DEBUG = bool(i)
except ValueError:
sys.exit('DEBUG env var must be set to string value of a 0 or 1')
else:
sys.exit('DEBUG env var is in unexpected format. Should be a string'
'containing either a 0 or a 1 - Got type %s' % type(DEBUG))
DATABASES = {}
DATABASES['default'] = dj_database_url.config(conn_max_age=600)
if DATABASES['default']['ENGINE'] == 'django.db.backends.mysql':
DATABASES['default']['OPTIONS'] = {'charset': 'utf8mb4'}
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get('EMAIL_HOST', None)
EMAIL_PORT = os.environ.get('EMAIL_PORT', 25)
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER', None)
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', None)
DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
EMAIL_USE_SSL = False
EMAIL_USE_TLS = False
_EMAIL_SSL_FLAVOR=os.environ.get('EMAIL_SSL_FLAVOR', None)
if _EMAIL_SSL_FLAVOR == "TLS":
EMAIL_USE_TLS = True
elif _EMAIL_SSL_FLAVOR == "SSL":
EMAIL_USE_SSL = True
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', None)
PINAX_STRIPE_PUBLIC_KEY = os.environ.get('STRIPE_PUBLIC_KEY', None)
PINAX_STRIPE_SECRET_KEY = os.environ.get('STRIPE_SECRET_KEY', None)
PINAX_STRIPE_SEND_EMAIL_RECEIPTS = False
ANALYTICS_KEY = os.environ.get('ANALYTICS_KEY', None)
saml2_entityid = os.environ.get('SAML2_ENTITYID', None)
saml2_sp_name = os.environ.get('SAML2_SP_NAME', None)
saml2_sp_assertion_service = os.environ.get('SAML2_SP_ASSERTION_SERVICE', None)
saml2_sp_slo_rdir = os.environ.get('SAML2_SP_SLO_RDIR', None)
saml2_sp_slo_post = os.environ.get('SAML2_SP_SLO_POST', None)
saml2_idp_metadata = {
'local': [os.environ.get('SAML2_IDP_METADATA_FILE', None)],
}
saml2_signing_key = os.environ.get('SAML2_SIGNING_KEY', None)
saml2_signing_crt = os.environ.get('SAML2_SIGNING_CRT', None)
saml2_encr_key = os.environ.get('SAML2_ENCRYPTION_KEY', None)
saml2_encr_crt = os.environ.get('SAML2_ENCRYPTION_CRT', None)
saml2_contact = {
'given_name': os.environ.get("META_GIVEN_NAME", 'Bastard'),
'sur_name': os.environ.get('META_FAM_NAME', 'Operator'),
'company': os.environ.get('META_COMPANY', 'Corp1'),
'email_address': os.environ.get('META_EMAIL', 'op@example.com'),
'contact_type': 'technical'},
fail = False
BADGER_DEFAULT_SVG = 'registrasion/badge.svg'
BADGER_DEFAULT_FORM = "registrasion/badge_form.html"
if SECRET_KEY is None:
print("FAILURE: You need to supply a DJANGO_SECRET_KEY "
"environment variable")
fail = True
if PINAX_STRIPE_PUBLIC_KEY is None:
print("FAILURE: You need to supply a STRIPE_PUBLIC_KEY "
"environment variable")
fail = True
if PINAX_STRIPE_SECRET_KEY is None:
print("FAILURE: You need to supply a STRIPE_SECRET_KEY "
"environment variable")
fail = True
if fail:
sys.exit('FAILURE: Missing environment variables.')
### Standard settings
ADMIN_USERNAMES = []
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*']
TIME_ZONE = "Pacific/Auckland"
DATE_FORMAT = "j F Y"
LANGUAGE_CODE = "en-au"
SITE_ID = int(os.environ.get("SITE_ID", 1))
USE_I18N = True
USE_L10N = True
USE_TZ = True
MEDIA_ROOT = os.path.join(PACKAGE_ROOT, "site_media", "media")
MEDIA_URL = "/site_media/media/"
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static/build')
STATIC_URL = '/static/build/'
STATICFILES_DIRS = [
os.path.join(PROJECT_ROOT, 'static/src'),
]
STATICFILES_FINDERS = [
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
"sass_processor.finders.CssFinder",
]
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
os.path.join(PACKAGE_ROOT, "templates"),
os.path.join(DJANGO_ROOT, 'forms/templates')
],
"APP_DIRS": True,
"OPTIONS": {
"debug": DEBUG,
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.template.context_processors.request",
"django.contrib.messages.context_processors.messages",
"pinax_theme_bootstrap.context_processors.theme",
"symposion.reviews.context_processors.reviews",
"django_settings_export.settings_export",
],
},
},
]
MIDDLEWARE_CLASSES = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.auth.middleware.SessionAuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"debug_toolbar.middleware.DebugToolbarMiddleware",
"reversion.middleware.RevisionMiddleware",
"waffle.middleware.WaffleMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django.contrib.flatpages.middleware.FlatpageFallbackMiddleware",
'pinaxcon.monkey_patch.MonkeyPatchMiddleware',
]
if DEV_MODE and DEV_MODE == "LAPTOP":
ROOT_URLCONF = "pinaxcon.devmode_urls"
else:
ROOT_URLCONF = "pinaxcon.urls"
# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = "pinaxcon.wsgi.application"
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.flatpages",
"django.contrib.messages",
"django.contrib.sessions",
"django.contrib.sites",
"django.contrib.staticfiles",
"django.contrib.humanize",
"debug_toolbar",
'djangosaml2',
# theme
"bootstrapform",
"pinax_theme_bootstrap",
"sass_processor",
# external
"easy_thumbnails",
"taggit",
"reversion",
"sitetree",
"pinax.eventlog",
# symposion
"symposion",
"symposion.conference",
"symposion.proposals",
"symposion.reviews",
"symposion.schedule",
"symposion.speakers",
"symposion.teams",
# Registrasion
"registrasion",
# Registrasion-stripe
"pinax.stripe",
"django_countries",
"registripe",
#registrasion-desk
"regidesk",
# admin - required by registrasion ??
"nested_admin",
# project
"pinaxcon",
"pinaxcon.proposals",
"pinaxcon.registrasion",
"pinaxcon.raffle",
"jquery",
"djangoformsetjs",
# testing and rollout
"django_nose",
"waffle",
"crispy_forms",
]
CRISPY_TEMPLATE_PACK = "bootstrap4"
DEBUG_TOOLBAR_PANELS = [
'debug_toolbar.panels.versions.VersionsPanel',
'debug_toolbar.panels.timer.TimerPanel',
'debug_toolbar.panels.settings.SettingsPanel',
'debug_toolbar.panels.headers.HeadersPanel',
'debug_toolbar.panels.request.RequestPanel',
'debug_toolbar.panels.sql.SQLPanel',
'debug_toolbar.panels.staticfiles.StaticFilesPanel',
'debug_toolbar.panels.cache.CachePanel',
'debug_toolbar.panels.signals.SignalsPanel',
'debug_toolbar.panels.logging.LoggingPanel',
'debug_toolbar.panels.templates.TemplatesPanel',
'debug_toolbar.panels.redirects.RedirectsPanel',
]
DEBUG_TOOLBAR_CONFIG = {
'INTERCEPT_REDIRECTS': False,
'SHOW_TOOLBAR_CALLBACK': lambda x: DEBUG,
}
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'simple': {
'format': '%(asctime)s %(levelname)s $(module)s %(message)s'
},
},
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'DEBUG',
'propagate': True,
},
'symposion.request': {
'handlers': ['mail_admins'],
'level': 'DEBUG',
'propagate': True,
},
},
'root': {
'handlers': ['console'],
'level': 'DEBUG'
},
}
FIXTURE_DIRS = [
os.path.join(PROJECT_ROOT, "fixtures"),
]
AUTHENTICATION_BACKENDS = [
'symposion.teams.backends.TeamPermissionsBackend',
'django.contrib.auth.backends.ModelBackend',
'djangosaml2.backends.Saml2Backend',
]
LOGIN_URL = '/saml2/login/'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
CONFERENCE_ID = 1
PROPOSAL_FORMS = {
"talk": "pinaxcon.proposals.forms.TalkProposalForm",
"tutorial": "pinaxcon.proposals.forms.TutorialProposalForm",
"miniconf": "pinaxcon.proposals.forms.MiniconfProposalForm",
### LCA2020 Miniconfs
"containers-miniconf": "pinaxcon.proposals.forms.ContainersProposalForm",
"creative-arts-miniconf": "pinaxcon.proposals.forms.CreativeArtsProposalForm",
"docs-miniconf": "pinaxcon.proposals.forms.DocsProposalForm",
"freebsd-miniconf": "pinaxcon.proposals.forms.FreeBsdProposalForm",
"games-miniconf": "pinaxcon.proposals.forms.GamesProposalForm",
"glam-miniconf": "pinaxcon.proposals.forms.GlamProposalForm",
"kernel-miniconf": "pinaxcon.proposals.forms.KernelProposalForm",
"open-education-miniconf": "pinaxcon.proposals.forms.OpenEducationProposalForm",
"open-hardware-miniconf": "pinaxcon.proposals.forms.OpenHardwareProposalForm",
"open-isa-miniconf": "pinaxcon.proposals.forms.OpenIsaProposalForm",
"security-miniconf": "pinaxcon.proposals.forms.SecurityProposalForm",
"sysadmin-miniconf": "pinaxcon.proposals.forms.SysAdminProposalForm",
}
# Registrasion bits:
ATTENDEE_PROFILE_MODEL = "pinaxcon.registrasion.models.AttendeeProfile"
ATTENDEE_PROFILE_FORM = "pinaxcon.registrasion.forms.ProfileForm"
INVOICE_CURRENCY = "AUD"
TICKET_PRODUCT_CATEGORY = 1
TERMS_PRODUCT_CATEGORY = 2
ATTENDEE_PROFILE_FORM = "pinaxcon.registrasion.forms.ProfileForm"
#REGIDESK
REGIDESK_BOARDING_GROUP = "Ready For Boarding"
# CSRF custom error screen
CSRF_FAILURE_VIEW = "pinaxcon.csrf_view.csrf_failure"
# Use nose to run all tests
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
# Tell nose to measure coverage on the 'foo' and 'bar' apps
NOSE_ARGS = [
'--with-coverage',
'--cover-package=registrasion.controllers,registrasion.models',
]
SASS_PROCESSOR_INCLUDE_DIRS = [
os.path.join(PROJECT_ROOT, 'static/src/bootstrap/scss'),
os.path.join(PROJECT_ROOT, 'static/src/scss'),
]
xmlsec_binary = '/usr/bin/xmlsec1'
if not os.path.isfile(xmlsec_binary):
sys.exit('ERROR: xmlsec1 binary missing, EXITING')
SAML_ATTRIBUTE_MAPPING = {
'uid': ('username', ),
'mail': ('email', ),
'givenName': ('first_name', ),
'sn': ('last_name', ),
}
SAML_CONFIG = {
'xmlsec_binary': xmlsec_binary,
'entityid': saml2_entityid,
'attribute_map_dir': os.path.join(PACKAGE_ROOT, 'saml2/attribute-maps'),
'service': {
'sp': {
'name': saml2_sp_name,
'endpoints': {
'assertion_consumer_service': [
saml2_sp_assertion_service,
],
'single_logout_service': [
(saml2_sp_slo_rdir, saml2.BINDING_HTTP_REDIRECT),
(saml2_sp_slo_post, saml2.BINDING_HTTP_POST),
],
},
'logout_requests_signed': True,
'required_attributes': ['uid', 'mail', 'givenName', 'sn'],
},
},
'metadata': saml2_idp_metadata,
'debug': 0,
'key_file': saml2_signing_key,
'cert_file': saml2_signing_crt,
'encryption_keypairs': [{
'key_file': saml2_encr_key,
'cert_file': saml2_encr_crt,
}],
'contact_person': saml2_contact,
'valid_for': 10,
}
if 'SAML_CONFIG_LOADER' in os.environ:
SAML_CONFIG_LOADER = os.environ.get('SAML_CONFIG_LOADER')
DEFAULT_FILE_STORAGE = 'gapc_storage.storage.GoogleCloudStorage'
GAPC_STORAGE = {
'num_retries': 2,
}
SETTINGS_EXPORT = [
'DEBUG',
'ANALYTICS_KEY',
]
if DEV_MODE and DEV_MODE == "LAPTOP":
print("ENABLING LAPTOP MODE")
from .devmode_settings import *
class Category(object):
tickets = []
@classmethod
def order(cls, ticket) -> int:
return (cls.tickets.index(ticket) + 1) * 10
@dataclass(frozen=True)
class Ticket:
name: str
regular_price: Decimal
earlybird_price: Decimal
def earlybird_discount(self):
return self.regular_price - self.earlybird_price
@dataclass(frozen=True)
class DinnerTicket:
name: str
price: Decimal
description: str
reservation: timedelta
cat: Category
def order(self):
return self.cat.order(self)
class PenguinDinnerTicket(DinnerTicket):
pass
class SpeakersDinnerTicket(DinnerTicket):
pass
class SpeakersDinnerCat(Category):
@classmethod
def create(cls, name: str, price: Decimal, description: str, reservation: timedelta) -> SpeakersDinnerTicket:
t = SpeakersDinnerTicket(name, price, description, reservation, cls)
cls.tickets.append(t)
return t
class PenguinDinnerCat(Category):
@classmethod
def create(cls, name: str, price: Decimal, description: str, reservation: timedelta) -> PenguinDinnerTicket:
t = PenguinDinnerTicket(name, price, description, reservation, cls)
cls.tickets.append(t)
return t
LCA_START = datetime(2020, 1, 13)
LCA_END = datetime(2020, 1, 17)
EARLY_BIRD_DEADLINE = datetime(2019, 11, 1)
PENGUIN_DINNER_TICKET_DATE = date(2020, 1, 15)
SPEAKER_DINNER_TICKET_DATE = date(2020, 1, 14)
PDNS_TICKET_DATE = date(2020, 1, 16)
TSHIRT_PRICE = Decimal("25.00")
CONTRIBUTOR = Ticket("Contributor", Decimal("1999.00"), Decimal("1849.00"))
PROFESSIONAL = Ticket("Professional", Decimal("1099.00"), Decimal("949.00"))
HOBBYIST = Ticket("Hobbyist", Decimal("549.00"), Decimal("399.00"))
STUDENT = Ticket("Student", Decimal("199.00"), None)
MINICONF_MT = Ticket("Monday and Tuesday Only", Decimal("198.00"), None)
MINICONF_M = Ticket("Monday Only", Decimal("99.00"), None)
MINICONF_T = Ticket("Tuesday Only", Decimal("99.00"), None)
MEDIA = Ticket("Media", Decimal("0.0"), None)
SPEAKER = Ticket("Speaker", Decimal("0.0"), None)
SPONSOR = Ticket("Sponsor", Decimal("0.0"), None)
CONFERENCE_ORG = Ticket("Conference Organiser", Decimal("0.0"), None)
CONFERENCE_VOL = Ticket("Conference Volunteer", Decimal("0.0"), None)
PENGUIN_DINNER = PenguinDinnerCat
PENGUIN_DINNER_ADULT = PenguinDinnerCat.create(
"Adult", Decimal("95.00"),
"Includes an adult's meal and full beverage service.",
timedelta(hours=1))
PENGUIN_DINNER_CHILD = PenguinDinnerCat.create(
"Child", Decimal("50.00"),
"Children 14 and under. "
"Includes a child's meal and soft drink service.",
timedelta(hours=1))
PENGUIN_DINNER_INFANT = PenguinDinnerCat.create(
"Infant", Decimal("0.0"),
"Includes no food or beverage service.",
timedelta(hours=1))
SPEAKERS_DINNER = SpeakersDinnerCat
SPEAKERS_DINNER_ADULT = SpeakersDinnerCat.create(
"Adult", Decimal("100.00"),
"Includes an adult's meal and full beverage service.",
timedelta(hours=1))
# SPEAKERS_DINNER_CHILD = SpeakersDinnerCat.create(
# "Child", Decimal("60.00"),
# "Children 14 and under. "
# "Includes a child's meal and soft drink service.",
# timedelta(hours=1))
# SPEAKERS_DINNER_INFANT = SpeakersDinnerCat.create(
# "Infant", Decimal("00.00"),
# "Infant must be seated in an adult's lap. "
# "No food or beverage service.",
# timedelta(hours=1))