From 07240ea035e52a4543e88069821023d9a31a1b95 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 17 May 2021 15:51:41 -0700
Subject: [PATCH 01/21] django startproject
---
.gitignore | 128 ++++++++++++++++++++++++++++++++++++++++++++
dj_idom/__init__.py | 0
dj_idom/asgi.py | 16 ++++++
dj_idom/settings.py | 125 ++++++++++++++++++++++++++++++++++++++++++
dj_idom/urls.py | 21 ++++++++
manage.py | 22 ++++++++
requirements.txt | 4 ++
7 files changed, 316 insertions(+)
create mode 100644 .gitignore
create mode 100644 dj_idom/__init__.py
create mode 100644 dj_idom/asgi.py
create mode 100644 dj_idom/settings.py
create mode 100644 dj_idom/urls.py
create mode 100644 manage.py
create mode 100644 requirements.txt
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..03576fff
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,128 @@
+
+# Django #
+logs
+*.log
+*.pot
+*.pyc
+.dccachea
+__pycache__
+db.sqlite3
+media
+cache
+static-deploy
+data
+settings.json
+
+# Backup files #
+*.bak
+
+# If you are using PyCharm #
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/dictionaries
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.xml
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/gradle.xml
+.idea/**/libraries
+*.iws /out/
+
+# Python #
+*.py[cod]
+*$py.class
+
+# Distribution / packaging
+.Python build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+.pytest_cache/
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery
+celerybeat-schedule.*
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+
+# Sublime Text #
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+*.sublime-workspace
+*.sublime-project
+
+# sftp configuration file
+sftp-config.json
+
+# Package control specific files Package
+Control.last-run
+Control.ca-list
+Control.ca-bundle
+Control.system-ca-bundle
+GitHub.sublime-settings
+
+# Visual Studio Code #
+.vscode
+.vscode/*
+.vscode/settings.json
+.vscode/tasks.json
+.vscode/launch.json
+.vscode/extensions.json
+.history
+%SystemDrive%
+
+# Mac file system
+.DS_Store
\ No newline at end of file
diff --git a/dj_idom/__init__.py b/dj_idom/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/dj_idom/asgi.py b/dj_idom/asgi.py
new file mode 100644
index 00000000..0890d130
--- /dev/null
+++ b/dj_idom/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for dj_idom project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj_idom.settings')
+
+application = get_asgi_application()
diff --git a/dj_idom/settings.py b/dj_idom/settings.py
new file mode 100644
index 00000000..088fc2d4
--- /dev/null
+++ b/dj_idom/settings.py
@@ -0,0 +1,125 @@
+"""
+Django settings for dj_idom project.
+
+Generated by 'django-admin startproject' using Django 3.2.3.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/3.2/ref/settings/
+"""
+
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-n!bd1#+7ufw5#9ipayu9k(lyu@za$c2ajbro7es(v8_7w1$=&c'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'dj_idom.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+ASGI_APPLICATION = 'dj_idom.asgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/3.2/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/3.2/howto/static-files/
+
+STATIC_URL = '/static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
diff --git a/dj_idom/urls.py b/dj_idom/urls.py
new file mode 100644
index 00000000..05e85330
--- /dev/null
+++ b/dj_idom/urls.py
@@ -0,0 +1,21 @@
+"""dj_idom URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path
+
+urlpatterns = [
+ path('admin/', admin.site.urls),
+]
diff --git a/manage.py b/manage.py
new file mode 100644
index 00000000..f23117b6
--- /dev/null
+++ b/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj_idom.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 00000000..23525b52
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+django<4.0.0
+daphne<4.0.0
+channels<4.0.0
+idom<1.0.0
\ No newline at end of file
From 5a03f824fe365ca359397d2fcd0c9d8cfee02788 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 17 May 2021 22:33:32 -0700
Subject: [PATCH 02/21] barebones websocket consumer
---
dj_idom/asgi.py | 23 +++++++++++++++++++++--
dj_idom/consumers.py | 18 ++++++++++++++++++
2 files changed, 39 insertions(+), 2 deletions(-)
create mode 100644 dj_idom/consumers.py
diff --git a/dj_idom/asgi.py b/dj_idom/asgi.py
index 0890d130..73b32f00 100644
--- a/dj_idom/asgi.py
+++ b/dj_idom/asgi.py
@@ -9,8 +9,27 @@
import os
+from django.conf.urls import url
from django.core.asgi import get_asgi_application
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj_idom.settings')
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dj_idom.settings")
-application = get_asgi_application()
+# Fetch ASGI application before importing dependencies that require ORM models.
+http_asgi_app = get_asgi_application()
+
+from channels.auth import AuthMiddlewareStack
+from channels.routing import ProtocolTypeRouter, URLRouter
+from channels.security.websocket import AllowedHostsOriginValidator
+
+from .consumers import CommandConsumer
+
+application = ProtocolTypeRouter(
+ {
+ # ASGI app has concurrency problems, see
+ # See https://github.com/django/channels/issues/1587
+ "http": http_asgi_app,
+ "websocket": AllowedHostsOriginValidator(
+ AuthMiddlewareStack(URLRouter([url("", CommandConsumer().as_asgi())]))
+ ),
+ }
+)
diff --git a/dj_idom/consumers.py b/dj_idom/consumers.py
new file mode 100644
index 00000000..e2fab96e
--- /dev/null
+++ b/dj_idom/consumers.py
@@ -0,0 +1,18 @@
+"""Anything used to construct a websocket endpoint"""
+from channels.generic.websocket import AsyncJsonWebsocketConsumer
+
+
+class CommandConsumer(AsyncJsonWebsocketConsumer):
+ """Websocket communication."""
+
+ # INITIAL CONNECTION
+ async def connect(self):
+ """When the browser attempts to connect to the server."""
+ # Accept the connection
+ await self.accept()
+ pass
+
+ # RECEIVING COMMANDS
+ async def receive_json(self, content, **kwargs):
+ """When the browser attempts to send a message to the server."""
+ pass
From 1591eb1a8b01711346d184dcea1605f91615aa8d Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 17 May 2021 22:39:47 -0700
Subject: [PATCH 03/21] create barebones http page
---
dj_idom/settings.py | 74 ++++++++++++++++++-------------------
dj_idom/templates/base.html | 15 ++++++++
dj_idom/urls.py | 6 +--
dj_idom/views.py | 8 ++++
4 files changed, 62 insertions(+), 41 deletions(-)
create mode 100644 dj_idom/templates/base.html
create mode 100644 dj_idom/views.py
diff --git a/dj_idom/settings.py b/dj_idom/settings.py
index 088fc2d4..d2753ceb 100644
--- a/dj_idom/settings.py
+++ b/dj_idom/settings.py
@@ -9,7 +9,7 @@
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
-
+import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -20,7 +20,7 @@
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = 'django-insecure-n!bd1#+7ufw5#9ipayu9k(lyu@za$c2ajbro7es(v8_7w1$=&c'
+SECRET_KEY = "django-insecure-n!bd1#+7ufw5#9ipayu9k(lyu@za$c2ajbro7es(v8_7w1$=&c"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
@@ -31,52 +31,52 @@
# Application definition
INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
]
MIDDLEWARE = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ "django.middleware.security.SecurityMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
]
-ROOT_URLCONF = 'dj_idom.urls'
+ROOT_URLCONF = "dj_idom.urls"
TEMPLATES = [
{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [os.path.join(BASE_DIR, "dj_idom", "templates")],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.debug",
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
],
},
},
]
-ASGI_APPLICATION = 'dj_idom.asgi.application'
+ASGI_APPLICATION = "dj_idom.asgi.application"
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': BASE_DIR / 'db.sqlite3',
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ "NAME": BASE_DIR / "db.sqlite3",
}
}
@@ -86,16 +86,16 @@
AUTH_PASSWORD_VALIDATORS = [
{
- 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
- 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
- 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
- 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
@@ -103,9 +103,9 @@
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
-TIME_ZONE = 'UTC'
+TIME_ZONE = "UTC"
USE_I18N = True
@@ -117,9 +117,9 @@
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
-STATIC_URL = '/static/'
+STATIC_URL = "/static/"
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
-DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
diff --git a/dj_idom/templates/base.html b/dj_idom/templates/base.html
new file mode 100644
index 00000000..d50b7c5f
--- /dev/null
+++ b/dj_idom/templates/base.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ IDOM
+
+
+
+ IDOM Test Page
+
+
+
\ No newline at end of file
diff --git a/dj_idom/urls.py b/dj_idom/urls.py
index 05e85330..8fdc66a8 100644
--- a/dj_idom/urls.py
+++ b/dj_idom/urls.py
@@ -13,9 +13,7 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
-from django.contrib import admin
from django.urls import path
+from .views import base_template
-urlpatterns = [
- path('admin/', admin.site.urls),
-]
+urlpatterns = [path("", base_template)]
diff --git a/dj_idom/views.py b/dj_idom/views.py
new file mode 100644
index 00000000..33775317
--- /dev/null
+++ b/dj_idom/views.py
@@ -0,0 +1,8 @@
+from django.template import loader
+from django.http import HttpResponse
+
+
+def base_template(request):
+ template = loader.get_template("base.html")
+ context = {}
+ return HttpResponse(template.render(context, request))
From 923276e7d43121708f7762ef8ac810f4e03d9400 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 17 May 2021 22:59:37 -0700
Subject: [PATCH 04/21] barebones unauthenticated websocket
---
dj_idom/asgi.py | 8 +-------
dj_idom/settings.py | 6 ++++++
dj_idom/static/scripts.js | 40 +++++++++++++++++++++++++++++++++++++
dj_idom/templates/base.html | 2 ++
requirements.txt | 8 ++++----
5 files changed, 53 insertions(+), 11 deletions(-)
create mode 100644 dj_idom/static/scripts.js
diff --git a/dj_idom/asgi.py b/dj_idom/asgi.py
index 73b32f00..b3538e6e 100644
--- a/dj_idom/asgi.py
+++ b/dj_idom/asgi.py
@@ -17,19 +17,13 @@
# Fetch ASGI application before importing dependencies that require ORM models.
http_asgi_app = get_asgi_application()
-from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
-from channels.security.websocket import AllowedHostsOriginValidator
from .consumers import CommandConsumer
application = ProtocolTypeRouter(
{
- # ASGI app has concurrency problems, see
- # See https://github.com/django/channels/issues/1587
"http": http_asgi_app,
- "websocket": AllowedHostsOriginValidator(
- AuthMiddlewareStack(URLRouter([url("", CommandConsumer().as_asgi())]))
- ),
+ "websocket": URLRouter([url("", CommandConsumer().as_asgi())]),
}
)
diff --git a/dj_idom/settings.py b/dj_idom/settings.py
index d2753ceb..456172e7 100644
--- a/dj_idom/settings.py
+++ b/dj_idom/settings.py
@@ -37,6 +37,7 @@
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
+ "channels", # Websocket library
]
MIDDLEWARE = [
@@ -123,3 +124,8 @@
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+# Static Files (CSS, JavaScript, Images)
+STATICFILES_DIRS = [
+ os.path.join(BASE_DIR, "dj_idom", "static"),
+]
diff --git a/dj_idom/static/scripts.js b/dj_idom/static/scripts.js
new file mode 100644
index 00000000..e1b97b3c
--- /dev/null
+++ b/dj_idom/static/scripts.js
@@ -0,0 +1,40 @@
+// Set up a websocket at the base endpoint
+let LOCATION = window.location;
+let WS_PROTOCOL = "";
+if (LOCATION.protocol == "https:") {
+ WS_PROTOCOL = "wss://";
+} else {
+ WS_PROTOCOL = "ws://";
+}
+let WS_ENDPOINT_URL = WS_PROTOCOL + LOCATION.host;
+let COMMAND_SOCKET = new WebSocket(WS_ENDPOINT_URL);
+
+// Receivable commands
+COMMAND_SOCKET.onmessage = function (response) {
+ // Websocket message received, parse for JSON
+ console.info(response);
+ json_response = JSON.parse(response.data);
+
+ // Check for valid commands
+ console.info("Websocket has recieved a message", json_response);
+};
+
+// Websocket open event
+COMMAND_SOCKET.onopen = function () {
+ console.info("Websocket has opened.");
+};
+
+// Websocket close event
+COMMAND_SOCKET.onclose = function () {
+ console.info("Websocket has closed.");
+};
+
+// Websocket error event
+COMMAND_SOCKET.onerror = function (error) {
+ console.error(
+ "Websocket encountered a crtical error: ",
+ error.message,
+ "Closing socket..."
+ );
+ COMMAND_SOCKET.close();
+};
diff --git a/dj_idom/templates/base.html b/dj_idom/templates/base.html
index d50b7c5f..35157fac 100644
--- a/dj_idom/templates/base.html
+++ b/dj_idom/templates/base.html
@@ -1,3 +1,4 @@
+{% load static %}
@@ -5,6 +6,7 @@
+
IDOM
diff --git a/requirements.txt b/requirements.txt
index 23525b52..a9a1490d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-django<4.0.0
-daphne<4.0.0
-channels<4.0.0
-idom<1.0.0
\ No newline at end of file
+django<4.0.0 # Django Library
+daphne<4.0.0 # Production ASGI webserver
+channels<4.0.0 # Django websocket features
+idom<1.0.0 # Python React
\ No newline at end of file
From 182cbd6eeaf46fa4309470e0e3f4b97e31cbd53d Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 18 May 2021 00:21:45 -0700
Subject: [PATCH 05/21] remove unneeded pass
---
dj_idom/consumers.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/dj_idom/consumers.py b/dj_idom/consumers.py
index e2fab96e..2f429284 100644
--- a/dj_idom/consumers.py
+++ b/dj_idom/consumers.py
@@ -10,7 +10,6 @@ async def connect(self):
"""When the browser attempts to connect to the server."""
# Accept the connection
await self.accept()
- pass
# RECEIVING COMMANDS
async def receive_json(self, content, **kwargs):
From 78369ea8799618d4cb93fc4f268af5ddfe9c541f Mon Sep 17 00:00:00 2001
From: rmorshea
Date: Sat, 12 Jun 2021 13:59:00 -0700
Subject: [PATCH 06/21] organize for dist as pypi package
---
dj_idom/consumers.py | 17 ----
noxfile.py | 60 ++++++++++++
pyproject.toml | 12 +++
requirements.txt | 8 +-
requirements/check-style.txt | 3 +
requirements/pkg-deps.txt | 2 +
requirements/test-env.txt | 2 +
requirements/test-run.txt | 1 +
setup.cfg | 11 +++
setup.py | 96 ++++++++++++++++++++
src/django_idom/__init__.py | 5 +
src/django_idom/websocket_consumer.py | 34 +++++++
manage.py => tests/manage.py | 4 +-
{dj_idom => tests/tests}/__init__.py | 0
{dj_idom => tests/tests}/asgi.py | 12 ++-
tests/tests/migrations/__init__.py | 0
{dj_idom => tests/tests}/settings.py | 10 +-
{dj_idom => tests/tests}/static/scripts.js | 0
{dj_idom => tests/tests}/templates/base.html | 18 ++--
tests/tests/tests.py | 3 +
{dj_idom => tests/tests}/urls.py | 6 +-
{dj_idom => tests/tests}/views.py | 6 ++
22 files changed, 267 insertions(+), 43 deletions(-)
delete mode 100644 dj_idom/consumers.py
create mode 100644 noxfile.py
create mode 100644 pyproject.toml
create mode 100644 requirements/check-style.txt
create mode 100644 requirements/pkg-deps.txt
create mode 100644 requirements/test-env.txt
create mode 100644 requirements/test-run.txt
create mode 100644 setup.cfg
create mode 100644 setup.py
create mode 100644 src/django_idom/__init__.py
create mode 100644 src/django_idom/websocket_consumer.py
rename manage.py => tests/manage.py (85%)
rename {dj_idom => tests/tests}/__init__.py (100%)
rename {dj_idom => tests/tests}/asgi.py (64%)
create mode 100644 tests/tests/migrations/__init__.py
rename {dj_idom => tests/tests}/settings.py (93%)
rename {dj_idom => tests/tests}/static/scripts.js (100%)
rename {dj_idom => tests/tests}/templates/base.html (62%)
create mode 100644 tests/tests/tests.py
rename {dj_idom => tests/tests}/urls.py (95%)
rename {dj_idom => tests/tests}/views.py (67%)
diff --git a/dj_idom/consumers.py b/dj_idom/consumers.py
deleted file mode 100644
index 2f429284..00000000
--- a/dj_idom/consumers.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""Anything used to construct a websocket endpoint"""
-from channels.generic.websocket import AsyncJsonWebsocketConsumer
-
-
-class CommandConsumer(AsyncJsonWebsocketConsumer):
- """Websocket communication."""
-
- # INITIAL CONNECTION
- async def connect(self):
- """When the browser attempts to connect to the server."""
- # Accept the connection
- await self.accept()
-
- # RECEIVING COMMANDS
- async def receive_json(self, content, **kwargs):
- """When the browser attempts to send a message to the server."""
- pass
diff --git a/noxfile.py b/noxfile.py
new file mode 100644
index 00000000..d939f348
--- /dev/null
+++ b/noxfile.py
@@ -0,0 +1,60 @@
+from __future__ import annotations
+
+import os
+import re
+import subprocess
+from pathlib import Path
+from typing import List, Tuple
+
+import nox
+from nox.sessions import Session
+
+
+HERE = Path(__file__).parent
+POSARGS_PATTERN = re.compile(r"^(\w+)\[(.+)\]$")
+
+
+@nox.session(reuse_venv=True)
+def format(session: Session) -> None:
+ install_requirements_file(session, "check-style")
+ session.run("black", ".")
+ session.run("isort", ".")
+
+
+@nox.session
+def test(session: Session) -> None:
+ """Run the complete test suite"""
+ session.install("--upgrade", "pip", "setuptools", "wheel")
+ test_suite(session)
+ test_style(session)
+
+
+@nox.session
+def test_suite(session: Session) -> None:
+ """Run the Python-based test suite"""
+ session.env["IDOM_DEBUG_MODE"] = "1"
+ install_requirements_file(session, "test-env")
+ session.install(".[all]")
+ session.run("figure-it-out")
+
+
+@nox.session
+def test_style(session: Session) -> None:
+ """Check that style guidelines are being followed"""
+ install_requirements_file(session, "check-style")
+ session.run("flake8", "src/django_idom", "tests")
+ black_default_exclude = r"\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist"
+ session.run(
+ "black",
+ ".",
+ "--check",
+ "--exclude",
+ rf"/({black_default_exclude}|venv|node_modules)/",
+ )
+ session.run("isort", ".", "--check-only")
+
+
+def install_requirements_file(session: Session, name: str) -> None:
+ file_path = HERE / "requirements" / (name + ".txt")
+ assert file_path.exists(), f"requirements file {file_path} does not exist"
+ session.install("-r", str(file_path))
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..18a77f5a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,12 @@
+[build-system]
+requires = ["setuptools>=42", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[tool.isort]
+multi_line_output = 3
+force_grid_wrap = 0
+use_parentheses = "True"
+ensure_newline_before_comments = "True"
+include_trailing_comma = "True"
+line_length = 88
+lines_after_imports = 2
diff --git a/requirements.txt b/requirements.txt
index a9a1490d..22728910 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-django<4.0.0 # Django Library
-daphne<4.0.0 # Production ASGI webserver
-channels<4.0.0 # Django websocket features
-idom<1.0.0 # Python React
\ No newline at end of file
+-r requirements/pkg-deps.txt
+-r requirements/check-style.txt
+-r requirements/test-env.txt
+-r requirements/test-run.txt
diff --git a/requirements/check-style.txt b/requirements/check-style.txt
new file mode 100644
index 00000000..5e5647ac
--- /dev/null
+++ b/requirements/check-style.txt
@@ -0,0 +1,3 @@
+black
+flake8
+isort
diff --git a/requirements/pkg-deps.txt b/requirements/pkg-deps.txt
new file mode 100644
index 00000000..b823d656
--- /dev/null
+++ b/requirements/pkg-deps.txt
@@ -0,0 +1,2 @@
+channels<4.0.0 # Django websocket features
+idom<1.0.0 # Python React
diff --git a/requirements/test-env.txt b/requirements/test-env.txt
new file mode 100644
index 00000000..d32ec43a
--- /dev/null
+++ b/requirements/test-env.txt
@@ -0,0 +1,2 @@
+django<4.0.0 # Django Library
+daphne<4.0.0 # Production ASGI webserver
diff --git a/requirements/test-run.txt b/requirements/test-run.txt
new file mode 100644
index 00000000..816817c6
--- /dev/null
+++ b/requirements/test-run.txt
@@ -0,0 +1 @@
+nox
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..998619dd
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,11 @@
+[bdist_wheel]
+universal=1
+
+[flake8]
+ignore = E203, E266, E501, W503, F811, N802
+max-line-length = 88
+max-complexity = 18
+select = B,C,E,F,W,T4,B9,N,ROH
+exclude =
+ .eggs/*
+ .nox/*
diff --git a/setup.py b/setup.py
new file mode 100644
index 00000000..dbdbbf67
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,96 @@
+import os
+import sys
+from pathlib import Path
+
+from setuptools import find_packages, setup
+
+
+# the name of the project
+name = "django_idom"
+
+# basic paths used to gather files
+root_dir = Path(__file__).parent
+src_dir = root_dir / "src"
+package_dir = src_dir / name
+
+
+# -----------------------------------------------------------------------------
+# Package Definition
+# -----------------------------------------------------------------------------
+
+
+package = {
+ "name": name,
+ "python_requires": ">=3.7",
+ "packages": find_packages(str(src_dir)),
+ "package_dir": {"": "src"},
+ "description": "Control the web with Python",
+ "author": "Ryan Morshead",
+ "author_email": "ryan.morshead@gmail.com",
+ "url": "https://github.com/idom-team/django-idom",
+ "license": "MIT",
+ "platforms": "Linux, Mac OS X, Windows",
+ "keywords": ["interactive", "widgets", "DOM", "React"],
+ "zip_safe": False,
+ "classifiers": [
+ "Framework :: Django",
+ "Framework :: Django :: 3.1",
+ "Framework :: Django :: 3.2",
+ "Operating System :: OS Independent",
+ "Intended Audience :: Developers",
+ "Intended Audience :: Science/Research",
+ "Topic :: Multimedia :: Graphics",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Environment :: Web Environment",
+ ],
+}
+
+
+# -----------------------------------------------------------------------------
+# Library Version
+# -----------------------------------------------------------------------------
+
+with open(os.path.join(package_dir, "__init__.py")) as f:
+ for line in f.read().split("\n"):
+ if line.startswith("__version__ = "):
+ package["version"] = eval(line.split("=", 1)[1])
+ break
+ else:
+ print("No version found in %s/__init__.py" % package_dir)
+ sys.exit(1)
+
+
+# -----------------------------------------------------------------------------
+# Requirements
+# -----------------------------------------------------------------------------
+
+
+requirements = []
+with (root_dir / "requirements" / "pkg-deps.txt").open() as f:
+ for line in map(str.strip, f):
+ if not line.startswith("#"):
+ requirements.append(line)
+package["install_requires"] = requirements
+
+
+# -----------------------------------------------------------------------------
+# Library Description
+# -----------------------------------------------------------------------------
+
+
+with (root_dir / "README.md").open() as f:
+ long_description = f.read()
+
+package["long_description"] = long_description
+package["long_description_content_type"] = "text/markdown"
+
+
+# -----------------------------------------------------------------------------
+# Install It
+# -----------------------------------------------------------------------------
+
+
+if __name__ == "__main__":
+ setup(**package)
diff --git a/src/django_idom/__init__.py b/src/django_idom/__init__.py
new file mode 100644
index 00000000..b62571ae
--- /dev/null
+++ b/src/django_idom/__init__.py
@@ -0,0 +1,5 @@
+__version__ = "0.0.1"
+
+from .websocket_consumer import IdomAsyncWebSocketConsumer
+
+__all__ = ["IdomAsyncWebSocketConsumer"]
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
new file mode 100644
index 00000000..e9a9e598
--- /dev/null
+++ b/src/django_idom/websocket_consumer.py
@@ -0,0 +1,34 @@
+"""Anything used to construct a websocket endpoint"""
+import asyncio
+from typing import Any
+
+from channels.generic.websocket import AsyncJsonWebsocketConsumer
+
+from idom.core.dispatcher import dispatch_single_view
+from idom.core.component import ComponentConstructor
+
+
+class IdomAsyncWebSocketConsumer(AsyncJsonWebsocketConsumer):
+ """Communicates with the browser to perform actions on-demand."""
+
+ def __init__(
+ self, component: ComponentConstructor, *args: Any, **kwargs: Any
+ ) -> None:
+ self._idom_component_constructor = component
+ super().__init__(*args, **kwargs)
+
+ async def connect(self) -> None:
+ self._idom_recv_queue = recv_queue = asyncio.Queue()
+ self._idom_dispatcher_future = dispatch_single_view(
+ self._idom_component_constructor,
+ self.send_json,
+ recv_queue.get,
+ )
+
+ async def close(self, *args: Any, **kwargs: Any) -> None:
+ self._idom_dispatcher_future.cancel()
+ await asyncio.wait([self._idom_dispatcher_future])
+ super().close(*args, **kwargs)
+
+ async def receive_json(self, content: Any, **kwargs: Any) -> None:
+ await self._idom_recv_queue.put(content)
diff --git a/manage.py b/tests/manage.py
similarity index 85%
rename from manage.py
rename to tests/manage.py
index f23117b6..b5361c64 100644
--- a/manage.py
+++ b/tests/manage.py
@@ -6,7 +6,7 @@
def main():
"""Run administrative tasks."""
- os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj_idom.settings')
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
@@ -18,5 +18,5 @@ def main():
execute_from_command_line(sys.argv)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/dj_idom/__init__.py b/tests/tests/__init__.py
similarity index 100%
rename from dj_idom/__init__.py
rename to tests/tests/__init__.py
diff --git a/dj_idom/asgi.py b/tests/tests/asgi.py
similarity index 64%
rename from dj_idom/asgi.py
rename to tests/tests/asgi.py
index b3538e6e..c007a784 100644
--- a/dj_idom/asgi.py
+++ b/tests/tests/asgi.py
@@ -1,5 +1,5 @@
"""
-ASGI config for dj_idom project.
+ASGI config for tests project.
It exposes the ASGI callable as a module-level variable named ``application``.
@@ -12,18 +12,22 @@
from django.conf.urls import url
from django.core.asgi import get_asgi_application
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dj_idom.settings")
+from .views import HelloWorld
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings")
# Fetch ASGI application before importing dependencies that require ORM models.
http_asgi_app = get_asgi_application()
from channels.routing import ProtocolTypeRouter, URLRouter
-from .consumers import CommandConsumer
+from django_idom import IdomAsyncWebSocketConsumer
application = ProtocolTypeRouter(
{
"http": http_asgi_app,
- "websocket": URLRouter([url("", CommandConsumer().as_asgi())]),
+ "websocket": URLRouter(
+ [url("", IdomAsyncWebSocketConsumer.as_asgi(component=HelloWorld))]
+ ),
}
)
diff --git a/tests/tests/migrations/__init__.py b/tests/tests/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/dj_idom/settings.py b/tests/tests/settings.py
similarity index 93%
rename from dj_idom/settings.py
rename to tests/tests/settings.py
index 456172e7..6620d1a4 100644
--- a/dj_idom/settings.py
+++ b/tests/tests/settings.py
@@ -1,5 +1,5 @@
"""
-Django settings for dj_idom project.
+Django settings for tests project.
Generated by 'django-admin startproject' using Django 3.2.3.
@@ -50,12 +50,12 @@
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
-ROOT_URLCONF = "dj_idom.urls"
+ROOT_URLCONF = "tests.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
- "DIRS": [os.path.join(BASE_DIR, "dj_idom", "templates")],
+ "DIRS": [os.path.join(BASE_DIR, "tests", "templates")],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
@@ -68,7 +68,7 @@
},
]
-ASGI_APPLICATION = "dj_idom.asgi.application"
+ASGI_APPLICATION = "tests.asgi.application"
# Database
@@ -127,5 +127,5 @@
# Static Files (CSS, JavaScript, Images)
STATICFILES_DIRS = [
- os.path.join(BASE_DIR, "dj_idom", "static"),
+ os.path.join(BASE_DIR, "tests", "static"),
]
diff --git a/dj_idom/static/scripts.js b/tests/tests/static/scripts.js
similarity index 100%
rename from dj_idom/static/scripts.js
rename to tests/tests/static/scripts.js
diff --git a/dj_idom/templates/base.html b/tests/tests/templates/base.html
similarity index 62%
rename from dj_idom/templates/base.html
rename to tests/tests/templates/base.html
index 35157fac..9e54666a 100644
--- a/dj_idom/templates/base.html
+++ b/tests/tests/templates/base.html
@@ -1,17 +1,15 @@
{% load static %}
-
-
-
-
-
+
+
+
+
IDOM
-
+
-
+
IDOM Test Page
-
-
-
\ No newline at end of file
+
+