Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions agate/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
**/__pycache__/
*.pyc
*.pyo
*.pyd

venv/
env/
.venv/

.env
*.env

.git/
.gitignore
.gitattributes

.DS_Store
Thumbs.db
.idea/
.vscode/

**/*~

*.sqlite3

docker-compose.yml
Dockerfile

staticfiles/
static/
media/
media_root/

build/
dist/
*.egg-info/

coverage_html_report/
*log

# The varys_config.cfg needs to be given when setting up the container
# e.g. with a volume mount or some other technique
varys_config.cfg
20 changes: 20 additions & 0 deletions agate/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM python:3.12-slim-bookworm

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=core.production

WORKDIR /app

COPY requirements.txt /app/

# Install requirements from the project
RUN pip install --upgrade pip && \
pip install --no-cache-dir -r requirements.txt

COPY . /app

COPY entrypoint.sh /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
CMD ["gunicorn", "core.wsgi:application", "--bind", "0.0.0.0:8000"]
213 changes: 213 additions & 0 deletions agate/core/production.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import os
from varys_message_retrieval import VarysMessageRetrieval

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATE_DIR = os.path.join(BASE_DIR, 'core/templates')


# Application definition

INSTALLED_APPS = [
# Django default apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
# Custom apps
'account',
'agate',
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
"corsheaders.middleware.CorsMiddleware",
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'core.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATE_DIR],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'libraries': {
'settings_value': 'core.templatetags.settings_value',
}
},
},
]

WSGI_APPLICATION = 'core.wsgi.application'


# Custom user model for authentication

AUTH_USER_MODEL = 'account.User'


# Password validation

AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]


# Internationalization

LANGUAGE_CODE = 'en-gb'

TIME_ZONE = 'Europe/London'

USE_I18N = False # Set to True if using internationalisation (translations) in your project

USE_TZ = True


# Static files (CSS, JavaScript, Images)

STATICFILES_DIRS = [os.path.join(BASE_DIR, "core/static")]
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static/")


# Media files (user uploaded content)

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')


# Default primary key field type
# https://docs.djangoproject.com/en/latest/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'


DEBUG = os.getenv("DEBUG", False)


# Storages

# Default STORAGES from Django documentation
# See: https://docs.djangoproject.com/en/4.2/ref/settings/#std-setting-STORAGES
STORAGES = {
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"},
}

# Use ManifestStaticFilesStorage when not in debug mode
if not DEBUG: # NOQA
STORAGES['staticfiles'] = {"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"}


# Logging

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'handlers': {
'stream': {
'class': 'logging.StreamHandler',
'level': 'DEBUG' if DEBUG else 'INFO', # NOQA
'formatter': 'verbose',
},
'file': {
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, 'django.log'),
'formatter': 'verbose',
'maxBytes': 104857600, # 100*1024*1024 bytes (100MB)
},
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler',
},
},
'loggers': {
'django': {
'handlers': ['stream', 'file', 'mail_admins'],
'level': 'DEBUG' if DEBUG else 'INFO', # NOQA
'propagate': 'True',
},
},
}

# We are required to set all these variables to run the app
SECRET_KEY = os.environ["SECRET_KEY"]

ALLOWED_HOSTS = os.environ["ALLOWED_HOSTS"].split(",")

# Set the database name below
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ["POSTGRES_DB"],
"USER": os.environ["POSTGRES_USER"],
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
"HOST": os.environ["POSTGRES_HOST"],
}
}

CORS_ALLOWED_ORIGINS = [i for i in os.getenv("CORS_ALLOWED_ORIGINS", "").split(",") if i]
CSRF_TRUSTED_ORIGINS = [i for i in os.getenv("CSRF_TRUSTED_ORIGINS", "").split(",") if i]

ONYX_DOMAIN = os.environ["ONYX_DOMAIN"]

MESSAGE_RETRIEVAL = VarysMessageRetrieval(
queue_suffix="agate",
timeout=1,
config_path=os.path.join(BASE_DIR, "varys_config.cfg"),
profile="test",
logfile="varys.log",
log_level="DEBUG",
auto_acknowledge=False
)

LIMITED_PROJECT_LIST = os.getenv("LIMITED_PROJECT_LIST", "synthscape").split(",")
8 changes: 8 additions & 0 deletions agate/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
set -e

echo "Applying database migrations..."
python manage.py migrate --noinput

echo "Starting server"
exec "$@"
11 changes: 11 additions & 0 deletions agate/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Please follow suitable version specifiers, as outlined in https://www.python.org/dev/peps/pep-0440/#version-specifiers
# For Django versioning we use ~= (e.g. Django~=4.2.0) which will keep the minor version up to date (e.g. the latest version prior to 4.3.0)

Django~=4.2.0
djangorestframework
django-cors-headers
psycopg2-binary
requests
apscheduler
varys-client
gunicorn