diff --git a/.gitignore b/.gitignore index 802daca..a2a3201 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ *.egg-info/ __pycache__ .vscode/ +.venv/ +postgres_data/ diff --git a/README.md b/README.md index 7faf99c..f1d83c7 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,25 @@ A Django app that allows tracking government plans. Deployed at: https://fragdenstaat.de/koalitionstracker/ -Currently has some hard dependencies on [froide](https://github.com/okfde/froide/) and some less hard ones on [fragdenstaat_de](https://github.com/okfde/fragdenstaat_de/). + +## Install stand-alone + +Requires [GDAL/Geos for GeoDjango](https://docs.djangoproject.com/en/4.1/ref/contrib/gis/install/geolibs/). + +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install -e . +./manage.py migrate +# Create admin user +./manage.py createsuperuser +./manage.py runserver +``` + + +1. Go to http://localhost:8000/admin/ +2. Setup a homepage in the CMS: http://localhost:8000/admin/cms/page/ +3. Setup a page (could be the homepage) and then choose under advanced setting the Govplan app as application. +4. Publish that page +5. Setup a government and plans via the admin. + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c941486 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + db: + image: postgis/postgis:14-3.3-alpine + volumes: + - ./postgres_data:/var/lib/postgresql/data/ + ports: + - "127.0.0.1:5432:5432" + environment: + POSTGRES_USER: govplan + POSTGRES_DB: govplan + POSTGRES_PASSWORD: govplan + +volumes: + postgres_data: {} diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..e170f6b --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/project/__init__.py b/project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/project/asgi.py b/project/asgi.py new file mode 100644 index 0000000..486c89b --- /dev/null +++ b/project/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for project project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") + +application = get_asgi_application() diff --git a/project/settings.py b/project/settings.py new file mode 100644 index 0000000..9c4581f --- /dev/null +++ b/project/settings.py @@ -0,0 +1,200 @@ +""" +Django settings for project project. + +Generated by 'django-admin startproject' using Django 4.1.6. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.1/ref/settings/ +""" + +import os +from pathlib import Path + +from django.utils.translation import gettext_lazy as _ + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent +PROJECT_DIR = Path(__file__).resolve().parent + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "django-insecure-8qh(ha_fw#3mc#y+rtd^se+e$-+n5%1o*d3%3p0d379q1zxypu" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + # "djangocms_admin_style", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.sites", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.humanize", + "django.contrib.gis", + # Froide apps + "froide_govplan.apps.FroideGovPlanConfig", + "froide.georegion", + "froide.publicbody", + "froide.follow", + "froide.organization", + "froide.team", + "froide.helper", + # Third party apps + "easy_thumbnails", + "filer", + "mptt", + "sekizai", + "cms", + "menus", + "treebeard", + # "oauth2_provider", + # "mfa", + "taggit", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.middleware.locale.LocaleMiddleware", # needs to be before CommonMiddleware + "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", + "cms.middleware.user.CurrentUserMiddleware", + "cms.middleware.page.CurrentPageMiddleware", + "cms.middleware.toolbar.ToolbarMiddleware", + "cms.middleware.language.LanguageCookieMiddleware", +] + +ROOT_URLCONF = "project.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + 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", + "sekizai.context_processors.sekizai", + "cms.context_processors.cms_settings", + ], + }, + }, +] + +WSGI_APPLICATION = "project.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/4.1/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.contrib.gis.db.backends.postgis", # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + # "NAME": "fragdenstaat_de", + "NAME": "govplan", + "USER": "govplan", + "PASSWORD": "govplan", + "HOST": "localhost", # Set to empty string for localhost. Not used with sqlite3. + "PORT": "5432", # Set to empty string for default. Not used with sqlite3. + } +} +GDAL_LIBRARY_PATH = os.environ.get("GDAL_LIBRARY_PATH") +GEOS_LIBRARY_PATH = os.environ.get("GEOS_LIBRARY_PATH") + + +# Password validation +# https://docs.djangoproject.com/en/4.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/4.1/topics/i18n/ + +LANGUAGE_CODE = "de" +LANGUAGES = [("de", _("German"))] + + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.1/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" +# AUTH_USER_MODEL = "account.User" + +TAGGIT_CASE_INSENSITIVE = True +TAGGIT_STRIP_UNICODE_WHEN_SLUGIFYING = True + +LOGIN_URL = "account-login" +X_FRAME_OPTIONS = "SAMEORIGIN" + +# Django CMS + +CMS_TEMPLATES = [ + ("froide_govplan/base.html", "Govplan base template"), +] + +# Froide + +SITE_ID = 1 +SITE_NAME = "GovPlan" +SITE_URL = "http://localhost:8000" + +FROIDE_CONFIG = { + "bounce_enabled": False, + "bounce_format": "bounce+{token}@example.com", + "bounce_max_age": 60 * 60 * 24 * 14, # 14 days + "bounce_format": "bounce+{token}@example.com", + "unsubscribe_enabled": False, + "unsubscribe_format": "unsub+{token}@example.com", +} + +# Govplan settings + +GOVPLAN_NAME = "GovPlan" +GOVPLAN_ENABLE_FOIREQUEST = False diff --git a/project/templates/base.html b/project/templates/base.html new file mode 100644 index 0000000..c430d20 --- /dev/null +++ b/project/templates/base.html @@ -0,0 +1,38 @@ +{% load i18n %}{% load static %}{% load cms_tags %}{% load sekizai_tags %} + + + + + + + {% block title %}{% page_attribute "page_title" %} - {{ SITE_NAME }}{% endblock %} + + {% block header_font %}{% endblock %} + + {% block css %} + + {% block extra_css %} + {% endblock %} + {% endblock %} + {% render_block "css" %} + + {% block meta %} + {# Translators: meta description #} + {% endblock %} + + {% block extra_head %} + {% endblock %} + + + {% cms_toolbar %} + {% block body_tag %}{% block body %}{% block main %}{% endblock %}{% endblock %}{% endblock %} + {% block extra_footer %}{% endblock %} + + {% block scripts %} + + {% endblock %} + {% render_block "js" %} + + {% block below_scripts %}{% endblock %} + + diff --git a/project/templates/cms/page.html b/project/templates/cms/page.html new file mode 100644 index 0000000..c3c2375 --- /dev/null +++ b/project/templates/cms/page.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% load cms_tags %} + +{% block body %} + {% block app_body %} + {% placeholder "content" %} + {% endblock %} +{% endblock %} diff --git a/project/templates/header.html b/project/templates/header.html new file mode 100644 index 0000000..44334e3 --- /dev/null +++ b/project/templates/header.html @@ -0,0 +1,3 @@ + diff --git a/project/templates/header_reduced.html b/project/templates/header_reduced.html new file mode 100644 index 0000000..9e8dc9f --- /dev/null +++ b/project/templates/header_reduced.html @@ -0,0 +1,5 @@ +{% extends "header.html" %} + +{% block nav_search %}{% endblock %} + +{% block nav %}{% endblock nav %} diff --git a/project/templates/snippets/meta.html b/project/templates/snippets/meta.html new file mode 100644 index 0000000..e69de29 diff --git a/project/urls.py b/project/urls.py new file mode 100644 index 0000000..9e2c772 --- /dev/null +++ b/project/urls.py @@ -0,0 +1,30 @@ +"""project URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import include, path + +# from froide_govplan.admin import govplan_admin_site + +urlpatterns = [ + path( + "follow/", + include("froide.follow.urls", namespace="follow"), + ), + # path("account/", include("froide.account.urls")), +] + [ + path("admin/", admin.site.urls), + path("", include("cms.urls")), +] diff --git a/project/wsgi.py b/project/wsgi.py new file mode 100644 index 0000000..b5da491 --- /dev/null +++ b/project/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for project 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/4.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") + +application = get_wsgi_application()