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 %} - - {% 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 -
    - -
    - -
    -
    -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 %} -

    -
    - {% csrf_token %} - {{ form.as_p }} - -
    -{% 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

    -
    - {% csrf_token %} - -
    -{% 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 }}

    -
    - {% csrf_token %} - - -
    -{% 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 %} -
    • -

      - {{ result }} -

      - {% 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

    -
    - Edit -
    -

    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 %} - - {% 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 - -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 }} -
    - {% csrf_token %} - {{ form.as_p }} - -
    -{% 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 %} -
    • -

      - {{ result }} -

      - {% 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