diff --git a/.gitignore b/.gitignore index e65e38a..934d8a0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ pom.xml.asc /resources/public/content/.git /resources/public/vendor /bower_components/ +/resources/public/js/lib/ .lein-deps-sum .lein-repl-history .lein-plugins/ @@ -21,4 +22,19 @@ pom.xml.asc *-init.clj profiles\.clj .bowerrc -bower.json \ No newline at end of file +bower.json +.idea + +src/clj/youyesyet/scratchpad\.clj + +*.iml + +resources/sql/queries\.auto\.sql + +resources/templates/auto/ + +src/clj/youyesyet/routes/auto\.clj + +src/clj/youyesyet/routes/auto_json\.clj + +resources/sql/youyesyet\.postgres\.sql diff --git a/README.md b/README.md index dd054c8..f21afe9 100644 --- a/README.md +++ b/README.md @@ -59,16 +59,24 @@ Do get the database initialised, run createdb youyesyet_dev -followed by +I'm no longer using Migratus as I'm using [Application Description Language]() +to generate the majority of the application, and, as changes are made to the application +description, new database schemas are generated. The database initialisation script will +be found at `resources/sql/youyesyet.postgres.sql`. Reference data initialisation scripts +will in due course be stored in the same directory. - lein migratus migrate - -**NOTE THAT** in the namespace *youyesyet.db.schema*, there are a series of functions *create-xxx-table!*. These are a snare and a delusion; they are how I originally bootstrapped the database, but are no longer used (and should probably be deleted). The database is now constructed using [migratus](https://github.com/yogthos/migratus). +Once we have a more or less finished application it may be worth going back to +[Migratus](https://github.com/yogthos/migratus); I might have a go at generating migrations from +diffs between successive versions of the application description. ## Running in a dev environment To run in a dev environment, checkout the *develop* branch +To download and install Javascript delendencies, run + + lein npm install + To start a development web server for the application, run: lein run diff --git a/doc/specification/api.v1.raml b/doc/specification/api.v1.raml new file mode 100644 index 0000000..44bd043 --- /dev/null +++ b/doc/specification/api.v1.raml @@ -0,0 +1,23 @@ +#%RAML 0.8 + --- + title: YouYesYet API + baseUri: https://api.yyy.scot/{version} + version: v1 + + +/canvassers: + get: + put: + +/electors: + get: + /{id}: + get: + /{address_id}: + get: + +# Location isn't a real entity in the database, but it is a means of searching for +# addresses and electors. +/location: + /{lat}/{long}/{radius}: + get: diff --git a/doc/specification/authorisation.md b/doc/specification/authorisation.md new file mode 100644 index 0000000..66b006e --- /dev/null +++ b/doc/specification/authorisation.md @@ -0,0 +1,17 @@ +# Security and authorisation + +Essentially we have six levels of authorisation, at essentially increasing levels of sensitivity. + +1. *Canvassers:* Any authenticated user essentially has this level of authorisation. Hence users of the app can all share the same database connections without problem. Therefore there will be one first-class database user for all canvassers, and they will not have individual real database logins. + +2. *Issue experts:* Issue experts respond to followup requests. Therefore they must be able to see the queue of requests and the details of the elector making the request. They don't need to see voter intentions and I don't believe the information they do need to see is particularly sensitive. So they too can share a single database-layer login and connection pool; whether this is the same login as used by the canvassers is an implementation detail but I don't believe that it's critical. + +3. *Issue editors:* Don't need to see much sensitive data (although they do need to see, in aggregate, what issues are being raised by electors in the field), but they do have the power to dictate the initial responses canvassers make to issues raised, so the information they can *write* is pretty sensitive. We need to be very sure that unauthorised users don't have the power to write this data. So I suggest that issue editors probably should have individual first class database logins. + +4. *Team leaders:* Need to be able to monitor the performance of their teams, to invite new users to the system and to block abusive users from the system. Again, these are significant functions which should be well protected from abuse. But we will have at least hundreds, probably thousands of team leaders across Scotland. I would prefer that they each had first class logins, but this may be impractical. But in any case, even if they use a shared login, it should not be the same shared login as used by canvassers. + +5. *Analyists* Need broad authorisation to read, but not write or edit, all sensitive data held by the system. They must have individual first class database logins. + +6. *Admins* Can necessarily read and write everything. They should definitely each have individual first class database logins. + +This means we have a hybrid authentication scheme; for lower levels, application layer security and shared connection pools are adequate. For higher levels, individual connections and database layer authorisation are required. It implies that the routes at the different layers should be separated into separate namespaces with separate authentication functions. diff --git a/doc/specification/entity-relationship-diagram.svg b/doc/specification/entity-relationship-diagram.svg index e68f1bf..5585709 100644 --- a/doc/specification/entity-relationship-diagram.svg +++ b/doc/specification/entity-relationship-diagram.svg @@ -14,7 +14,7 @@ viewBox="0 0 1052.3622 744.09448" id="svg2" version="1.1" - inkscape:version="0.91 r13725" + inkscape:version="0.92.3 (2405546, 2018-03-11)" sodipodi:docname="entity-relationship-diagram.svg"> @@ -25,16 +25,16 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.98994949" - inkscape:cx="455.50968" - inkscape:cy="346.1212" + inkscape:zoom="0.9899495" + inkscape:cx="472.36875" + inkscape:cy="325.73865" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="1920" - inkscape:window-height="1031" - inkscape:window-x="0" - inkscape:window-y="27" + inkscape:window-height="1043" + inkscape:window-x="1920" + inkscape:window-y="0" inkscape:window-maximized="1"> + x="16.060907" + y="312.36218" /> + y="335.1539" + style="font-size:20px;line-height:1.25">  YouYesYet: Entity Relationship Diagram - - - District - + x="117.1777" + y="360.40771" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold'">YouYesYet: Entity Relationship Diagram + District + Dwelling + sodipodi:role="line">Address Elector + y="683.08313" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Elector Canvasser   Authority + y="563.88513" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Authority Visit + y="563.88513" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Visit Intention @@ -244,20 +229,19 @@ y="794.50446" /> IssueIssueExpertise Issue @@ -286,19 +269,18 @@ id="rect4221" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> FollowupRequest @@ -311,21 +293,20 @@ y="915.72272" /> FollowupFollowupAction + y="945.27985" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Action @@ -535,13 +516,12 @@ id="rect4323" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> Version: 0.4Date: 20170401Author: Simon BrookeCopyright: (c) 2016 Simon Brooke for Radical Independence Campaign Introduced Visited Recorded Raised Authenticates Has About About Responded to Contains + x="330" + y="702.36218" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Expressed Contains Resides at Requested Performed To + y="562.36218" + style="font-size:20px;line-height:1.25">  Organiser-Organiser-ship TeamTeamMembership + y="581.11218" + id="tspan4385" + style="font-size:15px;line-height:1.25">Membership Has Has of + y="622.36218" + style="font-size:10px;line-height:1.25">of of + y="742.36218" + style="font-size:10px;line-height:1.25">of @@ -1047,16 +1001,15 @@ For + y="742.36218" + style="font-size:10px;line-height:1.25">For RoleRoleMembership + y="701.11218" + id="tspan4383" + style="font-size:15px;line-height:1.25">Membership @@ -1090,16 +1044,16 @@ x="561.61688" y="774.30139" /> Role + sodipodi:role="line" + style="font-size:15px;line-height:1.25">Role Is + y="677.36218" + style="font-size:10px;line-height:1.25">Is Includes + y="631.24536" + style="font-size:10px;line-height:1.25">Includes diff --git a/doc/specification/scaling.md b/doc/specification/scaling.md index 922c473..8174799 100644 --- a/doc/specification/scaling.md +++ b/doc/specification/scaling.md @@ -56,8 +56,38 @@ All this normalisation and memoisation reduces the number of read requests on th Note that [clojure.core.memoize](https://github.com/clojure/core.memoize) provides us with functions to create both size-limited, least-recently-used caches and duration limited, time-to-live caches. +### Searching the database for localities + At 56 degrees north there are 111,341 metres per degree of latitude, 62,392 metres per degree of longitude. So a 100 metre box is about 0.0016 degrees east-west and .0009 degrees north-south. If we simplify that slightly (and we don't need square boxes, we need units of area covering a group of people working together) then we can take .001 of a degree in either direction which is computationally cheap. +Of course we could have a search query like this + + select * from addresses + where latitude > 56.003 + and latitude < 56.004 + and longitude > -4.771 + and longitude < -4.770; + +And it would work - but it would be computationally expensive. If we call each of these .001 x .001 roughly-rectangles a **locality**, then we can give every locality an integer index as follows + + (defn locality-index + "Compute a locality for this `latitude`, `longitude` pair." + [latitude longitude] + (+ + (* 10000 ;; left-shift the latitude component four digits + (integer + (* latitude 1000))) + (- ;; invert the sign of the longitude component, since + ;; we're interested in localities West of Greenwich. + (integer + (* longitude 1000))))) + +For values in Scotland, this gives us a number comfortable smaller than the maximum size of a 32 bit integer. Note that this isn't generally the case, so to adapt this software for use in Canada, for example, a more general solution would need to be chosen; but this will do for now. If we compute this index at the time the address is geocoded, then we can achieve the exact same results as the query given above with a much simpler query: + + select * from address where locality = 560034770; + +If the locality field is indexed (which obviously it should be) this query becomes very cheap. + ### Geographic sharding Volunteers canvassing simultaneously in the same street or the same locality need to see in near real time which dwellings have been canvassed by other volunteers, otherwise we'll get the same households canvassed repeatedly, which wastes volunteer time and annoys voters. So they all need to be sending updates to, and receiving updates from, the same server. But volunteers canvassing in Aberdeen don't need to see in near real time what is happening in Edinburgh. diff --git a/dummies/mapview.png b/dummies/mapview.png deleted file mode 100644 index 95c419b..0000000 Binary files a/dummies/mapview.png and /dev/null differ diff --git a/dummies/mapview.svg b/dummies/mapview.svg deleted file mode 100644 index c909b17..0000000 --- a/dummies/mapview.svg +++ /dev/null @@ -1,4874 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - ? - - - - - - - - - - - - - - - - - - - - - - - ? - - - - ? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ? - - - - - - - - - - - - - - - - - - - - - - - ? - - - - ? - - - - ? - - - - ? - - - diff --git a/dummies/mapview.xcf b/dummies/mapview.xcf deleted file mode 100644 index 1604bec..0000000 Binary files a/dummies/mapview.xcf and /dev/null differ diff --git a/dummies/mapview_800.png b/dummies/mapview_800.png deleted file mode 100644 index 5173125..0000000 Binary files a/dummies/mapview_800.png and /dev/null differ diff --git a/dummies/mapview_800.xcf b/dummies/mapview_800.xcf deleted file mode 100644 index 47d5ccf..0000000 Binary files a/dummies/mapview_800.xcf and /dev/null differ diff --git a/dummies/occupants.png b/dummies/occupants.png deleted file mode 100644 index 78ada06..0000000 Binary files a/dummies/occupants.png and /dev/null differ diff --git a/dummies/occupants.svg b/dummies/occupants.svg deleted file mode 100644 index ed4486c..0000000 --- a/dummies/occupants.svg +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - 43 Imaginary Terrace - - - - John McNeil - Anne McNeil - Helen McNeil - - - - - - - - - - - - - - - - - - - - ? - - - - - - - - - - ? - - - - - - - - - - - - - - - - - - - ? - - - - - - - - - - - - - - - - diff --git a/dummies/occupants.xcf b/dummies/occupants.xcf deleted file mode 100644 index cdf7b64..0000000 Binary files a/dummies/occupants.xcf and /dev/null differ diff --git a/dummies/occupants_800.png b/dummies/occupants_800.png deleted file mode 100644 index 70b0d9f..0000000 Binary files a/dummies/occupants_800.png and /dev/null differ diff --git a/dummies/occupants_800.xcf b/dummies/occupants_800.xcf deleted file mode 100644 index eb7c497..0000000 Binary files a/dummies/occupants_800.xcf and /dev/null differ diff --git a/dummies/ujack.png b/dummies/ujack.png deleted file mode 100644 index d542490..0000000 Binary files a/dummies/ujack.png and /dev/null differ diff --git a/dummies/ujack.xcf b/dummies/ujack.xcf deleted file mode 100644 index a02e5df..0000000 Binary files a/dummies/ujack.xcf and /dev/null differ diff --git a/dummies/unknown.png b/dummies/unknown.png deleted file mode 100644 index da3ac5b..0000000 Binary files a/dummies/unknown.png and /dev/null differ diff --git a/dummies/unknown.xcf b/dummies/unknown.xcf deleted file mode 100644 index b1806e5..0000000 Binary files a/dummies/unknown.xcf and /dev/null differ diff --git a/env/dev/clj/youyesyet/core.clj b/env/dev/clj/youyesyet/core.clj index 1b703a4..c9f3446 100644 --- a/env/dev/clj/youyesyet/core.clj +++ b/env/dev/clj/youyesyet/core.clj @@ -1,4 +1,5 @@ -(ns youyesyet.core +(ns ^{:doc "Devalopment launcher, entirely boilerplate from Luminus."} + youyesyet.core (:require [youyesyet.handler :as handler] [luminus.repl-server :as repl] [luminus.http-server :as http] @@ -46,7 +47,9 @@ mount/start-with-args :started)] (log/info component "started")) - (.addShutdownHook (Runtime/getRuntime) (Thread. handler/destroy))) + (.addShutdownHook (Runtime/getRuntime) + (Thread. handler/destroy))) + (defn -main [& args] (cond @@ -57,4 +60,9 @@ (System/exit 0)) :else (start-app args))) - + +;; (mount/start) +;; (mount/stop) + + + diff --git a/env/dev/clj/youyesyet/dev_middleware.clj b/env/dev/clj/youyesyet/dev_middleware.clj index d64d5d5..23fd9db 100644 --- a/env/dev/clj/youyesyet/dev_middleware.clj +++ b/env/dev/clj/youyesyet/dev_middleware.clj @@ -1,10 +1,13 @@ (ns youyesyet.dev-middleware - (:require [ring.middleware.reload :refer [wrap-reload]] + (:require +;; [ring.middleware.reload :refer [wrap-reload]] ;; this fails with a self referential dependency, which I haven't tracked down. [selmer.middleware :refer [wrap-error-page]] - [prone.middleware :refer [wrap-exceptions]])) + [prone.middleware :refer [wrap-exceptions]] + )) (defn wrap-dev [handler] - (-> handler - wrap-reload - wrap-error-page - wrap-exceptions)) + (-> handler +;; wrap-reload + wrap-error-page + wrap-exceptions + )) diff --git a/env/dev/clj/youyesyet/env.clj b/env/dev/clj/youyesyet/env.clj index 87bfa55..ef35fd0 100644 --- a/env/dev/clj/youyesyet/env.clj +++ b/env/dev/clj/youyesyet/env.clj @@ -1,7 +1,8 @@ (ns youyesyet.env (:require [selmer.parser :as parser] [clojure.tools.logging :as log] - [youyesyet.dev-middleware :refer [wrap-dev]])) + [youyesyet.dev-middleware :refer [wrap-dev]] + )) (def defaults {:init @@ -11,4 +12,5 @@ :stop (fn [] (log/info "\n-=[youyesyet has shut down successfully]=-")) - :middleware wrap-dev}) + :middleware wrap-dev + }) diff --git a/env/dev/resources/config.edn b/env/dev/resources/config.edn index 6f14c22..0a5f540 100644 --- a/env/dev/resources/config.edn +++ b/env/dev/resources/config.edn @@ -1,4 +1,7 @@ {:dev true :port 3000 ;; when :nrepl-port is set the application starts the nREPL server on load - :nrepl-port 7000} + :nrepl-port 7000 + :site-title "Project Hope" + :site-logo "img/ProjectHopeLogo.png" + :motd "motd.md"} diff --git a/env/prod/resources/config.edn b/env/prod/resources/config.edn index b48cfbd..eeaa8d9 100644 --- a/env/prod/resources/config.edn +++ b/env/prod/resources/config.edn @@ -1,2 +1,4 @@ {:production true - :port 3000} + :port 3000 + :site-title "Project Hope" + :site-logo "img/ProjectHopeLogo.png"} diff --git a/followup.cljs b/followup.cljs deleted file mode 100644 index 836d622..0000000 --- a/followup.cljs +++ /dev/null @@ -1,72 +0,0 @@ -(ns youyesyet.views.followup - (:require [reagent.core :as r] - [re-frame.core :refer [reg-sub subscribe]] -;; [re-frame-forms.core :as form] -;; [re-frame-forms.input :as input] -;; [re-com.core :refer [h-box v-box box gap single-dropdown input-text checkbox label title hyperlink-href p]] -;; [re-com.dropdown :refer [filter-choices-by-keyword single-dropdown-args-desc]] - [youyesyet.ui-utils :as ui] -)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; -;;;; youyesyet.views.followup: followup-request view for youyesyet. -;;;; -;;;; This program is free software; you can redistribute it and/or -;;;; modify it under the terms of the GNU General Public License -;;;; as published by the Free Software Foundation; either version 2 -;;;; of the License, or (at your option) any later version. -;;;; -;;;; This program is distributed in the hope that it will be useful, -;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;;;; GNU General Public License for more details. -;;;; -;;;; You should have received a copy of the GNU General Public License -;;;; along with this program; if not, write to the Free Software -;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -;;;; USA. -;;;; -;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign -;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;; The pattern from the re-com demo (https://github.com/Day8/re-com) is to have -;;; one source file/namespace per view. Each namespace contains a function 'panel' -;;; whose output is an enlive-style specification of the view to be redered. -;;; I propose to follow this pattern. This file will (eventually) provide the followup-request view. - -;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#followup-request-form - -(defn panel - "Generate the followup-request panel." - [] - (js/console.log (str "Rendering follow-up form")) - - (let [issue @(subscribe [:issue]) - issues @(subscribe [:issues]) - elector @(subscribe [:elector]) - address @(subscribe [:address]) - form (form/make-form {:elector (:id elector) - :issue (:id issue)})] - [:div - [:h1 "Followup Request"] - (let [selected-elector-id (r/atom (:id elector)) - selected-issue (r/atom (:id issue))] - [:form {} - [:p.widget - [:label {:for "elector"} "Elector"] - [single-dropdown - :id elector - :choices (:electors address) - :model selected-elector-id - :label-fn #(:name %)]] - [:p.widget - [:label {:for "issue"} "Issue"] - [single-dropdown - :id issue - :choices (map #({:id % :label %}) (keys issues)) - :model issue]] - - ])])) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..14992a1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,200 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "enhanced-resolve": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz", + "integrity": "sha1-oRXDJQS2MC6Fp2Jp16V8zdli41k=", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.3.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.3" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "memory-fs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", + "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "tapable": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", + "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" + }, + "tern": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/tern/-/tern-0.21.0.tgz", + "integrity": "sha1-gJyHqCbhEklDmM+IlPfC0bNGTrc=", + "requires": { + "acorn": "^4.0.9", + "enhanced-resolve": "^2.2.2", + "glob": "^7.1.1", + "minimatch": "^3.0.3", + "resolve-from": "2.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/project.clj b/project.clj index dd974c5..b95d74c 100644 --- a/project.clj +++ b/project.clj @@ -1,16 +1,19 @@ -(defproject youyesyet "0.2.0" +(defproject youyesyet "0.1.0" :description "Canvassing tool for referenda" :url "https://github.com/simon-brooke/youyesyet" - :dependencies [[bouncer "1.0.1"] + :dependencies [[adl-support "0.1.0-SNAPSHOT"] + [bouncer "1.0.1"] [ch.qos.logback/logback-classic "1.2.2"] [clj-oauth "1.5.5"] [cljsjs/react-leaflet "0.12.3-4"] - [cljs-ajax "0.5.8"] + [cljs-ajax "0.7.3"] + [com.cemerick/url "0.1.1"] [compojure "1.5.2"] [conman "0.6.3"] [cprop "0.1.10"] + [day8.re-frame/http-fx "0.1.6"] [korma "0.4.3"] [lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]] [luminus/ring-ttl-session "0.3.1"] @@ -23,13 +26,14 @@ [mount "0.1.11"] [org.clojure/clojure "1.8.0"] [org.clojure/clojurescript "1.9.229" :scope "provided"] + [org.clojure/core.memoize "0.7.1"] [org.clojure/tools.cli "0.3.5"] [org.clojure/tools.logging "0.3.1"] [org.postgresql/postgresql "9.4.1212"] [org.webjars/bootstrap "4.0.0-alpha.6-1"] [org.webjars/font-awesome "4.7.0"] [org.webjars.bower/tether "1.4.0"] - [re-frame "0.9.2"] + [re-frame "0.10.5"] [reagent "0.6.1"] [reagent-utils "0.2.1"] [ring-middleware-format "0.7.2"] @@ -49,16 +53,15 @@ :main ^:skip-aot youyesyet.core :migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")} - :plugins [[lein-cprop "1.0.1"] - [migratus-lein "0.4.2"] - [org.clojars.punkisdead/lein-cucumber "1.0.5"] - [lein-cljsbuild "1.1.4"] - [lein-uberwar "0.2.0"] - [lein-bower "0.5.1"] + :plugins [[lein-cljsbuild "1.1.4"] + [lein-codox "0.10.3"] + [lein-cprop "1.0.1"] [lein-less "1.7.5"] - [lein-codox "0.10.3"]] + [lein-npm "0.6.2"] + [lein-uberwar "0.2.0"] + [migratus-lein "0.4.2"] + [org.clojars.punkisdead/lein-cucumber "1.0.5"]] - :bower-dependencies [[leaflet "0.7.3"]] :cucumber-feature-paths ["test/clj/features"] @@ -66,6 +69,15 @@ :languages [:clojure :clojurescript] :source-paths ["src/clj" "src/cljc" "src/cljs"]} + :npm {:dependencies [[datatables.net "1.10.19"] + [datatables.net-dt "1.10.19"] + [jquery "3.3.1"] + [leaflet "0.7.3"] ;; old version works, new ["1.3.1"] doesn't + [selectize "0.12.5"] + [signature_pad "2.3.2"] + [simplemde "1.11.2"]] + :root "resources/public/js/lib"} + :uberwar {:handler youyesyet.handler/app :init youyesyet.handler/init @@ -73,7 +85,8 @@ :name "youyesyet.war"} :clean-targets ^{:protect false} - [:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]] + [:target-path [:cljsbuild :builds :app :compiler :output-dir] + [:cljsbuild :builds :app :compiler :output-to]] :figwheel {:http-server-root "public" @@ -113,10 +126,10 @@ [com.cemerick/piggieback "0.2.2-SNAPSHOT"] [directory-naming/naming-java "0.8"] [doo "0.1.7"] - [figwheel-sidecar "0.5.9"]] + [figwheel-sidecar "0.5.15"]] :plugins [[com.jakemccrary/lein-test-refresh "0.18.1"] [lein-doo "0.1.7"] - [lein-figwheel "0.5.9"] + [lein-figwheel "0.5.15"] [org.clojure/clojurescript "1.9.495"]] :cljsbuild {:builds diff --git a/resources/migrations/20161014170335-basic-setup.up.sql b/resources/migrations/20161014170335-basic-setup.up.sql deleted file mode 100644 index b7b3277..0000000 --- a/resources/migrations/20161014170335-basic-setup.up.sql +++ /dev/null @@ -1,669 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20161014170335-basic-setup.up.sql: database schema for youyesyet. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2016 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -SET statement_timeout = 0; ---;; -SET lock_timeout = 0; ---;; -SET client_encoding = 'UTF8'; ---;; -SET standard_conforming_strings = on; ---;; -SET check_function_bodies = false; ---;; -SET client_min_messages = warning; ---;; --- --- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: --- - -CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; ---;; - --- --- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; ---;; - -SET search_path = public, pg_catalog; ---;; -SET default_tablespace = ''; ---;; -SET default_with_oids = false; ---;; --- --- Name: addresses; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS addresses ( - id integer NOT NULL, - address character varying(256) NOT NULL, - postcode character varying(16), - phone character varying(16), - district_id integer, - latitude real, - longitude real -); ---;; - -ALTER TABLE public.addresses OWNER TO youyesyet; ---;; --- --- Name: addresses_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE addresses_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - -ALTER TABLE public.addresses_id_seq OWNER TO youyesyet; ---;; --- --- Name: addresses_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE addresses_id_seq OWNED BY addresses.id; ---;; - --- --- Name: authorities; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS authorities ( - id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.authorities OWNER TO youyesyet; ---;; --- --- Name: canvassers; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS canvassers ( - id serial, - username character varying(32) NOT NULL, - fullname character varying(64) NOT NULL, - elector_id integer, - address_id integer NOT NULL, - phone character varying(16), - email character varying(128), - authority_id character varying(32) NOT NULL, - introduced_by int references canvassers(id), - authorised boolean -); ---;; - -ALTER TABLE public.canvassers OWNER TO youyesyet; ---;; --- --- Name: districts; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS districts ( - id integer NOT NULL, - name character varying(64) NOT NULL -); ---;; - -ALTER TABLE public.districts OWNER TO youyesyet; ---;; --- --- Name: electors; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS electors ( - id integer NOT NULL, - name character varying(64) NOT NULL, - address_id integer NOT NULL, - phone character varying(16), - email character varying(128) -); ---;; - -ALTER TABLE public.electors OWNER TO youyesyet; ---;; --- --- Name: followupactions; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS followupactions ( - id integer NOT NULL, - request_id integer NOT NULL, - actor integer NOT NULL, - date timestamp with time zone DEFAULT now() NOT NULL, - notes text, - closed boolean -); ---;; - -ALTER TABLE public.followupactions OWNER TO youyesyet; - --- --- Name: followupactions_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE followupactions_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - -ALTER TABLE public.followupactions_id_seq OWNER TO youyesyet; ---;; --- --- Name: followupactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE followupactions_id_seq OWNED BY followupactions.id; ---;; - --- --- Name: followupmethods; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS followupmethods ( - id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.followupmethods OWNER TO youyesyet; ---;; --- --- Name: followuprequests; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -insert into followupmethods values ('Telephone'); ---;; -insert into followupmethods values ('eMail'); ---;; -insert into followupmethods values ('Post'); ---;; - - -CREATE TABLE IF NOT EXISTS followuprequests ( - id integer NOT NULL, - elector_id integer NOT NULL, - visit_id integer NOT NULL, - issue_id character varying(32) NOT NULL, - method_id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.followuprequests OWNER TO youyesyet; ---;; - --- --- Name: followuprequests_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE followuprequests_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - - -ALTER TABLE public.followuprequests_id_seq OWNER TO youyesyet; ---;; - --- --- Name: followuprequests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE followuprequests_id_seq OWNED BY followuprequests.id; ---;; - - --- --- Name: issueexpertise; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS issueexpertise ( - canvasser_id integer NOT NULL, - issue_id character varying(32) NOT NULL, - method_id character varying(32) NOT NULL -); ---;; - - -ALTER TABLE public.issueexpertise OWNER TO youyesyet; ---;; - --- --- Name: issues; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS issues ( - id character varying(32) NOT NULL, - url character varying(256) -); ---;; - - -ALTER TABLE public.issues OWNER TO youyesyet; ---;; - --- --- Name: options; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS options ( - id character varying(32) NOT NULL -); ---;; - - -ALTER TABLE public.options OWNER TO youyesyet; ---;; - --- --- Name: schema_migrations; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS schema_migrations ( - id bigint NOT NULL -); ---;; - - -ALTER TABLE public.schema_migrations OWNER TO youyesyet; ---;; - --- --- Name: visits; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS visits ( - id integer NOT NULL, - address_id integer NOT NULL, - canvasser_id integer NOT NULL, - date timestamp with time zone DEFAULT now() NOT NULL -); ---;; - - -ALTER TABLE public.visits OWNER TO youyesyet; ---;; - --- --- Name: visits_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE visits_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - - -ALTER TABLE public.visits_id_seq OWNER TO youyesyet; ---;; - --- --- Name: visits_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE visits_id_seq OWNED BY visits.id; ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY addresses ALTER COLUMN id SET DEFAULT nextval('addresses_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions ALTER COLUMN id SET DEFAULT nextval('followupactions_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests ALTER COLUMN id SET DEFAULT nextval('followuprequests_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits ALTER COLUMN id SET DEFAULT nextval('visits_id_seq'::regclass); ---;; - - --- --- Name: addresses_address_key; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_address_key UNIQUE (address); ---;; - - --- --- Name: addresses_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_pkey PRIMARY KEY (id); ---;; - - --- --- Name: authorities_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY authorities - ADD CONSTRAINT authorities_pkey PRIMARY KEY (id); ---;; - - --- --- Name: canvassers_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_pkey PRIMARY KEY (id); ---;; - - --- --- Name: districts_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY districts - ADD CONSTRAINT districts_pkey PRIMARY KEY (id); ---;; - - --- --- Name: electors_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY electors - ADD CONSTRAINT electors_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followupactions_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followupmethods_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followupmethods - ADD CONSTRAINT followupmethods_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followuprequests_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_pkey PRIMARY KEY (id); ---;; - - --- --- Name: issues_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY issues - ADD CONSTRAINT issues_pkey PRIMARY KEY (id); ---;; - - --- --- Name: options_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY options - ADD CONSTRAINT options_pkey PRIMARY KEY (id); ---;; - - --- --- Name: schema_migrations_id_key; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - ---ALTER TABLE ONLY schema_migrations --- ADD CONSTRAINT schema_migrations_id_key UNIQUE (id); ---;; - - --- --- Name: visits_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_pkey PRIMARY KEY (id); ---;; - - --- --- Name: addresses_district_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_district_id_fkey FOREIGN KEY (district_id) REFERENCES districts(id); ---;; - - --- --- Name: canvassers_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: canvassers_authority_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_authority_id_fkey FOREIGN KEY (authority_id) REFERENCES authorities(id); ---;; - - --- --- Name: canvassers_elector_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_elector_id_fkey FOREIGN KEY (elector_id) REFERENCES electors(id); ---;; - -create unique index canvassers_username_ix on canvassers (username); -create unique index canvassers_email_ix on canvassers(email); - --- --- Name: electors_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY electors - ADD CONSTRAINT electors_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: followupactions_actor_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_actor_fkey FOREIGN KEY (actor) REFERENCES canvassers(id); ---;; - - --- --- Name: followupactions_request_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_request_id_fkey FOREIGN KEY (request_id) REFERENCES followuprequests(id); ---;; - - --- --- Name: followuprequests_elector_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_elector_id_fkey FOREIGN KEY (elector_id) REFERENCES electors(id); ---;; - - --- --- Name: followuprequests_issue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id); ---;; - - --- --- Name: followuprequests_method_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_method_id_fkey FOREIGN KEY (method_id) REFERENCES followupmethods(id); ---;; - - --- --- Name: followuprequests_visit_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_visit_id_fkey FOREIGN KEY (visit_id) REFERENCES visits(id); ---;; - - --- --- Name: issueexpertise_canvasser_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_canvasser_id_fkey FOREIGN KEY (canvasser_id) REFERENCES canvassers(id); ---;; - - --- --- Name: issueexpertise_issue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id); ---;; - - --- --- Name: issueexpertise_method_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_method_id_fkey FOREIGN KEY (method_id) REFERENCES followupmethods(id); ---;; - - --- --- Name: visits_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: visits_canvasser_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_canvasser_id_fkey FOREIGN KEY (canvasser_id) REFERENCES canvassers(id); ---;; - - --- --- Name: public; Type: ACL; Schema: -; Owner: postgres --- - -REVOKE ALL ON SCHEMA public FROM PUBLIC; ---;; - -REVOKE ALL ON SCHEMA public FROM postgres; ---;; - -GRANT ALL ON SCHEMA public TO postgres; ---;; - -GRANT ALL ON SCHEMA public TO PUBLIC; ---;; - - --- --- PostgreSQL database dump complete --- - diff --git a/resources/migrations/20170315190500-roles-and-teams.down.sql b/resources/migrations/20170315190500-roles-and-teams.down.sql deleted file mode 100644 index a8bdb6a..0000000 --- a/resources/migrations/20170315190500-roles-and-teams.down.sql +++ /dev/null @@ -1,13 +0,0 @@ -drop table teammemberships; - -drop table teamorganiserships; - -drop index ix_teams_name; - -drop table teams; - -drop table rolememberships; - -drop index ix_roles_name; - -drop table roles; diff --git a/resources/migrations/20170315190500-roles-and-teams.up.sql b/resources/migrations/20170315190500-roles-and-teams.up.sql deleted file mode 100644 index 7ff7ffc..0000000 --- a/resources/migrations/20170315190500-roles-and-teams.up.sql +++ /dev/null @@ -1,39 +0,0 @@ -create table if not exists roles ( - id serial primary key, - name varchar(64) not null -); - -create unique index ix_roles_name on roles(name); - -create table if not exists rolememberships ( - role_id integer not null references roles(id), - canvasser_id integer not null references canvassers(id) -); - -create table if not exists teams ( - id serial primary key, - name varchar(64) not null, - district_id integer not null references districts(id), - latitude real, - longitude real -); - -create unique index ix_teams_name on teams(name); - -create table if not exists teammemberships ( - team_id integer not null references teams(id), - canvasser_id integer not null references canvassers(id) -); - -create table if not exists teamorganiserships ( - team_id integer not null references teams(id), - canvasser_id integer not null references canvassers(id) -); - -alter table roles owner to youyesyet; - -alter table rolememberships owner to youyesyet; - -alter table teams owner to youyesyet; - -alter table teammemberships owner to youyesyet; diff --git a/resources/migrations/20170401115900-basic-reference-data.down.sql b/resources/migrations/20170401115900-basic-reference-data.down.sql deleted file mode 100644 index a0b3aa1..0000000 --- a/resources/migrations/20170401115900-basic-reference-data.down.sql +++ /dev/null @@ -1,17 +0,0 @@ --- this is just a teardown of everything set up in the corresponding .up.sql file - -delete from roles where name = 'Expert'; -delete from roles where name = 'Administrator'; -delete from roles where name = 'Recruiter'; -delete from roles where name = 'Organiser'; -delete from roles where name = 'Editor'; - -alter table issues drop column content; -alter table issues drop column current; - -delete from issues where id = 'Currency'; -delete from issues where id = 'Monarchy'; -delete from issues where id = 'Defence'; - -delete from options where id = 'Yes'; -delete from options where id = 'No'; diff --git a/resources/migrations/20170401115900-basic-reference-data.up.sql b/resources/migrations/20170401115900-basic-reference-data.up.sql deleted file mode 100644 index 26d3bf1..0000000 --- a/resources/migrations/20170401115900-basic-reference-data.up.sql +++ /dev/null @@ -1,58 +0,0 @@ - --- We don't explicitly instantiate the 'Canvasser' role since every user is --- deemed to be a canvasser. - - --- an 'Expert' is someone with expertise in one or more issues, who is --- trusted to discuss those issues in detail with electors. -insert into roles (name) values ('Expert'); - - --- an 'Administrator' is someone entitled to broadly alter reference data --- throughout the system. -insert into roles (name) values ('Administrator'); - - --- a 'Recruiter' is someone entitled to invite other people to become users --- ('Canvassers'). A Recruiter is entitled to lock the account of anyone they --- have recruited, recursively. -insert into roles (name) values ('Recruiter'); - - --- an 'Organiser' is someone who organises one or more local teams. An Organiser --- is entitled to exclude any Canvasser from any team they organise. -insert into roles (name) values ('Organiser'); - - --- an 'Editor' is someone entitled to add and edit issues. -insert into roles (name) values ('Editor'); - --- issue text is local; there may still in addition be a further link to more --- information, but the basic issue text should be part of the issue record. --- The text should fit on a phone screen without scrolling, so is reasonably --- short. -alter table issues add column content varchar(1024); - --- an issue may be current or not current; when not current it is not deleted --- from the system but kept because it may become current again later. Only --- current issues are shown in the app. Typically not fewer than three and not --- more than about seven issues should be current at any time. -alter table issues add column current boolean default false; - -insert into issues (id, content, current) values ('Currency', - 'Scotland could keep the Pound, or use the Euro. But we could also set up a new currency of our own.', - true); - -insert into issues (id, content, current) values ('Monarchy', - 'Scotland could keep the Queen. This is an issue to be decided after independence.', - true); - -insert into issues (id, content, current) values ('Defence', - 'Scotland will not have nuclear weapons, and will probably not choose to engage in far-off wars. But we could remain members of NATO.', - true); - - -insert into options (id) values ('Yes'); - -insert into options (id) values ('No'); - diff --git a/resources/migrations/20170415102900-core-views.down.sql b/resources/migrations/20170415102900-core-views.down.sql deleted file mode 100644 index c576947..0000000 --- a/resources/migrations/20170415102900-core-views.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -drop view if exists roles_by_canvasser; - -drop view if exists teams_by_canvasser; - -drop view if exists canvassers_by_team; - -drop view if exists canvassers_by_introducer; - -drop view if exists teams_by_organiser; - -drop view if exists organisers_by_team; diff --git a/resources/migrations/20170415102900-core-views.up.sql b/resources/migrations/20170415102900-core-views.up.sql deleted file mode 100644 index 53346a6..0000000 --- a/resources/migrations/20170415102900-core-views.up.sql +++ /dev/null @@ -1,59 +0,0 @@ - -create view roles_by_canvasser as - select canvassers.id as canvasser, roles.name - from roles, rolememberships, canvassers - where roles.id = rolememberships.role_id - and canvassers.id = rolememberships.canvasser_id - and canvassers.authorised = true; - -create view teams_by_canvasser as - select canvassers.id as canvasser, teams.id, teams.name, teams.latitude, teams.longitude - from teams, teammemberships, canvassers - where teams.id = teammemberships.team_id - and canvassers.id = teammemberships.canvasser_id; - -create view canvassers_by_team as - select teams.id as team, - canvassers.id, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone - from teams, teammemberships, canvassers - where teams.id = teammemberships.team_id - and canvassers.id = teammemberships.canvasser_id - and canvassers.authorised = true; - -create view canvassers_by_introducer as - select introducers.id as introducer, - canvassers.id as canvasser, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone, - canvassers.authorised - from canvassers, canvassers as introducers - where introducers.id = canvassers.introduced_by; - -create view teams_by_organiser as - select canvassers.id as organiser, - teams.id, - teams.name, - teams.latitude, - teams.longitude - from teams, teamorganiserships, canvassers - where teams.id = teamorganiserships.team_id - and canvassers.id = teamorganiserships.canvasser_id - and canvassers.authorised = true; - -create view organisers_by_team as - select teams.id as team, - canvassers.id, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone - from teams, teamorganiserships, canvassers - where teams.id = teamorganiserships.team_id - and canvassers.id = teamorganiserships.canvasser_id - and canvassers.authorised = true; diff --git a/resources/migrations/20170721084900-dwellings.down.sql b/resources/migrations/20170721084900-dwellings.down.sql deleted file mode 100644 index 674fb1b..0000000 --- a/resources/migrations/20170721084900-dwellings.down.sql +++ /dev/null @@ -1,69 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20170721084900.up.sql: add dwellings table, to deal with flatted addresses. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2017 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -alter table canvassers add column address_id integer references addresses(id); ---;; -alter table electors add column address_id integer references addresses(id); ---;; -alter table visits add column address_id integer references addresses(id); ---;; - -update canvassers set address_id = - (select address_id from dwellings where id = canvassers.dwelling_id); ---;; - -update electors set address_id = - (select address_id from dwellings where id = electors.dwelling_id); ---;; - -update visits set address_id = - (select address_id from dwellings where id = visits.dwelling_id); ---;; - -alter table canvassers alter column address_id set not null; ---;; -alter table electors alter column address_id set not null; ---;; -alter table visits alter column address_id set not null; ---;; - -alter table canvassers drop column dwelling_id; ---;; -alter table electors drop column dwelling_id; ---;; -alter table visits drop column dwelling_id; ---;; - -drop table if exists dwellings; ---;; diff --git a/resources/migrations/20170721084900-dwellings.up.sql b/resources/migrations/20170721084900-dwellings.up.sql deleted file mode 100644 index 74e6a27..0000000 --- a/resources/migrations/20170721084900-dwellings.up.sql +++ /dev/null @@ -1,87 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20170721084900.up.sql: add dwellings table, to deal with flatted addresses. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2017 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -CREATE TABLE IF NOT EXISTS dwellings ( - id serial NOT NULL primary key, - address_id integer NOT NULL references addresses(id), - sub_address varchar(16) -); ---;; - -ALTER TABLE public.dwellings OWNER TO youyesyet; ---;; - -INSERT INTO dwellings (address_id, sub_address) - SELECT DISTINCT id, '' FROM addresses; ---;; - -alter table canvassers add column dwelling_id integer references dwellings(id); ---;; -alter table electors add column dwelling_id integer references dwellings(id); ---;; -alter table visits add column dwelling_id integer references dwellings(id); ---;; - -update canvassers set dwelling_id = - (select id from dwellings where address_id = canvassers.address_id); ---;; - -update electors set dwelling_id = - (select id from dwellings where address_id = electors.address_id); ---;; - -update visits set dwelling_id = - (select id from dwellings where address_id = visits.address_id); ---;; - -alter table canvassers alter column dwelling_id set not null; ---;; -alter table electors alter column dwelling_id set not null; ---;; -alter table visits alter column dwelling_id set not null; ---;; - -alter table canvassers drop constraint canvassers_address_id_fkey; ---;; -alter table electors drop constraint electors_address_id_fkey; ---;; -alter table visits drop constraint visits_address_id_fkey; ---;; - -alter table canvassers drop column address_id; ---;; -alter table electors drop column address_id; ---;; -alter table visits drop column address_id; ---;; diff --git a/resources/motd.md b/resources/motd.md new file mode 100644 index 0000000..3d6bed8 --- /dev/null +++ b/resources/motd.md @@ -0,0 +1,9 @@ +## Welcome to Project Hope + +### Alpha test code + +This is a voter intention information system intended to be used by the 'Yes' side in the next Scottish independence referendum. + +Design documentation is [here](https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md). + +Although addresses in the database mostly are real, all personal data in the database is randomly generated and does not represent real people. diff --git a/resources/public/css/yyy-app.css b/resources/public/css/yyy-app.css index d7ee8fa..0277205 100644 --- a/resources/public/css/yyy-app.css +++ b/resources/public/css/yyy-app.css @@ -28,6 +28,12 @@ h1 { margin-top: 0; } +#signature-pad { + width: 300px; + border: thin solid white; + min-height: 150px; +} + /* desktops and laptops, primarily. Adapted to mouse; targets may be small */ @media all and (min-device-width: 1025px) { diff --git a/resources/public/css/yyy-common.css b/resources/public/css/yyy-common.css index 04fb6a9..36c2cd7 100644 --- a/resources/public/css/yyy-common.css +++ b/resources/public/css/yyy-common.css @@ -42,7 +42,7 @@ del { } div.content, form, p, pre, h1, h2, h3, h4, h5 { - padding: 0.25em 5%; + padding: 0.25em 2.5%; } dl, menu, ol, table, ul { @@ -78,6 +78,7 @@ header { margin-top: 0; width:100%; max-width: 100%; + min-height: 96px; /* yes, we don't approve of pixel values; but this is to ensure the logo fits. */ background-color: rgb(7, 57, 106); color: white; } @@ -103,7 +104,7 @@ input, select { padding: 0.25em 1.25em; } -input.action { +input.action, input.action-safe { color: white; background-color: rgb( 50, 109, 177); font-size: 125%; @@ -112,6 +113,7 @@ input.action { input.action-dangerous { color: white; background-color: red; + font-size: 125%; } input.required:after { @@ -163,7 +165,7 @@ th { #main-container{ } -#back-link, .back-link { +.back-link { min-width: 8em; padding: 0.25em 1em; background-color: gray; @@ -173,26 +175,26 @@ th { border-bottom-right-radius: 0.5em; } -#back-link:hover, #back-link:active, .back-link:hover, .back-link:active, { +.back-link:hover, .back-link:active, { text-decoration: none; background-color: rgb(160, 160, 160); } -#back-link:hover::before, #back-link:active::before { +.back-link:hover::before, .back-link:active::before { content: "< "; } -#back-link-container { +.back-link-container { float: left; text-align: left; } -#back-link-container, .big-link-container { +.back-link-container, .big-link-container { font-size: 200%; padding: 0.5em 0; } -#back-link-container > #back-link:hover::before, #back-link-container > #back-link:active::before { +.back-link-container > .back-link:hover::before, .back-link-container > .back-link:active::before { } @@ -238,6 +240,11 @@ th { border-bottom: thin solid white; } +#site-logo { + padding: 0.5em; + float: left; +} + /* but magically appears on mouseover */ #cookies:hover #more-about-cookies { display: block; @@ -248,6 +255,10 @@ th { border: thin solid silver; } +.editor-toolbar { + background-color: silver; +} + .error { width: 100%; background-color: red; @@ -335,7 +346,7 @@ th { /* desktops and laptops, primarily. Adapted to mouse; targets may be small */ @media all and (min-device-width: 1025px) { #content { - width: 80%; + width: 90%; float: right; padding-bottom: 5em; } diff --git a/resources/public/img/ProjectHopeLogo.png b/resources/public/img/ProjectHopeLogo.png new file mode 100644 index 0000000..2c2b00f Binary files /dev/null and b/resources/public/img/ProjectHopeLogo.png differ diff --git a/resources/public/img/ProjectHopeLogo4.png b/resources/public/img/ProjectHopeLogo4.png new file mode 100644 index 0000000..7b82d8b Binary files /dev/null and b/resources/public/img/ProjectHopeLogo4.png differ diff --git a/resources/public/img/authorities/GitHub.png b/resources/public/img/authorities/GitHub.png new file mode 100644 index 0000000..f69f657 Binary files /dev/null and b/resources/public/img/authorities/GitHub.png differ diff --git a/resources/public/img/authorities/GitHub.xcf b/resources/public/img/authorities/GitHub.xcf new file mode 100644 index 0000000..936861d Binary files /dev/null and b/resources/public/img/authorities/GitHub.xcf differ diff --git a/resources/public/img/authorities/Twitter.jpg b/resources/public/img/authorities/Twitter.jpg new file mode 100644 index 0000000..93e24cc Binary files /dev/null and b/resources/public/img/authorities/Twitter.jpg differ diff --git a/resources/public/img/authorities/Twitter.png b/resources/public/img/authorities/Twitter.png new file mode 100644 index 0000000..ee1a7a1 Binary files /dev/null and b/resources/public/img/authorities/Twitter.png differ diff --git a/resources/public/img/authorities/Twitter.xcf b/resources/public/img/authorities/Twitter.xcf new file mode 100644 index 0000000..cad1df4 Binary files /dev/null and b/resources/public/img/authorities/Twitter.xcf differ diff --git a/resources/public/img/gender/female.png b/resources/public/img/gender/Female.png similarity index 100% rename from resources/public/img/gender/female.png rename to resources/public/img/gender/Female.png diff --git a/resources/public/img/gender/male.png b/resources/public/img/gender/Male.png similarity index 100% rename from resources/public/img/gender/male.png rename to resources/public/img/gender/Male.png diff --git a/resources/public/img/gender/fluid.png b/resources/public/img/gender/Non-binary.png similarity index 100% rename from resources/public/img/gender/fluid.png rename to resources/public/img/gender/Non-binary.png diff --git a/resources/public/img/gender/unknown.png b/resources/public/img/gender/Unknown.png similarity index 100% rename from resources/public/img/gender/unknown.png rename to resources/public/img/gender/Unknown.png diff --git a/resources/public/img/option/no-selected.png b/resources/public/img/option/No-selected.png similarity index 100% rename from resources/public/img/option/no-selected.png rename to resources/public/img/option/No-selected.png diff --git a/resources/public/img/option/no-unselected.png b/resources/public/img/option/No-unselected.png similarity index 100% rename from resources/public/img/option/no-unselected.png rename to resources/public/img/option/No-unselected.png diff --git a/resources/public/img/option/yes-selected.png b/resources/public/img/option/Yes-selected.png similarity index 100% rename from resources/public/img/option/yes-selected.png rename to resources/public/img/option/Yes-selected.png diff --git a/resources/public/img/option/yes-unselected.png b/resources/public/img/option/Yes-unselected.png similarity index 100% rename from resources/public/img/option/yes-unselected.png rename to resources/public/img/option/Yes-unselected.png diff --git a/resources/migrations/20161014170335-basic-setup.down.sql b/resources/sql/locality-trigger.sql similarity index 60% rename from resources/migrations/20161014170335-basic-setup.down.sql rename to resources/sql/locality-trigger.sql index 1005cee..00452b3 100644 --- a/resources/migrations/20161014170335-basic-setup.down.sql +++ b/resources/sql/locality-trigger.sql @@ -1,6 +1,6 @@ --------------------------------------------------------------------------------- +------------------------------------------------------------------------------; ---- ----- 20161014170335-basic-setup.down.sql: database schema for youyesyet. +---- locality-trigger.sql: compute localities for addresses ---- ---- This program is free software; you can redistribute it and/or ---- modify it under the terms of the GNU General Public License @@ -19,32 +19,17 @@ ---- ---- Copyright (C) 2016 Simon Brooke for Radical Independence Campaign ---- --------------------------------------------------------------------------------- +------------------------------------------------------------------------------; --- intended to reverse out the database changes made in --- 20161014170335-basic-setup.up.sql +---- See also: src/cljc/locality.cljc -drop table addresses cascade; ---;; -drop table authorities cascade; ---;; -drop table canvassers cascade; ---;; -drop table districts cascade; ---;; -drop table electors cascade; ---;; -drop table followupactions cascade; ---;; -drop table followupmethods cascade; ---;; -drop table followuprequests cascade; ---;; -drop table issueexpertise cascade; ---;; -drop table issues cascade; ---;; -drop table options cascade; ---;; -drop table visits cascade; ---;; +CREATE FUNCTION compute_locality() RETURNS trigger AS $compute_locality$ + BEGIN + NEW.locality = (1000 * floor(NEW.latitude * 100)) - + floor(NEW.longitude * 100); + RETURN NEW; + END; +$compute_locality$ LANGUAGE plpgsql; + +CREATE TRIGGER compute_locality BEFORE INSERT OR UPDATE ON addresses + FOR EACH ROW EXECUTE PROCEDURE compute_locality(); diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index e2170b8..a50d78f 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------; ---- ----- youyesyet.routes.authenticated: routes and pages for authenticated users. +---- queries.sql: manually maintained queries. ---- ---- This program is free software; you can redistribute it and/or ---- modify it under the terms of the GNU General Public License @@ -23,322 +23,40 @@ -- This file gets slurped in and converted into simple functions by the line -- in youyesyet.db.core.clj: --- (conman/bind-connection *db* "sql/queries.sql") +-- (conman/bind-connection *db* "sql/queries-auto.sql" "sql/queries.sql") -- the functions then appeare in the youyesyet.db.core namespace. - --- :name create-address! :! :n --- :doc creates a new address record -INSERT INTO addresses -(address, postcode, district_id, latitude, longitude) -VALUES (:address, :postcode, :district, :latitude, :longitude) -RETURNING id - --- :name update-address! :! :n --- :doc update an existing address record -UPDATE addresses -SET address = :address, postcode = :postcode, latitude = :latitude, longitude = :longitude -WHERE id = :id - --- :name get-address :? :1 --- :doc retrieve a address given the id. -SELECT * FROM addresses -WHERE id = :id - --- :name get-addresses-by-postcode - --- :name delete-address! :! :n --- :doc delete a address given the id -DELETE FROM addresses -WHERE id = :id - - --- :name create-authority! :! :n --- :doc creates a new authority record -INSERT INTO authorities -(id) -VALUES (:id) -RETURNING id - --- :name update-authority! :! :n --- :doc update an existing authority record -UPDATE authorities -SET id = :id -WHERE id = :id - --- :name get-authority :? :1 --- :doc retrieve a authority given the id. -SELECT * FROM authorities -WHERE id = :id - --- :name get-authorities :? :0 --- :doc retrieve all authorities -SELECT id FROM authorities - --- :name delete-authority! :! :n --- :doc delete a authority given the id -DELETE FROM authorities -WHERE id = :id - - --- :name create-canvasser! :! :n --- :doc creates a new canvasser record -INSERT INTO canvassers -(username, fullname, elector_id, dwelling_id, phone, email, authority_id, authorised) -VALUES (:username, :fullname, :elector_id, :dwelling_id, :phone, :email, :authority_id, :authorised) -RETURNING id - --- :name update-canvasser! :! :n --- :doc update an existing canvasser record -UPDATE canvassers -SET username = :username, fullname = :fullname, elector_id = :elector_id, dwelling_id = :dwelling_id, phone = :phone, email = :email, authority_id = :authority_id, authorised = :authorised -WHERE id = :id - --- :name get-canvasser :? :1 --- :doc retrieve a canvasser given the id. -SELECT * FROM canvassers -WHERE id = :id - --- :name get-canvasser-by-username :? :1 --- :doc rerieve a canvasser given the username. -SELECT * FROM canvassers -WHERE username = :username - --- :name get-canvasser-by-email :? :1 --- :doc rerieve a canvasser given the email address. -SELECT * FROM canvassers -WHERE email = :email - --- :name delete-canvasser! :! :n --- :doc delete a canvasser given the id -DELETE FROM canvassers -WHERE id = :id - - --- :name create-district! :! :n --- :doc creates a new district record -INSERT INTO districts -(id, name) -VALUES (:id, :name) -RETURNING id - --- :name update-district! :! :n --- :doc update an existing district record -UPDATE districts -SET name = :name -WHERE id = :id - --- :name get-district :? :1 --- :doc retrieve a district given the id. -SELECT * FROM districts -WHERE id = :id - --- :name delete-district! :! :n --- :doc delete a district given the id -DELETE FROM districts -WHERE id = :id - - --- :name get-dwelling :? :1 --- :doc retrieve a dwelling given the id. -SELECT * FROM dwellings -WHERE id = :id - --- :name delete-dwelling! :! :n --- :doc delete a dwelling given the id -DELETE FROM dwellings -WHERE id = :id - --- :name create-dwelling! :! :n --- :doc creates a new dwelling record -INSERT INTO dwellings -(id, address_id, sub_address) -VALUES (:id, :address_id, :sub_address) -RETURNING id - --- :name update-dwelling! :! :n --- :doc update an existing dwelling record -UPDATE dwellings -SET address_id = :address_id, - sub_address = :sub_address -WHERE id = :id - --- :name get-dwelling :? :1 --- :doc retrieve a dwelling given the id. -SELECT * FROM dwellings -WHERE id = :id - --- :name delete-dwelling! :! :n --- :doc delete a dwelling given the id -DELETE FROM dwellings -WHERE id = :id - - --- :name create-elector! :! :n --- :doc creates a new elector record -INSERT INTO electors -(name, dwelling_id, phone, email) -VALUES (:name, :dwelling_id, :phone, :email) -RETURNING id - --- :name update-elector! :! :n --- :doc update an existing elector record -UPDATE electors -SET name = :name, dwelling_id = :dwelling_id, phone = :phone, email = :email -WHERE id = :id - --- :name get-elector :? :1 --- :doc retrieve a elector given the id. -SELECT * FROM electors -WHERE id = :id - --- :name delete-elector! :! :n --- :doc delete a elector given the id -DELETE FROM electors -WHERE id = :id - - --- :name create-followupaction! :! :n --- :doc creates a new followupaction record -INSERT INTO followupactions -(request_id, actor, date, notes, closed) -VALUES (:request_id, :actor, :date, :notes, :closed) -RETURNING id - --- We don't update followup actions. They're permanent record. - --- :name get-followupaction :? :1 --- :doc retrieve a followupaction given the id. -SELECT * FROM followupactions -WHERE id = :id - --- We don't delete followup actions. They're permanent record. - - --- followup methods are reference data, do not need to be programmatically maintained. - - --- :name create-followuprequest! :! :n --- :doc creates a new followupaction record -INSERT INTO followuprequests -(elector_id, visit_id, issue_id, method_id) -VALUES (:elector_id, :visit_id, :issue_id, :method_id) -RETURNING id - --- We don't update followup requests. They're permanent record. - --- :name get-followuprequest :? :1 --- :doc retrieve a followupaction given the id. -SELECT * FROM followuprequests -WHERE id = :id - --- We don't delete followup requests. They're permanent record. - - --- :name create-issueexpertise! :! :n --- :doc creates a new issueexpertise record -INSERT INTO issueexpertise -(canvasser_id, issue_id, method_id) -VALUES (:canvasser_id, :issue_id, :method_id) --- issueexertise is a link table, doesn't have an id field. - --- :name update-issueexpertise! :! :n --- :doc update an existing issueexpertise record -UPDATE issueexpertise -SET canvasser_id = :canvasser_id, issue_id = :issue_id, method_id = :method_id -WHERE id = :id - --- :name get-issueexpertise :? :1 --- :doc retrieve a issueexpertise given the canvasser_id - --- getting it by its own id is unlikely to be interesting or useful. -SELECT * FROM issueexpertise -WHERE canvasser_id = :canvasser_id - --- :name delete-issueexpertise! :! :n --- :doc delete a issueexpertise given the id -DELETE FROM issueexpertise -WHERE id = :id - - --- :name create-issue! :! :n --- :doc creates a new issue record -INSERT INTO issues -(id, url, content, current) -VALUES (:id, :url, :content, :current) -RETURNING id - - --- :name update-issue! :! :n --- :doc update an existing issue record -UPDATE issues -SET url = :url, content = :content, current = :current -WHERE id = :id - --- :name get-issue :? :1 --- :doc retrieve a issue given the id - -SELECT * FROM issues -WHERE id = :id - --- :name delete-issue! :! :n --- :doc delete a issue given the id -DELETE FROM issues -WHERE id = :id - - --- options is virtually reference data; it's not urgent to create a programmatic means of editing - --- :name create-visit! :! :n --- :doc creates a new visit record -INSERT INTO visits -(dwelling_id, canvasser_id) -VALUES (:dwelling_id, :canvasser_id) -RETURNING id - --- visits is audit data; we don't update it. - --- :name get-visit :? :1 --- :doc retrieve a visit given the id. -SELECT * FROM visits -WHERE id = :id - --- visits is audit data; we don't delete it. - - --- views are select only - --- :name get-roles-by-canvasser :? :* --- :doc Get the role names for the canvasser with the specified id -select name from roles_by_canvasser - where canvasser = :canvasser - --- :name get-teams-by-canvasser :? :* --- :doc Get details of the teams which the canvasser with the specified id is member of. -select * from teams_by_canvasser - where canvasser = :canvasser_id - --- :name get-canvassers-by-team :? :* --- :doc Get details of all canvassers who are members of the team with the specified id -select * from canvassers_by_team - where team = :team_id - --- :name get-canvassers-by-team :? :* --- :doc Get details of all authorised canvassers who are members of this team. -select * from canvassers_by_introducer - where introducer = :introducer_id - --- :name get-canvassers-by-search :? :* --- :doc Get details of all authorised canvassers whose details match this search string. -select * from canvassers - where name like '%' || :search || '%' - or username like '%' || :search || '%' - or email like '%' || :search || '%' - --- :name get-teams_by_organiser :? :* --- :doc Get details of all the teams organised by the canvasser with the specified id -select * from teams_by_organiser - where organiser = :organiser_id - --- :name get-organisers-by-team :? :* --- :doc Get details of all organisers of the team with the specified id -select * from organisers_by_team - where team = :team_id +-- Note that queries generated by ADL are in the file +-- resources/sql/queries-auto.sql; they do not have to be (and should not be) +-- redefined here. + +-- :name list-addresses-by-locality :? :* +-- :doc lists all existing address records in a given locality +SELECT * +FROM addresses +WHERE locality = :locality + + +-- :name list-open-requests :? :* +-- :doc lists all existing followuprequest records which have not been closed and which the :expert has expertise to answer. +SELECT DISTINCT request.*, + electors.name ||', '|| electors.gender AS elector_id_expanded, + addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS visit_id_expanded, + request.issue_id as issue_id_expanded, + request.method_id AS method_id_expanded, + visits.date +FROM followuprequests as request, + ln_experts_issues_canvassers as expertise, + canvassers as experts, + electors, + addresses, + visits +where not exists (select * from followupactions as action + where action.request_id = request.id + and action.closed = true) +and request.elector_id = electors.id +and request.visit_id = visits.id +and visits.address_id = addresses.id +and request.issue_id = expertise.issue_id +and expertise.canvasser_id = :expert +ORDER BY visits.date desc diff --git a/resources/sql/test-canvassers.sql b/resources/sql/test-canvassers.sql new file mode 100644 index 0000000..0bfb6b8 --- /dev/null +++ b/resources/sql/test-canvassers.sql @@ -0,0 +1,65 @@ + +insert into canvassers (username, fullname, elector_id, address_id, authority_id) + values ('test_admin', 'Michael Thomson', 14, 4, 'Twitter'); + +insert into canvassers (username, fullname, elector_id, address_id, authority_id) + values ('test_analyst', 'Jack Lang', 13, 4, 'Twitter'); + +insert into canvassers (username, fullname, elector_id, address_id, authority_id) + values ('test_canvasser', 'Catriona Lang', 12, 4, 'Twitter'); + +insert into canvassers (username, fullname, elector_id, address_id, authority_id) + values ('test_editor', 'Ursula Lang', 11, 4, 'Twitter'); + +insert into canvassers (username, fullname, elector_id, address_id, authority_id) + values ('test_expert', 'Charlie Gourlay', 18, 5, 'Twitter'); + +insert into canvassers (username, fullname, elector_id, address_id, authority_id) + values ('test_organiser', 'Jude Morrison', 15, 5, 'Twitter'); + +insert into ln_members_roles_canvassers + values (1, (select id from canvassers where username='test_admin')); +insert into ln_members_roles_canvassers + values (2, (select id from canvassers where username='test_analyst')); +insert into ln_members_roles_canvassers + values (3, (select id from canvassers where username='test_editor')); +insert into ln_members_roles_canvassers + values (4, (select id from canvassers where username='test_organiser')); +insert into ln_members_roles_canvassers + values (5, (select id from canvassers where username='test_expert')); +insert into ln_members_roles_canvassers + values (6, (select id from canvassers where username='test_canvasser')); + +insert into teams (name, district_id, latitude, longitude) +values ('Yes Stewartry', 1, 54.94, -3.94); + +insert into ln_members_teams_canvassers +values ( + (select id from teams where name='Yes Stewartry'), + (select id from canvassers where username='test_admin')); + +insert into ln_members_teams_canvassers +values ( + (select id from teams where name='Yes Stewartry'), + (select id from canvassers where username='test_analyst')); + +insert into ln_members_teams_canvassers +values ( + (select id from teams where name='Yes Stewartry'), + (select id from canvassers where username='test_editor')); + +insert into ln_members_teams_canvassers +values ( + (select id from teams where name='Yes Stewartry'), + (select id from canvassers where username='test_organiser')); + +insert into ln_members_teams_canvassers +values ( + (select id from teams where name='Yes Stewartry'), + (select id from canvassers where username='test_expert')); + +insert into ln_members_teams_canvassers +values ( + (select id from teams where name='Yes Stewartry'), + (select id from canvassers where username='test_canvasser')); + diff --git a/resources/sql/youyesyet.postgres.sql b/resources/sql/youyesyet.postgres.sql new file mode 100644 index 0000000..e6e4bc4 --- /dev/null +++ b/resources/sql/youyesyet.postgres.sql @@ -0,0 +1,1133 @@ +------------------------------------------------------------------------ +-- Database definition for application +-- +-- youyesyet version 0.1.1 +-- +-- auto-generated by [Application Description Language framework] +-- +-- (https://github.com/simon-brooke/adl) at 20180718T215811.044Z +-- +-- A web-app intended to be used by canvassers +-- campaigning for a 'Yes' vote in the second independence +-- referendum. The web-app will be delivered to canvassers out +-- knocking doors primarily through an HTML5/React single-page app +-- designed to work on a mobile phone; it's possible that someone +-- else may do an Android of iPhone native app to address the same +-- back end but at present I have no plans for this. There must also +-- be an administrative interface through which privileged users can +-- set the system up and authorise canvassers, and a 'followup' +-- interface through which issue-expert specialist canvassers can +-- address particular electors' queries. +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +-- security group admin +------------------------------------------------------------------------ + +CREATE GROUP admin; + +------------------------------------------------------------------------ +-- security group analysts +------------------------------------------------------------------------ + +CREATE GROUP analysts; + +------------------------------------------------------------------------ +-- security group canvassers +------------------------------------------------------------------------ + +CREATE GROUP canvassers; + +------------------------------------------------------------------------ +-- security group issueeditors +------------------------------------------------------------------------ + +CREATE GROUP issueeditors; + +------------------------------------------------------------------------ +-- security group issueexperts +------------------------------------------------------------------------ + +CREATE GROUP issueexperts; + +------------------------------------------------------------------------ +-- security group public +------------------------------------------------------------------------ + +CREATE GROUP public; + +------------------------------------------------------------------------ +-- security group teamorganisers +------------------------------------------------------------------------ + +CREATE GROUP teamorganisers; + +------------------------------------------------------------------------ +-- primary table addresses for entity addresses +-- +-- Addresses of all buildings which contain +-- dwellings. +------------------------------------------------------------------------ +CREATE TABLE addresses +( + id SERIAL NOT NULL PRIMARY KEY, + address VARCHAR(256) NOT NULL, + postcode VARCHAR(16) CONSTRAINT pattern_14 CHECK (postcode ~* '^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$'), + phone VARCHAR(16), + district_id INTEGER, + latitude DOUBLE PRECISION, + longitude DOUBLE PRECISION, + locality INTEGER +); +GRANT SELECT ON addresses TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON addresses TO admin ; +GRANT UPDATE ON addresses TO admin ; +GRANT DELETE ON addresses TO admin ; + +------------------------------------------------------------------------ +-- primary table authorities for entity authorities +-- +-- Authorities which may authenticate canvassers to +-- the system. +------------------------------------------------------------------------ +CREATE TABLE authorities +( + id VARCHAR(32) NOT NULL PRIMARY KEY, + request_token_uri VARCHAR(256) NOT NULL, + access_token_uri VARCHAR(256) NOT NULL, + authorize_uri VARCHAR(256) NOT NULL, + consumer_key VARCHAR(32) DEFAULT 'youyesyet' NOT NULL, + consumer_secret VARCHAR(256) NOT NULL +); +GRANT SELECT ON authorities TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON authorities TO admin ; +GRANT UPDATE ON authorities TO admin ; +GRANT DELETE ON authorities TO admin ; + +------------------------------------------------------------------------ +-- primary table canvassers for entity canvassers +-- +-- Primary users of the system: those actually +-- interviewing electors. +------------------------------------------------------------------------ +CREATE TABLE canvassers +( + id SERIAL NOT NULL PRIMARY KEY, + username VARCHAR(32) NOT NULL, + fullname VARCHAR(64) NOT NULL, + avatar VARCHAR(), + bio TEXT, + elector_id INTEGER, + address_id INTEGER NOT NULL, + phone VARCHAR(16), + email VARCHAR(128), + authority_id VARCHAR(32) NOT NULL, + authorised BOOLEAN +); +GRANT SELECT ON canvassers TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON canvassers TO admin, + canvassers, + teamorganisers ; +GRANT UPDATE ON canvassers TO admin, + canvassers, + teamorganisers ; +GRANT DELETE ON canvassers TO admin ; + +------------------------------------------------------------------------ +-- primary table districts for entity districts +-- +-- Electoral districts: TODO: Shape (polygon) +-- information will need to be added, for use in +-- maps. +------------------------------------------------------------------------ +CREATE TABLE districts +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL +); +GRANT SELECT ON districts TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + public, + teamorganisers ; +GRANT INSERT ON districts TO admin ; +GRANT UPDATE ON districts TO admin ; +GRANT DELETE ON districts TO admin ; + +------------------------------------------------------------------------ +-- primary table dwellings for entity dwellings +-- +-- All dwellings within addresses in the system; a +-- dwelling is a house, flat or appartment in which electors live. +-- Every address should have at least one dwelling; essentially, +-- an address maps onto a street door and dwellings map onto +-- what's behind that door. So a tenement or a block of flats +-- would be one address with many dwellings. +------------------------------------------------------------------------ +CREATE TABLE dwellings +( + id SERIAL NOT NULL PRIMARY KEY, + address_id INTEGER NOT NULL, + sub_address VARCHAR(32) +); +GRANT SELECT ON dwellings TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON dwellings TO admin ; +GRANT UPDATE ON dwellings TO admin ; +GRANT DELETE ON dwellings TO admin ; + +------------------------------------------------------------------------ +-- primary table electors for entity electors +-- +-- All electors known to the system; electors are +-- people believed to be entitled to vote in the current +-- campaign. +------------------------------------------------------------------------ +CREATE TABLE electors +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL, + dwelling_id INTEGER NOT NULL, + phone VARCHAR(16), + email VARCHAR(128), + gender VARCHAR(32) DEFAULT 'Unknown', + signature TEXT +); +GRANT SELECT ON electors TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON electors TO admin ; +GRANT UPDATE ON electors TO admin ; +GRANT DELETE ON electors TO admin ; + +------------------------------------------------------------------------ +-- primary table events for entity events +-- +-- +-- An event to which a team or teams are invited. Typically created +-- by the team organiser(s). +-- May be a training event, a social event or a canvassing +-- session. +-- +------------------------------------------------------------------------ +CREATE TABLE events +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL, + date DATE, + time TIME, + decription TEXT, + cancelled BOOLEAN DEFAULT false +); +GRANT SELECT ON events TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON events TO admin, + teamorganisers ; +GRANT UPDATE ON events TO admin, + teamorganisers ; +GRANT DELETE ON events TO admin ; + +------------------------------------------------------------------------ +-- primary table followupactions for entity followupactions +-- +-- Actions taken on followup +-- requests. +------------------------------------------------------------------------ +CREATE TABLE followupactions +( + id SERIAL NOT NULL PRIMARY KEY, + request_id INTEGER NOT NULL, + actor INTEGER NOT NULL, + date TIMESTAMP DEFAULT 'now()' NOT NULL, + notes TEXT, + closed BOOLEAN DEFAULT false +); +GRANT SELECT ON followupactions TO admin, + analysts, + canvassers, + issueeditors, + issueexperts ; +GRANT INSERT ON followupactions TO admin, + issueexperts ; +GRANT UPDATE ON followupactions TO admin ; +GRANT DELETE ON followupactions TO admin ; + +------------------------------------------------------------------------ +-- primary table followupmethods for entity followupmethods +-- +-- Methods which may be used to follow up a followup request. Reference +-- data. +------------------------------------------------------------------------ +CREATE TABLE followupmethods +( + id VARCHAR(32) NOT NULL PRIMARY KEY +); +GRANT SELECT ON followupmethods TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON followupmethods TO admin ; +GRANT UPDATE ON followupmethods TO admin ; +GRANT DELETE ON followupmethods TO admin ; + +------------------------------------------------------------------------ +-- primary table followuprequests for entity followuprequests +-- +-- Requests for a followup with an issue +-- expert +------------------------------------------------------------------------ +CREATE TABLE followuprequests +( + id SERIAL NOT NULL PRIMARY KEY, + elector_id INTEGER NOT NULL, + visit_id INTEGER NOT NULL, + issue_id VARCHAR(32) NOT NULL, + method_id VARCHAR(32) NOT NULL +); +GRANT SELECT ON followuprequests TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON followuprequests TO admin, + canvassers ; + + + +------------------------------------------------------------------------ +-- primary table genders for entity genders +-- +-- All genders which may be assigned to +-- electors. +------------------------------------------------------------------------ +CREATE TABLE genders +( + id VARCHAR(32) NOT NULL PRIMARY KEY +); +GRANT SELECT ON genders TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON genders TO admin ; +GRANT UPDATE ON genders TO admin ; +GRANT DELETE ON genders TO admin ; + +------------------------------------------------------------------------ +-- primary table intentions for entity intentions +-- +-- Intentions of electors to vote for options +-- elicited in visits. +------------------------------------------------------------------------ +CREATE TABLE intentions +( + id SERIAL NOT NULL PRIMARY KEY, + visit_id INTEGER NOT NULL, + elector_id INTEGER, + option_id VARCHAR(32) NOT NULL, + locality INTEGER NOT NULL +); +GRANT SELECT ON intentions TO admin, + analysts, + canvassers ; +GRANT INSERT ON intentions TO admin, + canvassers ; + + + +------------------------------------------------------------------------ +-- primary table issues for entity issues +-- +-- Issues believed to be of interest to electors, +-- about which they may have questions. +------------------------------------------------------------------------ +CREATE TABLE issues +( + id VARCHAR(32) NOT NULL PRIMARY KEY, + url VARCHAR(256), + current BOOLEAN DEFAULT true, + brief TEXT +); +GRANT SELECT ON issues TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON issues TO admin, + issueeditors ; +GRANT UPDATE ON issues TO admin, + issueeditors ; +GRANT DELETE ON issues TO admin ; + +------------------------------------------------------------------------ +-- primary table options for entity options +-- +-- Options in the election or referendum being +-- canvassed on +------------------------------------------------------------------------ +CREATE TABLE options +( + id VARCHAR(32) NOT NULL PRIMARY KEY +); +GRANT SELECT ON options TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON options TO admin ; +GRANT UPDATE ON options TO admin ; +GRANT DELETE ON options TO admin ; + +------------------------------------------------------------------------ +-- primary table roles for entity roles +-- +-- A role (essentially, the same as a group, but +-- application layer rather than database layer) of which a user +-- may be a member. +------------------------------------------------------------------------ +CREATE TABLE roles +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL +); +GRANT SELECT ON roles TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON roles TO admin ; +GRANT UPDATE ON roles TO admin ; +GRANT DELETE ON roles TO admin ; + +------------------------------------------------------------------------ +-- primary table teams for entity teams +------------------------------------------------------------------------ +CREATE TABLE teams +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL, + district_id INTEGER NOT NULL, + latitude DOUBLE PRECISION, + longitude DOUBLE PRECISION +); +GRANT SELECT ON teams TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON teams TO admin, + teamorganisers ; +GRANT UPDATE ON teams TO admin, + teamorganisers ; +GRANT DELETE ON teams TO admin ; + +------------------------------------------------------------------------ +-- primary table visits for entity visits +-- +-- All visits made by canvassers to dwellings in +-- which opinions were recorded. +------------------------------------------------------------------------ +CREATE TABLE visits +( + id SERIAL NOT NULL PRIMARY KEY, + address_id INTEGER NOT NULL, + canvasser_id INTEGER NOT NULL, + date TIMESTAMP DEFAULT 'now()' NOT NULL +); +GRANT SELECT ON visits TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON visits TO admin, + canvassers, + teamorganisers ; +GRANT UPDATE ON visits TO admin ; +GRANT DELETE ON visits TO admin ; + +------------------------------------------------------------------------ +-- convenience view lv_addresses of entity addresses for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_addresses AS +SELECT addresses.address, + addresses.postcode, + addresses.phone, + districts.name AS district_id_expanded, + addresses.district_id, + addresses.latitude, + addresses.longitude, + addresses.locality, + addresses.id +FROM addresses, districts +WHERE addresses.district_id = districts.id +; +GRANT SELECT ON lv_addresses TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_authorities of entity authorities for lists, et +-- cetera +------------------------------------------------------------------------ +CREATE VIEW lv_authorities AS +SELECT authorities.request_token_uri, + authorities.access_token_uri, + authorities.authorize_uri, + authorities.consumer_key, + authorities.consumer_secret, + authorities.id +FROM authorities +; +GRANT SELECT ON lv_authorities TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_canvassers of entity canvassers for lists, et +-- cetera +------------------------------------------------------------------------ +CREATE VIEW lv_canvassers AS +SELECT canvassers.username, + canvassers.fullname, + canvassers.avatar, + canvassers.bio, + electors.name AS elector_id_expanded, + canvassers.elector_id, + addresses.address ||', '|| addresses.postcode AS address_id_expanded, + canvassers.address_id, + canvassers.phone, + canvassers.email, + authorities.id AS authority_id_expanded, + canvassers.authority_id, + canvassers.authorised, + canvassers.id +FROM canvassers, authorities, addresses, electors +WHERE canvassers.elector_id = electors.id + AND canvassers.address_id = addresses.id + AND canvassers.authority_id = authorities.id +; +GRANT SELECT ON lv_canvassers TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_districts of entity districts for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_districts AS +SELECT districts.name, + districts.id +FROM districts +; +GRANT SELECT ON lv_districts TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + public, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_dwellings of entity dwellings for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_dwellings AS +SELECT addresses.address ||', '|| addresses.postcode AS address_id_expanded, + dwellings.address_id, + dwellings.sub_address, + dwellings.id +FROM dwellings, addresses +WHERE dwellings.address_id = addresses.id +; +GRANT SELECT ON lv_dwellings TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_electors of entity electors for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_electors AS +SELECT electors.name, + addresses.address ||', '|| addresses.postcode ||', '|| dwellings.sub_address AS dwelling_id_expanded, + electors.dwelling_id, + electors.phone, + electors.email, + genders.id AS gender_expanded, + electors.gender, + electors.signature, + electors.id +FROM dwellings, addresses, genders, electors +WHERE electors.dwelling_id = dwellings.id + AND electors.gender = genders.id +; +GRANT SELECT ON lv_electors TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_events of entity events for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_events AS +SELECT events.name, + events.date, + events.time, + events.decription, + events.cancelled, + events.id +FROM events +; +GRANT SELECT ON lv_events TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_followupactions of entity followupactions for +-- lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_followupactions AS +SELECT electors.name ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| visits.date ||', '|| issues.id AS request_id_expanded, + followupactions.request_id, + canvassers.username ||', '|| canvassers.fullname ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| canvassers.phone ||', '|| canvassers.email AS actor_expanded, + followupactions.actor, + followupactions.date, + followupactions.notes, + followupactions.closed, + followupactions.id +FROM followuprequests, visits, canvassers, addresses, followupactions, issues, electors +WHERE followupactions.request_id = followuprequests.id + AND followupactions.actor = canvassers.id +; +GRANT SELECT ON lv_followupactions TO admin, + analysts, + canvassers, + issueeditors, + issueexperts ; + +------------------------------------------------------------------------ +-- convenience view lv_followupmethods of entity followupmethods for +-- lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_followupmethods AS +SELECT followupmethods.id +FROM followupmethods +; +GRANT SELECT ON lv_followupmethods TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_followuprequests of entity followuprequests for +-- lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_followuprequests AS +SELECT electors.name AS elector_id_expanded, + followuprequests.elector_id, + addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS visit_id_expanded, + followuprequests.visit_id, + issues.id AS issue_id_expanded, + followuprequests.issue_id, + followupmethods.id AS method_id_expanded, + followuprequests.method_id, + followuprequests.id +FROM followuprequests, visits, addresses, issues, electors, followupmethods +WHERE followuprequests.elector_id = electors.id + AND followuprequests.visit_id = visits.id + AND followuprequests.issue_id = issues.id + AND followuprequests.method_id = followupmethods.id +; +GRANT SELECT ON lv_followuprequests TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_genders of entity genders for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_genders AS +SELECT genders.id +FROM genders +; +GRANT SELECT ON lv_genders TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_intentions of entity intentions for lists, et +-- cetera +------------------------------------------------------------------------ +CREATE VIEW lv_intentions AS +SELECT addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS visit_id_expanded, + intentions.visit_id, + electors.name AS elector_id_expanded, + intentions.elector_id, + options.id AS option_id_expanded, + intentions.option_id, + intentions.locality, + intentions.id +FROM visits, intentions, addresses, electors, options +WHERE intentions.visit_id = visits.id + AND intentions.elector_id = electors.id + AND intentions.option_id = options.id +; +GRANT SELECT ON lv_intentions TO admin, + analysts, + canvassers ; + +------------------------------------------------------------------------ +-- convenience view lv_issues of entity issues for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_issues AS +SELECT issues.url, + issues.current, + issues.brief, + issues.id +FROM issues +; +GRANT SELECT ON lv_issues TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_options of entity options for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_options AS +SELECT options.id +FROM options +; +GRANT SELECT ON lv_options TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_roles of entity roles for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_roles AS +SELECT roles.name, + roles.id +FROM roles +; +GRANT SELECT ON lv_roles TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_teams of entity teams for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_teams AS +SELECT teams.name, + districts.name AS district_id_expanded, + teams.district_id, + teams.latitude, + teams.longitude, + teams.id +FROM teams, districts +WHERE teams.district_id = districts.id +; +GRANT SELECT ON lv_teams TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_visits of entity visits for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_visits AS +SELECT addresses.address ||', '|| addresses.postcode AS address_id_expanded, + visits.address_id, + canvassers.username ||', '|| canvassers.fullname ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| canvassers.phone ||', '|| canvassers.email AS canvasser_id_expanded, + visits.canvasser_id, + visits.date, + visits.id +FROM visits, canvassers, addresses +WHERE visits.address_id = addresses.id + AND visits.canvasser_id = canvassers.id +; +GRANT SELECT ON lv_visits TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- referential integrity links for primary tables +------------------------------------------------------------------------ + +ALTER TABLE addresses ADD CONSTRAINT ri_addresses_districts_district_id + FOREIGN KEY( district_id ) + REFERENCES districts(id) + ON DELETE NO ACTION ; + +ALTER TABLE canvassers ADD CONSTRAINT ri_canvassers_addresses_address_id + FOREIGN KEY( address_id ) + REFERENCES addresses(id) + ON DELETE NO ACTION ; + +ALTER TABLE canvassers ADD CONSTRAINT ri_canvassers_authorities_authority_id + FOREIGN KEY( authority_id ) + REFERENCES authorities(id) + ON DELETE NO ACTION ; + +ALTER TABLE canvassers ADD CONSTRAINT ri_canvassers_electors_elector_id + FOREIGN KEY( elector_id ) + REFERENCES electors(id) + ON DELETE NO ACTION ; + +ALTER TABLE dwellings ADD CONSTRAINT ri_dwellings_addresses_address_id + FOREIGN KEY( address_id ) + REFERENCES addresses(id) + ON DELETE NO ACTION ; + +ALTER TABLE electors ADD CONSTRAINT ri_electors_dwellings_dwelling_id + FOREIGN KEY( dwelling_id ) + REFERENCES dwellings(id) + ON DELETE NO ACTION ; + +ALTER TABLE electors ADD CONSTRAINT ri_electors_genders_gender + FOREIGN KEY( gender ) + REFERENCES genders(id) + ON DELETE NO ACTION ; + +ALTER TABLE followupactions ADD CONSTRAINT ri_followupactions_canvassers_actor + FOREIGN KEY( actor ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE followupactions ADD CONSTRAINT ri_followupactions_followuprequests_request_id + FOREIGN KEY( request_id ) + REFERENCES followuprequests(id) + ON DELETE NO ACTION ; + +ALTER TABLE followuprequests ADD CONSTRAINT ri_followuprequests_electors_elector_id + FOREIGN KEY( elector_id ) + REFERENCES electors(id) + ON DELETE NO ACTION ; + +ALTER TABLE followuprequests ADD CONSTRAINT ri_followuprequests_issues_issue_id + FOREIGN KEY( issue_id ) + REFERENCES issues(id) + ON DELETE NO ACTION ; + +ALTER TABLE followuprequests ADD CONSTRAINT ri_followuprequests_followupmethods_method_id + FOREIGN KEY( method_id ) + REFERENCES followupmethods(id) + ON DELETE NO ACTION ; + +ALTER TABLE followuprequests ADD CONSTRAINT ri_followuprequests_visits_visit_id + FOREIGN KEY( visit_id ) + REFERENCES visits(id) + ON DELETE NO ACTION ; + +ALTER TABLE intentions ADD CONSTRAINT ri_intentions_electors_elector_id + FOREIGN KEY( elector_id ) + REFERENCES electors(id) + ON DELETE NO ACTION ; + +ALTER TABLE intentions ADD CONSTRAINT ri_intentions_options_option_id + FOREIGN KEY( option_id ) + REFERENCES options(id) + ON DELETE NO ACTION ; + +ALTER TABLE intentions ADD CONSTRAINT ri_intentions_visits_visit_id + FOREIGN KEY( visit_id ) + REFERENCES visits(id) + ON DELETE NO ACTION ; + +ALTER TABLE teams ADD CONSTRAINT ri_teams_districts_district_id + FOREIGN KEY( district_id ) + REFERENCES districts(id) + ON DELETE NO ACTION ; + +ALTER TABLE visits ADD CONSTRAINT ri_visits_addresses_address_id + FOREIGN KEY( address_id ) + REFERENCES addresses(id) + ON DELETE NO ACTION ; + +ALTER TABLE visits ADD CONSTRAINT ri_visits_canvassers_canvasser_id + FOREIGN KEY( canvasser_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +------------------------------------------------------------------------ +-- link table joining canvassers with issues +------------------------------------------------------------------------ +CREATE TABLE ln_expertise_canvassers_issues +( + canvasser_id INTEGER, + issue_id VARCHAR(32) +); +GRANT SELECT ON ln_expertise_canvassers_issues TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_expertise_canvassers_issues TO admin, + canvassers, + teamorganisers ; +GRANT UPDATE ON ln_expertise_canvassers_issues TO admin, + canvassers, + teamorganisers ; +GRANT DELETE ON ln_expertise_canvassers_issues TO admin ; + +ALTER TABLE ln_expertise_canvassers_issues ADD CONSTRAINT ri_ln_expertise_canvassers_issues_canvassers_canvasser_id + FOREIGN KEY( canvasser_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_expertise_canvassers_issues ADD CONSTRAINT ri_ln_expertise_canvassers_issues_issues_issue_id + FOREIGN KEY( issue_id ) + REFERENCES issues(id) + ON DELETE NO ACTION ; + +------------------------------------------------------------------------ +-- link table joining canvassers with roles +------------------------------------------------------------------------ +CREATE TABLE ln_roles_canvassers_roles +( + canvasser_id INTEGER, + role_id INTEGER +); +GRANT SELECT ON ln_roles_canvassers_roles TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_roles_canvassers_roles TO admin, + canvassers, + teamorganisers ; +GRANT UPDATE ON ln_roles_canvassers_roles TO admin, + canvassers, + teamorganisers ; +GRANT DELETE ON ln_roles_canvassers_roles TO admin ; + +ALTER TABLE ln_roles_canvassers_roles ADD CONSTRAINT ri_ln_roles_canvassers_roles_canvassers_canvasser_id + FOREIGN KEY( canvasser_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_roles_canvassers_roles ADD CONSTRAINT ri_ln_roles_canvassers_roles_roles_role_id + FOREIGN KEY( role_id ) + REFERENCES roles(id) + ON DELETE NO ACTION ; + +------------------------------------------------------------------------ +-- link table joining events with teams +------------------------------------------------------------------------ +CREATE TABLE ln_teams_events_teams +( + event_id INTEGER, + team_id INTEGER +); +GRANT SELECT ON ln_teams_events_teams TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_teams_events_teams TO admin, + teamorganisers ; +GRANT UPDATE ON ln_teams_events_teams TO admin, + teamorganisers ; +GRANT DELETE ON ln_teams_events_teams TO admin ; + +ALTER TABLE ln_teams_events_teams ADD CONSTRAINT ri_ln_teams_events_teams_events_event_id + FOREIGN KEY( event_id ) + REFERENCES events(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_teams_events_teams ADD CONSTRAINT ri_ln_teams_events_teams_teams_team_id + FOREIGN KEY( team_id ) + REFERENCES teams(id) + ON DELETE NO ACTION ; + +------------------------------------------------------------------------ +-- link table joining issues with canvassers +------------------------------------------------------------------------ +CREATE TABLE ln_experts_issues_canvassers +( + issue_id VARCHAR(32), + canvasser_id INTEGER +); +GRANT SELECT ON ln_experts_issues_canvassers TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_experts_issues_canvassers TO admin, + issueeditors ; +GRANT UPDATE ON ln_experts_issues_canvassers TO admin, + issueeditors ; +GRANT DELETE ON ln_experts_issues_canvassers TO admin ; + +ALTER TABLE ln_experts_issues_canvassers ADD CONSTRAINT ri_ln_experts_issues_canvassers_canvassers_canvasser_id + FOREIGN KEY( canvasser_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_experts_issues_canvassers ADD CONSTRAINT ri_ln_experts_issues_canvassers_issues_issue_id + FOREIGN KEY( issue_id ) + REFERENCES issues(id) + ON DELETE NO ACTION ; + +------------------------------------------------------------------------ +-- link table joining roles with canvassers +------------------------------------------------------------------------ +CREATE TABLE ln_members_roles_canvassers +( + role_id INTEGER, + canvasser_id INTEGER +); +GRANT SELECT ON ln_members_roles_canvassers TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_members_roles_canvassers TO admin ; +GRANT UPDATE ON ln_members_roles_canvassers TO admin ; +GRANT DELETE ON ln_members_roles_canvassers TO admin ; + +ALTER TABLE ln_members_roles_canvassers ADD CONSTRAINT ri_ln_members_roles_canvassers_canvassers_canvasser_id + FOREIGN KEY( canvasser_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_members_roles_canvassers ADD CONSTRAINT ri_ln_members_roles_canvassers_roles_role_id + FOREIGN KEY( role_id ) + REFERENCES roles(id) + ON DELETE NO ACTION ; + +------------------------------------------------------------------------ +-- link table joining teams with canvassers +------------------------------------------------------------------------ +CREATE TABLE ln_members_teams_canvassers +( + team_id INTEGER, + canvasser_id INTEGER +); +GRANT SELECT ON ln_members_teams_canvassers TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_members_teams_canvassers TO admin, + teamorganisers ; +GRANT UPDATE ON ln_members_teams_canvassers TO admin, + teamorganisers ; +GRANT DELETE ON ln_members_teams_canvassers TO admin ; + +ALTER TABLE ln_members_teams_canvassers ADD CONSTRAINT ri_ln_members_teams_canvassers_canvassers_canvasser_id + FOREIGN KEY( canvasser_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_members_teams_canvassers ADD CONSTRAINT ri_ln_members_teams_canvassers_teams_team_id + FOREIGN KEY( team_id ) + REFERENCES teams(id) + ON DELETE NO ACTION ; + +------------------------------------------------------------------------ +-- link table joining teams with canvassers +------------------------------------------------------------------------ +CREATE TABLE ln_organisers_teams_canvassers +( + team_id INTEGER, + canvasser_id INTEGER +); +GRANT SELECT ON ln_organisers_teams_canvassers TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_organisers_teams_canvassers TO admin, + teamorganisers ; +GRANT UPDATE ON ln_organisers_teams_canvassers TO admin, + teamorganisers ; +GRANT DELETE ON ln_organisers_teams_canvassers TO admin ; + +ALTER TABLE ln_organisers_teams_canvassers ADD CONSTRAINT ri_ln_organisers_teams_canvassers_canvassers_canvasser_id + FOREIGN KEY( canvasser_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_organisers_teams_canvassers ADD CONSTRAINT ri_ln_organisers_teams_canvassers_teams_team_id + FOREIGN KEY( team_id ) + REFERENCES teams(id) + ON DELETE NO ACTION ; \ No newline at end of file diff --git a/resources/templates/about.html b/resources/templates/about.html new file mode 100644 index 0000000..0812752 --- /dev/null +++ b/resources/templates/about.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} +{% block big-links %} +{% endblock %} +{% block content %} +{{motd|safe}} +{% endblock %} + diff --git a/resources/templates/app.html b/resources/templates/app.html index 024aa39..acf8274 100644 --- a/resources/templates/app.html +++ b/resources/templates/app.html @@ -1,4 +1,13 @@ -{% extends "base-authenticated.html" %} +{% extends "base.html" %} +{% block head %} + + + + + + + {{site-title}}: {{title}} +{% endblock %} {% block whole-page %}
@@ -24,12 +33,21 @@
{% endblock %} {% block extra-script %} + var user = { + "username": "{{user.username}}", + "fullname": "{{user.fullname}}", + "id": {{user.id|default:-1}}, + "authorised": {{user.authorised|default:false}} + }; +{% endblock %} +{% block extra-tail %} - - - + + +{% script "js/lib/node_modules/signature_pad/dist/signature_pad.min.js" %} +{% script "js/lib/node_modules/leaflet/dist/leaflet.js" %} {% script "/js/app.js" %} {% endblock %} diff --git a/resources/templates/base-authenticated.html b/resources/templates/base-authenticated.html deleted file mode 100644 index 7da389b..0000000 --- a/resources/templates/base-authenticated.html +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - {{title}} - - - {% block whole-page %} -
- - -

- {{title}} -

-
- -
- -
- {% block content %} - {% endblock %} -
- -
- - {% endblock %} - - {% block extra-script %} - {% endblock %} - - - - - - diff --git a/resources/templates/base-unauthenticated.html b/resources/templates/base-unauthenticated.html deleted file mode 100644 index 4e41c8f..0000000 --- a/resources/templates/base-unauthenticated.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - {% block title %}{% endblock %}{{title}} - - -
- - -

- {{title}} -

-
- -
- -
- {% block content %} - {% endblock %} -
-
- - - - diff --git a/resources/templates/base.html b/resources/templates/base.html new file mode 100644 index 0000000..54b4f02 --- /dev/null +++ b/resources/templates/base.html @@ -0,0 +1,104 @@ + + + + {% block head %} + + + + {% style "/css/yyy-common.css" %} + {% style "/css/yyy-site.css" %} + {% style "/css/spinner.css" %} + + {% script "/js/lib/node_modules/jquery/dist/jquery.min.js" %} + {{site-title}}: {{title}} + {% endblock %} + {% block extra-head %} + + + {% endblock %} + + + {% block whole-page %} + + {% block top %} +
+ +

{{title}}

+ {% if message|not-empty %} +
+ {{ message }} +
+ {% endif %} + {% if error|not-empty %} +
+ {{ error }} +
+ {% endif %} +
+ {% endblock %} +
+ + +
+ {% block content %} + + {% endblock %} +
+
+
+ {% block foot %} + + + {% endblock %} + {% endblock %} + + {% block extra-tail %} + + + {% endblock %} + + + + + + diff --git a/resources/templates/call-me-accepted.html b/resources/templates/call-me-accepted.html index a720998..9bcc836 100644 --- a/resources/templates/call-me-accepted.html +++ b/resources/templates/call-me-accepted.html @@ -1,41 +1,15 @@ -{% extends "base-unauthenticated.html" %} +{% extends "base.html" %} {% block big-links %} - {% endblock %} {% block content %} +

+ We'll be in touch! +

Thank you, {{name}}. Someone will call you shortly on {{phone}} to talk to you about {{concern}}.

-
-

- - -

-

- - -

-

- - -

-

- - -

-

- - -

-
{% endblock %} diff --git a/resources/templates/call-me.html b/resources/templates/call-me.html index 299e97d..bb2414e 100644 --- a/resources/templates/call-me.html +++ b/resources/templates/call-me.html @@ -1,14 +1,10 @@ -{% extends "base-unauthenticated.html" %} -{% block big-links %} - -{% endblock %} +{% extends "base.html" %} {% block content %}

Use this form to request someone to phone you to discuss your concerns about independence.

+ {% csrf-field %}

@@ -25,7 +21,7 @@

diff --git a/resources/templates/canvasser.html b/resources/templates/canvasser.html deleted file mode 100644 index b223313..0000000 --- a/resources/templates/canvasser.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "base-authenticated.html" %} -{% block title %} -{% endblock %} -{% block content %} - - {% if canvasser %} - - {% endif %} -

- - -

-

- (TODO: Not absolutely sure what I'm going to do for an elector id widget yet.) -

-

- - {% if address.id %} - - - {{address.address}} - - {% else %} - (TODO: Some sort of address lookup widget goes here.) - {% endif %} -

-

- - -

-

- - -

-

- - -

- -

- id serial, - username character varying(32) NOT NULL, - fullname character varying(64) NOT NULL, - elector_id integer, - address_id integer NOT NULL, - phone character varying(16), - email character varying(128), - authority_id character varying(32) NOT NULL, - introduced_by int references canvassers(id), - authorised boolean - -
- -{% endblock %} diff --git a/resources/templates/error.html b/resources/templates/error.html index 6fcd237..f811553 100644 --- a/resources/templates/error.html +++ b/resources/templates/error.html @@ -1,38 +1,7 @@ - - - - Something bad happened - - - {% style "/assets/bootstrap/css/bootstrap.min.css" %} - {% style "/assets/bootstrap/css/bootstrap-theme.min.css" %} - - +{% extends "base.html" %} +{% block big-links %} +{% endblock %} +{% block content %}
@@ -52,5 +21,5 @@
- - +{% endblock %} + diff --git a/resources/templates/home.html b/resources/templates/home.html index ea4c068..fe8359f 100644 --- a/resources/templates/home.html +++ b/resources/templates/home.html @@ -1,9 +1,15 @@ -{% extends "base-unauthenticated.html" %} +{% extends "base.html" %} {% block big-links %} - - + + {% endblock %} +{% block content %} + +{{motd|safe}} + +{% endblock %} + diff --git a/resources/templates/issue-expert/list.html b/resources/templates/issue-expert/list.html new file mode 100644 index 0000000..629fc72 --- /dev/null +++ b/resources/templates/issue-expert/list.html @@ -0,0 +1,146 @@ +{% extends "base.html" %} + + + +{% block back-links %} +
+ +
+ +{% endblock %} +{% block big-links %} +
+ +{% ifmemberof %} + +{% endifmemberof %} +
+ +{% endblock %} +{% block content %} +
+{% csrf-field %} + + + + + + + + + + + + + + + + + + + + + + +{% for record in records %} + + + + + + + + +{% endfor %} + +
+Id + +Elector_id + +Visit_id + +Issue_id + +Method_id + +  +
+ + + + + + + + + + + +
+{{ record.id }} + + +{{ record.elector_id_expanded }} + + + +{{ record.visit_id_expanded }} + + + +{{ record.issue_id_expanded }} + + + +{{ record.method_id_expanded }} + + + +View + +
+
+ +{% endblock %} +{% block extra-script %} + + var form = document.getElementById('list-followuprequests-Followuprequests'); + var ow = document.getElementById('offset'); + var lw = document.getElementById('limit'); + form.addEventListener('submit', function() { + ow.value='0'; + }); + + var prevSelector = document.getElementById('prev-selector'); + if (prevSelector != null) { + prevSelector.addEventListener('click', function () { + if (parseInt(ow.value)===0) { + window.location = '{{servlet-context}}/admin'; + } else { + ow.value=(parseInt(ow.value)-parseInt(lw.value)); + console.log('Updated offset to ' + ow.value); + form.submit(); + } + }); + } + + document.getElementById('next-selector').addEventListener('click', function () { + ow.value=(parseInt(ow.value)+parseInt(lw.value)); + console.log('Updated offset to ' + ow.value); + form.submit(); + }); +{% endblock %} diff --git a/resources/templates/issue-expert/request.html b/resources/templates/issue-expert/request.html new file mode 100644 index 0000000..51ea0a0 --- /dev/null +++ b/resources/templates/issue-expert/request.html @@ -0,0 +1,157 @@ +{% extends "base.html" %} + + + +{% block extra-head %} + {% script "/js/lib/node_modules/simplemde/dist/simplemde.min.js" %} + {% style "/js/lib/node_modules/simplemde/dist/simplemde.min.css" %} +{% endblock %} +{% block content %} +
+
+ {% csrf-field %} + +

+ + {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} + + {{elector.name}} ({{elector.gender}}) + + {% else %} + + You are not permitted to view elector of followuprequests + + {% endifmemberof %} +

+

+ + {% ifmemberof issueexperts analysts issueeditors admin %} + + by {{visit.canvasser_id_expanded}} at {{visit.date}} + + {% else %} + + You are not permitted to view visit of followuprequests + + {% endifmemberof %} +

+

+ + {% ifmemberof issueexperts admin %} +

+ {{issue.brief|safe}} +
+ {% else %} + + You are not permitted to view issue of followuprequests + + {% endifmemberof %} +

+

+ + {% ifmemberof issueexperts admin %} + + {% ifequal record.method_id "Phone" %}{{elector.phone}}{% endifequal %} + {% ifequal record.method_id "eMail" %}{{elector.email}}{% endifequal %} + + {% else %} + + You are not permitted to view method of followuprequests + + {% endifmemberof %} +

+ {% if actions|length > 0 %} +

+ + + + + + + + + + + {% for action in actions %} + + + + + + + + + {% endfor %} + +
+ Actor + + Date + + Closed? +
{{action.actor}}{{action.date}}{{action.closed}}
{{action.notes|safe}}
+

+ {% endif %} +

+ + {% ifmemberof admin issueexperts %} + + {% endifmemberof %} +

+

+ + {% ifmemberof admin issueexperts %} + + {% endifmemberof %} +

+ + {% ifmemberof admin issueexperts %} +

+ + +

+ {% endifmemberof %} +
+
+ +{% endblock %} +{% block extra-tail %} + +{% endblock %} diff --git a/resources/templates/login.html b/resources/templates/login.html index 5ee3706..d813e31 100644 --- a/resources/templates/login.html +++ b/resources/templates/login.html @@ -1,11 +1,26 @@ -{% extends "base-unauthenticated.html" %} +{% extends "base.html" %} +{% block big-links %} + {% for authority in authorities %} + + {% endfor %} +{% endblock %} + {% block content %} -

- We're not going to do login in the long term; we're going to use oauth. - This is a temporary login form. -

-
+ {% csrf-field %} +

+ Or use a test username and password +

+

+ We're not going to do login in the long term; we're going to use + oauth. + This is a temporary login form. +

diff --git a/resources/templates/notyet.html b/resources/templates/notyet.html index 47b9fec..9574c55 100644 --- a/resources/templates/notyet.html +++ b/resources/templates/notyet.html @@ -1,4 +1,4 @@ -{% extends "base-unauthenticated.html" %} +{% extends "base.html" %} {% block big-links %}