Initial commit
parent
1d7ea8cafc
commit
a2e52d453c
@ -0,0 +1,5 @@
|
||||
pytest
|
||||
pytest-cov
|
||||
flake8
|
||||
black
|
||||
wagtail
|
@ -0,0 +1,44 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --output-file dev_requirements.txt dev_requirements.in
|
||||
#
|
||||
--trusted-host pypi.python.org
|
||||
|
||||
appdirs==1.4.3 # via black
|
||||
atomicwrites==1.2.1 # via pytest
|
||||
attrs==18.2.0 # via black, pytest
|
||||
beautifulsoup4==4.6.0 # via wagtail
|
||||
black==18.9b0
|
||||
certifi==2018.10.15 # via requests
|
||||
chardet==3.0.4 # via requests
|
||||
click==7.0 # via black
|
||||
coverage==4.5.1 # via pytest-cov
|
||||
django-modelcluster==4.2 # via wagtail
|
||||
django-taggit==0.23.0 # via wagtail
|
||||
django-treebeard==4.3 # via wagtail
|
||||
django==2.1.2 # via django-taggit, django-treebeard, wagtail
|
||||
djangorestframework==3.9.0 # via wagtail
|
||||
draftjs-exporter==2.1.3 # via wagtail
|
||||
flake8==3.6.0
|
||||
html5lib==1.0.1 # via wagtail
|
||||
idna==2.7 # via requests
|
||||
mccabe==0.6.1 # via flake8
|
||||
more-itertools==4.3.0 # via pytest
|
||||
pillow==5.3.0 # via wagtail
|
||||
pluggy==0.8.0 # via pytest
|
||||
py==1.7.0 # via pytest
|
||||
pycodestyle==2.4.0 # via flake8
|
||||
pyflakes==2.0.0 # via flake8
|
||||
pytest-cov==2.6.0
|
||||
pytest==3.9.3
|
||||
pytz==2018.7 # via django, django-modelcluster, wagtail
|
||||
requests==2.20.0 # via wagtail
|
||||
six==1.11.0 # via html5lib, more-itertools, pytest, wagtail
|
||||
toml==0.10.0 # via black
|
||||
unidecode==1.0.22 # via wagtail
|
||||
urllib3==1.24 # via requests
|
||||
wagtail==2.3
|
||||
webencodings==0.5.1 # via html5lib
|
||||
willow==1.1 # via wagtail
|
@ -0,0 +1,21 @@
|
||||
FROM python:3.7
|
||||
LABEL maintainer="drew@androiddrew.comw"
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV DJANGO_ENV dev
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
RUN pip install -r /code/requirements.txt
|
||||
RUN pip install gunicorn
|
||||
|
||||
COPY . /code/
|
||||
WORKDIR /code/
|
||||
|
||||
RUN python manage.py migrate
|
||||
|
||||
RUN useradd wagtail
|
||||
RUN chown -R wagtail /code
|
||||
USER wagtail
|
||||
|
||||
EXPOSE 8000
|
||||
CMD exec gunicorn cms.wsgi:application --bind 0.0.0.0:8000 --workers 3
|
@ -0,0 +1,149 @@
|
||||
"""
|
||||
Django settings for cms project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 2.1.2.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/2.1/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
|
||||
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
BASE_DIR = os.path.dirname(PROJECT_DIR)
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"home",
|
||||
"search",
|
||||
"wagtail.contrib.forms",
|
||||
"wagtail.contrib.redirects",
|
||||
"wagtail.embeds",
|
||||
"wagtail.sites",
|
||||
"wagtail.users",
|
||||
"wagtail.snippets",
|
||||
"wagtail.documents",
|
||||
"wagtail.images",
|
||||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
"wagtail.core",
|
||||
"modelcluster",
|
||||
"taggit",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"wagtail.core.middleware.SiteMiddleware",
|
||||
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "cms.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [os.path.join(PROJECT_DIR, "templates")],
|
||||
"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",
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = "cms.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
|
||||
|
||||
# DATABASES = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.sqlite3',
|
||||
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
|
||||
|
||||
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
|
||||
# https://docs.djangoproject.com/en/2.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.1/howto/static-files/
|
||||
|
||||
STATICFILES_FINDERS = [
|
||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
]
|
||||
|
||||
STATICFILES_DIRS = [os.path.join(PROJECT_DIR, "static")]
|
||||
|
||||
# ManifestStaticFilesStorage is recommended in production, to prevent outdated
|
||||
# Javascript / CSS assets being served from cache (e.g. after a Wagtail upgrade).
|
||||
# See https://docs.djangoproject.com/en/2.1/ref/contrib/staticfiles/#manifeststaticfilesstorage
|
||||
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
|
||||
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
|
||||
# Wagtail settings
|
||||
|
||||
WAGTAIL_SITE_NAME = "datasketch.io"
|
||||
|
||||
# Base URL to use when referring to full URLs within the Wagtail admin backend -
|
||||
# e.g. in notification emails. Don't include '/admin' or a trailing slash
|
||||
BASE_URL = "https://datasketch.io"
|
@ -0,0 +1,28 @@
|
||||
from .base import *
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = "gi5#h%%co$&cnxt36b8g13o2$$=)!3v+v^iyp)7!^qvrhr)w3+"
|
||||
|
||||
# SECURITY WARNING: define the correct hosts in production!
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": "datasketch",
|
||||
"USER": "datasketch",
|
||||
"PASSWORD": os.environ.get("DB_PASSWD") if os.environ.get("DB_PASSWD") else "",
|
||||
"HOST": os.environ.get("DB_HOST") if os.environ.get("DB_HOST") else "127.0.0.1",
|
||||
"PORT": "5432",
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
from .local import *
|
||||
except ImportError:
|
||||
pass
|
@ -0,0 +1,8 @@
|
||||
from .base import *
|
||||
|
||||
DEBUG = False
|
||||
|
||||
try:
|
||||
from .local import *
|
||||
except ImportError:
|
||||
pass
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block body_class %}template-404{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Page not found</h1>
|
||||
|
||||
<h2>Sorry, this page could not be found.</h2>
|
||||
{% endblock %}
|
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Internal server error</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Internal server error</h1>
|
||||
|
||||
<h2>Sorry, there seems to be an error. Please try again soon.</h2>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,41 @@
|
||||
{% load static wagtailuserbar %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>
|
||||
{% block title %}
|
||||
{% if self.seo_title %}{{ self.seo_title }}{% else %}{{ self.title }}{% endif %}
|
||||
{% endblock %}
|
||||
{% block title_suffix %}
|
||||
{% with self.get_site.site_name as site_name %}
|
||||
{% if site_name %}- {{ site_name }}{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
</title>
|
||||
<meta name="description" content="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
{# Global stylesheets #}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/tachyons.min.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/cms.css' %}">
|
||||
|
||||
{% block extra_css %}
|
||||
{# Override this in templates to add extra stylesheets #}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="{% block body_class %}{% endblock %}">
|
||||
{% wagtailuserbar %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
{# Global javascript #}
|
||||
<script type="text/javascript" src="{% static 'js/cms.js' %}"></script>
|
||||
|
||||
{% block extra_js %}
|
||||
{# Override this in templates to add extra javascript #}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,32 @@
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail.core import urls as wagtail_urls
|
||||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
|
||||
from search import views as search_views
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^django-admin/", admin.site.urls),
|
||||
url(r"^admin/", include(wagtailadmin_urls)),
|
||||
url(r"^documents/", include(wagtaildocs_urls)),
|
||||
url(r"^search/$", search_views.search, name="search"),
|
||||
# For anything not caught by a more specific rule above, hand over to
|
||||
# Wagtail's page serving mechanism. This should be the last pattern in
|
||||
# the list:
|
||||
url(r"", include(wagtail_urls)),
|
||||
# Alternatively, if you want Wagtail pages to be served from a subpath
|
||||
# of your site, rather than the site root:
|
||||
# url(r'^pages/', include(wagtail_urls)),
|
||||
]
|
||||
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
# Serve static and media files from development server
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for cms project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings.dev")
|
||||
|
||||
application = get_wsgi_application()
|
@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("wagtailcore", "0040_page_draft_title")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="HomePage",
|
||||
fields=[
|
||||
(
|
||||
"page_ptr",
|
||||
models.OneToOneField(
|
||||
on_delete=models.CASCADE,
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="wagtailcore.Page",
|
||||
),
|
||||
)
|
||||
],
|
||||
options={"abstract": False},
|
||||
bases=("wagtailcore.page",),
|
||||
)
|
||||
]
|
@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_homepage(apps, schema_editor):
|
||||
# Get models
|
||||
ContentType = apps.get_model("contenttypes.ContentType")
|
||||
Page = apps.get_model("wagtailcore.Page")
|
||||
Site = apps.get_model("wagtailcore.Site")
|
||||
HomePage = apps.get_model("home.HomePage")
|
||||
|
||||
# Delete the default homepage
|
||||
# If migration is run multiple times, it may have already been deleted
|
||||
Page.objects.filter(id=2).delete()
|
||||
|
||||
# Create content type for homepage model
|
||||
homepage_content_type, __ = ContentType.objects.get_or_create(
|
||||
model="homepage", app_label="home"
|
||||
)
|
||||
|
||||
# Create a new homepage
|
||||
homepage = HomePage.objects.create(
|
||||
title="Home",
|
||||
draft_title="Home",
|
||||
slug="home",
|
||||
content_type=homepage_content_type,
|
||||
path="00010001",
|
||||
depth=2,
|
||||
numchild=0,
|
||||
url_path="/home/",
|
||||
)
|
||||
|
||||
# Create a site with the new homepage set as the root
|
||||
Site.objects.create(hostname="localhost", root_page=homepage, is_default_site=True)
|
||||
|
||||
|
||||
def remove_homepage(apps, schema_editor):
|
||||
# Get models
|
||||
ContentType = apps.get_model("contenttypes.ContentType")
|
||||
HomePage = apps.get_model("home.HomePage")
|
||||
|
||||
# Delete the default homepage
|
||||
# Page and Site objects CASCADE
|
||||
HomePage.objects.filter(slug="home", depth=2).delete()
|
||||
|
||||
# Delete content type for homepage model
|
||||
ContentType.objects.filter(model="homepage", app_label="home").delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("home", "0001_initial")]
|
||||
|
||||
operations = [migrations.RunPython(create_homepage, remove_homepage)]
|
@ -0,0 +1,7 @@
|
||||
from django.db import models
|
||||
|
||||
from wagtail.core.models import Page
|
||||
|
||||
|
||||
class HomePage(Page):
|
||||
pass
|
@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block body_class %}helvetica{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Welcome to Datasketch.io!</h1>
|
||||
{% endblock %}
|
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings.dev")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
@ -0,0 +1,3 @@
|
||||
Django>=2.1,<2.2
|
||||
wagtail>=2.3,<2.4
|
||||
psycopg2>=2.7.5
|
@ -0,0 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static wagtailcore_tags %}
|
||||
|
||||
{% block body_class %}template-searchresults{% endblock %}
|
||||
|
||||
{% block title %}Search{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Search</h1>
|
||||
|
||||
<form action="{% url 'search' %}" method="get">
|
||||
<input type="text" name="query"{% if search_query %} value="{{ search_query }}"{% endif %}>
|
||||
<input type="submit" value="Search" class="button">
|
||||
</form>
|
||||
|
||||
{% if search_results %}
|
||||
<ul>
|
||||
{% for result in search_results %}
|
||||
<li>
|
||||
<h4><a href="{% pageurl result %}">{{ result }}</a></h4>
|
||||
{% if result.search_description %}
|
||||
{{ result.search_description }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% if search_results.has_previous %}
|
||||
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&page={{ search_results.previous_page_number }}">Previous</a>
|
||||
{% endif %}
|
||||
|
||||
{% if search_results.has_next %}
|
||||
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&page={{ search_results.next_page_number }}">Next</a>
|
||||
{% endif %}
|
||||
{% elif search_query %}
|
||||
No results found
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -0,0 +1,35 @@
|
||||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||
from django.shortcuts import render
|
||||
|
||||
from wagtail.core.models import Page
|
||||
from wagtail.search.models import Query
|
||||
|
||||
|
||||
def search(request):
|
||||
search_query = request.GET.get("query", None)
|
||||
page = request.GET.get("page", 1)
|
||||
|
||||
# Search
|
||||
if search_query:
|
||||
search_results = Page.objects.live().search(search_query)
|
||||
query = Query.get(search_query)
|
||||
|
||||
# Record hit
|
||||
query.add_hit()
|
||||
else:
|
||||
search_results = Page.objects.none()
|
||||
|
||||
# Pagination
|
||||
paginator = Paginator(search_results, 10)
|
||||
try:
|
||||
search_results = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
search_results = paginator.page(1)
|
||||
except EmptyPage:
|
||||
search_results = paginator.page(paginator.num_pages)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"search/search.html",
|
||||
{"search_query": search_query, "search_results": search_results},
|
||||
)
|
Loading…
Reference in New Issue