diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 928cf83..896472f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -11,9 +11,9 @@ jobs:
strategy:
matrix:
variant: [spatialite, postgis, npm]
- python-version: ["3.11"]
- django-version: [4.2.2]
- drf-version: [3.14.0]
+ python-version: ["3.12"]
+ django-version: [5.0.3]
+ drf-version: [3.15.1]
steps:
- uses: actions/checkout@v2
with:
@@ -25,15 +25,15 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
- python -m pip install --upgrade pip
+ python -m pip install --upgrade pip setuptools
python -m pip install build
python -m pip install flake8 pytest wheel
python -m pip install django==${{ matrix.django-version }}
python -m pip install djangorestframework==${{ matrix.drf-version }}
python -m pip install xlsconv==2.0.0
- python -m pip install wq.build==2.0.0
- python -m pip install wq.app==2.0.0
- python -m pip install wq.db==2.0.0
+ python -m pip install wq.build==2.1.0
+ python -m pip install wq.app==2.1.0
+ python -m pip install wq.db==2.1.0
- name: Test build
run: |
python -m build
@@ -81,7 +81,7 @@ jobs:
python-version: ["3.11"]
node-version: [18]
package:
- - cra-template
+ - create
- expo-template
- rollup-plugin
steps:
diff --git a/.gitmodules b/.gitmodules
index 2c6e360..2248757 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
[submodule "django_project"]
path = wq/create/django_project
url = https://github.com/wq/wq-django-template.git
+[submodule "packages/create/template"]
+ path = packages/create/template
+ url = https://github.com/wq/wq-vite-template.git
diff --git a/packages/cra-template/README.md b/packages/cra-template/README.md
deleted file mode 100644
index 1b569ba..0000000
--- a/packages/cra-template/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-[![@wq/cra-template][logo]][docs]
-
-This is the [Create React App][create-react-app] template for projects utilizing the [wq framework]. It uses [@wq/app], [@wq/material], and [@wq/map-gl] to generate a configuration-driven interface for collecting and managing geospatial field data. This template is generally meant to be used together with [wq.create]. See wq's [Getting Started] docs for more information.
-
-### [Documentation][docs]
-
-[logo]: https://wq.io/images/@wq/cra-template.svg
-[docs]: https://wq.io/@wq/cra-template
-
-[wq framework]: https://wq.io/
-[@wq/app]: https://wq.io/@wq/app
-[@wq/material]: https://wq.io/@wq/material
-[@wq/map-gl]: https://wq.io/@wq/map-gl
-[wq.create]: https://wq.io/wq.create/
-[Getting Started]: https://wq.io/overview/setup
-
-[create-react-app]: https://facebook.github.io/create-react-app/docs/getting-started
diff --git a/packages/cra-template/package.json b/packages/cra-template/package.json
deleted file mode 100644
index 3c12956..0000000
--- a/packages/cra-template/package.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "name": "@wq/cra-template",
- "version": "2.0.0",
- "description": "Create React App template for use with @wq/app",
- "main": "template.json",
- "repository": {
- "type": "git",
- "url": "https://github.com/wq/wq.create.git",
- "directory": "packages/cra-template"
- },
- "keywords": [
- "wq",
- "app",
- "create-react-app",
- "template",
- "offline",
- "data-collection"
- ],
- "author": "S. Andrew Sheppard",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/wq/wq.create/issues"
- },
- "homepage": "https://wq.io/@wq/cra-template",
- "files": [
- "template",
- "template.json"
- ]
-}
diff --git a/packages/cra-template/template.json b/packages/cra-template/template.json
deleted file mode 100644
index c51e548..0000000
--- a/packages/cra-template/template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "package": {
- "proxy": "http://localhost:8000",
- "dependencies": {
- "@wq/app": "^2.0.0",
- "@wq/material": "^2.0.0",
- "@wq/material-web": "^2.0.0",
- "@wq/map-gl": "^2.0.0",
- "@wq/map-gl-web": "^2.0.0",
- "maplibre-gl": "^3.1.0"
- },
- "eslintConfig": {
- "extends": ["react-app"]
- }
- }
-}
diff --git a/packages/cra-template/template/gitignore b/packages/cra-template/template/gitignore
deleted file mode 100644
index 4d29575..0000000
--- a/packages/cra-template/template/gitignore
+++ /dev/null
@@ -1,23 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# production
-/build
-
-# misc
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
diff --git a/packages/cra-template/template/public/favicon.ico b/packages/cra-template/template/public/favicon.ico
deleted file mode 100755
index 47e9b6c..0000000
Binary files a/packages/cra-template/template/public/favicon.ico and /dev/null differ
diff --git a/packages/cra-template/template/public/icon-1024.png b/packages/cra-template/template/public/icon-1024.png
deleted file mode 100644
index c1c7d62..0000000
Binary files a/packages/cra-template/template/public/icon-1024.png and /dev/null differ
diff --git a/packages/cra-template/template/public/icon.svg b/packages/cra-template/template/public/icon.svg
deleted file mode 100644
index fc96acb..0000000
--- a/packages/cra-template/template/public/icon.svg
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-
diff --git a/packages/cra-template/template/public/index.html b/packages/cra-template/template/public/index.html
deleted file mode 100644
index 0494e69..0000000
--- a/packages/cra-template/template/public/index.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- {{ title }}
-
-
-
-
-
-
-
diff --git a/packages/cra-template/template/public/manifest.json b/packages/cra-template/template/public/manifest.json
deleted file mode 100644
index ab0ec13..0000000
--- a/packages/cra-template/template/public/manifest.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "short_name": "{{ project_name }}",
- "name": "{{ title }}",
- "icons": [
- {
- "src": "favicon.ico",
- "sizes": "64x64 32x32 24x24 16x16",
- "type": "image/x-icon"
- },
- {
- "src": "icon-192.png",
- "type": "image/png",
- "sizes": "192x192"
- },
- {
- "src": "icon-512.png",
- "type": "image/png",
- "sizes": "512x512"
- }
- ],
- "start_url": ".",
- "display": "fullscreen",
- "theme_color": "#000000",
- "background_color": "#ffffff"
-}
diff --git a/packages/cra-template/template/public/robots.txt b/packages/cra-template/template/public/robots.txt
deleted file mode 100644
index 01b0f9a..0000000
--- a/packages/cra-template/template/public/robots.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# https://www.robotstxt.org/robotstxt.html
-User-agent: *
diff --git a/packages/cra-template/template/src/data/config.js b/packages/cra-template/template/src/data/config.js
deleted file mode 100644
index edbaf7f..0000000
--- a/packages/cra-template/template/src/data/config.js
+++ /dev/null
@@ -1,107 +0,0 @@
-const config = {
- store: {
- service: "https://example.com/",
- },
- material: {
- theme: {
- primary: "#7500ae",
- secondary: "#0088bd",
- },
- },
- map: {
- bounds: [
- [-180, -70],
- [180, 70],
- ],
- },
- pages: {
- index: {
- verbose_name: "Home",
- url: "",
- show_in_index: false,
- },
- login: {
- url: "login",
- },
- logout: {
- url: "logout",
- },
- observation: {
- cache: "first_page",
- background_sync: true,
- name: "observation",
- url: "observations",
- list: true,
- form: [
- {
- name: "date",
- label: "Date",
- hint: "The date when the observation was taken",
- type: "date",
- },
- {
- name: "category",
- label: "Category",
- hint: "Observation type",
- type: "select one",
- "wq:ForeignKey": "category",
- "wq:related_name": "observation_set",
- },
- {
- name: "geometry",
- label: "Location",
- bind: {
- required: true,
- },
- hint: "The location of the observation",
- type: "geopoint",
- },
- {
- name: "photo",
- label: "Photo",
- hint: "Photo of the observation",
- type: "image",
- },
- {
- name: "notes",
- label: "Notes",
- hint: "Field observations and notes",
- type: "text",
- multiline: true,
- },
- ],
- verbose_name: "observation",
- verbose_name_plural: "observations",
- ordering: ["-date"],
- label_template: "{{date}}",
- },
- category: {
- cache: "all",
- background_sync: false,
- name: "category",
- url: "categories",
- list: true,
- form: [
- {
- name: "name",
- label: "Name",
- bind: {
- required: true,
- },
- "wq:length": 255,
- type: "string",
- },
- {
- name: "description",
- label: "Description",
- type: "text",
- },
- ],
- verbose_name: "category",
- verbose_name_plural: "categories",
- label_template: "{{name}}",
- },
- },
-};
-
-export default config;
diff --git a/packages/cra-template/template/src/index.js b/packages/cra-template/template/src/index.js
deleted file mode 100644
index fba5d71..0000000
--- a/packages/cra-template/template/src/index.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import app from "@wq/app";
-import material from "@wq/material";
-import mapgl from "@wq/map-gl";
-import maplibre from "maplibre-gl";
-import config from "./data/config";
-import * as serviceWorker from "./serviceWorker";
-
-import "maplibre-gl/dist/maplibre-gl.css";
-import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css";
-
-mapgl.setEngine(maplibre);
-
-app.use([material, mapgl]);
-
-async function init() {
- // const response = await fetch('/config.json'),
- // config = await response.json(); // Load directly from wq.db
- await app.init(config);
- await app.prefetchAll();
- if (config.debug) {
- window.wq = app;
- }
-}
-
-init();
-
-// If you want your app to work offline and load faster, you can change
-// unregister() to register() below. Note this comes with some pitfalls.
-// Learn more about service workers: https://bit.ly/CRA-PWA
-serviceWorker.unregister();
diff --git a/packages/cra-template/template/src/serviceWorker.js b/packages/cra-template/template/src/serviceWorker.js
deleted file mode 100644
index a683e53..0000000
--- a/packages/cra-template/template/src/serviceWorker.js
+++ /dev/null
@@ -1,136 +0,0 @@
-// This optional code is used to register a service worker.
-// register() is not called by default.
-
-// This lets the app load faster on subsequent visits in production, and gives
-// it offline capabilities. However, it also means that developers (and users)
-// will only see deployed updates on subsequent visits to a page, after all the
-// existing tabs open on the page have been closed, since previously cached
-// resources are updated in the background.
-
-// To learn more about the benefits of this model and instructions on how to
-// opt-in, read https://bit.ly/CRA-PWA
-
-const isLocalhost = Boolean(
- window.location.hostname === "localhost" ||
- // [::1] is the IPv6 localhost address.
- window.location.hostname === "[::1]" ||
- // 127.0.0.1/8 is considered localhost for IPv4.
- window.location.hostname.match(
- /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
- )
-);
-
-export function register(config) {
- if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
- // The URL constructor is available in all browsers that support SW.
- const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
- if (publicUrl.origin !== window.location.origin) {
- // Our service worker won't work if PUBLIC_URL is on a different origin
- // from what our page is served on. This might happen if a CDN is used to
- // serve assets; see https://github.com/facebook/create-react-app/issues/2374
- return;
- }
-
- window.addEventListener("load", () => {
- const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
-
- if (isLocalhost) {
- // This is running on localhost. Let's check if a service worker still exists or not.
- checkValidServiceWorker(swUrl, config);
-
- // Add some additional logging to localhost, pointing developers to the
- // service worker/PWA documentation.
- navigator.serviceWorker.ready.then(() => {
- console.log(
- "This web app is being served cache-first by a service " +
- "worker. To learn more, visit https://bit.ly/CRA-PWA"
- );
- });
- } else {
- // Is not localhost. Just register service worker
- registerValidSW(swUrl, config);
- }
- });
- }
-}
-
-function registerValidSW(swUrl, config) {
- navigator.serviceWorker
- .register(swUrl)
- .then((registration) => {
- registration.onupdatefound = () => {
- const installingWorker = registration.installing;
- if (installingWorker == null) {
- return;
- }
- installingWorker.onstatechange = () => {
- if (installingWorker.state === "installed") {
- if (navigator.serviceWorker.controller) {
- // At this point, the updated precached content has been fetched,
- // but the previous service worker will still serve the older
- // content until all client tabs are closed.
- console.log(
- "New content is available and will be used when all " +
- "tabs for this page are closed. See https://bit.ly/CRA-PWA."
- );
-
- // Execute callback
- if (config && config.onUpdate) {
- config.onUpdate(registration);
- }
- } else {
- // At this point, everything has been precached.
- // It's the perfect time to display a
- // "Content is cached for offline use." message.
- console.log("Content is cached for offline use.");
-
- // Execute callback
- if (config && config.onSuccess) {
- config.onSuccess(registration);
- }
- }
- }
- };
- };
- })
- .catch((error) => {
- console.error("Error during service worker registration:", error);
- });
-}
-
-function checkValidServiceWorker(swUrl, config) {
- // Check if the service worker can be found. If it can't reload the page.
- fetch(swUrl)
- .then((response) => {
- // Ensure service worker exists, and that we really are getting a JS file.
- const contentType = response.headers.get("content-type");
- if (
- response.status === 404 ||
- (contentType != null &&
- contentType.indexOf("javascript") === -1)
- ) {
- // No service worker found. Probably a different app. Reload the page.
- navigator.serviceWorker.ready.then((registration) => {
- registration.unregister().then(() => {
- window.location.reload();
- });
- });
- } else {
- // Service worker found. Proceed as normal.
- registerValidSW(swUrl, config);
- }
- })
- .catch(() => {
- console.log(
- "No internet connection found. App is running in offline mode."
- );
- });
-}
-
-export function unregister() {
- if ("serviceWorker" in navigator) {
- navigator.serviceWorker.ready.then((registration) => {
- registration.unregister();
- });
- }
-}
diff --git a/packages/create/README.md b/packages/create/README.md
new file mode 100644
index 0000000..ff4b9b7
--- /dev/null
+++ b/packages/create/README.md
@@ -0,0 +1,16 @@
+[![@wq/create][logo]][docs]
+
+This command leverages [wq-vite-template] to generate [Vite] projects utilizing the [wq framework]. The template uses [@wq/app], [@wq/material], and [@wq/map-gl] to generate a configuration-driven interface for collecting and managing geospatial field data. This template is generally meant to be used together with [wq.create]. See wq's [Getting Started] docs for more information.
+
+### [Documentation][docs]
+
+[logo]: https://wq.io/images/@wq/create.svg
+[docs]: https://wq.io/@wq/create
+[wq framework]: https://wq.io/
+[@wq/app]: https://wq.io/@wq/app
+[@wq/material]: https://wq.io/@wq/material
+[@wq/map-gl]: https://wq.io/@wq/map-gl
+[wq.create]: https://wq.io/wq.create/
+[Getting Started]: https://wq.io/overview/setup
+[wq-vite-template]: https://github.com/wq/wq-vite-template
+[Vite]: https://vitejs.dev/
diff --git a/packages/create/index.js b/packages/create/index.js
new file mode 100644
index 0000000..f046706
--- /dev/null
+++ b/packages/create/index.js
@@ -0,0 +1,22 @@
+#!/usr/bin/env node
+
+const fs = require("fs"),
+ path = require("path"),
+ template = path.join(path.dirname(__filename), "template"),
+ project = process.argv[2] || "project";
+
+try {
+ fs.cpSync(template, project, {
+ recursive: true,
+ force: false,
+ errorOnExist: true,
+ });
+ const pkgFile = path.join(project, "package.json");
+ pkg = JSON.parse(fs.readFileSync(pkgFile));
+ pkg.name = project;
+ fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 4) + "\n");
+ fs.rmSync(path.join(project, "package-lock.json"));
+ console.log(`@wq/create: Created ${project} from wq-vite-template`);
+} catch (e) {
+ console.log(e.message || "@wq/create: Error creating project");
+}
diff --git a/packages/create/package-lock.json b/packages/create/package-lock.json
new file mode 100644
index 0000000..cd2d036
--- /dev/null
+++ b/packages/create/package-lock.json
@@ -0,0 +1,15 @@
+{
+ "name": "@wq/create",
+ "version": "2.1.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@wq/create",
+ "version": "2.1.1",
+ "bin": {
+ "wq-create": "index.js"
+ }
+ }
+ }
+}
diff --git a/packages/create/package.json b/packages/create/package.json
new file mode 100644
index 0000000..88e5d05
--- /dev/null
+++ b/packages/create/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@wq/create",
+ "version": "2.1.1",
+ "bin": {
+ "wq-create": "./index.js"
+ },
+ "files": [
+ "template"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/wq/wq.create.git",
+ "directory": "packages/create"
+ },
+ "bugs": {
+ "url": "https://github.com/wq/wq.create/issues"
+ },
+ "homepage": "https://wq.io/@wq/create"
+}
diff --git a/packages/create/template b/packages/create/template
new file mode 160000
index 0000000..fe9db42
--- /dev/null
+++ b/packages/create/template
@@ -0,0 +1 @@
+Subproject commit fe9db42b7e82c604da8b80b9cd09b23b429a75ce
diff --git a/packages/rollup-plugin/package.json b/packages/rollup-plugin/package.json
index c2ca51d..51111bc 100644
--- a/packages/rollup-plugin/package.json
+++ b/packages/rollup-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@wq/rollup-plugin",
- "version": "2.0.0",
+ "version": "2.1.0",
"description": "Build custom wq plugins that integrate with wq.js",
"type": "module",
"main": "src/index.js",
diff --git a/packages/rollup-plugin/src/index.js b/packages/rollup-plugin/src/index.js
index 9203632..f663e2a 100644
--- a/packages/rollup-plugin/src/index.js
+++ b/packages/rollup-plugin/src/index.js
@@ -1,10 +1,10 @@
import modules from "./modules.js";
const prefix = "\0wq-bundle:",
- defaultConfig = {urlBase: "."};
+ defaultConfig = { urlBase: "." };
export default function wq(config) {
- const { urlBase } = {...defaultConfig,...config};
+ const { urlBase } = { ...defaultConfig, ...config };
return {
name: "@wq/rollup-plugin",
resolveId(id) {
diff --git a/packages/rollup-plugin/src/modules.js b/packages/rollup-plugin/src/modules.js
index 41f21e3..0d642aa 100644
--- a/packages/rollup-plugin/src/modules.js
+++ b/packages/rollup-plugin/src/modules.js
@@ -543,6 +543,7 @@ const modules = {
"DefaultList",
"FileLink",
"ForeignKey",
+ "ForeignKeyLink",
"Form",
"FormError",
"ImagePreview",
@@ -551,11 +552,13 @@ const modules = {
"Loading",
"Login",
"Logout",
+ "ManyToManyLink",
"Message",
"NavMenu",
"NotFound",
"OutboxList",
"PropertyTable",
+ "RelatedLinks",
"RouteContext",
"Server",
"autoFormData",
@@ -689,6 +692,7 @@ const modules = {
"AutoOverlay",
"DefaultDetail",
"DefaultList",
+ "DefaultPopup",
"Geo",
"GeoCode",
"GeoCoords",
@@ -706,6 +710,8 @@ const modules = {
"computeBounds",
"useBasemapComponents",
"useFeatureCollection",
+ "useFeatureUrl",
+ "useFeatureValues",
"useGeoJSON",
"useGeoTools",
"useGeolocation",
@@ -713,6 +719,7 @@ const modules = {
"useMapInstance",
"useMapState",
"useOverlayComponents",
+ "useStyleProp",
],
hasDefault: true,
},
diff --git a/packages/rollup-plugin/update_modules.sh b/packages/rollup-plugin/update_modules.sh
index 4a74a93..d153c7b 100755
--- a/packages/rollup-plugin/update_modules.sh
+++ b/packages/rollup-plugin/update_modules.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-wget https://unpkg.com/wq@next -O wq.js
+wget https://unpkg.com/wq@latest -O wq.js
sed -i "s/^import[^;]*;//" wq.js
node update_modules.js > src/modules.js
npm run prettier
diff --git a/pyproject.toml b/pyproject.toml
index 7a640e2..911d633 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,7 +9,7 @@ authors = [
]
description = "Project scaffolding tools for creating a new application with the wq framework."
readme = "README.md"
-requires-python = ">=3.7"
+requires-python = ">=3.8"
license = {text = "MIT"}
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -18,16 +18,16 @@ classifiers = [
"Natural Language :: English",
"Programming Language :: JavaScript",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
"Framework :: Django",
- "Framework :: Django :: 3.2",
"Framework :: Django :: 4.0",
"Framework :: Django :: 4.1",
"Framework :: Django :: 4.2",
+ "Framework :: Django :: 5.0",
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Topic :: Software Development :: Code Generators",
"Topic :: Text Processing :: Markup :: HTML",
@@ -35,8 +35,8 @@ classifiers = [
"Topic :: Software Development :: Pre-processors",
]
dependencies = [
- "wq.build>=2.0.0",
- "xlsconv>=2.0.0,<2.1.0",
+ "wq.build>=2.1.0",
+ "xlsconv>=2.0.0,<2.2.0",
"psycopg2-binary",
]
diff --git a/tests/expected/config0.json b/tests/expected/config0.json
index 9e93762..3fc0b08 100644
--- a/tests/expected/config0.json
+++ b/tests/expected/config0.json
@@ -8,52 +8,62 @@
"url": "logout",
"name": "logout"
},
- "category": {
- "icon": "config",
- "description": "Manage available categories",
- "section": "Admin",
- "show_in_index": "can_change",
- "cache": "all",
- "background_sync": false,
- "name": "category",
- "url": "categories",
- "list": true,
- "form": [
- {
- "name": "name",
- "label": "Name",
- "bind": {
- "required": true
- },
- "wq:length": 255,
- "type": "string"
- },
- {
- "name": "description",
- "label": "Description",
- "type": "text"
- }
- ],
- "verbose_name": "category",
- "verbose_name_plural": "categories",
- "label_template": "{{name}}"
+ "index": {
+ "url": "",
+ "icon": "directions",
+ "verbose_name": "Map",
+ "description": "Project overview map",
+ "section": "Contributions",
+ "order": 0,
+ "map": {
+ "mapId": "map",
+ "layers": [
+ {
+ "name": "Observations",
+ "type": "geojson",
+ "url": "/observations.geojson",
+ "popup": "observation"
+ }
+ ]
+ },
+ "name": "index"
},
"observation": {
"icon": "list",
"description": "View and submit photos on map",
"section": "Contributions",
+ "order": 1,
"cache": "first_page",
"background_sync": true,
"map": [
{
"mode": "list",
- "autoLayers": true,
- "layers": []
+ "layers": [
+ {
+ "name": "Observations",
+ "type": "geojson",
+ "data": [
+ "context_feature_collection",
+ "geometry"
+ ],
+ "popup": "observation",
+ "cluster": true
+ }
+ ]
},
{
"mode": "detail",
- "autoLayers": true,
- "layers": []
+ "layers": [
+ {
+ "name": "Observation",
+ "type": "geojson",
+ "data": [
+ "context_feature",
+ "geometry"
+ ],
+ "popup": "observation"
+ }
+ ]
},
{
"mode": "edit",
@@ -106,17 +116,48 @@
"ordering": [
"-date"
],
- "label_template": "{{date}}"
+ "label_template": "{{date}}",
+ "geometry_fields": [
+ {
+ "name": "geometry",
+ "label": "Location",
+ "type": "geopoint"
+ }
+ ]
},
- "index": {
- "url": "",
- "name": "index",
- "show_in_index": false,
- "verbose_name": "test Project"
+ "category": {
+ "icon": "config",
+ "description": "Manage available categories",
+ "section": "Admin",
+ "order": 100,
+ "show_in_index": "can_change",
+ "cache": "all",
+ "background_sync": false,
+ "name": "category",
+ "url": "categories",
+ "list": true,
+ "form": [
+ {
+ "name": "name",
+ "label": "Name",
+ "bind": {
+ "required": true
+ },
+ "wq:length": 255,
+ "type": "string"
+ },
+ {
+ "name": "description",
+ "label": "Description",
+ "type": "text"
+ }
+ ],
+ "verbose_name": "category",
+ "verbose_name_plural": "categories",
+ "label_template": "{{name}}"
}
},
"site_title": "test Project",
- "logo": "/icon-192.png",
"router": {
"base_url": ""
},
@@ -126,6 +167,7 @@
"format": "json"
}
},
+ "logo": "/static/app/images/icon-192.png",
"material": {
"theme": {
"primary": "#7500ae",
diff --git a/tests/expected/config1.json b/tests/expected/config1.json
index ea2227a..838b210 100644
--- a/tests/expected/config1.json
+++ b/tests/expected/config1.json
@@ -12,11 +12,14 @@
"url": "",
"name": "index",
"show_in_index": false,
- "verbose_name": "test Project"
+ "verbose_name": "test Project",
+ "map": {
+ "mapId": "map",
+ "layers": []
+ }
}
},
"site_title": "test Project",
- "logo": "/icon-192.png",
"router": {
"base_url": ""
},
@@ -26,6 +29,7 @@
"format": "json"
}
},
+ "logo": "/static/app/images/icon-192.png",
"material": {
"theme": {
"primary": "#7500ae",
diff --git a/tests/expected/config2.json b/tests/expected/config2.json
index 771d22a..2e722e4 100644
--- a/tests/expected/config2.json
+++ b/tests/expected/config2.json
@@ -14,17 +14,35 @@
"map": [
{
"mode": "list",
- "autoLayers": true,
- "layers": []
+ "layers": [
+ {
+ "name": "Locations",
+ "type": "geojson",
+ "data": [
+ "context_feature_collection",
+ "geometry"
+ ],
+ "popup": "location",
+ "cluster": true
+ }
+ ]
},
{
"mode": "detail",
- "autoLayers": true,
- "layers": []
+ "layers": [
+ {
+ "name": "Location",
+ "type": "geojson",
+ "data": [
+ "context_feature",
+ "geometry"
+ ],
+ "popup": "location"
+ }
+ ]
},
{
"mode": "edit",
- "autoLayers": true,
"layers": []
}
],
@@ -75,17 +93,27 @@
}
],
"verbose_name": "location",
- "verbose_name_plural": "locations"
+ "verbose_name_plural": "locations",
+ "geometry_fields": [
+ {
+ "name": "geometry",
+ "label": "Location",
+ "type": "geopoint"
+ }
+ ]
},
"index": {
"url": "",
"name": "index",
"show_in_index": false,
- "verbose_name": "test Project"
+ "verbose_name": "test Project",
+ "map": {
+ "mapId": "map",
+ "layers": []
+ }
}
},
"site_title": "test Project",
- "logo": "/icon-192.png",
"router": {
"base_url": ""
},
@@ -95,6 +123,7 @@
"format": "json"
}
},
+ "logo": "/static/app/images/icon-192.png",
"material": {
"theme": {
"primary": "#7500ae",
diff --git a/tests/expected/config3.json b/tests/expected/config3.json
index ec39432..d38f926 100644
--- a/tests/expected/config3.json
+++ b/tests/expected/config3.json
@@ -8,60 +8,41 @@
"url": "logout",
"name": "logout"
},
- "observation": {
- "cache": "first_page",
- "background_sync": true,
- "name": "observation",
- "url": "observations",
- "list": true,
- "form": [
- {
- "name": "location",
- "label": "Site",
- "hint": "The site where the observation was taken",
- "type": "select one",
- "wq:ForeignKey": "location",
- "wq:related_name": "observation_set"
- },
- {
- "name": "date",
- "label": "Date",
- "hint": "The date when the observation was taken",
- "type": "date"
- },
- {
- "name": "photo",
- "label": "Photo",
- "hint": "Photo of the site",
- "type": "image"
- },
- {
- "name": "notes",
- "label": "Notes",
- "hint": "Field observations and notes",
- "type": "text"
- }
- ],
- "verbose_name": "observation",
- "verbose_name_plural": "observations"
- },
"location": {
"cache": "first_page",
"background_sync": true,
"map": [
{
"mode": "list",
- "autoLayers": true,
- "layers": []
+ "layers": [
+ {
+ "name": "Locations",
+ "type": "geojson",
+ "data": [
+ "context_feature_collection",
+ "geometry"
+ ],
+ "popup": "location",
+ "cluster": true
+ }
+ ]
},
{
"mode": "detail",
- "autoLayers": true,
- "layers": []
+ "layers": [
+ {
+ "name": "Location",
+ "type": "geojson",
+ "data": [
+ "context_feature",
+ "geometry"
+ ],
+ "popup": "location"
+ }
+ ]
},
{
"mode": "edit",
- "autoLayers": true,
"layers": []
}
],
@@ -112,17 +93,64 @@
}
],
"verbose_name": "location",
- "verbose_name_plural": "locations"
+ "verbose_name_plural": "locations",
+ "geometry_fields": [
+ {
+ "name": "geometry",
+ "label": "Location",
+ "type": "geopoint"
+ }
+ ]
+ },
+ "observation": {
+ "cache": "first_page",
+ "background_sync": true,
+ "name": "observation",
+ "url": "observations",
+ "list": true,
+ "form": [
+ {
+ "name": "location",
+ "label": "Site",
+ "hint": "The site where the observation was taken",
+ "type": "select one",
+ "wq:ForeignKey": "location",
+ "wq:related_name": "observation_set"
+ },
+ {
+ "name": "date",
+ "label": "Date",
+ "hint": "The date when the observation was taken",
+ "type": "date"
+ },
+ {
+ "name": "photo",
+ "label": "Photo",
+ "hint": "Photo of the site",
+ "type": "image"
+ },
+ {
+ "name": "notes",
+ "label": "Notes",
+ "hint": "Field observations and notes",
+ "type": "text"
+ }
+ ],
+ "verbose_name": "observation",
+ "verbose_name_plural": "observations"
},
"index": {
"url": "",
"name": "index",
"show_in_index": false,
- "verbose_name": "test Project"
+ "verbose_name": "test Project",
+ "map": {
+ "mapId": "map",
+ "layers": []
+ }
}
},
"site_title": "test Project",
- "logo": "/icon-192.png",
"router": {
"base_url": ""
},
@@ -132,6 +160,7 @@
"format": "json"
}
},
+ "logo": "/static/app/images/icon-192.png",
"material": {
"theme": {
"primary": "#7500ae",
diff --git a/tests/test-deploy.sh b/tests/test-deploy.sh
index afb1b77..59cdb33 100755
--- a/tests/test-deploy.sh
+++ b/tests/test-deploy.sh
@@ -7,7 +7,6 @@ rm -rf output
mkdir output
if [[ "$TEST_VARIANT" == "postgis" ]]; then
- export DJANGO_SETTINGS_MODULE="test_project.settings.prod"
GIS_FLAG="--with-gis"
elif [[ "$TEST_VARIANT" == "spatialite" ]]; then
GIS_FLAG="--with-gis"
@@ -31,20 +30,25 @@ else
NPM_FLAG="--without-npm"
fi;
-wq create test_project ./test_project -d test.wq.io -t "test Project" $NPM_FLAG $GIS_FLAG
-cd test_project
+GUNICORN_FLAG="--with-apache"
-# Verify ./deploy.sh works
-./deploy.sh 0.0.0
+wq create test_project ./test_project -d test.wq.io -t "test Project" $NPM_FLAG $GIS_FLAG $GUNICORN_FLAG
+cd test_project
# Load db and verify initial config
if [[ "$TEST_VARIANT" == "postgis" ]]; then
- sed -i "s/'USER': 'test_project'/'USER': '$USER'/" db/test_project/settings/prod.py
- sed -i "s/ALLOWED_HOSTS.*/ALLOWED_HOSTS = ['localhost']/" db/test_project/settings/prod.py
+ sed -i "s/\"USER\": \"postgres\"/\"USER\": \"$USER\"/" db/test_project/settings/dev.py
+ sed -i "s/\"HOST\": \"localhost\"/\"HOST\": \"\"/" db/test_project/settings/dev.py
else
+ sed -i "s/\# *\"/\"/" db/test_project/settings/dev.py
+ sed -i "s/\# \}/\}/" db/test_project/settings/dev.py
# See https://code.djangoproject.com/ticket/32935
$MANAGE shell -c "import django;django.db.connection.cursor().execute('SELECT InitSpatialMetaData(1);')";
fi;
+
+# Verify ./deploy.sh works
+./deploy.sh 0.0.0
+
$MANAGE migrate
$MANAGE dump_config > $OUTPUT/config0.json
$COMPARE expected/config0.json output/config0.json
diff --git a/wq/create/django_project b/wq/create/django_project
index f7c52eb..014f98e 160000
--- a/wq/create/django_project
+++ b/wq/create/django_project
@@ -1 +1 @@
-Subproject commit f7c52ebd7a9fabb1c5bea972dd6e9772c0da2b44
+Subproject commit 014f98e86f9634318ab81221f5a51110aced1921
diff --git a/wq/create/projects.py b/wq/create/projects.py
index f925b44..28e4825 100644
--- a/wq/create/projects.py
+++ b/wq/create/projects.py
@@ -23,10 +23,10 @@
if os.name == "nt":
- NPX_COMMAND = "npx.cmd"
+ NPM_COMMAND = "npm.cmd"
DEPLOY_SCRIPT = "deploy.bat"
else:
- NPX_COMMAND = "npx"
+ NPM_COMMAND = "npm"
DEPLOY_SCRIPT = "./deploy.sh"
@@ -36,7 +36,8 @@ def add_arguments(self, parser):
parser.add_argument("--domain", help="Web Domain")
parser.add_argument("--title", help="Site Title")
parser.add_argument("--with-gis", help="Enable GeoDjango")
- parser.add_argument("--with-npm", help="Enable NPM")
+ parser.add_argument("--with-npm", help="Enable NPM)")
+ parser.add_argument("--with-gunicorn", help="Enable Gunicorn")
parser.add_argument("--wq-create-version", help="wq create version")
@@ -51,7 +52,12 @@ def add_arguments(self, parser):
@click.option(
"--with-npm/--without-npm",
default=None,
- help="Enable NPM (& Create React App)",
+ help="Enable NPM (Vite with @wq/rollup-plugin)",
+)
+@click.option(
+ "--with-gunicorn/--with-apache",
+ default=None,
+ help="Use Gunicorn + Whitenoise instead of Apache WSGI",
)
def create(
project_name,
@@ -60,6 +66,7 @@ def create(
title=None,
with_gis=None,
with_npm=None,
+ with_gunicorn=None,
):
"""
Start a new project with wq.app and wq.db. A new Django project will be
@@ -74,10 +81,26 @@ def create(
See https://wq.io/overview/setup for more tips on getting started with wq.
"""
- do_create(project_name, destination, domain, title, with_gis, with_npm)
+ do_create(
+ project_name,
+ destination,
+ domain,
+ title,
+ with_gis,
+ with_npm,
+ with_gunicorn,
+ )
-def do_create(project_name, destination, domain, title, with_gis, with_npm):
+def do_create(
+ project_name,
+ destination,
+ domain,
+ title,
+ with_gis,
+ with_npm,
+ with_gunicorn,
+):
any_prompts = False
if project_name is None:
@@ -117,7 +140,14 @@ def do_create(project_name, destination, domain, title, with_gis, with_npm):
if with_npm is None:
any_prompts = True
with_npm = click.confirm(
- "Enable NPM / Create React App? (Requires Node.js)",
+ "Enable NPM / Vite + @wq/rollup-plugin? (Requires Node.js)",
+ default=False,
+ )
+
+ if with_gunicorn is None:
+ any_prompts = True
+ with_gunicorn = click.confirm(
+ "Enable Gunicorn + Whitenoise instead of Apache WSGI?",
default=False,
)
@@ -134,6 +164,7 @@ def do_create(project_name, destination, domain, title, with_gis, with_npm):
wq_create_version=VERSION,
with_gis=with_gis,
with_npm=with_npm,
+ with_gunicorn=with_gunicorn,
)
call_command(StartProjectCommand(), *args, **kwargs)
@@ -143,43 +174,47 @@ def do_create(project_name, destination, domain, title, with_gis, with_npm):
for dep in freeze.freeze():
print(dep, file=f)
+ os.remove(os.path.join(path, "app", "README.md"))
if with_npm:
- shutil.rmtree(os.path.join(path, "app"))
+ project_static_dir = os.path.join(path, "db", project_name, "static")
+ os.makedirs(project_static_dir, exist_ok=True)
+ shutil.move(os.path.join(path, "app"), project_static_dir)
subprocess.check_call(
- [
- NPX_COMMAND,
- "create-react-app",
- project_name,
- "--template",
- "@wq",
- ],
- cwd=path,
+ [NPM_COMMAND, "init", "@wq", project_name], cwd=path
)
os.rename(
os.path.join(path, project_name),
os.path.join(path, "app"),
)
- for filename in ("index.html", "manifest.json"):
- filepath = os.path.join(path, "app", "public", filename)
+ for filename in ("index.html", "vite.config.js"):
+ filepath = os.path.join(path, "app", filename)
with open(filepath) as f:
content = f.read()
- content = content.replace("{{ title }}", title)
- content = content.replace("{{ project_name }}", project_name)
+ content = content.replace("Example Project", title)
+ content = content.replace("project", project_name)
with open(filepath, "w") as f:
f.write(content)
- else:
- os.remove(os.path.join(path, "app", "README.md"))
+ subprocess.check_call(
+ [NPM_COMMAND, "install"], cwd=os.path.join(path, "app")
+ )
+
+ if with_gunicorn:
+ os.remove(os.path.join(path, "conf", f"{project_name}.conf"))
flags = []
if with_gis:
flags.append("GIS")
if with_npm:
flags.append("NPM")
+ if with_gunicorn:
+ flags.append("Gunicorn")
- if len(flags) == 3:
- flag_summary = " with {0}, {1}, and {2} support".format(*flags)
+ if len(flags) > 2:
+ flag_summary = " with {first}, and {last} support".format(
+ first=", ".join(flags[:-1]), last=flags[-1]
+ )
elif len(flags) == 2:
flag_summary = " with {0} and {1} support".format(*flags)
elif len(flags) == 1: