diff --git a/Makefile b/Makefile
index 4200f95da..94f7565d8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4656 +1,125 @@
-# Project Makefile
-#
-# A makefile to automate setup of a Wagtail CMS project and related tasks.
-#
-# https://github.com/aclark4life/project-makefile
-#
-# --------------------------------------------------------------------------------
-# Set the default goal to be `git commit -a -m $(GIT_MESSAGE)` and `git push`
-# --------------------------------------------------------------------------------
-
-.DEFAULT_GOAL := git-commit
-
-# --------------------------------------------------------------------------------
-# Single line variables to be used by phony target rules
-# --------------------------------------------------------------------------------
-
-ADD_DIR := mkdir -pv
-ADD_FILE := touch
-AWS_OPTS := --no-cli-pager --output table
-COPY_DIR := cp -rv
-COPY_FILE := cp -v
-DEL_DIR := rm -rv
-DEL_FILE := rm -v
-DJANGO_DB_COL = awk -F\= '{print $$2}'
-DJANGO_DB_URL = eb ssh -c "source /opt/elasticbeanstalk/deployment/custom_env_var; env | grep DATABASE_URL"
-DJANGO_DB_HOST = $(shell $(DJANGO_DB_URL) | $(DJANGO_DB_COL) |\
- python -c 'import dj_database_url; url = input(); url = dj_database_url.parse(url); print(url["HOST"])')
-DJANGO_DB_NAME = $(shell $(DJANGO_DB_URL) | $(DJANGO_DB_COL) |\
- python -c 'import dj_database_url; url = input(); url = dj_database_url.parse(url); print(url["NAME"])')
-DJANGO_DB_PASS = $(shell $(DJANGO_DB_URL) | $(DJANGO_DB_COL) |\
- python -c 'import dj_database_url; url = input(); url = dj_database_url.parse(url); print(url["PASSWORD"])')
-DJANGO_DB_USER = $(shell $(DJANGO_DB_URL) | $(DJANGO_DB_COL) |\
- python -c 'import dj_database_url; url = input(); url = dj_database_url.parse(url); print(url["USER"])')
-DJANGO_BACKEND_APPS_FILE := backend/apps.py
-DJANGO_CUSTOM_ADMIN_FILE := backend/admin.py
-DJANGO_FRONTEND_FILES = .babelrc .browserslistrc .eslintrc .nvmrc .stylelintrc.json frontend package-lock.json \
- package.json postcss.config.js
-DJANGO_SETTINGS_DIR = backend/settings
-DJANGO_SETTINGS_BASE_FILE = $(DJANGO_SETTINGS_DIR)/base.py
-DJANGO_SETTINGS_DEV_FILE = $(DJANGO_SETTINGS_DIR)/dev.py
-DJANGO_SETTINGS_PROD_FILE = $(DJANGO_SETTINGS_DIR)/production.py
-DJANGO_SETTINGS_SECRET_KEY = $(shell openssl rand -base64 48)
-DJANGO_URLS_FILE = backend/urls.py
-EB_DIR_NAME := .elasticbeanstalk
-EB_ENV_NAME ?= $(PROJECT_NAME)-$(GIT_BRANCH)-$(GIT_REV)
-EB_PLATFORM ?= "Python 3.11 running on 64bit Amazon Linux 2023"
-EC2_INSTANCE_MAX ?= 1
-EC2_INSTANCE_MIN ?= 1
-EC2_INSTANCE_PROFILE ?= aws-elasticbeanstalk-ec2-role
-EC2_INSTANCE_TYPE ?= t4g.small
-EC2_LB_TYPE ?= application
-EDITOR_REVIEW = subl
-GIT_ADD := git add
-GIT_BRANCH = $(shell git branch --show-current)
-GIT_BRANCHES = $(shell git branch -a)
-GIT_CHECKOUT = git checkout
-GIT_COMMIT_MSG = "Update $(PROJECT_NAME)"
-GIT_COMMIT = git commit
-GIT_PUSH = git push
-GIT_PUSH_FORCE = git push --force-with-lease
-GIT_REV = $(shell git rev-parse --short HEAD)
-MAKEFILE_CUSTOM_FILE := project.mk
-PACKAGE_NAME = $(shell echo $(PROJECT_NAME) | sed 's/-/_/g')
-PAGER ?= less
-PIP_ENSURE = python -m ensurepip
-PIP_INSTALL_PLONE_CONSTRAINTS = https://dist.plone.org/release/6.0.11.1/constraints.txt
-PROJECT_DIRS = backend contactpage home privacy siteuser
-PROJECT_EMAIL := aclark@aclark.net
-PROJECT_NAME = project-makefile
-RANDIR := $(shell openssl rand -base64 12 | sed 's/\///g')
-TMPDIR := $(shell mktemp -d)
-UNAME := $(shell uname)
-WAGTAIL_CLEAN_DIRS = backend contactpage dist frontend home logging_demo model_form_demo node_modules payments privacy search sitepage siteuser
-WAGTAIL_CLEAN_FILES = .babelrc .browserslistrc .dockerignore .eslintrc .gitignore .nvmrc .stylelintrc.json Dockerfile db.sqlite3 docker-compose.yml manage.py package-lock.json package.json postcss.config.js requirements-test.txt requirements.txt
-
-# --------------------------------------------------------------------------------
-# Include $(MAKEFILE_CUSTOM_FILE) if it exists
-# --------------------------------------------------------------------------------
-
-ifneq ($(wildcard $(MAKEFILE_CUSTOM_FILE)),)
- include $(MAKEFILE_CUSTOM_FILE)
-endif
-
-# --------------------------------------------------------------------------------
-# Multi-line variables to be used in phony target rules
-# --------------------------------------------------------------------------------
-
-define DJANGO_ALLAUTH_BASE_TEMPLATE
-{% extends 'base.html' %}
-endef
-
-define DJANGO_API_SERIALIZERS
-from rest_framework import serializers
-from siteuser.models import User
-
-
-class UserSerializer(serializers.HyperlinkedModelSerializer):
- class Meta:
- model = User
- fields = ["url", "username", "email", "is_staff"]
-endef
-
-define DJANGO_API_VIEWS
-from ninja import NinjaAPI
-from rest_framework import viewsets
-from siteuser.models import User
-from .serializers import UserSerializer
-
-api = NinjaAPI()
-
-
-@api.get("/hello")
-def hello(request):
- return "Hello world"
-
-
-class UserViewSet(viewsets.ModelViewSet):
- queryset = User.objects.all()
- serializer_class = UserSerializer
-endef
-
-define DJANGO_APP_TESTS
-from django.test import TestCase
-from django.urls import reverse
-from .models import YourModel
-from .forms import YourForm
-
-
-class YourModelTest(TestCase):
- def setUp(self):
- self.instance = YourModel.objects.create(field1="value1", field2="value2")
-
- def test_instance_creation(self):
- self.assertIsInstance(self.instance, YourModel)
- self.assertEqual(self.instance.field1, "value1")
- self.assertEqual(self.instance.field2, "value2")
-
- def test_str_method(self):
- self.assertEqual(str(self.instance), "Expected String Representation")
-
-
-class YourViewTest(TestCase):
- def setUp(self):
- self.instance = YourModel.objects.create(field1="value1", field2="value2")
-
- def test_view_url_exists_at_desired_location(self):
- response = self.client.get("/your-url/")
- self.assertEqual(response.status_code, 200)
-
- def test_view_url_accessible_by_name(self):
- response = self.client.get(reverse("your-view-name"))
- self.assertEqual(response.status_code, 200)
-
- def test_view_uses_correct_template(self):
- response = self.client.get(reverse("your-view-name"))
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, "your_template.html")
-
- def test_view_context(self):
- response = self.client.get(reverse("your-view-name"))
- self.assertEqual(response.status_code, 200)
- self.assertIn("context_variable", response.context)
-
-
-class YourFormTest(TestCase):
- def test_form_valid_data(self):
- form = YourForm(data={"field1": "value1", "field2": "value2"})
- self.assertTrue(form.is_valid())
-
- def test_form_invalid_data(self):
- form = YourForm(data={"field1": "", "field2": "value2"})
- self.assertFalse(form.is_valid())
- self.assertIn("field1", form.errors)
-
- def test_form_save(self):
- form = YourForm(data={"field1": "value1", "field2": "value2"})
- if form.is_valid():
- instance = form.save()
- self.assertEqual(instance.field1, "value1")
- self.assertEqual(instance.field2, "value2")
-endef
-
-define DJANGO_BACKEND_APPS
-from django.contrib.admin.apps import AdminConfig
-
-
-class CustomAdminConfig(AdminConfig):
- default_site = "backend.admin.CustomAdminSite"
-endef
-
-define DJANGO_BASE_TEMPLATE
-{% load static webpack_loader %}
-
-
-
-
-
- {% block title %}{% endblock %}
- {% block title_suffix %}{% endblock %}
-
-
- {% stylesheet_pack 'app' %}
- {% block extra_css %}{# Override this in templates to add extra stylesheets #}{% endblock %}
-
- {% include 'favicon.html' %}
- {% csrf_token %}
-
-
-
-
- {% include 'header.html' %}
- {% if messages %}
-
- {% for message in messages %}
-
- {{ message }}
-
-
- {% endfor %}
-
- {% endif %}
-
- {% block content %}{% endblock %}
-
-
- {% include 'footer.html' %}
- {% include 'offcanvas.html' %}
- {% javascript_pack 'app' %}
- {% block extra_js %}{# Override this in templates to add extra javascript #}{% endblock %}
-
-
-endef
-
-define DJANGO_CUSTOM_ADMIN
-from django.contrib.admin import AdminSite
-
-
-class CustomAdminSite(AdminSite):
- site_header = "Project Makefile"
- site_title = "Project Makefile"
- index_title = "Project Makefile"
-
-
-custom_admin_site = CustomAdminSite(name="custom_admin")
-endef
-
-define DJANGO_DOCKERCOMPOSE
-version: '3'
-
-services:
- db:
- image: postgres:latest
- volumes:
- - postgres_data:/var/lib/postgresql/data
- environment:
- POSTGRES_DB: project
- POSTGRES_USER: admin
- POSTGRES_PASSWORD: admin
-
- web:
- build: .
- command: sh -c "python manage.py migrate && gunicorn project.wsgi:application -b 0.0.0.0:8000"
- volumes:
- - .:/app
- ports:
- - "8000:8000"
- depends_on:
- - db
- environment:
- DATABASE_URL: postgres://admin:admin@db:5432/project
-
-volumes:
- postgres_data:
-endef
-
-define DJANGO_DOCKERFILE
-FROM amazonlinux:2023
-RUN dnf install -y shadow-utils python3.11 python3.11-pip make nodejs20-npm nodejs postgresql15 postgresql15-server
-USER postgres
-RUN initdb -D /var/lib/pgsql/data
-USER root
-RUN useradd wagtail
-EXPOSE 8000
-ENV PYTHONUNBUFFERED=1 PORT=8000
-COPY requirements.txt /
-RUN python3.11 -m pip install -r /requirements.txt
-WORKDIR /app
-RUN chown wagtail:wagtail /app
-COPY --chown=wagtail:wagtail . .
-USER wagtail
-RUN npm-20 install; npm-20 run build
-RUN python3.11 manage.py collectstatic --noinput --clear
-CMD set -xe; pg_ctl -D /var/lib/pgsql/data -l /tmp/logfile start; python3.11 manage.py migrate --noinput; gunicorn backend.wsgi:application
-endef
-
-define DJANGO_FAVICON_TEMPLATE
-{% load static %}
-
-endef
-
-define DJANGO_FOOTER_TEMPLATE
-
-endef
-
-define DJANGO_FRONTEND_APP
-import React from 'react';
-import { createRoot } from 'react-dom/client';
-import 'bootstrap';
-import '@fortawesome/fontawesome-free/js/fontawesome';
-import '@fortawesome/fontawesome-free/js/solid';
-import '@fortawesome/fontawesome-free/js/regular';
-import '@fortawesome/fontawesome-free/js/brands';
-import getDataComponents from '../dataComponents';
-import UserContextProvider from '../context';
-import * as components from '../components';
-import "../styles/index.scss";
-import "../styles/theme-blue.scss";
-import "./config";
-
-const { ErrorBoundary } = components;
-const dataComponents = getDataComponents(components);
-const container = document.getElementById('app');
-const root = createRoot(container);
-const App = () => (
-
-
- {dataComponents}
-
-
-);
-root.render();
-endef
-
-define DJANGO_FRONTEND_APP_CONFIG
-import '../utils/themeToggler.js';
-// import '../utils/tinymce.js';
-endef
-
-define DJANGO_FRONTEND_BABELRC
-{
- "presets": [
- [
- "@babel/preset-react",
- ],
- [
- "@babel/preset-env",
- {
- "useBuiltIns": "usage",
- "corejs": "3.0.0"
- }
- ]
- ],
- "plugins": [
- "@babel/plugin-syntax-dynamic-import",
- "@babel/plugin-transform-class-properties"
- ]
-}
-endef
-
-define DJANGO_FRONTEND_COMPONENTS
-export { default as ErrorBoundary } from './ErrorBoundary';
-export { default as UserMenu } from './UserMenu';
-endef
-
-define DJANGO_FRONTEND_COMPONENT_CLOCK
-// Via ChatGPT
-import React, { useState, useEffect, useCallback, useRef } from 'react';
-import PropTypes from 'prop-types';
-
-const Clock = ({ color = '#fff' }) => {
- const [date, setDate] = useState(new Date());
- const [blink, setBlink] = useState(true);
- const timerID = useRef();
-
- const tick = useCallback(() => {
- setDate(new Date());
- setBlink(prevBlink => !prevBlink);
- }, []);
-
- useEffect(() => {
- timerID.current = setInterval(() => tick(), 1000);
-
- // Return a cleanup function to be run on component unmount
- return () => clearInterval(timerID.current);
- }, [tick]);
-
- const formattedDate = date.toLocaleDateString(undefined, {
- weekday: 'short',
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- });
-
- const formattedTime = date.toLocaleTimeString(undefined, {
- hour: 'numeric',
- minute: 'numeric',
- });
-
- return (
- <>
- {formattedDate} {formattedTime}
- >
- );
-};
-
-Clock.propTypes = {
- color: PropTypes.string,
-};
-
-export default Clock;
-endef
-
-define DJANGO_FRONTEND_COMPONENT_ERROR
-import { Component } from 'react';
-import PropTypes from 'prop-types';
-
-class ErrorBoundary extends Component {
- constructor (props) {
- super(props);
- this.state = { hasError: false };
- }
-
- static getDerivedStateFromError () {
- return { hasError: true };
- }
-
- componentDidCatch (error, info) {
- const { onError } = this.props;
- console.error(error);
- onError && onError(error, info);
- }
-
- render () {
- const { children = null } = this.props;
- const { hasError } = this.state;
-
- return hasError ? null : children;
- }
-}
-
-ErrorBoundary.propTypes = {
- onError: PropTypes.func,
- children: PropTypes.node,
-};
-
-export default ErrorBoundary;
-endef
-
-define DJANGO_FRONTEND_COMPONENT_USER_MENU
-// UserMenu.js
-import React from 'react';
-import PropTypes from 'prop-types';
-
-function handleLogout() {
- window.location.href = '/accounts/logout';
-}
-
-const UserMenu = ({ isAuthenticated, isSuperuser, textColor }) => {
- return (
-
- {isAuthenticated ? (
-
-
-
-
-
-
- ) : (
-
-
-
- )}
-
- );
-};
-
-UserMenu.propTypes = {
- isAuthenticated: PropTypes.bool.isRequired,
- isSuperuser: PropTypes.bool.isRequired,
- textColor: PropTypes.string,
-};
-
-export default UserMenu;
-endef
-
-define DJANGO_FRONTEND_CONTEXT_INDEX
-export { UserContextProvider as default } from './UserContextProvider';
-endef
-
-define DJANGO_FRONTEND_CONTEXT_USER_PROVIDER
-// UserContextProvider.js
-import React, { createContext, useContext, useState } from 'react';
-import PropTypes from 'prop-types';
-
-const UserContext = createContext();
-
-export const UserContextProvider = ({ children }) => {
- const [isAuthenticated, setIsAuthenticated] = useState(false);
-
- const login = () => {
- try {
- // Add logic to handle login, set isAuthenticated to true
- setIsAuthenticated(true);
- } catch (error) {
- console.error('Login error:', error);
- // Handle error, e.g., show an error message to the user
- }
- };
-
- const logout = () => {
- try {
- // Add logic to handle logout, set isAuthenticated to false
- setIsAuthenticated(false);
- } catch (error) {
- console.error('Logout error:', error);
- // Handle error, e.g., show an error message to the user
- }
- };
-
- return (
-
- {children}
-
- );
-};
-
-UserContextProvider.propTypes = {
- children: PropTypes.node.isRequired,
-};
-
-export const useUserContext = () => {
- const context = useContext(UserContext);
-
- if (!context) {
- throw new Error('useUserContext must be used within a UserContextProvider');
- }
-
- return context;
-};
-
-// Add PropTypes for the return value of useUserContext
-useUserContext.propTypes = {
- isAuthenticated: PropTypes.bool.isRequired,
- login: PropTypes.func.isRequired,
- logout: PropTypes.func.isRequired,
-};
-endef
-
-define DJANGO_FRONTEND_ESLINTRC
-{
- "env": {
- "browser": true,
- "es2021": true,
- "node": true
- },
- "extends": [
- "eslint:recommended",
- "plugin:react/recommended"
- ],
- "overrides": [
- {
- "env": {
- "node": true
- },
- "files": [
- ".eslintrc.{js,cjs}"
- ],
- "parserOptions": {
- "sourceType": "script"
- }
- }
- ],
- "parserOptions": {
- "ecmaVersion": "latest",
- "sourceType": "module"
- },
- "plugins": [
- "react"
- ],
- "rules": {
- "no-unused-vars": "off"
- },
- settings: {
- react: {
- version: 'detect',
- },
- },
-}
-endef
-
-define DJANGO_FRONTEND_OFFCANVAS_TEMPLATE
-
-
-
-
- -
- Home
-
- {% for child in current_site.root_page.get_children %}
- -
- {{ child }}
-
- {% endfor %}
- -
-
-
-
-
-
-
-
-
-endef
-
-define DJANGO_FRONTEND_PORTAL
-// Via pwellever
-import React from 'react';
-import { createPortal } from 'react-dom';
-
-const parseProps = data => Object.entries(data).reduce((result, [key, value]) => {
- if (value.toLowerCase() === 'true') {
- value = true;
- } else if (value.toLowerCase() === 'false') {
- value = false;
- } else if (value.toLowerCase() === 'null') {
- value = null;
- } else if (!isNaN(parseFloat(value)) && isFinite(value)) {
- // Parse numeric value
- value = parseFloat(value);
- } else if (
- (value[0] === '[' && value.slice(-1) === ']') || (value[0] === '{' && value.slice(-1) === '}')
- ) {
- // Parse JSON strings
- value = JSON.parse(value);
- }
-
- result[key] = value;
- return result;
-}, {});
-
-// This method of using portals instead of calling ReactDOM.render on individual components
-// ensures that all components are mounted under a single React tree, and are therefore able
-// to share context.
-
-export default function getPageComponents (components) {
- const getPortalComponent = domEl => {
- // The element's "data-component" attribute is used to determine which component to render.
- // All other "data-*" attributes are passed as props.
- const { component: componentName, ...rest } = domEl.dataset;
- const Component = components[componentName];
- if (!Component) {
- console.error(`Component "$${componentName}" not found.`);
- return null;
- }
- const props = parseProps(rest);
- domEl.innerHTML = '';
-
- // eslint-disable-next-line no-unused-vars
- const { ErrorBoundary } = components;
- return createPortal(
-
-
- ,
- domEl,
- );
- };
-
- return Array.from(document.querySelectorAll('[data-component]')).map(getPortalComponent);
-}
-endef
-
-define DJANGO_FRONTEND_STYLES
-// If you comment out code below, bootstrap will use red as primary color
-// and btn-primary will become red
-
-// $primary: red;
-
-@import "~bootstrap/scss/bootstrap.scss";
-
-.jumbotron {
- // should be relative path of the entry scss file
- background-image: url("../../vendors/images/sample.jpg");
- background-size: cover;
-}
-
-#theme-toggler-authenticated:hover {
- cursor: pointer; /* Change cursor to pointer on hover */
- color: #007bff; /* Change color on hover */
-}
-
-#theme-toggler-anonymous:hover {
- cursor: pointer; /* Change cursor to pointer on hover */
- color: #007bff; /* Change color on hover */
-}
-endef
-
-define DJANGO_FRONTEND_THEME_BLUE
-@import "~bootstrap/scss/bootstrap.scss";
-
-[data-bs-theme="blue"] {
- --bs-body-color: var(--bs-white);
- --bs-body-color-rgb: #{to-rgb($$white)};
- --bs-body-bg: var(--bs-blue);
- --bs-body-bg-rgb: #{to-rgb($$blue)};
- --bs-tertiary-bg: #{$$blue-600};
-
- .dropdown-menu {
- --bs-dropdown-bg: #{color-mix($$blue-500, $$blue-600)};
- --bs-dropdown-link-active-bg: #{$$blue-700};
- }
-
- .btn-secondary {
- --bs-btn-bg: #{color-mix($gray-600, $blue-400, .5)};
- --bs-btn-border-color: #{rgba($$white, .25)};
- --bs-btn-hover-bg: #{color-adjust(color-mix($gray-600, $blue-400, .5), 5%)};
- --bs-btn-hover-border-color: #{rgba($$white, .25)};
- --bs-btn-active-bg: #{color-adjust(color-mix($gray-600, $blue-400, .5), 10%)};
- --bs-btn-active-border-color: #{rgba($$white, .5)};
- --bs-btn-focus-border-color: #{rgba($$white, .5)};
-
- // --bs-btn-focus-box-shadow: 0 0 0 .25rem rgba(255, 255, 255, 20%);
- }
-}
-endef
-
-define DJANGO_FRONTEND_THEME_TOGGLER
-document.addEventListener('DOMContentLoaded', function () {
- const rootElement = document.documentElement;
- const anonThemeToggle = document.getElementById('theme-toggler-anonymous');
- const authThemeToggle = document.getElementById('theme-toggler-authenticated');
- if (authThemeToggle) {
- localStorage.removeItem('data-bs-theme');
- }
- const anonSavedTheme = localStorage.getItem('data-bs-theme');
- if (anonSavedTheme) {
- rootElement.setAttribute('data-bs-theme', anonSavedTheme);
- }
- if (anonThemeToggle) {
- anonThemeToggle.addEventListener('click', function () {
- const currentTheme = rootElement.getAttribute('data-bs-theme') || 'light';
- const newTheme = currentTheme === 'light' ? 'dark' : 'light';
- rootElement.setAttribute('data-bs-theme', newTheme);
- localStorage.setItem('data-bs-theme', newTheme);
- });
- }
- if (authThemeToggle) {
- const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
- authThemeToggle.addEventListener('click', function () {
- const currentTheme = rootElement.getAttribute('data-bs-theme') || 'light';
- const newTheme = currentTheme === 'light' ? 'dark' : 'light';
- fetch('/user/update_theme_preference/', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'X-CSRFToken': csrfToken, // Include the CSRF token in the headers
- },
- body: JSON.stringify({ theme: newTheme }),
- })
- .then(response => response.json())
- .then(data => {
- rootElement.setAttribute('data-bs-theme', newTheme);
- })
- .catch(error => {
- console.error('Error updating theme preference:', error);
- });
- });
- }
-});
-endef
-
-define DJANGO_HEADER_TEMPLATE
-
-endef
-
-define DJANGO_HOME_PAGE_ADMIN
-from django.contrib import admin # noqa
-
-# Register your models here.
-endef
-
-define DJANGO_HOME_PAGE_MODELS
-from django.db import models # noqa
-
-# Create your models here.
-endef
-
-define DJANGO_HOME_PAGE_TEMPLATE
-{% extends "base.html" %}
-{% block content %}
-
-
-{% endblock %}
-endef
-
-define DJANGO_HOME_PAGE_URLS
-from django.urls import path
-from .views import HomeView
-
-urlpatterns = [path("", HomeView.as_view(), name="home")]
-endef
-
-define DJANGO_HOME_PAGE_VIEWS
-from django.views.generic import TemplateView
-
-
-class HomeView(TemplateView):
- template_name = "home.html"
-endef
-
-define DJANGO_LOGGING_DEMO_ADMIN
-# Register your models here.
-endef
-
-define DJANGO_LOGGING_DEMO_MODELS
-from django.db import models # noqa
-
-# Create your models here.
-endef
-
-define DJANGO_LOGGING_DEMO_SETTINGS
-INSTALLED_APPS.append("logging_demo") # noqa
-endef
-
-define DJANGO_LOGGING_DEMO_URLS
-from django.urls import path
-from .views import logging_demo
-
-urlpatterns = [
- path("", logging_demo, name="logging_demo"),
-]
-endef
-
-define DJANGO_LOGGING_DEMO_VIEWS
-from django.http import HttpResponse
-import logging
-
-logger = logging.getLogger(__name__)
-
-
-def logging_demo(request):
- logger.debug("Hello, world!")
- return HttpResponse("Hello, world!")
-endef
-
-define DJANGO_MANAGE_PY
-#!/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", "backend.settings.dev")
- 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()
-endef
-
-define DJANGO_MODEL_FORM_DEMO_ADMIN
-from django.contrib import admin
-from .models import ModelFormDemo
-
-
-@admin.register(ModelFormDemo)
-class ModelFormDemoAdmin(admin.ModelAdmin):
- pass
-endef
-
-define DJANGO_MODEL_FORM_DEMO_FORMS
-from django import forms
-from .models import ModelFormDemo
-
-
-class ModelFormDemoForm(forms.ModelForm):
- class Meta:
- model = ModelFormDemo
- fields = ["name", "email", "age", "is_active"]
-endef
-
-define DJANGO_MODEL_FORM_DEMO_MODEL
-from django.db import models
-from django.shortcuts import reverse
-
-
-class ModelFormDemo(models.Model):
- name = models.CharField(max_length=100, blank=True, null=True)
- email = models.EmailField(blank=True, null=True)
- age = models.IntegerField(blank=True, null=True)
- is_active = models.BooleanField(default=True)
- created_at = models.DateTimeField(auto_now_add=True)
-
- def __str__(self):
- return self.name or f"test-model-{self.pk}"
-
- def get_absolute_url(self):
- return reverse("model_form_demo_detail", kwargs={"pk": self.pk})
-endef
-
-define DJANGO_MODEL_FORM_DEMO_TEMPLATE_DETAIL
-{% extends 'base.html' %}
-{% block content %}
- Test Model Detail: {{ model_form_demo.name }}
- Name: {{ model_form_demo.name }}
- Email: {{ model_form_demo.email }}
- Age: {{ model_form_demo.age }}
- Active: {{ model_form_demo.is_active }}
- Created At: {{ model_form_demo.created_at }}
- Edit Test Model
-{% endblock %}
-endef
-
-define DJANGO_MODEL_FORM_DEMO_TEMPLATE_FORM
-{% extends 'base.html' %}
-{% block content %}
-
- {% if form.instance.pk %}
- Update Test Model
- {% else %}
- Create Test Model
- {% endif %}
-
-
-{% endblock %}
-endef
-
-define DJANGO_MODEL_FORM_DEMO_TEMPLATE_LIST
-{% extends 'base.html' %}
-{% block content %}
- Test Models List
-
- Create New Test Model
-{% endblock %}
-endef
-
-define DJANGO_MODEL_FORM_DEMO_URLS
-from django.urls import path
-from .views import (
- ModelFormDemoListView,
- ModelFormDemoCreateView,
- ModelFormDemoUpdateView,
- ModelFormDemoDetailView,
-)
-
-urlpatterns = [
- path("", ModelFormDemoListView.as_view(), name="model_form_demo_list"),
- path("create/", ModelFormDemoCreateView.as_view(), name="model_form_demo_create"),
- path(
- "/update/",
- ModelFormDemoUpdateView.as_view(),
- name="model_form_demo_update",
- ),
- path("/", ModelFormDemoDetailView.as_view(), name="model_form_demo_detail"),
-]
-endef
-
-define DJANGO_MODEL_FORM_DEMO_VIEWS
-from django.views.generic import ListView, CreateView, UpdateView, DetailView
-from .models import ModelFormDemo
-from .forms import ModelFormDemoForm
-
-
-class ModelFormDemoListView(ListView):
- model = ModelFormDemo
- template_name = "model_form_demo_list.html"
- context_object_name = "model_form_demos"
-
-
-class ModelFormDemoCreateView(CreateView):
- model = ModelFormDemo
- form_class = ModelFormDemoForm
- template_name = "model_form_demo_form.html"
-
- def form_valid(self, form):
- form.instance.created_by = self.request.user
- return super().form_valid(form)
-
-
-class ModelFormDemoUpdateView(UpdateView):
- model = ModelFormDemo
- form_class = ModelFormDemoForm
- template_name = "model_form_demo_form.html"
-
-
-class ModelFormDemoDetailView(DetailView):
- model = ModelFormDemo
- template_name = "model_form_demo_detail.html"
- context_object_name = "model_form_demo"
-endef
-
-define DJANGO_PAYMENTS_ADMIN
-from django.contrib import admin
-from .models import Product, Order
-
-admin.site.register(Product)
-admin.site.register(Order)
-endef
-
-define DJANGO_PAYMENTS_FORM
-from django import forms
-
-
-class PaymentsForm(forms.Form):
- stripeToken = forms.CharField(widget=forms.HiddenInput())
- amount = forms.DecimalField(
- max_digits=10, decimal_places=2, widget=forms.HiddenInput()
- )
-endef
-
-define DJANGO_PAYMENTS_MIGRATION_0002
-from django.db import migrations
-import os
-import secrets
-import logging
-
-logger = logging.getLogger(__name__)
-
-
-def generate_default_key():
- return "sk_test_" + secrets.token_hex(24)
-
-
-def set_stripe_api_keys(apps, schema_editor):
- # Get the Stripe API Key model
- APIKey = apps.get_model("djstripe", "APIKey")
-
- # Fetch the keys from environment variables or generate default keys
- test_secret_key = os.environ.get("STRIPE_TEST_SECRET_KEY", generate_default_key())
- live_secret_key = os.environ.get("STRIPE_LIVE_SECRET_KEY", generate_default_key())
-
- logger.info("STRIPE_TEST_SECRET_KEY: %s", test_secret_key)
- logger.info("STRIPE_LIVE_SECRET_KEY: %s", live_secret_key)
-
- # Check if the keys are not already in the database
- if not APIKey.objects.filter(secret=test_secret_key).exists():
- APIKey.objects.create(secret=test_secret_key, livemode=False)
- logger.info("Added test secret key to the database.")
- else:
- logger.info("Test secret key already exists in the database.")
-
- if not APIKey.objects.filter(secret=live_secret_key).exists():
- APIKey.objects.create(secret=live_secret_key, livemode=True)
- logger.info("Added live secret key to the database.")
- else:
- logger.info("Live secret key already exists in the database.")
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("payments", "0001_initial"),
- ]
-
- operations = [
- migrations.RunPython(set_stripe_api_keys),
- ]
-endef
-
-define DJANGO_PAYMENTS_MIGRATION_0003
-from django.db import migrations
-
-
-def create_initial_products(apps, schema_editor):
- Product = apps.get_model("payments", "Product")
- Product.objects.create(name="T-shirt", description="A cool T-shirt", price=20.00)
- Product.objects.create(name="Mug", description="A nice mug", price=10.00)
- Product.objects.create(name="Hat", description="A stylish hat", price=15.00)
-
-
-class Migration(migrations.Migration):
- dependencies = [
- (
- "payments",
- "0002_set_stripe_api_keys",
- ),
- ]
-
- operations = [
- migrations.RunPython(create_initial_products),
- ]
-endef
-
-define DJANGO_PAYMENTS_MODELS
-from django.db import models
-
-
-class Product(models.Model):
- name = models.CharField(max_length=100)
- description = models.TextField()
- price = models.DecimalField(max_digits=10, decimal_places=2)
-
- def __str__(self):
- return self.name
-
-
-class Order(models.Model):
- product = models.ForeignKey(Product, on_delete=models.CASCADE)
- created_at = models.DateTimeField(auto_now_add=True)
- stripe_checkout_session_id = models.CharField(max_length=200)
-
- def __str__(self):
- return f"Order {self.id} for {self.product.name}"
-endef
-
-define DJANGO_PAYMENTS_TEMPLATE_CANCEL
-{% extends "base.html" %}
-{% block title %}Cancel{% endblock %}
-{% block content %}
- Payment Cancelled
- Your payment was cancelled.
-{% endblock %}
-endef
-
-define DJANGO_PAYMENTS_TEMPLATE_CHECKOUT
-{% extends "base.html" %}
-{% block title %}Checkout{% endblock %}
-{% block content %}
- Checkout
-
-{% endblock %}
-endef
-
-define DJANGO_PAYMENTS_TEMPLATE_PRODUCT_DETAIL
-{% extends "base.html" %}
-{% block title %}{{ product.name }}{% endblock %}
-{% block content %}
- {{ product.name }}
- {{ product.description }}
- Price: ${{ product.price }}
-
-{% endblock %}
-endef
-
-define DJANGO_PAYMENTS_TEMPLATE_PRODUCT_LIST
-{% extends "base.html" %}
-{% block title %}Products{% endblock %}
-{% block content %}
- Products
-
-{% endblock %}
-endef
-
-define DJANGO_PAYMENTS_TEMPLATE_SUCCESS
-{% extends "base.html" %}
-{% block title %}Success{% endblock %}
-{% block content %}
- Payment Successful
- Thank you for your purchase!
-{% endblock %}
-endef
-
-define DJANGO_PAYMENTS_URLS
-from django.urls import path
-from .views import (
- CheckoutView,
- SuccessView,
- CancelView,
- ProductListView,
- ProductDetailView,
-)
-
-urlpatterns = [
- path("", ProductListView.as_view(), name="product_list"),
- path("product//", ProductDetailView.as_view(), name="product_detail"),
- path("checkout/", CheckoutView.as_view(), name="checkout"),
- path("success/", SuccessView.as_view(), name="success"),
- path("cancel/", CancelView.as_view(), name="cancel"),
-]
-endef
-
-define DJANGO_PAYMENTS_VIEW
-from django.conf import settings
-from django.shortcuts import render, redirect, get_object_or_404
-from django.views.generic import TemplateView, View, ListView, DetailView
-import stripe
-from .models import Product, Order
-
-stripe.api_key = settings.STRIPE_TEST_SECRET_KEY
-
-
-class ProductListView(ListView):
- model = Product
- template_name = "payments/product_list.html"
- context_object_name = "products"
-
-
-class ProductDetailView(DetailView):
- model = Product
- template_name = "payments/product_detail.html"
- context_object_name = "product"
-
-
-class CheckoutView(View):
- template_name = "payments/checkout.html"
-
- def get(self, request, *args, **kwargs):
- products = Product.objects.all()
- return render(request, self.template_name, {"products": products})
-
- def post(self, request, *args, **kwargs):
- product_id = request.POST.get("product_id")
- product = get_object_or_404(Product, id=product_id)
-
- session = stripe.checkout.Session.create(
- payment_method_types=["card"],
- line_items=[
- {
- "price_data": {
- "currency": "usd",
- "product_data": {
- "name": product.name,
- },
- "unit_amount": int(product.price * 100),
- },
- "quantity": 1,
- }
- ],
- mode="payment",
- success_url="http://localhost:8000/payments/success/",
- cancel_url="http://localhost:8000/payments/cancel/",
- )
-
- Order.objects.create(product=product, stripe_checkout_session_id=session.id)
- return redirect(session.url, code=303)
-
-
-class SuccessView(TemplateView):
-
- template_name = "payments/success.html"
-
-
-class CancelView(TemplateView):
-
- template_name = "payments/cancel.html"
-endef
-
-define DJANGO_SEARCH_FORMS
-from django import forms
-
-
-class SearchForm(forms.Form):
- query = forms.CharField(max_length=100, required=True, label="Search")
-
-endef
-
-define DJANGO_SEARCH_SETTINGS
-SEARCH_MODELS = [
- # Add search models here.
-]
-endef
-
-define DJANGO_SEARCH_TEMPLATE
-{% extends "base.html" %}
-{% block body_class %}template-searchresults{% endblock %}
-{% block title %}Search{% endblock %}
-{% block content %}
- Search
-
- {% if search_results %}
-
- {% for result in search_results %}
- -
-
- {% if result.search_description %}{{ result.search_description }}{% endif %}
-
- {% endfor %}
-
- {% if search_results.has_previous %}
- Previous
- {% endif %}
- {% if search_results.has_next %}
- Next
- {% endif %}
- {% elif search_query %}
- No results found
- {% else %}
- No results found. Try a test query?
- {% endif %}
-{% endblock %}
-endef
-
-define DJANGO_SEARCH_URLS
-from django.urls import path
-from .views import SearchView
-
-urlpatterns = [
- path("search/", SearchView.as_view(), name="search"),
-]
-endef
-
-define DJANGO_SEARCH_UTILS
-from django.apps import apps
-from django.conf import settings
-
-def get_search_models():
- models = []
- for model_path in settings.SEARCH_MODELS:
- app_label, model_name = model_path.split(".")
- model = apps.get_model(app_label, model_name)
- models.append(model)
- return models
-endef
-
-define DJANGO_SEARCH_VIEWS
-from django.views.generic import ListView
-from django.db import models
-from django.db.models import Q
-from .forms import SearchForm
-from .utils import get_search_models
-
-
-class SearchView(ListView):
- template_name = "your_app/search_results.html"
- context_object_name = "results"
- paginate_by = 10
-
- def get_queryset(self):
- form = SearchForm(self.request.GET)
- query = None
- results = []
-
- if form.is_valid():
- query = form.cleaned_data["query"]
- search_models = get_search_models()
-
- for model in search_models:
- fields = [f.name for f in model._meta.fields if isinstance(f, (models.CharField, models.TextField))]
- queries = [Q(**{f"{field}__icontains": query}) for field in fields]
- model_results = model.objects.filter(queries.pop())
-
- for item in queries:
- model_results = model_results.filter(item)
-
- results.extend(model_results)
-
- return results
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["form"] = SearchForm(self.request.GET)
- context["query"] = self.request.GET.get("query", "")
- return context
-endef
-
-define DJANGO_SETTINGS_AUTHENTICATION_BACKENDS
-AUTHENTICATION_BACKENDS = [
- "django.contrib.auth.backends.ModelBackend",
- "allauth.account.auth_backends.AuthenticationBackend",
-]
-endef
-
-define DJANGO_SETTINGS_BASE
-# $(PROJECT_NAME)
-#
-# Uncomment next two lines to enable custom admin
-# INSTALLED_APPS = [app for app in INSTALLED_APPS if app != 'django.contrib.admin']
-# INSTALLED_APPS.append('backend.apps.CustomAdminConfig')
-import os # noqa
-import dj_database_url # noqa
-
-DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
-EXPLORER_CONNECTIONS = {"Default": "default"}
-EXPLORER_DEFAULT_CONNECTION = "default"
-LOGIN_REDIRECT_URL = "/"
-PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-SILENCED_SYSTEM_CHECKS = ["django_recaptcha.recaptcha_test_key_error"]
-BASE_DIR = os.path.dirname(PROJECT_DIR)
-STATICFILES_DIRS = []
-WEBPACK_LOADER = {
- "MANIFEST_FILE": os.path.join(BASE_DIR, "frontend/build/manifest.json"),
-}
-STATICFILES_DIRS.append(os.path.join(BASE_DIR, "frontend/build"))
-TEMPLATES[0]["DIRS"].append(os.path.join(PROJECT_DIR, "templates"))
-endef
-
-define DJANGO_SETTINGS_BASE_MINIMAL
-# $(PROJECT_NAME)
-import os # noqa
-import dj_database_url # noqa
-
-INSTALLED_APPS.append("debug_toolbar")
-INSTALLED_APPS.append("webpack_boilerplate")
-PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-BASE_DIR = os.path.dirname(PROJECT_DIR)
-STATICFILES_DIRS = []
-STATICFILES_DIRS.append(os.path.join(BASE_DIR, "frontend/build"))
-TEMPLATES[0]["DIRS"].append(os.path.join(PROJECT_DIR, "templates"))
-WEBPACK_LOADER = {
- "MANIFEST_FILE": os.path.join(BASE_DIR, "frontend/build/manifest.json"),
-}
-endef
-
-define DJANGO_SETTINGS_CRISPY_FORMS
-CRISPY_TEMPLATE_PACK = "bootstrap5"
-CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
-endef
-
-define DJANGO_SETTINGS_DATABASE
-DATABASE_URL = os.environ.get("DATABASE_URL", "postgres://:@:/$(PROJECT_NAME)")
-DATABASES["default"] = dj_database_url.parse(DATABASE_URL)
-endef
-
-define DJANGO_SETTINGS_DEV
-from .base import * # noqa
-
-# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
-
-# SECURITY WARNING: define the correct hosts in production!
-ALLOWED_HOSTS = ["*"]
-
-EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
-
-try:
- from .local import * # noqa
-except ImportError:
- pass
-
-LOGGING = {
- "version": 1,
- "disable_existing_loggers": False,
- "formatters": {
- "verbose": {
- "format": "{levelname} {asctime} {module} {message}",
- "style": "{",
- },
- "simple": {
- "format": "{levelname} {message}",
- "style": "{",
- },
- },
- "handlers": {
- "console": {
- "level": "DEBUG",
- "class": "logging.StreamHandler",
- "formatter": "verbose",
- },
- },
- "loggers": {
- "django": {
- "handlers": ["console"],
- "level": "DEBUG",
- "propagate": True,
- },
- },
-}
-
-INTERNAL_IPS = [
- "127.0.0.1",
-]
-
-MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware") # noqa
-MIDDLEWARE.append("hijack.middleware.HijackUserMiddleware") # noqa
-INSTALLED_APPS.append("django.contrib.admindocs") # noqa
-SECRET_KEY = "$(DJANGO_SETTINGS_SECRET_KEY)"
-endef
-
-
-define DJANGO_SETTINGS_HOME_PAGE
-INSTALLED_APPS.append("home")
-endef
-
-define DJANGO_SETTINGS_INSTALLED_APPS
-INSTALLED_APPS.append("allauth")
-INSTALLED_APPS.append("allauth.account")
-INSTALLED_APPS.append("allauth.socialaccount")
-INSTALLED_APPS.append("crispy_bootstrap5")
-INSTALLED_APPS.append("crispy_forms")
-INSTALLED_APPS.append("debug_toolbar")
-INSTALLED_APPS.append("django_extensions")
-INSTALLED_APPS.append("django_recaptcha")
-INSTALLED_APPS.append("rest_framework")
-INSTALLED_APPS.append("rest_framework.authtoken")
-INSTALLED_APPS.append("webpack_boilerplate")
-INSTALLED_APPS.append("explorer")
-endef
-
-define DJANGO_SETTINGS_MIDDLEWARE
-MIDDLEWARE.append("allauth.account.middleware.AccountMiddleware")
-endef
-
-define DJANGO_SETTINGS_MODEL_FORM_DEMO
-INSTALLED_APPS.append("model_form_demo") # noqa
-endef
-
-define DJANGO_SETTINGS_PAYMENTS
-DJSTRIPE_FOREIGN_KEY_TO_FIELD = "id"
-DJSTRIPE_WEBHOOK_VALIDATION = "retrieve_event"
-STRIPE_PUBLISHABLE_KEY = os.environ.get("STRIPE_PUBLISHABLE_KEY")
-STRIPE_SECRET_KEY = os.environ.get("STRIPE_SECRET_KEY")
-STRIPE_TEST_SECRET_KEY = os.environ.get("STRIPE_TEST_SECRET_KEY")
-INSTALLED_APPS.append("payments") # noqa
-INSTALLED_APPS.append("djstripe") # noqa
-endef
-
-define DJANGO_SETTINGS_REST_FRAMEWORK
-REST_FRAMEWORK = {
- # Use Django's standard `django.contrib.auth` permissions,
- # or allow read-only access for unauthenticated users.
- "DEFAULT_PERMISSION_CLASSES": [
- "rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly"
- ]
-}
-endef
-
-define DJANGO_SETTINGS_SITEUSER
-INSTALLED_APPS.append("siteuser") # noqa
-AUTH_USER_MODEL = "siteuser.User"
-endef
-
-define DJANGO_SETTINGS_PROD
-from .base import * # noqa
-from backend.utils import get_ec2_metadata
-
-DEBUG = False
-
-try:
- from .local import * # noqa
-except ImportError:
- pass
-
-LOCAL_IPV4 = get_ec2_metadata()
-ALLOWED_HOSTS.append(LOCAL_IPV4) # noqa
-endef
-
-define DJANGO_SETTINGS_THEMES
-THEMES = [
- ("light", "Light Theme"),
- ("dark", "Dark Theme"),
-]
-endef
-
-define DJANGO_SITEUSER_ADMIN
-from django.contrib.auth.admin import UserAdmin
-from django.contrib import admin
-
-from .models import User
-
-admin.site.register(User, UserAdmin)
-endef
-
-define DJANGO_SITEUSER_EDIT_TEMPLATE
-{% extends 'base.html' %}
-{% load crispy_forms_tags %}
-{% block content %}
- Edit User
- {% crispy form %}
-{% endblock %}
-endef
-
-define DJANGO_SITEUSER_FORM
-from django import forms
-from django.contrib.auth.forms import UserChangeForm
-from crispy_forms.helper import FormHelper
-from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit
-from .models import User
-
-
-class SiteUserForm(UserChangeForm):
- bio = forms.CharField(widget=forms.Textarea(attrs={"id": "editor"}), required=False)
-
- class Meta(UserChangeForm.Meta):
- model = User
- fields = ("username", "user_theme_preference", "bio", "rate")
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.helper = FormHelper()
- self.helper.form_method = "post"
- self.helper.layout = Layout(
- Fieldset(
- "Edit Your Profile",
- "username",
- "user_theme_preference",
- "bio",
- "rate",
- ),
- ButtonHolder(Submit("submit", "Save", css_class="btn btn-primary")),
- )
-endef
-
-define DJANGO_SITEUSER_MODEL
-from django.db import models
-from django.contrib.auth.models import AbstractUser, Group, Permission
-from django.conf import settings
-
-
-class User(AbstractUser):
- groups = models.ManyToManyField(Group, related_name="siteuser_set", blank=True)
- user_permissions = models.ManyToManyField(
- Permission, related_name="siteuser_set", blank=True
- )
-
- user_theme_preference = models.CharField(
- max_length=10, choices=settings.THEMES, default="light"
- )
-
- bio = models.TextField(blank=True, null=True)
- rate = models.FloatField(blank=True, null=True)
-endef
-
-define DJANGO_SITEUSER_URLS
-from django.urls import path
-from .views import UserProfileView, UpdateThemePreferenceView, UserEditView
-
-urlpatterns = [
- path("profile/", UserProfileView.as_view(), name="user-profile"),
- path(
- "update_theme_preference/",
- UpdateThemePreferenceView.as_view(),
- name="update_theme_preference",
- ),
- path("/edit/", UserEditView.as_view(), name="user-edit"),
-]
-endef
-
-define DJANGO_SITEUSER_VIEW
-import json
-
-from django.contrib.auth.mixins import LoginRequiredMixin
-from django.http import JsonResponse
-from django.utils.decorators import method_decorator
-from django.views import View
-from django.views.decorators.csrf import csrf_exempt
-from django.views.generic import DetailView
-from django.views.generic.edit import UpdateView
-from django.urls import reverse_lazy
-
-from .models import User
-from .forms import SiteUserForm
-
-
-class UserProfileView(LoginRequiredMixin, DetailView):
- model = User
- template_name = "profile.html"
-
- def get_object(self, queryset=None):
- return self.request.user
-
-
-@method_decorator(csrf_exempt, name="dispatch")
-class UpdateThemePreferenceView(View):
- def post(self, request, *args, **kwargs):
- try:
- data = json.loads(request.body.decode("utf-8"))
- new_theme = data.get("theme")
- user = request.user
- user.user_theme_preference = new_theme
- user.save()
- response_data = {"theme": new_theme}
- return JsonResponse(response_data)
- except json.JSONDecodeError as e:
- return JsonResponse({"error": e}, status=400)
-
- def http_method_not_allowed(self, request, *args, **kwargs):
- return JsonResponse({"error": "Invalid request method"}, status=405)
-
-
-class UserEditView(LoginRequiredMixin, UpdateView):
- model = User
- template_name = "user_edit.html" # Create this template in your templates folder
- form_class = SiteUserForm
-
- def get_success_url(self):
- # return reverse_lazy("user-profile", kwargs={"pk": self.object.pk})
- return reverse_lazy("user-profile")
-endef
-
-define DJANGO_SITEUSER_VIEW_TEMPLATE
-{% extends 'base.html' %}
-{% block content %}
- User Profile
-
- Username: {{ user.username }}
- Theme: {{ user.user_theme_preference }}
- Bio: {{ user.bio|default:""|safe }}
- Rate: {{ user.rate|default:"" }}
-{% endblock %}
-endef
-
-define DJANGO_URLS
-from django.contrib import admin
-from django.urls import path, include
-from django.conf import settings
-
-urlpatterns = [
- path("django/", admin.site.urls),
-]
-endef
-
-define DJANGO_URLS_ALLAUTH
-urlpatterns += [path("accounts/", include("allauth.urls"))]
-endef
-
-define DJANGO_URLS_API
-from rest_framework import routers # noqa
-from .api import UserViewSet, api # noqa
-
-router = routers.DefaultRouter()
-router.register(r"users", UserViewSet)
-# urlpatterns += [path("api/", include(router.urls))]
-urlpatterns += [path("api/", api.urls)]
-endef
-
-define DJANGO_URLS_DEBUG_TOOLBAR
-if settings.DEBUG:
- urlpatterns += [path("__debug__/", include("debug_toolbar.urls"))]
-endef
-
-define DJANGO_URLS_HOME_PAGE
-urlpatterns += [path("", include("home.urls"))]
-endef
-
-define DJANGO_URLS_LOGGING_DEMO
-urlpatterns += [path("logging-demo/", include("logging_demo.urls"))]
-endef
-
-define DJANGO_URLS_MODEL_FORM_DEMO
-urlpatterns += [path("model-form-demo/", include("model_form_demo.urls"))]
-endef
-
-define DJANGO_URLS_PAYMENTS
-urlpatterns += [path("payments/", include("payments.urls"))]
-endef
-
-define DJANGO_URLS_SITEUSER
-urlpatterns += [path("user/", include("siteuser.urls"))]
-endef
-
-define DJANGO_UTILS
-from django.urls import URLResolver
-import requests
-
-
-def get_ec2_metadata():
- try:
- # Step 1: Get the token
- token_url = "http://169.254.169.254/latest/api/token"
- headers = {"X-aws-ec2-metadata-token-ttl-seconds": "21600"}
- response = requests.put(token_url, headers=headers)
- response.raise_for_status() # Raise an error for bad responses
-
- token = response.text
-
- # Step 2: Use the token to get the instance metadata
- metadata_url = "http://169.254.169.254/latest/meta-data/local-ipv4"
- headers = {"X-aws-ec2-metadata-token": token}
- response = requests.get(metadata_url, headers=headers)
- response.raise_for_status() # Raise an error for bad responses
-
- metadata = response.text
- return metadata
- except requests.RequestException as e:
- print(f"Error retrieving EC2 metadata: {e}")
- return None
-
-
-# Function to remove a specific URL pattern based on its route (including catch-all)
-def remove_urlpattern(urlpatterns, route_to_remove):
- urlpatterns[:] = [
- urlpattern
- for urlpattern in urlpatterns
- if not (
- isinstance(urlpattern, URLResolver)
- and urlpattern.pattern._route == route_to_remove
- )
- ]
-endef
-
-define EB_CUSTOM_ENV_EC2_USER
-files:
- "/home/ec2-user/.bashrc":
- mode: "000644"
- owner: ec2-user
- group: ec2-user
- content: |
- # .bashrc
-
- # Source global definitions
- if [ -f /etc/bashrc ]; then
- . /etc/bashrc
- fi
-
- # User specific aliases and functions
- set -o vi
-
- source <(sed -E -n 's/[^#]+/export &/ p' /opt/elasticbeanstalk/deployment/custom_env_var)
-endef
-
-define EB_CUSTOM_ENV_VAR_FILE
-#!/bin/bash
-
-# Via https://aws.amazon.com/premiumsupport/knowledge-center/elastic-beanstalk-env-variables-linux2/
-
-#Create a copy of the environment variable file.
-cat /opt/elasticbeanstalk/deployment/env | perl -p -e 's/(.*)=(.*)/export $$1="$$2"/;' > /opt/elasticbeanstalk/deployment/custom_env_var
-
-#Set permissions to the custom_env_var file so this file can be accessed by any user on the instance. You can restrict permissions as per your requirements.
-chmod 644 /opt/elasticbeanstalk/deployment/custom_env_var
-
-# add the virtual env path in.
-VENV=/var/app/venv/`ls /var/app/venv`
-cat <> /opt/elasticbeanstalk/deployment/custom_env_var
-VENV=$$ENV
-EOF
-
-#Remove duplicate files upon deployment.
-rm -f /opt/elasticbeanstalk/deployment/*.bak
-endef
-
-define GIT_IGNORE
-__pycache__
-*.pyc
-dist/
-node_modules/
-_build/
-.elasticbeanstalk/
-db.sqlite3
-static/
-backend/var
-endef
-
-define JENKINS_FILE
-pipeline {
- agent any
- stages {
- stage('') {
- steps {
- echo ''
- }
- }
- }
-}
-endef
-
-define MAKEFILE_CUSTOM
-# Custom Makefile
-# Add your custom makefile commands here
-#
-# PROJECT_NAME := my-new-project
-endef
-
-define PIP_INSTALL_REQUIREMENTS_TEST
-pytest
-pytest-runner
-coverage
-pytest-mock
-pytest-cov
-hypothesis
-selenium
-pytest-django
-factory-boy
-flake8
-tox
-endef
-
-define PROGRAMMING_INTERVIEW
-from rich import print as rprint
-from rich.console import Console
-from rich.panel import Panel
-
-import argparse
-import locale
-import math
-import time
-
-import code # noqa
-import readline # noqa
-import rlcompleter # noqa
-
-
-locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
-
-
-class DataStructure:
- # Data Structure: Binary Tree
- class TreeNode:
- def __init__(self, value=0, left=None, right=None):
- self.value = value
- self.left = left
- self.right = right
-
- # Data Structure: Stack
- class Stack:
- def __init__(self):
- self.items = []
-
- def push(self, item):
- self.items.append(item)
-
- def pop(self):
- if not self.is_empty():
- return self.items.pop()
- return None
-
- def peek(self):
- if not self.is_empty():
- return self.items[-1]
- return None
-
- def is_empty(self):
- return len(self.items) == 0
-
- def size(self):
- return len(self.items)
-
- # Data Structure: Queue
- class Queue:
- def __init__(self):
- self.items = []
-
- def enqueue(self, item):
- self.items.append(item)
-
- def dequeue(self):
- if not self.is_empty():
- return self.items.pop(0)
- return None
-
- def is_empty(self):
- return len(self.items) == 0
-
- def size(self):
- return len(self.items)
-
- # Data Structure: Linked List
- class ListNode:
- def __init__(self, value=0, next=None):
- self.value = value
- self.next = next
-
-
-class Interview(DataStructure):
-
- # Protected methods for factorial calculation
- def _factorial_recursive(self, n):
- if n == 0:
- return 1
- return n * self._factorial_recursive(n - 1)
-
- def _factorial_divide_and_conquer(self, low, high):
- if low > high:
- return 1
- if low == high:
- return low
- mid = (low + high) // 2
- return self._factorial_divide_and_conquer(
- low, mid
- ) * self._factorial_divide_and_conquer(mid + 1, high)
-
- # Recursive Factorial with Timing
- def factorial_recursive(self, n):
- start_time = time.time() # Start timing
- result = self._factorial_recursive(n) # Calculate factorial
- end_time = time.time() # End timing
- elapsed_time = end_time - start_time
- return f" Factorial: {locale.format_string("%.2f", result, grouping=True)} Elapsed time: {elapsed_time:.6f}"
-
- # Iterative Factorial with Timing
- def factorial_iterative(self, n):
- start_time = time.time() # Start timing
- result = 1
- for i in range(1, n + 1):
- result *= i
- end_time = time.time() # End timing
- elapsed_time = end_time - start_time
- return f" Factorial: {locale.format_string("%.2f", result, grouping=True)} Elapsed time: {elapsed_time:.6f}"
-
- # Divide and Conquer Factorial with Timing
- def factorial_divide_and_conquer(self, n):
- start_time = time.time() # Start timing
- result = self._factorial_divide_and_conquer(1, n) # Calculate factorial
- end_time = time.time() # End timing
- elapsed_time = end_time - start_time
- return f" Factorial: {locale.format_string("%.2f", result, grouping=True)} Elapsed time: {elapsed_time:.6f}"
-
- # Built-in Factorial with Timing
- def factorial_builtin(self, n):
- start_time = time.time() # Start timing
- result = math.factorial(n) # Calculate factorial using built-in
- end_time = time.time() # End timing
-
- # Calculate elapsed time
- elapsed_time = end_time - start_time
-
- # Print complexity and runtime
- return f" Factorial: {locale.format_string("%.2f", result, grouping=True)} Elapsed time: {elapsed_time:.6f}"
-
- # Recursion: Fibonacci
- def fibonacci_recursive(self, n):
- if n <= 1:
- return n
- return self.fibonacci_recursive(n - 1) + self.fibonacci_recursive(n - 2)
-
- # Iteration: Fibonacci
- def fibonacci_iterative(self, n):
- if n <= 1:
- return n
- a, b = 0, 1
- for _ in range(n - 1):
- a, b = b, a + b
- return b
-
- # Searching: Linear Search
- def linear_search(self, arr, target):
- for i, value in enumerate(arr):
- if value == target:
- return i
- return -1
-
- # Searching: Binary Search
- def binary_search(self, arr, target):
- left, right = 0, len(arr) - 1
- while left <= right:
- mid = (left + right) // 2
- if arr[mid] == target:
- return mid
- elif arr[mid] < target:
- left = mid + 1
- else:
- right = mid - 1
- return -1
-
- # Sorting: Bubble Sort
- def bubble_sort(self, arr):
- n = len(arr)
- for i in range(n):
- for j in range(0, n - i - 1):
- if arr[j] > arr[j + 1]:
- arr[j], arr[j + 1] = arr[j + 1], arr[j]
- return arr
-
- # Sorting: Merge Sort
- def merge_sort(self, arr):
- if len(arr) > 1:
- mid = len(arr) // 2
- left_half = arr[:mid]
- right_half = arr[mid:]
-
- self.merge_sort(left_half)
- self.merge_sort(right_half)
-
- i = j = k = 0
-
- while i < len(left_half) and j < len(right_half):
- if left_half[i] < right_half[j]:
- arr[k] = left_half[i]
- i += 1
- else:
- arr[k] = right_half[j]
- j += 1
- k += 1
-
- while i < len(left_half):
- arr[k] = left_half[i]
- i += 1
- k += 1
-
- while j < len(right_half):
- arr[k] = right_half[j]
- j += 1
- k += 1
- return arr
-
- def insert_linked_list(self, head, value):
- new_node = self.ListNode(value)
- if not head:
- return new_node
- current = head
- while current.next:
- current = current.next
- current.next = new_node
- return head
-
- def print_linked_list(self, head):
- current = head
- while current:
- print(current.value, end=" -> ")
- current = current.next
- print("None")
-
- def inorder_traversal(self, root):
- return (
- self.inorder_traversal(root.left)
- + [root.value]
- + self.inorder_traversal(root.right)
- if root
- else []
- )
-
- def preorder_traversal(self, root):
- return (
- [root.value]
- + self.preorder_traversal(root.left)
- + self.preorder_traversal(root.right)
- if root
- else []
- )
-
- def postorder_traversal(self, root):
- return (
- self.postorder_traversal(root.left)
- + self.postorder_traversal(root.right)
- + [root.value]
- if root
- else []
- )
-
- # Graph Algorithms: Depth-First Search
- def dfs(self, graph, start):
- visited, stack = set(), [start]
- while stack:
- vertex = stack.pop()
- if vertex not in visited:
- visited.add(vertex)
- stack.extend(set(graph[vertex]) - visited)
- return visited
-
- # Graph Algorithms: Breadth-First Search
- def bfs(self, graph, start):
- visited, queue = set(), [start]
- while queue:
- vertex = queue.pop(0)
- if vertex not in visited:
- visited.add(vertex)
- queue.extend(set(graph[vertex]) - visited)
- return visited
-
-
-def setup_readline(local):
-
- # Enable tab completion
- readline.parse_and_bind("tab: complete")
- # Optionally, you can set the completer function manually
- readline.set_completer(rlcompleter.Completer(local).complete)
-
-
-def main():
-
- console = Console()
- interview = Interview()
-
- parser = argparse.ArgumentParser(description="Programming Interview Questions")
-
- parser.add_argument(
- "-f", "--factorial", type=int, help="Factorial algorithm examples"
- )
- parser.add_argument("--fibonacci", type=int, help="Fibonacci algorithm examples")
- parser.add_argument(
- "--search", action="store_true", help="Search algorithm examples"
- )
- parser.add_argument("--sort", action="store_true", help="Search algorithm examples")
- parser.add_argument("--stack", action="store_true", help="Stack algorithm examples")
- parser.add_argument("--queue", action="store_true", help="Queue algorithm examples")
- parser.add_argument(
- "--list", action="store_true", help="Linked List algorithm examples"
- )
- parser.add_argument(
- "--tree", action="store_true", help="Tree traversal algorithm examples"
- )
- parser.add_argument("--graph", action="store_true", help="Graph algorithm examples")
- parser.add_argument(
- "-i", "--interactive", action="store_true", help="Interactive mode"
- )
-
- args = parser.parse_args()
-
- if args.factorial:
- # Factorial examples
- console.rule("Factorial Examples")
- rprint(
- Panel(
- "[bold cyan]Recursive Factorial - Time Complexity: O(n)[/bold cyan]\n"
- + str(interview.factorial_recursive(args.factorial)),
- title="Factorial Recursive",
- )
- )
- rprint(
- Panel(
- "[bold cyan]Iterative Factorial - Time Complexity: O(n)[/bold cyan]\n"
- + str(interview.factorial_iterative(args.factorial)),
- title="Factorial Iterative",
- )
- )
- rprint(
- Panel(
- "[bold cyan]Built-in Factorial - Time Complexity: O(n)[/bold cyan]\n"
- + str(interview.factorial_builtin(args.factorial)),
- title="Factorial Built-in",
- )
- )
- rprint(
- Panel(
- "[bold cyan]Divide and Conquer Factorial - Time Complexity: O(n log n)[/bold cyan]\n"
- + str(interview.factorial_divide_and_conquer(args.factorial)),
- title="Factorial Divide and Conquer",
- )
- )
- exit()
-
- if args.fibonacci:
- # Fibonacci examples
- console.rule("Fibonacci Examples")
- rprint(
- Panel(
- str(interview.fibonacci_recursive(args.fibonacci)),
- title="Fibonacci Recursive",
- )
- )
- rprint(
- Panel(
- str(interview.fibonacci_iterative(args.fibonacci)),
- title="Fibonacci Iterative",
- )
- )
- exit()
-
- if args.search:
- # Searching examples
- console.rule("Searching Examples")
- array = [1, 3, 5, 7, 9]
- rprint(Panel(str(interview.linear_search(array, 5)), title="Linear Search"))
- rprint(Panel(str(interview.binary_search(array, 5)), title="Binary Search"))
- exit()
-
- if args.sort:
- # Sorting examples
- console.rule("Sorting Examples")
- unsorted_array = [64, 34, 25, 12, 22, 11, 90]
- rprint(
- Panel(
- str(interview.bubble_sort(unsorted_array.copy())), title="Bubble Sort"
- )
- )
- rprint(
- Panel(str(interview.merge_sort(unsorted_array.copy())), title="Merge Sort")
- )
- exit()
-
- if args.stack:
- # Stack example
- console.rule("Stack Example")
- stack = interview.Stack()
- stack.push(1)
- stack.push(2)
- stack.push(3)
- rprint(Panel(str(stack.pop()), title="Stack Pop"))
- rprint(Panel(str(stack.peek()), title="Stack Peek"))
- rprint(Panel(str(stack.size()), title="Stack Size"))
-
- if args.queue:
- # Queue example
- console.rule("Queue Example")
- queue = interview.Queue()
- queue.enqueue(1)
- queue.enqueue(2)
- queue.enqueue(3)
- rprint(Panel(str(queue.dequeue()), title="Queue Dequeue"))
- rprint(Panel(str(queue.is_empty()), title="Queue Is Empty"))
- rprint(Panel(str(queue.size()), title="Queue Size"))
-
- if args.list:
- # Linked List example
- console.rule("Linked List Example")
- head = None
- head = interview.insert_linked_list(head, 1)
- head = interview.insert_linked_list(head, 2)
- head = interview.insert_linked_list(head, 3)
- interview.print_linked_list(head) # Output: 1 -> 2 -> 3 -> None
-
- if args.tree:
- # Tree Traversal example
- console.rule("Tree Traversal Example")
- root = interview.TreeNode(1)
- root.left = interview.TreeNode(2)
- root.right = interview.TreeNode(3)
- root.left.left = interview.TreeNode(4)
- root.left.right = interview.TreeNode(5)
- rprint(Panel(str(interview.inorder_traversal(root)), title="Inorder Traversal"))
- rprint(
- Panel(str(interview.preorder_traversal(root)), title="Preorder Traversal")
- )
- rprint(
- Panel(str(interview.postorder_traversal(root)), title="Postorder Traversal")
- )
-
- if args.graph:
- # Graph Algorithms example
- console.rule("Graph Algorithms Example")
- graph = {
- "A": ["B", "C"],
- "B": ["A", "D", "E"],
- "C": ["A", "F"],
- "D": ["B"],
- "E": ["B", "F"],
- "F": ["C", "E"],
- }
- rprint(Panel(str(interview.dfs(graph, "A")), title="DFS"))
- rprint(Panel(str(interview.bfs(graph, "A")), title="BFS"))
-
- if args.interactive:
- # Starting interactive session with tab completion
- setup_readline(locals())
- banner = "Interactive programming interview session started. Type 'exit()' or 'Ctrl-D' to exit."
- code.interact(
- banner=banner,
- local=locals(),
- exitmsg="Great interview!",
- )
-
-
-if __name__ == "__main__":
- main()
-
-endef
-
-define PYTHON_CI_YAML
-name: Build Wheels
-endef
-
-define PYTHON_LICENSE_TXT
-MIT License
-
-Copyright (c) [YEAR] [OWNER NAME]
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-endef
-
-define PYTHON_PROJECT_TOML
-[build-system]
-endef
-
-define SEPARATOR
-.==========================================================================================================================================.
-| |
-| _|_|_| _| _| _| _| _| _|_| _| _| |
-| _| _| _| _|_| _|_| _|_| _|_|_| _|_|_|_| _|_| _|_| _|_|_| _| _| _|_| _| _| _|_| |
-| _|_|_| _|_| _| _| _| _|_|_|_| _| _| _| _| _| _| _| _|_| _|_|_|_| _|_|_|_| _| _| _|_|_|_| |
-| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| |
-| _| _| _|_| _| _|_|_| _|_|_| _|_| _| _| _|_|_| _| _| _|_|_| _| _| _| _|_|_| |
-| _| |
-| _| |
-`=========================================================================================================================================='
-endef
-
-define TINYMCE_JS
-import tinymce from 'tinymce';
-import 'tinymce/icons/default';
-import 'tinymce/themes/silver';
-import 'tinymce/skins/ui/oxide/skin.css';
-import 'tinymce/plugins/advlist';
-import 'tinymce/plugins/code';
-import 'tinymce/plugins/emoticons';
-import 'tinymce/plugins/emoticons/js/emojis';
-import 'tinymce/plugins/link';
-import 'tinymce/plugins/lists';
-import 'tinymce/plugins/table';
-import 'tinymce/models/dom';
-
-tinymce.init({
- selector: 'textarea#editor',
- plugins: 'advlist code emoticons link lists table',
- toolbar: 'bold italic | bullist numlist | link emoticons',
- skin: false,
- content_css: false,
-});
-endef
-
-define WAGTAIL_BASE_TEMPLATE
-{% load static wagtailcore_tags wagtailuserbar webpack_loader %}
-
-
-
-
-
- {% block title %}
- {% if page.seo_title %}
- {{ page.seo_title }}
- {% else %}
- {{ page.title }}
- {% endif %}
- {% endblock %}
- {% block title_suffix %}
- {% wagtail_site as current_site %}
- {% if current_site and current_site.site_name %}- {{ current_site.site_name }}{% endif %}
- {% endblock %}
-
- {% if page.search_description %}{% endif %}
-
- {# Force all links in the live preview panel to be opened in a new tab #}
- {% if request.in_preview_panel %}{% endif %}
- {% stylesheet_pack 'app' %}
- {% block extra_css %}{# Override this in templates to add extra stylesheets #}{% endblock %}
-
- {% include 'favicon.html' %}
- {% csrf_token %}
-
-
-
- {% wagtailuserbar %}
-
- {% include 'header.html' %}
- {% if messages %}
-
- {% for message in messages %}
-
- {{ message }}
-
-
- {% endfor %}
-
- {% endif %}
-
- {% block content %}{% endblock %}
-
-
- {% include 'footer.html' %}
- {% include 'offcanvas.html' %}
- {% javascript_pack 'app' %}
- {% block extra_js %}{# Override this in templates to add extra javascript #}{% endblock %}
-
-
-endef
-
-define WAGTAIL_BLOCK_CAROUSEL
-
-
- {% for image in block.value.images %}
-
- {% endfor %}
-
-
- {% for image in block.value.images %}
-
-
-
-
{{ image.title }}
-
-
- {% endfor %}
-
-
-
-
-endef
-
-define WAGTAIL_BLOCK_MARKETING
-{% load wagtailcore_tags %}
-
- {% if block.value.images.0 %}
- {% include 'blocks/carousel_block.html' %}
- {% else %}
- {{ self.title }}
- {{ self.content }}
- {% endif %}
-
-endef
-
-define WAGTAIL_CONTACT_PAGE_LANDING
-{% extends 'base.html' %}
-{% block content %}Thank you!
{% endblock %}
-endef
-
-define WAGTAIL_CONTACT_PAGE_MODEL
-from django.db import models
-from modelcluster.fields import ParentalKey
-from wagtail.admin.panels import (
- FieldPanel, FieldRowPanel,
- InlinePanel, MultiFieldPanel
-)
-from wagtail.fields import RichTextField
-from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
-
-
-class FormField(AbstractFormField):
- page = ParentalKey('ContactPage', on_delete=models.CASCADE, related_name='form_fields')
-
-
-class ContactPage(AbstractEmailForm):
- intro = RichTextField(blank=True)
- thank_you_text = RichTextField(blank=True)
-
- content_panels = AbstractEmailForm.content_panels + [
- FieldPanel('intro'),
- InlinePanel('form_fields', label="Form fields"),
- FieldPanel('thank_you_text'),
- MultiFieldPanel([
- FieldRowPanel([
- FieldPanel('from_address', classname="col6"),
- FieldPanel('to_address', classname="col6"),
- ]),
- FieldPanel('subject'),
- ], "Email"),
- ]
-
- class Meta:
- verbose_name = "Contact Page"
-endef
-
-define WAGTAIL_CONTACT_PAGE_TEMPLATE
-{% extends 'base.html' %}
-{% load crispy_forms_tags static wagtailcore_tags %}
-{% block content %}
- {{ page.title }}
- {{ page.intro|richtext }}
-
-{% endblock %}
-endef
-
-define WAGTAIL_CONTACT_PAGE_TEST
-from django.test import TestCase
-from wagtail.test.utils import WagtailPageTestCase
-from wagtail.models import Page
-
-from contactpage.models import ContactPage, FormField
-
-class ContactPageTest(TestCase, WagtailPageTestCase):
- def test_contact_page_creation(self):
- # Create a ContactPage instance
- contact_page = ContactPage(
- title='Contact',
- intro='Welcome to our contact page!',
- thank_you_text='Thank you for reaching out.'
- )
-
- # Save the ContactPage instance
- self.assertEqual(contact_page.save_revision().publish().get_latest_revision_as_page(), contact_page)
-
- def test_form_field_creation(self):
- # Create a ContactPage instance
- contact_page = ContactPage(
- title='Contact',
- intro='Welcome to our contact page!',
- thank_you_text='Thank you for reaching out.'
- )
- # Save the ContactPage instance
- contact_page_revision = contact_page.save_revision()
- contact_page_revision.publish()
-
- # Create a FormField associated with the ContactPage
- form_field = FormField(
- page=contact_page,
- label='Your Name',
- field_type='singleline',
- required=True
- )
- form_field.save()
-
- # Retrieve the ContactPage from the database
- contact_page_from_db = Page.objects.get(id=contact_page.id).specific
-
- # Check if the FormField is associated with the ContactPage
- self.assertEqual(contact_page_from_db.form_fields.first(), form_field)
-
- def test_contact_page_form_submission(self):
- # Create a ContactPage instance
- contact_page = ContactPage(
- title='Contact',
- intro='Welcome to our contact page!',
- thank_you_text='Thank you for reaching out.'
- )
- # Save the ContactPage instance
- contact_page_revision = contact_page.save_revision()
- contact_page_revision.publish()
-
- # Simulate a form submission
- form_data = {
- 'your_name': 'John Doe',
- # Add other form fields as needed
- }
-
- response = self.client.post(contact_page.url, form_data)
-
- # Check if the form submission is successful (assuming a 302 redirect)
- self.assertEqual(response.status_code, 302)
-
- # You may add more assertions based on your specific requirements
-endef
-
-define WAGTAIL_HEADER_PREFIX
-{% load wagtailcore_tags %}
-{% wagtail_site as current_site %}
-endef
-
-define WAGTAIL_HOME_PAGE_MODEL
-from wagtail.models import Page
-from wagtail.fields import StreamField
-from wagtail import blocks
-from wagtail.admin.panels import FieldPanel
-from wagtail.images.blocks import ImageChooserBlock
-
-
-class MarketingBlock(blocks.StructBlock):
- title = blocks.CharBlock(required=False, help_text="Enter the block title")
- content = blocks.RichTextBlock(required=False, help_text="Enter the block content")
- images = blocks.ListBlock(
- ImageChooserBlock(required=False),
- help_text="Select one or two images for column display. Select three or more images for carousel display.",
- )
- image = ImageChooserBlock(
- required=False, help_text="Select one image for background display."
- )
- block_class = blocks.CharBlock(
- required=False,
- help_text="Enter a CSS class for styling the marketing block",
- classname="full title",
- default="vh-100 bg-secondary",
- )
- image_class = blocks.CharBlock(
- required=False,
- help_text="Enter a CSS class for styling the column display image(s)",
- classname="full title",
- default="img-thumbnail p-5",
- )
- layout_class = blocks.CharBlock(
- required=False,
- help_text="Enter a CSS class for styling the layout.",
- classname="full title",
- default="d-flex flex-row",
- )
-
- class Meta:
- icon = "placeholder"
- template = "blocks/marketing_block.html"
-
-
-class HomePage(Page):
- template = "home/home_page.html" # Create a template for rendering the home page
-
- marketing_blocks = StreamField(
- [
- ("marketing_block", MarketingBlock()),
- ],
- blank=True,
- null=True,
- use_json_field=True,
- )
- content_panels = Page.content_panels + [
- FieldPanel("marketing_blocks"),
- ]
-
- class Meta:
- verbose_name = "Home Page"
-endef
-
-define WAGTAIL_HOME_PAGE_TEMPLATE
-{% extends "base.html" %}
-{% load wagtailcore_tags %}
-{% block content %}
-
- {% for block in page.marketing_blocks %}
- {% include_block block %}
- {% endfor %}
-
-{% endblock %}
-endef
-
-define WAGTAIL_PRIVACY_PAGE_MODEL
-from wagtail.models import Page
-from wagtail.admin.panels import FieldPanel
-from wagtailmarkdown.fields import MarkdownField
-
-
-class PrivacyPage(Page):
- """
- A Wagtail Page model for the Privacy Policy page.
- """
-
- template = "privacy_page.html"
-
- body = MarkdownField()
-
- content_panels = Page.content_panels + [
- FieldPanel("body", classname="full"),
- ]
-
- class Meta:
- verbose_name = "Privacy Page"
-endef
-
-define WAGTAIL_PRIVACY_PAGE_TEMPLATE
-{% extends 'base.html' %}
-{% load wagtailmarkdown %}
-{% block content %}{{ page.body|markdown }}
{% endblock %}
-endef
-
-define WAGTAIL_SEARCH_TEMPLATE
-{% extends "base.html" %}
-{% load static wagtailcore_tags %}
-{% block body_class %}template-searchresults{% endblock %}
-{% block title %}Search{% endblock %}
-{% block content %}
- Search
-
- {% if search_results %}
-
- {% for result in search_results %}
- -
-
- {% if result.search_description %}{{ result.search_description }}{% endif %}
-
- {% endfor %}
-
- {% if search_results.has_previous %}
- Previous
- {% endif %}
- {% if search_results.has_next %}
- Next
- {% endif %}
- {% elif search_query %}
- No results found
- {% else %}
- No results found. Try a test query?
- {% endif %}
-{% endblock %}
-endef
-
-define WAGTAIL_SEARCH_URLS
-from django.urls import path
-from .views import search
-
-urlpatterns = [path("", search, name="search")]
-endef
-
-define WAGTAIL_SETTINGS
-INSTALLED_APPS.append("wagtail_color_panel")
-INSTALLED_APPS.append("wagtail_modeladmin")
-INSTALLED_APPS.append("wagtail.contrib.settings")
-INSTALLED_APPS.append("wagtailmarkdown")
-INSTALLED_APPS.append("wagtailmenus")
-INSTALLED_APPS.append("wagtailseo")
-TEMPLATES[0]["OPTIONS"]["context_processors"].append(
- "wagtail.contrib.settings.context_processors.settings"
-)
-TEMPLATES[0]["OPTIONS"]["context_processors"].append(
- "wagtailmenus.context_processors.wagtailmenus"
-)
-endef
-
-define WAGTAIL_SITEPAGE_MODEL
-from wagtail.models import Page
-
-
-class SitePage(Page):
- template = "sitepage/site_page.html"
-
- class Meta:
- verbose_name = "Site Page"
-endef
-
-define WAGTAIL_SITEPAGE_TEMPLATE
-{% extends 'base.html' %}
-{% block content %}
- {{ page.title }}
-{% endblock %}
-endef
-
-define WAGTAIL_URLS
-from django.conf import settings
-from django.urls import include, path
-from django.contrib import admin
-
-from wagtail.admin import urls as wagtailadmin_urls
-from wagtail.documents import urls as wagtaildocs_urls
-
-from search import views as search_views
-
-urlpatterns = [
- path("django/", admin.site.urls),
- path("wagtail/", include(wagtailadmin_urls)),
- path("documents/", include(wagtaildocs_urls)),
- path("search/", search_views.search, name="search"),
-]
-
-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)
-endef
-
-define WAGTAIL_URLS_HOME
-urlpatterns += [
- # 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:
- path("", include("wagtail.urls")),
- # Alternatively, if you want Wagtail pages to be served from a subpath
- # of your site, rather than the site root:
- # path("pages/", include("wagtail.urls"),
-]
-endef
-
-define WEBPACK_CONFIG_JS
-const path = require('path');
-
-module.exports = {
- mode: 'development',
- entry: './src/index.js',
- output: {
- filename: 'bundle.js',
- path: path.resolve(__dirname, 'dist'),
- },
-};
-endef
-
-define WEBPACK_INDEX_HTML
-
-
-
-
-
- Hello, Webpack!
-
-
-
-
-
-endef
-
-define WEBPACK_INDEX_JS
-const message = "Hello, World!";
-console.log(message);
-endef
-
-define WEBPACK_REVEAL_CONFIG_JS
-const path = require('path');
-const MiniCssExtractPlugin = require('mini-css-extract-plugin');
-
-module.exports = {
- mode: 'development',
- entry: './src/index.js',
- output: {
- filename: 'bundle.js',
- path: path.resolve(__dirname, 'dist'),
- },
- module: {
- rules: [
- {
- test: /\.css$$/,
- use: [MiniCssExtractPlugin.loader, 'css-loader'],
- },
- ],
- },
- plugins: [
- new MiniCssExtractPlugin({
- filename: 'bundle.css',
- }),
- ],
-};
-endef
-
-define WEBPACK_REVEAL_INDEX_HTML
-
-
-
-
-
- Project Makefile
-
-
-
-
-
- Slide 1: Draw some circles
-
-
- Slide 2: Draw the rest of the owl
-
-
-
-
-
-endef
-
-define WEBPACK_REVEAL_INDEX_JS
-import 'reveal.js/dist/reveal.css';
-import 'reveal.js/dist/theme/black.css';
-import Reveal from 'reveal.js';
-import RevealNotes from 'reveal.js/plugin/notes/notes.js';
-Reveal.initialize({ slideNumber: true, plugins: [ RevealNotes ]});
-endef
-
-# ------------------------------------------------------------------------------
-# Export variables used by phony target rules
-# ------------------------------------------------------------------------------
-
-export DJANGO_ALLAUTH_BASE_TEMPLATE
-export DJANGO_API_SERIALIZERS
-export DJANGO_API_VIEWS
-export DJANGO_APP_TESTS
-export DJANGO_BACKEND_APPS
-export DJANGO_BASE_TEMPLATE
-export DJANGO_CUSTOM_ADMIN
-export DJANGO_DOCKERCOMPOSE
-export DJANGO_DOCKERFILE
-export DJANGO_FAVICON_TEMPLATE
-export DJANGO_FOOTER_TEMPLATE
-export DJANGO_FRONTEND_APP
-export DJANGO_FRONTEND_APP_CONFIG
-export DJANGO_FRONTEND_BABELRC
-export DJANGO_FRONTEND_COMPONENTS
-export DJANGO_FRONTEND_COMPONENT_CLOCK
-export DJANGO_FRONTEND_COMPONENT_ERROR
-export DJANGO_FRONTEND_COMPONENT_USER_MENU
-export DJANGO_FRONTEND_CONTEXT_INDEX
-export DJANGO_FRONTEND_CONTEXT_USER_PROVIDER
-export DJANGO_FRONTEND_ESLINTRC
-export DJANGO_FRONTEND_OFFCANVAS_TEMPLATE
-export DJANGO_FRONTEND_PORTAL
-export DJANGO_FRONTEND_STYLES
-export DJANGO_FRONTEND_THEME_BLUE
-export DJANGO_FRONTEND_THEME_TOGGLER
-export DJANGO_HEADER_TEMPLATE
-export DJANGO_HOME_PAGE_ADMIN
-export DJANGO_HOME_PAGE_MODELS
-export DJANGO_HOME_PAGE_TEMPLATE
-export DJANGO_HOME_PAGE_URLS
-export DJANGO_HOME_PAGE_VIEWS
-export DJANGO_LOGGING_DEMO_ADMIN
-export DJANGO_LOGGING_DEMO_MODELS
-export DJANGO_LOGGING_DEMO_SETTINGS
-export DJANGO_LOGGING_DEMO_URLS
-export DJANGO_LOGGING_DEMO_VIEWS
-export DJANGO_MANAGE_PY
-export DJANGO_MODEL_FORM_DEMO_ADMIN
-export DJANGO_MODEL_FORM_DEMO_FORMS
-export DJANGO_MODEL_FORM_DEMO_MODEL
-export DJANGO_MODEL_FORM_DEMO_TEMPLATE_DETAIL
-export DJANGO_MODEL_FORM_DEMO_TEMPLATE_FORM
-export DJANGO_MODEL_FORM_DEMO_TEMPLATE_LIST
-export DJANGO_MODEL_FORM_DEMO_URLS
-export DJANGO_MODEL_FORM_DEMO_VIEWS
-export DJANGO_PAYMENTS_ADMIN
-export DJANGO_PAYMENTS_FORM
-export DJANGO_PAYMENTS_MIGRATION_0002
-export DJANGO_PAYMENTS_MIGRATION_0003
-export DJANGO_PAYMENTS_MODELS
-export DJANGO_PAYMENTS_TEMPLATE_CANCEL
-export DJANGO_PAYMENTS_TEMPLATE_CHECKOUT
-export DJANGO_PAYMENTS_TEMPLATE_PRODUCT_DETAIL
-export DJANGO_PAYMENTS_TEMPLATE_PRODUCT_LIST
-export DJANGO_PAYMENTS_TEMPLATE_SUCCESS
-export DJANGO_PAYMENTS_URLS
-export DJANGO_PAYMENTS_VIEW
-export DJANGO_SEARCH_FORMS
-export DJANGO_SEARCH_SETTINGS
-export DJANGO_SEARCH_TEMPLATE
-export DJANGO_SEARCH_URLS
-export DJANGO_SEARCH_UTILS
-export DJANGO_SEARCH_VIEWS
-export DJANGO_SETTINGS_AUTHENTICATION_BACKENDS
-export DJANGO_SETTINGS_BASE
-export DJANGO_SETTINGS_BASE_MINIMAL
-export DJANGO_SETTINGS_CRISPY_FORMS
-export DJANGO_SETTINGS_DATABASE
-export DJANGO_SETTINGS_DEV
-export DJANGO_SETTINGS_HOME_PAGE
-export DJANGO_SETTINGS_INSTALLED_APPS
-export DJANGO_SETTINGS_MIDDLEWARE
-export DJANGO_SETTINGS_MODEL_FORM_DEMO
-export DJANGO_SETTINGS_PAYMENTS
-export DJANGO_SETTINGS_PROD
-export DJANGO_SETTINGS_REST_FRAMEWORK
-export DJANGO_SETTINGS_SITEUSER
-export DJANGO_SETTINGS_THEMES
-export DJANGO_SITEUSER_ADMIN
-export DJANGO_SITEUSER_EDIT_TEMPLATE
-export DJANGO_SITEUSER_FORM
-export DJANGO_SITEUSER_MODEL
-export DJANGO_SITEUSER_URLS
-export DJANGO_SITEUSER_VIEW
-export DJANGO_SITEUSER_VIEW_TEMPLATE
-export DJANGO_URLS
-export DJANGO_URLS_ALLAUTH
-export DJANGO_URLS_API
-export DJANGO_URLS_DEBUG_TOOLBAR
-export DJANGO_URLS_HOME_PAGE
-export DJANGO_URLS_LOGGING_DEMO
-export DJANGO_URLS_MODEL_FORM_DEMO
-export DJANGO_URLS_SITEUSER
-export DJANGO_UTILS
-export EB_CUSTOM_ENV_EC2_USER
-export EB_CUSTOM_ENV_VAR_FILE
-export GIT_IGNORE
-export JENKINS_FILE
-export MAKEFILE_CUSTOM
-export PIP_INSTALL_REQUIREMENTS_TEST
-export PROGRAMMING_INTERVIEW
-export PYTHON_CI_YAML
-export PYTHON_LICENSE_TXT
-export PYTHON_PROJECT_TOML
-export SEPARATOR
-export TINYMCE_JS
-export WAGTAIL_BASE_TEMPLATE
-export WAGTAIL_BLOCK_CAROUSEL
-export WAGTAIL_BLOCK_MARKETING
-export WAGTAIL_CONTACT_PAGE_LANDING
-export WAGTAIL_CONTACT_PAGE_MODEL
-export WAGTAIL_CONTACT_PAGE_TEMPLATE
-export WAGTAIL_CONTACT_PAGE_TEST
-export WAGTAIL_HOME_PAGE_MODEL
-export WAGTAIL_HOME_PAGE_TEMPLATE
-export WAGTAIL_HOME_PAGE_URLS
-export WAGTAIL_HOME_PAGE_VIEWS
-export WAGTAIL_PRIVACY_PAGE_MODEL
-export WAGTAIL_PRIVACY_PAGE_MODEL
-export WAGTAIL_PRIVACY_PAGE_TEMPLATE
-export WAGTAIL_SEARCH_TEMPLATE
-export WAGTAIL_SEARCH_URLS
-export WAGTAIL_SETTINGS
-export WAGTAIL_SITEPAGE_MODEL
-export WAGTAIL_SITEPAGE_TEMPLATE
-export WAGTAIL_URLS
-export WAGTAIL_URLS_HOME
-export WEBPACK_CONFIG_JS
-export WEBPACK_INDEX_HTML
-export WEBPACK_INDEX_JS
-export WEBPACK_REVEAL_CONFIG_JS
-export WEBPACK_REVEAL_INDEX_HTML
-export WEBPACK_REVEAL_INDEX_JS
-
-# ------------------------------------------------------------------------------
-# Multi-line phony target rules
-# ------------------------------------------------------------------------------
-
-.PHONY: aws-check-env-profile-default
-aws-check-env-profile-default:
-ifndef AWS_PROFILE
- $(error AWS_PROFILE is undefined)
-endif
-
-.PHONY: aws-check-env-region-default
-aws-check-env-region-default:
-ifndef AWS_REGION
- $(error AWS_REGION is undefined)
-endif
-
-.PHONY: aws-secret-default
-aws-secret-default: aws-check-env
- @SECRET_KEY=$$(openssl rand -base64 48); aws ssm put-parameter --name "SECRET_KEY" --value "$$SECRET_KEY" --type String
-
-.PHONY: aws-sg-default
-aws-sg-default: aws-check-env
- aws ec2 describe-security-groups $(AWS_OPTS)
-
-.PHONY: aws-ssm-default
-aws-ssm-default: aws-check-env
- aws ssm describe-parameters $(AWS_OPTS)
- @echo "Get parameter values with: aws ssm getparameter --name ."
-
-.PHONY: aws-subnet-default
-aws-subnet-default: aws-check-env
- aws ec2 describe-subnets $(AWS_OPTS)
-
-.PHONY: aws-vol-available-default
-aws-vol-available-default: aws-check-env
- aws ec2 describe-volumes --filters Name=status,Values=available --query "Volumes[*].{ID:VolumeId,Size:Size}" --output table
-
-.PHONY: aws-vol-default
-aws-vol-default: aws-check-env
- aws ec2 describe-volumes --output table
-
-.PHONY: aws-vpc-default
-aws-vpc-default: aws-check-env
- aws ec2 describe-vpcs $(AWS_OPTS)
-
-.PHONY: db-import-default
-db-import-default:
- @psql $(DJANGO_DB_NAME) < $(DJANGO_DB_NAME).sql
-
-.PHONY: db-init-default
-db-init-default:
- -dropdb $(PROJECT_NAME)
- -createdb $(PROJECT_NAME)
-
-.PHONY: db-init-mysql-default
-db-init-mysql-default:
- -mysqladmin -u root drop $(PROJECT_NAME)
- -mysqladmin -u root create $(PROJECT_NAME)
-
-.PHONY: db-init-test-default
-db-init-test-default:
- -dropdb test_$(PROJECT_NAME)
- -createdb test_$(PROJECT_NAME)
-
-.PHONY: django-allauth-default
-django-allauth-default:
- $(ADD_DIR) backend/templates/allauth/layouts
- @echo "$$DJANGO_ALLAUTH_BASE_TEMPLATE" > backend/templates/allauth/layouts/base.html
- @echo "$$DJANGO_URLS_ALLAUTH" >> $(DJANGO_URLS_FILE)
- -$(GIT_ADD) backend/templates/allauth/layouts/base.html
-
-.PHONY: django-app-tests-default
-django-app-tests-default:
- @echo "$$DJANGO_APP_TESTS" > $(APP_DIR)/tests.py
-
-.PHONY: django-base-template-default
-django-base-template-default:
- @$(ADD_DIR) backend/templates
- @echo "$$DJANGO_BASE_TEMPLATE" > backend/templates/base.html
- -$(GIT_ADD) backend/templates/base.html
-
-.PHONY: django-custom-admin-default
-django-custom-admin-default:
- @echo "$$DJANGO_CUSTOM_ADMIN" > $(DJANGO_CUSTOM_ADMIN_FILE)
- @echo "$$DJANGO_BACKEND_APPS" > $(DJANGO_BACKEND_APPS_FILE)
- -$(GIT_ADD) backend/*.py
-
-.PHONY: django-db-shell-default
-django-db-shell-default:
- python manage.py dbshell
-
-.PHONY: django-dockerfile-default
-django-dockerfile-default:
- @echo "$$DJANGO_DOCKERFILE" > Dockerfile
- -$(GIT_ADD) Dockerfile
- @echo "$$DJANGO_DOCKERCOMPOSE" > docker-compose.yml
- -$(GIT_ADD) docker-compose.yml
-
-.PHONY: django-favicon-default
-django-favicon-default:
- @echo "$$DJANGO_FAVICON_TEMPLATE" > backend/templates/favicon.html
- -$(GIT_ADD) backend/templates/favicon.html
-
-.PHONY: django-footer-template-default
-django-footer-template-default:
- @echo "$$DJANGO_FOOTER_TEMPLATE" > backend/templates/footer.html
- -$(GIT_ADD) backend/templates/footer.html
-
-.PHONY: django-frontend-default
-django-frontend-default: python-webpack-init
- $(ADD_DIR) frontend/src/context
- $(ADD_DIR) frontend/src/images
- $(ADD_DIR) frontend/src/utils
- @echo "$$DJANGO_FRONTEND_APP" > frontend/src/application/app.js
- @echo "$$DJANGO_FRONTEND_APP_CONFIG" > frontend/src/application/config.js
- @echo "$$DJANGO_FRONTEND_BABELRC" > frontend/.babelrc
- @echo "$$DJANGO_FRONTEND_COMPONENT_CLOCK" > frontend/src/components/Clock.js
- @echo "$$DJANGO_FRONTEND_COMPONENT_ERROR" > frontend/src/components/ErrorBoundary.js
- @echo "$$DJANGO_FRONTEND_CONTEXT_INDEX" > frontend/src/context/index.js
- @echo "$$DJANGO_FRONTEND_CONTEXT_USER_PROVIDER" > frontend/src/context/UserContextProvider.js
- @echo "$$DJANGO_FRONTEND_COMPONENT_USER_MENU" > frontend/src/components/UserMenu.js
- @echo "$$DJANGO_FRONTEND_COMPONENTS" > frontend/src/components/index.js
- @echo "$$DJANGO_FRONTEND_ESLINTRC" > frontend/.eslintrc
- @echo "$$DJANGO_FRONTEND_PORTAL" > frontend/src/dataComponents.js
- @echo "$$DJANGO_FRONTEND_STYLES" > frontend/src/styles/index.scss
- @echo "$$DJANGO_FRONTEND_THEME_BLUE" > frontend/src/styles/theme-blue.scss
- @echo "$$DJANGO_FRONTEND_THEME_TOGGLER" > frontend/src/utils/themeToggler.js
- # @echo "$$TINYMCE_JS" > frontend/src/utils/tinymce.js
- @$(MAKE) npm-install-django
- @$(MAKE) npm-install-django-dev
- -$(GIT_ADD) $(DJANGO_FRONTEND_FILES)
-
-.PHONY: django-graph-default
-django-graph-default:
- python manage.py graph_models -a -o $(PROJECT_NAME).png
-
-.PHONY: django-header-template-default
-django-header-template-default:
- @echo "$$DJANGO_HEADER_TEMPLATE" > backend/templates/header.html
- -$(GIT_ADD) backend/templates/header.html
-
-.PHONY: django-home-default
-django-home-default:
- python manage.py startapp home
- $(ADD_DIR) home/templates
- @echo "$$DJANGO_HOME_PAGE_ADMIN" > home/admin.py
- @echo "$$DJANGO_HOME_PAGE_MODELS" > home/models.py
- @echo "$$DJANGO_HOME_PAGE_TEMPLATE" > home/templates/home.html
- @echo "$$DJANGO_HOME_PAGE_VIEWS" > home/views.py
- @echo "$$DJANGO_HOME_PAGE_URLS" > home/urls.py
- @echo "$$DJANGO_URLS_HOME_PAGE" >> $(DJANGO_URLS_FILE)
- @echo "$$DJANGO_SETTINGS_HOME_PAGE" >> $(DJANGO_SETTINGS_BASE_FILE)
- export APP_DIR="home"; $(MAKE) django-app-tests
- -$(GIT_ADD) home/templates
- -$(GIT_ADD) home/*.py
- -$(GIT_ADD) home/migrations/*.py
-
-.PHONY: django-init-default
-django-init-default: separator \
- db-init \
- django-install \
- django-project \
- django-utils \
- pip-freeze \
- pip-init-test \
- django-settings-directory \
- django-custom-admin \
- django-dockerfile \
- django-offcanvas-template \
- django-header-template \
- django-footer-template \
- django-base-template \
- django-manage-py \
- django-urls \
- django-urls-debug-toolbar \
- django-allauth \
- django-favicon \
- git-ignore \
- django-settings-base \
- django-settings-dev \
- django-settings-prod \
- django-siteuser \
- django-home \
- django-rest-serializers \
- django-rest-views \
- django-urls-api \
- django-frontend \
- django-migrate \
- django-su
-
-.PHONY: django-init-minimal-default
-django-init-minimal-default: separator \
- db-init \
- django-install-minimal \
- django-project \
- django-settings-directory \
- django-settings-base-minimal \
- django-settings-dev \
- pip-freeze \
- pip-init-test \
- django-custom-admin \
- django-dockerfile \
- django-offcanvas-template \
- django-header-template \
- django-footer-template \
- django-base-template \
- django-manage-py \
- django-urls \
- django-urls-debug-toolbar \
- django-favicon \
- django-settings-prod \
- django-home \
- django-utils \
- django-frontend \
- django-migrate \
- git-ignore \
- django-su
-
-.PHONY: django-init-wagtail-default
-django-init-wagtail-default: separator \
- db-init \
- django-install \
- wagtail-install \
- wagtail-project \
- django-utils \
- pip-freeze \
- pip-init-test \
- django-custom-admin \
- django-dockerfile \
- django-offcanvas-template \
- wagtail-header-prefix-template \
- django-header-template \
- wagtail-base-template \
- django-footer-template \
- django-manage-py \
- wagtail-home \
- wagtail-urls \
- django-urls-debug-toolbar \
- django-allauth \
- django-favicon \
- git-ignore \
- wagtail-search \
- django-settings-base \
- django-settings-dev \
- django-settings-prod \
- wagtail-settings \
- django-siteuser \
- django-model-form-demo \
- django-logging-demo \
- django-payments-demo-default \
- django-rest-serializers \
- django-rest-views \
- django-urls-api \
- wagtail-urls-home \
- django-frontend \
- django-migrate \
- django-su
-
-.PHONY: django-install-default
-django-install-default:
- $(PIP_ENSURE)
- python -m pip install \
- Django \
- Faker \
- boto3 \
- crispy-bootstrap5 \
- djangorestframework \
- django-allauth \
- django-after-response \
- django-ckeditor \
- django-colorful \
- django-cors-headers \
- django-countries \
- django-crispy-forms \
- django-debug-toolbar \
- django-extensions \
- django-hijack \
- django-honeypot \
- django-imagekit \
- django-import-export \
- django-ipware \
- django-multiselectfield \
- django-ninja \
- django-phonenumber-field \
- django-recurrence \
- django-recaptcha \
- django-registration \
- django-richtextfield \
- django-sendgrid-v5 \
- django-social-share \
- django-sql-explorer \
- django-storages \
- django-tables2 \
- django-timezone-field \
- django-widget-tweaks \
- dj-database-url \
- dj-rest-auth \
- dj-stripe \
- docutils \
- enmerkar \
- gunicorn \
- html2docx \
- icalendar \
- mailchimp-marketing \
- mailchimp-transactional \
- phonenumbers \
- pipdeptree \
- psycopg2-binary \
- pydotplus \
- python-webpack-boilerplate \
- python-docx \
- reportlab \
- texttable
-
-.PHONY: django-install-minimal-default
-django-install-minimal-default:
- $(PIP_ENSURE)
- python -m pip install \
- Django \
- dj-database-url \
- django-debug-toolbar \
- python-webpack-boilerplate
-
-.PHONY: django-lint-default
-django-lint-default:
- -ruff format -v
- -djlint --reformat --format-css --format-js .
- -ruff check -v --fix
-
-.PHONY: django-loaddata-default
-django-loaddata-default:
- python manage.py loaddata
-
-.PHONY: django-logging-demo-default
-django-logging-demo-default:
- python manage.py startapp logging_demo
- @echo "$$DJANGO_LOGGING_DEMO_ADMIN" > logging_demo/admin.py
- @echo "$$DJANGO_LOGGING_DEMO_MODELS" > logging_demo/models.py
- @echo "$$DJANGO_LOGGING_DEMO_SETTINGS" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_LOGGING_DEMO_URLS" > logging_demo/urls.py
- @echo "$$DJANGO_LOGGING_DEMO_VIEWS" > logging_demo/views.py
- @echo "$$DJANGO_URLS_LOGGING_DEMO" >> $(DJANGO_URLS_FILE)
- export APP_DIR="logging_demo"; $(MAKE) django-app-tests
- -$(GIT_ADD) logging_demo/*.py
- -$(GIT_ADD) logging_demo/migrations/*.py
-
-.PHONY: django-manage-py-default
-django-manage-py-default:
- @echo "$$DJANGO_MANAGE_PY" > manage.py
- -$(GIT_ADD) manage.py
-
-.PHONY: django-migrate-default
-django-migrate-default:
- python manage.py migrate
-
-.PHONY: django-migrations-make-default
-django-migrations-make-default:
- python manage.py makemigrations
-
-.PHONY: django-migrations-show-default
-django-migrations-show-default:
- python manage.py showmigrations
-
-.PHONY: django-model-form-demo-default
-django-model-form-demo-default:
- python manage.py startapp model_form_demo
- @echo "$$DJANGO_MODEL_FORM_DEMO_ADMIN" > model_form_demo/admin.py
- @echo "$$DJANGO_MODEL_FORM_DEMO_FORMS" > model_form_demo/forms.py
- @echo "$$DJANGO_MODEL_FORM_DEMO_MODEL" > model_form_demo/models.py
- @echo "$$DJANGO_MODEL_FORM_DEMO_URLS" > model_form_demo/urls.py
- @echo "$$DJANGO_MODEL_FORM_DEMO_VIEWS" > model_form_demo/views.py
- $(ADD_DIR) model_form_demo/templates
- @echo "$$DJANGO_MODEL_FORM_DEMO_TEMPLATE_DETAIL" > model_form_demo/templates/model_form_demo_detail.html
- @echo "$$DJANGO_MODEL_FORM_DEMO_TEMPLATE_FORM" > model_form_demo/templates/model_form_demo_form.html
- @echo "$$DJANGO_MODEL_FORM_DEMO_TEMPLATE_LIST" > model_form_demo/templates/model_form_demo_list.html
- @echo "$$DJANGO_SETTINGS_MODEL_FORM_DEMO" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_URLS_MODEL_FORM_DEMO" >> $(DJANGO_URLS_FILE)
- export APP_DIR="model_form_demo"; $(MAKE) django-app-tests
- python manage.py makemigrations
- -$(GIT_ADD) model_form_demo/*.py
- -$(GIT_ADD) model_form_demo/templates
- -$(GIT_ADD) model_form_demo/migrations
-
-.PHONY: django-offcanvas-template-default
-django-offcanvas-template-default:
- -$(ADD_DIR) backend/templates
- @echo "$$DJANGO_FRONTEND_OFFCANVAS_TEMPLATE" > backend/templates/offcanvas.html
- -$(GIT_ADD) backend/templates/offcanvas.html
-
-.PHONY: django-open-default
-django-open-default:
-ifeq ($(UNAME), Linux)
- @echo "Opening on Linux."
- xdg-open http://0.0.0.0:8000
-else ifeq ($(UNAME), Darwin)
- @echo "Opening on macOS (Darwin)."
- open http://0.0.0.0:8000
-else
- @echo "Unable to open on: $(UNAME)"
-endif
-
-.PHONY: django-payments-demo-default
-django-payments-demo-default:
- python manage.py startapp payments
- @echo "$$DJANGO_PAYMENTS_FORM" > payments/forms.py
- @echo "$$DJANGO_PAYMENTS_MODELS" > payments/models.py
- @echo "$$DJANGO_PAYMENTS_ADMIN" > payments/admin.py
- @echo "$$DJANGO_PAYMENTS_VIEW" > payments/views.py
- @echo "$$DJANGO_PAYMENTS_URLS" > payments/urls.py
- $(ADD_DIR) payments/templates/payments
- $(ADD_DIR) payments/management/commands
- @echo "$$DJANGO_PAYMENTS_TEMPLATE_CANCEL" > payments/templates/payments/cancel.html
- @echo "$$DJANGO_PAYMENTS_TEMPLATE_CHECKOUT" > payments/templates/payments/checkout.html
- @echo "$$DJANGO_PAYMENTS_TEMPLATE_SUCCESS" > payments/templates/payments/success.html
- @echo "$$DJANGO_PAYMENTS_TEMPLATE_PRODUCT_LIST" > payments/templates/payments/product_list.html
- @echo "$$DJANGO_PAYMENTS_TEMPLATE_PRODUCT_DETAIL" > payments/templates/payments/product_detail.html
- @echo "$$DJANGO_SETTINGS_PAYMENTS" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_URLS_PAYMENTS" >> $(DJANGO_URLS_FILE)
- export APP_DIR="payments"; $(MAKE) django-app-tests
- python manage.py makemigrations payments
- @echo "$$DJANGO_PAYMENTS_MIGRATION_0002" > payments/migrations/0002_set_stripe_api_keys.py
- @echo "$$DJANGO_PAYMENTS_MIGRATION_0003" > payments/migrations/0003_create_initial_products.py
- -$(GIT_ADD) payments/
-
-.PHONY: django-project-default
-django-project-default:
- django-admin startproject backend .
- -$(GIT_ADD) backend
-
-.PHONY: django-rest-serializers-default
-django-rest-serializers-default:
- @echo "$$DJANGO_API_SERIALIZERS" > backend/serializers.py
- -$(GIT_ADD) backend/serializers.py
-
-.PHONY: django-rest-views-default
-django-rest-views-default:
- @echo "$$DJANGO_API_VIEWS" > backend/api.py
- -$(GIT_ADD) backend/api.py
-
-.PHONY: django-search-default
-django-search-default:
- python manage.py startapp search
- $(ADD_DIR) search/templates
- @echo "$$DJANGO_SEARCH_TEMPLATE" > search/templates/search.html
- @echo "$$DJANGO_SEARCH_FORMS" > search/forms.py
- @echo "$$DJANGO_SEARCH_URLS" > search/urls.py
- @echo "$$DJANGO_SEARCH_UTILS" > search/utils.py
- @echo "$$DJANGO_SEARCH_VIEWS" > search/views.py
- @echo "$$DJANGO_SEARCH_SETTINGS" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "INSTALLED_APPS.append('search')" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "urlpatterns += [path('search/', include('search.urls'))]" >> $(DJANGO_URLS_FILE)
- -$(GIT_ADD) search/templates
- -$(GIT_ADD) search/*.py
-
-.PHONY: django-secret-key-default
-django-secret-key-default:
- @python -c "from secrets import token_urlsafe; print(token_urlsafe(50))"
-
-.PHONY: django-serve-default
-django-serve-default:
- npm run watch &
- python manage.py runserver 0.0.0.0:8000
-
-.PHONY: django-settings-base-default
-django-settings-base-default:
- @echo "$$DJANGO_SETTINGS_BASE" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_SETTINGS_AUTHENTICATION_BACKENDS" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_SETTINGS_REST_FRAMEWORK" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_SETTINGS_THEMES" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_SETTINGS_DATABASE" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_SETTINGS_INSTALLED_APPS" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_SETTINGS_MIDDLEWARE" >> $(DJANGO_SETTINGS_BASE_FILE)
- @echo "$$DJANGO_SETTINGS_CRISPY_FORMS" >> $(DJANGO_SETTINGS_BASE_FILE)
-
-.PHONY: django-settings-base-minimal-default
-django-settings-base-minimal-default:
- @echo "$$DJANGO_SETTINGS_BASE_MINIMAL" >> $(DJANGO_SETTINGS_BASE_FILE)
-
-.PHONY: django-settings-dev-default
-django-settings-dev-default:
- @echo "# $(PROJECT_NAME)" > $(DJANGO_SETTINGS_DEV_FILE)
- @echo "$$DJANGO_SETTINGS_DEV" >> backend/settings/dev.py
- -$(GIT_ADD) $(DJANGO_SETTINGS_DEV_FILE)
-
-.PHONY: django-settings-directory-default
-django-settings-directory-default:
- @$(ADD_DIR) $(DJANGO_SETTINGS_DIR)
- @$(COPY_FILE) backend/settings.py backend/settings/base.py
- @$(DEL_FILE) backend/settings.py
- -$(GIT_ADD) backend/settings/*.py
-
-.PHONY: django-settings-prod-default
-django-settings-prod-default:
- @echo "$$DJANGO_SETTINGS_PROD" > $(DJANGO_SETTINGS_PROD_FILE)
- -$(GIT_ADD) $(DJANGO_SETTINGS_PROD_FILE)
-
-.PHONY: django-shell-default
-django-shell-default:
- python manage.py shell
-
-.PHONY: django-siteuser-default
-django-siteuser-default:
- python manage.py startapp siteuser
- $(ADD_DIR) siteuser/templates/
- @echo "$$DJANGO_SITEUSER_FORM" > siteuser/forms.py
- @echo "$$DJANGO_SITEUSER_MODEL" > siteuser/models.py
- @echo "$$DJANGO_SITEUSER_ADMIN" > siteuser/admin.py
- @echo "$$DJANGO_SITEUSER_VIEW" > siteuser/views.py
- @echo "$$DJANGO_SITEUSER_URLS" > siteuser/urls.py
- @echo "$$DJANGO_SITEUSER_VIEW_TEMPLATE" > siteuser/templates/profile.html
- @echo "$$DJANGO_SITEUSER_TEMPLATE" > siteuser/templates/user.html
- @echo "$$DJANGO_SITEUSER_EDIT_TEMPLATE" > siteuser/templates/user_edit.html
- @echo "$$DJANGO_URLS_SITEUSER" >> $(DJANGO_URLS_FILE)
- @echo "$$DJANGO_SETTINGS_SITEUSER" >> $(DJANGO_SETTINGS_BASE_FILE)
- export APP_DIR="siteuser"; $(MAKE) django-app-tests
- -$(GIT_ADD) siteuser/templates
- -$(GIT_ADD) siteuser/*.py
- python manage.py makemigrations siteuser
- -$(GIT_ADD) siteuser/migrations/*.py
-
-.PHONY: django-static-default
-django-static-default:
- python manage.py collectstatic --noinput
-
-.PHONY: django-su-default
-django-su-default:
- DJANGO_SUPERUSER_PASSWORD=admin python manage.py createsuperuser --noinput --username=admin --email=$(PROJECT_EMAIL)
-
-.PHONY: django-test-default
-django-test-default: npm-install django-static
- -$(MAKE) pip-install-test
- python manage.py test
-
-.PHONY: django-urls-api-default
-django-urls-api-default:
- @echo "$$DJANGO_URLS_API" >> $(DJANGO_URLS_FILE)
- -$(GIT_ADD) $(DJANGO_URLS_FILE)
-
-.PHONY: django-urls-debug-toolbar-default
-django-urls-debug-toolbar-default:
- @echo "$$DJANGO_URLS_DEBUG_TOOLBAR" >> $(DJANGO_URLS_FILE)
-
-.PHONY: django-urls-default
-django-urls-default:
- @echo "$$DJANGO_URLS" > $(DJANGO_URLS_FILE)
- -$(GIT_ADD) $(DJANGO_URLS_FILE)
-
-.PHONY: django-urls-show-default
-django-urls-show-default:
- python manage.py show_urls
-
-.PHONY: django-user-default
-django-user-default:
- python manage.py shell -c "from django.contrib.auth.models import User; \
- User.objects.create_user('user', '', 'user')"
-
-.PHONY: django-utils-default
-django-utils-default:
- @echo "$$DJANGO_UTILS" > backend/utils.py
- -$(GIT_ADD) backend/utils.py
-
-.PHONY: docker-build-default
-docker-build-default:
- podman build -t $(PROJECT_NAME) .
-
-.PHONY: docker-compose-default
-docker-compose-default:
- podman compose up
-
-.PHONY: docker-list-default
-docker-list-default:
- podman container list --all
- podman images --all
-
-.PHONY: docker-run-default
-docker-run-default:
- podman run $(PROJECT_NAME)
-
-.PHONY: docker-serve-default
-docker-serve-default:
- podman run -p 8000:8000 $(PROJECT_NAME)
-
-.PHONY: docker-shell-default
-docker-shell-default:
- podman run -it $(PROJECT_NAME) /bin/bash
-
-.PHONY: eb-check-env-default
-eb-check-env-default: # https://stackoverflow.com/a/4731504/185820
-ifndef EB_SSH_KEY
- $(error EB_SSH_KEY is undefined)
-endif
-ifndef VPC_ID
- $(error VPC_ID is undefined)
-endif
-ifndef VPC_SG
- $(error VPC_SG is undefined)
-endif
-ifndef VPC_SUBNET_EC2
- $(error VPC_SUBNET_EC2 is undefined)
-endif
-ifndef VPC_SUBNET_ELB
- $(error VPC_SUBNET_ELB is undefined)
-endif
-
-.PHONY: eb-create-default
-eb-create-default: aws-check-env eb-check-env
- eb create $(EB_ENV_NAME) \
- -im $(EC2_INSTANCE_MIN) \
- -ix $(EC2_INSTANCE_MAX) \
- -ip $(EC2_INSTANCE_PROFILE) \
- -i $(EC2_INSTANCE_TYPE) \
- -k $(EB_SSH_KEY) \
- -p $(EB_PLATFORM) \
- --elb-type $(EC2_LB_TYPE) \
- --vpc \
- --vpc.id $(VPC_ID) \
- --vpc.elbpublic \
- --vpc.publicip \
- --vpc.ec2subnets $(VPC_SUBNET_EC2) \
- --vpc.elbsubnets $(VPC_SUBNET_ELB) \
- --vpc.securitygroups $(VPC_SG)
-
-.PHONY: eb-custom-env-default
-eb-custom-env-default:
- $(ADD_DIR) .ebextensions
- @echo "$$EB_CUSTOM_ENV_EC2_USER" > .ebextensions/bash.config
- -$(GIT_ADD) .ebextensions/bash.config
- $(ADD_DIR) .platform/hooks/postdeploy
- @echo "$$EB_CUSTOM_ENV_VAR_FILE" > .platform/hooks/postdeploy/setenv.sh
- -$(GIT_ADD) .platform/hooks/postdeploy/setenv.sh
-
-.PHONY: eb-deploy-default
-eb-deploy-default:
- eb deploy
-
-.PHONY: eb-export-default
-eb-export-default:
- @if [ ! -d $(EB_DIR_NAME) ]; then \
- echo "Directory $(EB_DIR_NAME) does not exist"; \
- else \
- echo "Directory $(EB_DIR_NAME) does exist!"; \
- eb ssh --quiet -c "export PGPASSWORD=$(DJANGO_DB_PASS); pg_dump -U $(DJANGO_DB_USER) -h $(DJANGO_DB_HOST) $(DJANGO_DB_NAME)" > $(DJANGO_DB_NAME).sql; \
- echo "Wrote $(DJANGO_DB_NAME).sql"; \
- fi
-
-.PHONY: eb-restart-default
-eb-restart-default:
- eb ssh -c "systemctl restart web"
-
-.PHONY: eb-rebuild-default
-eb-rebuild-default:
- aws elasticbeanstalk rebuild-environment --environment-name $(ENV_NAME)
-
-.PHONY: eb-upgrade-default
-eb-upgrade-default:
- eb upgrade
-
-.PHONY: eb-init-default
-eb-init-default: aws-check-env-profile
- eb init --profile=$(AWS_PROFILE)
-
-.PHONY: eb-list-default
-eb-list-platforms-default:
- aws elasticbeanstalk list-platform-versions
-
-.PHONY: eb-list-databases-default
-eb-list-databases-default:
- @eb ssh --quiet -c "export PGPASSWORD=$(DJANGO_DB_PASS); psql -l -U $(DJANGO_DB_USER) -h $(DJANGO_DB_HOST) $(DJANGO_DB_NAME)"
-
-.PHONY: eb-logs-default
-eb-logs-default:
- eb logs
-
-.PHONY: eb-print-env-default
-eb-print-env-default:
- eb printenv
-
-.PHONY: favicon-default
-favicon-init-default:
- dd if=/dev/urandom bs=64 count=1 status=none | base64 | convert -size 16x16 -depth 8 -background none -fill white label:@- favicon.png
- convert favicon.png favicon.ico
- -$(GIT_ADD) favicon.ico
- $(DEL_FILE) favicon.png
-
-.PHONY: git-ignore-default
-git-ignore-default:
- @echo "$$GIT_IGNORE" > .gitignore
- -$(GIT_ADD) .gitignore
-
-.PHONY: git-branches-default
-git-branches-default:
- -for i in $(GIT_BRANCHES) ; do \
- -@$(GIT_CHECKOUT) -t $$i ; done
-
-.PHONY: git-commit-message-clean-default
-git-commit-message-clean-default:
- -@$(GIT_COMMIT) -a -m "Clean"
-
-.PHONY: git-commit-message-default
-git-commit-message-default:
- -@$(GIT_COMMIT) -a -m $(GIT_COMMIT_MSG)
-
-.PHONY: git-commit-message-empty-default
-git-commit-message-empty-default:
- -@$(GIT_COMMIT) --allow-empty -m "Empty-Commit"
-
-.PHONY: git-commit-message-init-default
-git-commit-message-init-default:
- -@$(GIT_COMMIT) -a -m "Init"
-
-.PHONY: git-commit-message-last-default
-git-commit-message-last-default:
- git log -1 --pretty=%B > $(TMPDIR)/commit.txt
- -$(GIT_COMMIT) -a -F $(TMPDIR)/commit.txt
-
-.PHONY: git-commit-message-lint-default
-git-commit-message-lint-default:
- -@$(GIT_COMMIT) -a -m "Lint"
-
-.PHONY: git-commit-message-mk-default
-git-commit-message-mk-default:
- -@$(GIT_COMMIT) project.mk -m "Add/update $(MAKEFILE_CUSTOM_FILE)"
-
-.PHONY: git-commit-message-rename-default
-git-commit-message-rename-default:
- -@$(GIT_COMMIT) -a -m "Rename"
-
-.PHONY: git-commit-message-sort-default
-git-commit-message-sort-default:
- -@$(GIT_COMMIT) -a -m "Sort"
-
-.PHONY: git-push-default
-git-push-default:
- -@$(GIT_PUSH)
-
-.PHONY: git-push-force-default
-git-push-force-default:
- -@$(GIT_PUSH_FORCE)
-
-.PHONY: git-commit-edit-default
-git-commit-edit-default:
- -$(GIT_COMMIT) -a
-
-.PHONY: git-prune-default
-git-prune-default:
- git remote update origin --prune
-
-.PHONY: git-set-upstream-default
-git-set-upstream-default:
- git push --set-upstream origin main
-
-.PHONY: git-set-default-default
-git-set-default-default:
- gh repo set-default
-
-.PHONY: git-short-default
-git-short-default:
- @echo $(GIT_REV)
-
-.PHONY: help-default
-help-default:
- @echo "Project Makefile 🤷"
- @echo "Usage: make [options] [target] ..."
- @echo "Examples:"
- @echo " make help Print this message"
- @echo " make list-defines list all defines in the Makefile"
- @echo " make list-commands list all targets in the Makefile"
-
-.PHONY: jenkins-init-default
-jenkins-init-default:
- @echo "$$JENKINS_FILE" > Jenkinsfile
-
-.PHONY: makefile-list-commands-default
-makefile-list-commands-default:
- @for makefile in $(MAKEFILE_LIST); do \
- echo "Commands from $$makefile:"; \
- $(MAKE) -pRrq -f $$makefile : 2>/dev/null | \
- awk -v RS= -F: '/^# File/,/^# Finished Make data base/ { \
- if ($$1 !~ "^[#.]") { sub(/-default$$/, "", $$1); print $$1 } }' | \
- egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | \
- tr ' ' '\n' | \
- sort | \
- awk '{print $$0}' ; \
- echo; \
- done | $(PAGER)
-
-.PHONY: makefile-list-defines-default
-makefile-list-defines-default:
- @grep '^define [A-Za-z_][A-Za-z0-9_]*' Makefile
-
-.PHONY: makefile-list-exports-default
-makefile-list-exports-default:
- @grep '^export [A-Z][A-Z_]*' Makefile
-
-.PHONY: makefile-list-targets-default
-makefile-list-targets-default:
- @perl -ne 'print if /^\s*\.PHONY:/ .. /^[a-zA-Z0-9_-]+:/;' Makefile | grep -v .PHONY
-
-.PHONY: make-default
-make-default:
- -$(GIT_ADD) Makefile
- -$(GIT_COMMIT) Makefile -m "Add/update project-makefile files"
- -git push
-
-.PHONY: npm-init-default
-npm-init-default:
- npm init -y
- -$(GIT_ADD) package.json
- -$(GIT_ADD) package-lock.json
-
-.PHONY: npm-build-default
-npm-build-default:
- npm run build
-
-.PHONY: npm-install-default
-npm-install-default:
- npm install
- -$(GIT_ADD) package-lock.json
-
-.PHONY: npm-install-django-default
-npm-install-django-default:
- npm install \
- @fortawesome/fontawesome-free \
- @fortawesome/fontawesome-svg-core \
- @fortawesome/free-brands-svg-icons \
- @fortawesome/free-solid-svg-icons \
- @fortawesome/react-fontawesome \
- bootstrap \
- camelize \
- date-fns \
- history \
- mapbox-gl \
- query-string \
- react-animate-height \
- react-chartjs-2 \
- react-copy-to-clipboard \
- react-date-range \
- react-dom \
- react-dropzone \
- react-hook-form \
- react-image-crop \
- react-map-gl \
- react-modal \
- react-resize-detector \
- react-select \
- react-swipeable \
- snakeize \
- striptags \
- url-join \
- viewport-mercator-project
-
-.PHONY: npm-install-django-dev-default
-npm-install-django-dev-default:
- npm install \
- eslint-plugin-react \
- eslint-config-standard \
- eslint-config-standard-jsx \
- @babel/core \
- @babel/preset-env \
- @babel/preset-react \
- --save-dev
-
-.PHONY: npm-serve-default
-npm-serve-default:
- npm run start
-
-.PHONY: npm-test-default
-npm-test-default:
- npm run test
-
-.PHONY: pip-deps-default
-pip-deps-default:
- $(PIP_ENSURE)
- python -m pip install pipdeptree
- python -m pipdeptree
- pipdeptree
-
-.PHONY: pip-freeze-default
-pip-freeze-default:
- $(PIP_ENSURE)
- python -m pip freeze | sort > $(TMPDIR)/requirements.txt
- mv -f $(TMPDIR)/requirements.txt .
- -$(GIT_ADD) requirements.txt
-
-.PHONY: pip-init-default
-pip-init-default:
- touch requirements.txt
- -$(GIT_ADD) requirements.txt
-
-.PHONY: pip-init-test-default
-pip-init-test-default:
- @echo "$$PIP_INSTALL_REQUIREMENTS_TEST" > requirements-test.txt
- -$(GIT_ADD) requirements-test.txt
-
-.PHONY: pip-install-default
-pip-install-default:
- $(PIP_ENSURE)
- $(MAKE) pip-upgrade
- python -m pip install wheel
- python -m pip install -r requirements.txt
-
-.PHONY: pip-install-dev-default
-pip-install-dev-default:
- $(PIP_ENSURE)
- python -m pip install -r requirements-dev.txt
-
-.PHONY: pip-install-test-default
-pip-install-test-default:
- $(PIP_ENSURE)
- python -m pip install -r requirements-test.txt
-
-.PHONY: pip-install-upgrade-default
-pip-install-upgrade-default:
- cat requirements.txt | awk -F\= '{print $$1}' > $(TMPDIR)/requirements.txt
- mv -f $(TMPDIR)/requirements.txt .
- $(PIP_ENSURE)
- python -m pip install -U -r requirements.txt
- python -m pip freeze | sort > $(TMPDIR)/requirements.txt
- mv -f $(TMPDIR)/requirements.txt .
-
-.PHONY: pip-upgrade-default
-pip-upgrade-default:
- $(PIP_ENSURE)
- python -m pip install -U pip
-
-.PHONY: pip-uninstall-default
-pip-uninstall-default:
- $(PIP_ENSURE)
- python -m pip freeze | xargs python -m pip uninstall -y
-
-.PHONY: plone-clean-default
-plone-clean-default:
- $(DEL_DIR) $(PROJECT_NAME)
- $(DEL_DIR) $(PACKAGE_NAME)
-
-.PHONY: plone-init-default
-plone-init-default: git-ignore plone-install plone-instance plone-serve
-
-.PHONY: plone-install-default
-plone-install-default:
- $(PIP_ENSURE)
- python -m pip install plone -c $(PIP_INSTALL_PLONE_CONSTRAINTS)
-
-.PHONY: plone-instance-default
-plone-instance-default:
- mkwsgiinstance -d backend -u admin:admin
- cat backend/etc/zope.ini | sed -e 's/host = 127.0.0.1/host = 0.0.0.0/; s/port = 8080/port = 8000/' > $(TMPDIR)/zope.ini
- mv -f $(TMPDIR)/zope.ini backend/etc/zope.ini
- -$(GIT_ADD) backend/etc/site.zcml
- -$(GIT_ADD) backend/etc/zope.conf
- -$(GIT_ADD) backend/etc/zope.ini
-
-.PHONY: plone-serve-default
-plone-serve-default:
- runwsgi backend/etc/zope.ini
-
-.PHONY: plone-build-default
-plone-build-default:
- buildout
-
-.PHONY: programming-interview-default
-programming-interview-default:
- @echo "$$PROGRAMMING_INTERVIEW" > interview.py
- @echo "Created interview.py!"
- -@$(GIT_ADD) interview.py > /dev/null 2>&1
-
-# .NOT_PHONY!
-$(MAKEFILE_CUSTOM_FILE):
- @echo "$$MAKEFILE_CUSTOM" > $(MAKEFILE_CUSTOM_FILE)
- -$(GIT_ADD) $(MAKEFILE_CUSTOM_FILE)
-
-.PHONY: python-license-default
-python-license-default:
- @echo "$(PYTHON_LICENSE_TXT)" > LICENSE.txt
- -$(GIT_ADD) LICENSE.txt
-
-.PHONY: python-project-default
-python-project-default:
- @echo "$(PYTHON_PROJECT_TOML)" > pyproject.toml
- -$(GIT_ADD) pyproject.toml
-
-.PHONY: python-serve-default
-python-serve-default:
- @echo "\n\tServing HTTP on http://0.0.0.0:8000\n"
- python3 -m http.server
-
-.PHONY: python-sdist-default
-python-sdist-default:
- $(PIP_ENSURE)
- python setup.py sdist --format=zip
-
-.PHONY: python-webpack-init-default
-python-webpack-init-default:
- python manage.py webpack_init --no-input
-
-.PHONY: python-ci-default
-python-ci-default:
- $(ADD_DIR) .github/workflows
- @echo "$(PYTHON_CI_YAML)" > .github/workflows/build_wheels.yml
- -$(GIT_ADD) .github/workflows/build_wheels.yml
-
-.PHONY: rand-default
-rand-default:
- @openssl rand -base64 12 | sed 's/\///g'
-
-.PHONY: readme-init-default
-readme-init-default:
- @echo "# $(PROJECT_NAME)" > README.md
- -$(GIT_ADD) README.md
-
-.PHONY: readme-edit-default
-readme-edit-default:
- $(EDITOR) README.md
-
-.PHONY: reveal-init-default
-reveal-init-default: webpack-init-reveal
- npm install \
- css-loader \
- mini-css-extract-plugin \
- reveal.js \
- style-loader
- jq '.scripts += {"build": "webpack"}' package.json > \
- $(TMPDIR)/tmp.json && mv $(TMPDIR)/tmp.json package.json
- jq '.scripts += {"start": "webpack serve --mode development --port 8000 --static"}' package.json > \
- $(TMPDIR)/tmp.json && mv $(TMPDIR)/tmp.json package.json
- jq '.scripts += {"watch": "webpack watch --mode development"}' package.json > \
- $(TMPDIR)/tmp.json && mv $(TMPDIR)/tmp.json package.json
-
-.PHONY: reveal-serve-default
-reveal-serve-default:
- npm run watch &
- python -m http.server
-
-.PHONY: review-default
-review-default:
-ifeq ($(UNAME), Darwin)
- $(EDITOR_REVIEW) `find backend/ -name \*.py` `find backend/ -name \*.html` `find frontend/ -name \*.js` `find frontend/ -name \*.js`
-else
- @echo "Unsupported"
-endif
-
-.PHONY: separator-default
-separator-default:
- @echo "$$SEPARATOR"
-
-.PHONY: sphinx-init-default
-sphinx-init-default: sphinx-install
- sphinx-quickstart -q -p $(PROJECT_NAME) -a $(USER) -v 0.0.1 $(RANDIR)
- $(COPY_DIR) $(RANDIR)/* .
- $(DEL_DIR) $(RANDIR)
- -$(GIT_ADD) index.rst
- -$(GIT_ADD) conf.py
- $(DEL_FILE) make.bat
- -@$(GIT_CHECKOUT) Makefile
- $(MAKE) git-ignore
-
-.PHONY: sphinx-theme-init-default
-sphinx-theme-init-default:
- export DJANGO_FRONTEND_THEME_NAME=$(PROJECT_NAME)_theme; \
- $(ADD_DIR) $$DJANGO_FRONTEND_THEME_NAME ; \
- $(ADD_FILE) $$DJANGO_FRONTEND_THEME_NAME/__init__.py ; \
- -$(GIT_ADD) $$DJANGO_FRONTEND_THEME_NAME/__init__.py ; \
- $(ADD_FILE) $$DJANGO_FRONTEND_THEME_NAME/theme.conf ; \
- -$(GIT_ADD) $$DJANGO_FRONTEND_THEME_NAME/theme.conf ; \
- $(ADD_FILE) $$DJANGO_FRONTEND_THEME_NAME/layout.html ; \
- -$(GIT_ADD) $$DJANGO_FRONTEND_THEME_NAME/layout.html ; \
- $(ADD_DIR) $$DJANGO_FRONTEND_THEME_NAME/static/css ; \
- $(ADD_FILE) $$DJANGO_FRONTEND_THEME_NAME/static/css/style.css ; \
- $(ADD_DIR) $$DJANGO_FRONTEND_THEME_NAME/static/js ; \
- $(ADD_FILE) $$DJANGO_FRONTEND_THEME_NAME/static/js/script.js ; \
- -$(GIT_ADD) $$DJANGO_FRONTEND_THEME_NAME/static
-
-.PHONY: sphinx-install-default
-sphinx-install-default:
- echo "Sphinx\n" > requirements.txt
- @$(MAKE) pip-install
- @$(MAKE) pip-freeze
- -$(GIT_ADD) requirements.txt
-
-.PHONY: sphinx-build-default
-sphinx-build-default:
- sphinx-build -b html -d _build/doctrees . _build/html
- sphinx-build -b rinoh . _build/rinoh
-
-.PHONY: sphinx-serve-default
-sphinx-serve-default:
- cd _build/html;python3 -m http.server
-
-.PHONY: wagtail-base-template-default
-wagtail-base-template-default:
- @echo "$$WAGTAIL_BASE_TEMPLATE" > backend/templates/base.html
-
-.PHONY: wagtail-clean-default
-wagtail-clean-default:
- -@for dir in $(shell echo "$(WAGTAIL_CLEAN_DIRS)"); do \
- echo "Cleaning $$dir"; \
- $(DEL_DIR) $$dir >/dev/null 2>&1; \
- done
- -@for file in $(shell echo "$(WAGTAIL_CLEAN_FILES)"); do \
- echo "Cleaning $$file"; \
- $(DEL_FILE) $$file >/dev/null 2>&1; \
- done
-
-.PHONY: wagtail-contactpage-default
-wagtail-contactpage-default:
- python manage.py startapp contactpage
- @echo "$$WAGTAIL_CONTACT_PAGE_MODEL" > contactpage/models.py
- @echo "$$WAGTAIL_CONTACT_PAGE_TEST" > contactpage/tests.py
- $(ADD_DIR) contactpage/templates/contactpage/
- @echo "$$WAGTAIL_CONTACT_PAGE_TEMPLATE" > contactpage/templates/contactpage/contact_page.html
- @echo "$$WAGTAIL_CONTACT_PAGE_LANDING" > contactpage/templates/contactpage/contact_page_landing.html
- @echo "INSTALLED_APPS.append('contactpage')" >> $(DJANGO_SETTINGS_BASE_FILE)
- python manage.py makemigrations contactpage
- -$(GIT_ADD) contactpage/templates
- -$(GIT_ADD) contactpage/*.py
- -$(GIT_ADD) contactpage/migrations/*.py
-
-.PHONY: wagtail-header-prefix-template-default
-wagtail-header-prefix-template-default:
- @echo "$$WAGTAIL_HEADER_PREFIX" > backend/templates/header.html
-
-.PHONY: wagtail-home-default
-wagtail-home-default:
- @echo "$$WAGTAIL_HOME_PAGE_MODEL" > home/models.py
- @echo "$$WAGTAIL_HOME_PAGE_TEMPLATE" > home/templates/home/home_page.html
- $(ADD_DIR) home/templates/blocks
- @echo "$$WAGTAIL_BLOCK_MARKETING" > home/templates/blocks/marketing_block.html
- @echo "$$WAGTAIL_BLOCK_CAROUSEL" > home/templates/blocks/carousel_block.html
- -$(GIT_ADD) home/templates
- -$(GIT_ADD) home/*.py
- python manage.py makemigrations home
- -$(GIT_ADD) home/migrations/*.py
-
-.PHONY: wagtail-install-default
-wagtail-install-default:
- $(PIP_ENSURE)
- python -m pip install \
- wagtail \
- wagtailmenus \
- wagtail-color-panel \
- wagtail-django-recaptcha \
- wagtail-markdown \
- wagtail-modeladmin \
- wagtail-seo \
- weasyprint \
- whitenoise \
- xhtml2pdf
-
-.PHONY: wagtail-private-default
-wagtail-privacy-default:
- python manage.py startapp privacy
- @echo "$$WAGTAIL_PRIVACY_PAGE_MODEL" > privacy/models.py
- $(ADD_DIR) privacy/templates
- @echo "$$WAGTAIL_PRIVACY_PAGE_TEMPLATE" > privacy/templates/privacy_page.html
- @echo "INSTALLED_APPS.append('privacy')" >> $(DJANGO_SETTINGS_BASE_FILE)
- python manage.py makemigrations privacy
- -$(GIT_ADD) privacy/templates
- -$(GIT_ADD) privacy/*.py
- -$(GIT_ADD) privacy/migrations/*.py
-
-.PHONY: wagtail-project-default
-wagtail-project-default:
- wagtail start backend .
- $(DEL_FILE) home/templates/home/welcome_page.html
- -$(GIT_ADD) backend/
- -$(GIT_ADD) .dockerignore
- -$(GIT_ADD) Dockerfile
- -$(GIT_ADD) manage.py
- -$(GIT_ADD) requirements.txt
-
-.PHONY: wagtail-search-default
-wagtail-search-default:
- @echo "$$WAGTAIL_SEARCH_TEMPLATE" > search/templates/search/search.html
- @echo "$$WAGTAIL_SEARCH_URLS" > search/urls.py
- -$(GIT_ADD) search/templates
- -$(GIT_ADD) search/*.py
-
-.PHONY: wagtail-settings-default
-wagtail-settings-default:
- @echo "$$WAGTAIL_SETTINGS" >> $(DJANGO_SETTINGS_BASE_FILE)
-
-.PHONY: wagtail-sitepage-default
-wagtail-sitepage-default:
- python manage.py startapp sitepage
- @echo "$$WAGTAIL_SITEPAGE_MODEL" > sitepage/models.py
- $(ADD_DIR) sitepage/templates/sitepage/
- @echo "$$WAGTAIL_SITEPAGE_TEMPLATE" > sitepage/templates/sitepage/site_page.html
- @echo "INSTALLED_APPS.append('sitepage')" >> $(DJANGO_SETTINGS_BASE_FILE)
- python manage.py makemigrations sitepage
- -$(GIT_ADD) sitepage/templates
- -$(GIT_ADD) sitepage/*.py
- -$(GIT_ADD) sitepage/migrations/*.py
-
-.PHONY: wagtail-urls-default
-wagtail-urls-default:
- @echo "$$WAGTAIL_URLS" > $(DJANGO_URLS_FILE)
-
-.PHONY: wagtail-urls-home-default
-wagtail-urls-home-default:
- @echo "$$WAGTAIL_URLS_HOME" >> $(DJANGO_URLS_FILE)
-
-.PHONY: webpack-init-default
-webpack-init-default: npm-init
- @echo "$$WEBPACK_CONFIG_JS" > webpack.config.js
- -$(GIT_ADD) webpack.config.js
- npm install --save-dev webpack webpack-cli webpack-dev-server
- $(ADD_DIR) src/
- @echo "$$WEBPACK_INDEX_JS" > src/index.js
- -$(GIT_ADD) src/index.js
- @echo "$$WEBPACK_INDEX_HTML" > index.html
- -$(GIT_ADD) index.html
- $(MAKE) git-ignore
-
-.PHONY: webpack-init-reveal-default
-webpack-init-reveal-default: npm-init
- @echo "$$WEBPACK_REVEAL_CONFIG_JS" > webpack.config.js
- -$(GIT_ADD) webpack.config.js
- npm install --save-dev webpack webpack-cli webpack-dev-server
- $(ADD_DIR) src/
- @echo "$$WEBPACK_REVEAL_INDEX_JS" > src/index.js
- -$(GIT_ADD) src/index.js
- @echo "$$WEBPACK_REVEAL_INDEX_HTML" > index.html
- -$(GIT_ADD) index.html
- $(MAKE) git-ignore
-
-# --------------------------------------------------------------------------------
-# Single-line phony target rules
-# --------------------------------------------------------------------------------
-
-.PHONY: aws-check-env-default
-aws-check-env-default: aws-check-env-profile aws-check-env-region
-
-.PHONY: ce-default
-ce-default: git-commit-edit git-push
-
-.PHONY: clean-default
-clean-default: wagtail-clean
-
-.PHONY: cp-default
-cp-default: git-commit-message git-push
-
-.PHONY: db-dump-default
-db-dump-default: eb-export
-
-.PHONY: dbshell-default
-dbshell-default: django-db-shell
-
-.PHONY: deploy-default
-deploy-default: eb-deploy
-
-.PHONY: d-default
-d-default: eb-deploy
-
-.PHONY: deps-default
-deps-default: pip-deps
-
-.PHONY: e-default
-e-default: edit
-
-.PHONY: edit-default
-edit-default: readme-edit
-
-.PHONY: empty-default
-empty-default: git-commit-message-empty git-push
-
-.PHONY: fp-default
-fp-default: git-push-force
-
-.PHONY: freeze-default
-freeze-default: pip-freeze git-push
-
-.PHONY: git-commit-default
-git-commit-default: git-commit-message git-push
-
-.PHONY: git-commit-clean-default
-git-commit-clean-default: git-commit-message-clean git-push
-
-.PHONY: git-commit-init-default
-git-commit-init-default: git-commit-message-init git-push
-
-.PHONY: git-commit-lint-default
-git-commit-lint-default: git-commit-message-lint git-push
-
-.PHONY: gitignore-default
-gitignore-default: git-ignore
-
-.PHONY: h-default
-h-default: help
-
-.PHONY: init-default
-init-default: django-init-wagtail django-serve
-
-.PHONY: init-wagtail-default
-init-wagtail-default: django-init-wagtail
-
-.PHONY: install-default
-install-default: pip-install
-
-.PHONY: l-default
-l-default: makefile-list-commands
-
-.PHONY: last-default
-last-default: git-commit-message-last git-push
-
-.PHONY: lint-default
-lint-default: django-lint
-
-.PHONY: list-commands-default
-list-commands-default: makefile-list-commands
-
-.PHONY: list-defines-default
-list-defines-default: makefile-list-defines
-
-.PHONY: list-exports-default
-list-exports-default: makefile-list-exports
-
-.PHONY: list-targets-default
-list-targets-default: makefile-list-targets
-
-.PHONY: migrate-default
-migrate-default: django-migrate
-
-.PHONY: migrations-default
-migrations-default: django-migrations-make
-
-.PHONY: migrations-show-default
-migrations-show-default: django-migrations-show
-
-.PHONY: mk-default
-mk-default: project.mk git-commit-message-mk git-push
-
-.PHONY: open-default
-open-default: django-open
-
-.PHONY: o-default
-o-default: django-open
-
-.PHONY: readme-default
-readme-default: readme-init
-
-.PHONY: rename-default
-rename-default: git-commit-message-rename git-push
-
-.PHONY: s-default
-s-default: django-serve
-
-.PHONY: shell-default
-shell-default: django-shell
-
-.PHONY: serve-default
-serve-default: django-serve
-
-.PHONY: static-default
-static-default: django-static
-
-.PHONY: sort-default
-sort-default: git-commit-message-sort git-push
-
-.PHONY: su-default
-su-default: django-su
-
-.PHONY: test-default
-test-default: django-test
-
-.PHONY: t-default
-t-default: django-test
-
-.PHONY: u-default
-u-default: help
-
-.PHONY: urls-default
-urls-default: django-urls-show
-
-# --------------------------------------------------------------------------------
-# Allow customizing rules defined in this Makefile with rules defined in
-# $(MAKEFILE_CUSTOM_FILE)
-# --------------------------------------------------------------------------------
-
-%: %-default # https://stackoverflow.com/a/49804748
- @ true
+.DEFAULT_GOAL := help
+
+.PHONY: clean
+clean:
+ rm src/PIL/*.so || true
+ rm -r build || true
+ find . -name __pycache__ | xargs rm -r || true
+
+.PHONY: coverage
+coverage:
+ python3 -c "import pytest" > /dev/null 2>&1 || python3 -m pip install pytest
+ python3 -m pytest -qq
+ rm -r htmlcov || true
+ python3 -c "import coverage" > /dev/null 2>&1 || python3 -m pip install coverage
+ python3 -m coverage report
+
+.PHONY: doc
+.PHONY: html
+doc html:
+ python3 -c "import PIL" > /dev/null 2>&1 || python3 -m pip install .
+ $(MAKE) -C docs html
+
+.PHONY: htmlview
+htmlview:
+ python3 -c "import PIL" > /dev/null 2>&1 || python3 -m pip install .
+ $(MAKE) -C docs htmlview
+
+.PHONY: doccheck
+doccheck:
+ $(MAKE) doc
+# Don't make our tests rely on the links in the docs being up every single build.
+# We don't control them. But do check, and update them to the target of their redirects.
+ $(MAKE) -C docs linkcheck || true
+
+.PHONY: docserve
+docserve:
+ cd docs/_build/html && python3 -m http.server 2> /dev/null&
+
+.PHONY: help
+help:
+ @echo "Welcome to Pillow development. Please use \`make \` where is one of"
+ @echo " clean remove build products"
+ @echo " coverage run coverage test (in progress)"
+ @echo " doc make HTML docs"
+ @echo " docserve run an HTTP server on the docs directory"
+ @echo " html make HTML docs"
+ @echo " htmlview open the index page built by the html target in your browser"
+ @echo " install make and install"
+ @echo " install-coverage make and install with C coverage"
+ @echo " lint run the lint checks"
+ @echo " lint-fix run Ruff to (mostly) fix lint issues"
+ @echo " release-test run code and package tests before release"
+ @echo " test run tests on installed Pillow"
+
+.PHONY: install
+install:
+ python3 -m pip -v install .
+ python3 selftest.py
+
+.PHONY: install-coverage
+install-coverage:
+ CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip -v install .
+ python3 selftest.py
+
+.PHONY: debug
+debug:
+# make a debug version if we don't have a -dbg python. Leaves in symbols
+# for our stuff, kills optimization, and redirects to dev null so we
+# see any build failures.
+ make clean > /dev/null
+ CFLAGS='-g -O0' python3 -m pip -v install . > /dev/null
+
+.PHONY: release-test
+release-test:
+ python3 Tests/check_release_notes.py
+ python3 -m pip install -e .[tests]
+ python3 selftest.py
+ python3 -m pytest Tests
+ python3 -m pip install .
+ python3 -m pytest -qq
+ python3 -m check_manifest
+ python3 -m pyroma .
+ $(MAKE) readme
+
+.PHONY: sdist
+sdist:
+ python3 -m build --help > /dev/null 2>&1 || python3 -m pip install build
+ python3 -m build --sdist
+ python3 -m twine --help > /dev/null 2>&1 || python3 -m pip install twine
+ python3 -m twine check --strict dist/*
+
+.PHONY: test
+test:
+ python3 -c "import pytest" > /dev/null 2>&1 || python3 -m pip install pytest
+ python3 -m pytest -qq
+
+.PHONY: valgrind
+valgrind:
+ python3 -c "import pytest_valgrind" > /dev/null 2>&1 || python3 -m pip install pytest-valgrind
+ PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp --leak-check=no \
+ --log-file=/tmp/valgrind-output \
+ python3 -m pytest --no-memcheck -vv --valgrind --valgrind-log=/tmp/valgrind-output
+
+.PHONY: readme
+readme:
+ python3 -c "import markdown2" > /dev/null 2>&1 || python3 -m pip install markdown2
+ python3 -m markdown2 README.md > .long-description.html && open .long-description.html
+
+
+.PHONY: lint
+lint:
+ python3 -c "import tox" > /dev/null 2>&1 || python3 -m pip install tox
+ python3 -m tox -e lint
+
+.PHONY: lint-fix
+lint-fix:
+ python3 -c "import black" > /dev/null 2>&1 || python3 -m pip install black
+ python3 -m black .
+ python3 -c "import ruff" > /dev/null 2>&1 || python3 -m pip install ruff
+ python3 -m ruff --fix .
+
+.PHONY: mypy
+mypy:
+ python3 -c "import tox" > /dev/null 2>&1 || python3 -m pip install tox
+ python3 -m tox -e mypy