From e425c56ec2a5feaa075e6ccd404d872192e30e50 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 1 Apr 2017 15:11:50 +0100 Subject: [PATCH 01/20] #5: Started work on roles switching --- doc/specification/database.md | 218 ++++++++++++++++++ .../entity-relationship-diagram.svg | 67 +++++- ...170401115900-basic-reference-data-down.sql | 0 ...20170401115900-basic-reference-data-up.sql | 58 +++++ resources/sql/queries.sql | 1 + resources/templates/base-authenticated.html | 74 ++++++ resources/templates/base-unauthenticated.html | 58 +++++ resources/templates/roles.html | 13 ++ src/clj/youyesyet/db/core.clj | 8 +- src/clj/youyesyet/handler.clj | 23 ++ src/clj/youyesyet/routes/authenticated.clj | 35 +++ src/clj/youyesyet/routes/home.clj | 78 ++++++- src/cljc/youyesyet/validation.cljc | 23 ++ src/cljs/youyesyet/ajax.cljs | 24 ++ 14 files changed, 661 insertions(+), 19 deletions(-) create mode 100644 doc/specification/database.md create mode 100644 resources/migrations/20170401115900-basic-reference-data-down.sql create mode 100644 resources/migrations/20170401115900-basic-reference-data-up.sql create mode 100644 resources/templates/base-authenticated.html create mode 100644 resources/templates/base-unauthenticated.html create mode 100644 resources/templates/roles.html create mode 100644 src/clj/youyesyet/routes/authenticated.clj diff --git a/doc/specification/database.md b/doc/specification/database.md new file mode 100644 index 0000000..2656621 --- /dev/null +++ b/doc/specification/database.md @@ -0,0 +1,218 @@ +# Database Specification + +Note that this is a work in progress. Read it in concert with the Entity-Relationship Diagram. + +Tables are listed in alphabetical order. + +## Address + +The postal address of a dwelling at which electors are registered. + + 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 + ); + +## Authority + +An *oauth* authority which authenticates canvassers. *Note that* there will need to be substantially more in this table but I don't yet know what. + + CREATE TABLE IF NOT EXISTS authorities ( + id character varying(32) NOT NULL + ); + + +## Canvasser + +A user of the system. + + 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, + authorised boolean + ); + + +## District + +An electoral district. + + CREATE TABLE IF NOT EXISTS districts ( + id integer NOT NULL, + name character varying(64) NOT NULL + ); + + +## Elector + +Someone entitled to cast a vote in the referendum. + + 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) + ); + +## Followup Action + +An action performed by an issue expert in response to a followup request. + + 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 + ); + +## Followup Method + +A method for responding to a followup request; reference data. + + CREATE TABLE IF NOT EXISTS followupmethods ( + id character varying(32) NOT NULL + ); + + insert into followupmethods values ('Telephone'); + insert into followupmethods values ('eMail'); + insert into followupmethods values ('Post'); + +## Followup Request + +A request recorded by a canvasser for an issue expert to contact an elector with regard to a particular issue. + + 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 + ); + +## Intention + +An intention, by an elector, to vote for an option; captured by a canvasser during a visit. + + CREATE TABLE IF NOT EXISTS intentions ( + id serial not null, + elector integer not null references elector(id), + option varchar(32) not null references option(id), + visit integer not null references visit(id), + date timestamp with time zone DEFAULT now() NOT NULL + ); + + +## Issue + +An issue which might affect electors' decisions regarding their intention. + + CREATE TABLE IF NOT EXISTS issues ( + id character varying(32) NOT NULL, + url character varying(256), + content varchar(1024), + current default false + ); + + +## Issue expertise + +Expertise of a canvasser able to use a method, in an issue. + + 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 + ); + + +## Option + +An option for which an elector may have an intention to vote. + + CREATE TABLE IF NOT EXISTS options ( + id character varying(32) NOT NULL + ); + + +## Role + +A role (other than basic *Canvasser*) that a user may have in the system. Reference data. + + create table if not exists roles ( + id serial primary key, + name varchar(64) not null + ); + + +## Role Member + +Membership of a user (*Canvasser*) of an additional role; link table. + + create table if not exists rolememberships ( + role_id integer not null references roles(id), + canvasser_id integer not null references canvassers(id) + ); + + +## Team + +A team of canvassers in a locality who are known to one another and frequently +canvas together. + + 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 + ); + + +## Team Member + +Membership of a user (*Canvasser*) of a particular team. Canvassers may join multiple teams. Link table. + + create table if not exists teammemberships ( + team_id integer not null references teams(id), + canvasser_id integer not null references canvassers(id) + ); + + +## Team Organiser + +A relationship which defines a user (*Canvasser*) as an organiser of a team. A team may +have more than one organiser. An organiser (if they also have the role 'Recruiter', which +they often will have) may recruit additional Canvassers as members of their team, or +accept applications by canvassers to join their team. An organiser may promote a member of +the team to organiser of the team, and may also exclude a member from the team. + + create table if not exists teamorganiserships ( + team_id integer not null references teams(id), + canvasser_id integer not null references canvassers(id) + ); + + +## Visit + +A visit by a canvasser to an address on a date to solicit intentions from electors. + + 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 + ); diff --git a/doc/specification/entity-relationship-diagram.svg b/doc/specification/entity-relationship-diagram.svg index 0de6cf0..7068dd2 100644 --- a/doc/specification/entity-relationship-diagram.svg +++ b/doc/specification/entity-relationship-diagram.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.979899" - inkscape:cx="833.70674" - inkscape:cy="324.89697" + inkscape:zoom="2.8" + inkscape:cx="764.16287" + inkscape:cy="256.90499" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -58,12 +58,13 @@ id="layer1" transform="translate(0,-308.26772)"> + x="8.484766" + y="312.36221" + sodipodi:insensitive="true" /> Addresss + sodipodi:role="line">Address @@ -613,14 +614,14 @@ y="394.48404" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">Version: 0.2Version: 0.3Date: 20170315Date: 20170401 + + FollowupMethod + + + + diff --git a/resources/migrations/20170401115900-basic-reference-data-down.sql b/resources/migrations/20170401115900-basic-reference-data-down.sql new file mode 100644 index 0000000..e69de29 diff --git a/resources/migrations/20170401115900-basic-reference-data-up.sql b/resources/migrations/20170401115900-basic-reference-data-up.sql new file mode 100644 index 0000000..6ca3028 --- /dev/null +++ b/resources/migrations/20170401115900-basic-reference-data-up.sql @@ -0,0 +1,58 @@ + +-- 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 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/sql/queries.sql b/resources/sql/queries.sql index 4191f67..ab15d8e 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -19,3 +19,4 @@ WHERE id = :id -- :doc delete a user given the id DELETE FROM users WHERE id = :id + diff --git a/resources/templates/base-authenticated.html b/resources/templates/base-authenticated.html new file mode 100644 index 0000000..79c1558 --- /dev/null +++ b/resources/templates/base-authenticated.html @@ -0,0 +1,74 @@ + + + + + + + + + {% block title %}{% endblock %}{{title}} + + +
+
+ You are logged in as {{username}} +
+ + +

+ {% block title %} + {% endblock %} + {{title}} +

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

+ {{title}} +

+
+ +
+ +
+ {% block content %} + {% endblock %} +
+
+ + + + diff --git a/resources/templates/roles.html b/resources/templates/roles.html new file mode 100644 index 0000000..1c61866 --- /dev/null +++ b/resources/templates/roles.html @@ -0,0 +1,13 @@ +{% extends "base-authenticated.html" %} +{% block title %} +{% endblock %} +{% block big-links %} + + {% for role in roles %} + + {% endfor %} +{% endblock %} diff --git a/src/clj/youyesyet/db/core.clj b/src/clj/youyesyet/db/core.clj index 536cc3b..a0eec57 100644 --- a/src/clj/youyesyet/db/core.clj +++ b/src/clj/youyesyet/db/core.clj @@ -15,9 +15,13 @@ Timestamp PreparedStatement])) +;; TODO: I am CERTANLY misunderstanding something here. We ought to be getting +;; the database connection string and credentials fomr profiles.clj +;; (def ^:dynamic *db* {:name "java:comp/env/jdbc/EmployeeDB"}) (defstate ^:dynamic *db* - :start (conman/connect! {:jdbc-url (env :database-url)}) - :stop (conman/disconnect! *db*)) + :start (conman/connect! {:jdbc-url (env :database-url) + :driver-class-name "org.postgresql.Driver"}) + :stop (conman/disconnect! *db*)) (conman/bind-connection *db* "sql/queries.sql") diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index f898fe5..ad5462d 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -10,6 +10,29 @@ [clojure.tools.logging :as log] [youyesyet.config :refer [env]])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.handler: handlers for starting and stopping the webapp. +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (mount/defstate init-app :start ((or (:init defaults) identity)) :stop ((or (:stop defaults) identity))) diff --git a/src/clj/youyesyet/routes/authenticated.clj b/src/clj/youyesyet/routes/authenticated.clj new file mode 100644 index 0000000..b5c2644 --- /dev/null +++ b/src/clj/youyesyet/routes/authenticated.clj @@ -0,0 +1,35 @@ +(ns youyesyet.routes.authenticated + (:require [clojure.walk :refer [keywordize-keys]] + [noir.response :as nresponse] + [noir.util.route :as route] + [youyesyet.layout :as layout] + [youyesyet.db.core :as db-core] + [compojure.core :refer [defroutes GET POST]] + [ring.util.http-response :as response] + [clojure.java.io :as io])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.routes.authenticated: routes and pages for authenticated users. +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; (defn roles-page [request] +;; (if diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index 7fbf9f7..90f2f12 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -1,15 +1,81 @@ (ns youyesyet.routes.home - (:require [youyesyet.layout :as layout] + (:require [clojure.walk :refer [keywordize-keys]] + [noir.response :as nresponse] + [noir.util.route :as route] + [youyesyet.layout :as layout] [youyesyet.db.core :as db-core] - [compojure.core :refer [defroutes GET]] + [compojure.core :refer [defroutes GET POST]] [ring.util.http-response :as response] [clojure.java.io :as io])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.routes.home: routes and pages for unauthenticated users. +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn app-page [] + (layout/render "app.html")) + +(defn about-page [] + (layout/render "about.html")) + +(defn call-me-page [request] + (if + request + (do + ;; do something to store it in the database + (layout/render "call-me-accepted.html" (:params request))) + (layout/render "call-me.html" + {:title "Please call me!" + ;; TODO: Issues need to be fetched from the database + :concerns nil}))) + (defn home-page [] - (layout/render "home.html")) + (layout/render "home.html" {:title "You Yes Yet?"})) + +(defn login-page + "This is very temporary. We're going to do authentication by oauth." + [request] + (let [params (keywordize-keys (:form-params request)) + session (:session request) + username (:username params) + password (:password params) + redirect-to (or (:redirect-to params) "app")] + (if + (and (= username "test") (= password "test")) + (do + (assoc (response/found redirect-to) :session (assoc session :user username))) + (layout/render "login.html" {:title "Please log in" :redirect-to redirect-to})))) + (defroutes home-routes (GET "/" [] (home-page)) - (GET "/docs" [] (-> (response/ok (-> "docs/docs.md" io/resource slurp)) - (response/header "Content-Type" "text/plain; charset=utf-8")))) - + (GET "/home" [] (home-page)) + (GET "/about" [] (about-page)) + (GET "/app" [] (route/restricted (app-page))) + (GET "/call-me" [] (call-me-page nil)) + (POST "/call-me" request (call-me-page request)) + (GET "/auth" request (login-page request)) + (POST "/auth" request (login-page request)) + (GET "/notyet" [] (layout/render "notyet.html" + {:title "Can we persuade you?"})) + (GET "/supporter" [] (layout/render "supporter.html" + {:title "Have you signed up as a canvasser yet?"}))) diff --git a/src/cljc/youyesyet/validation.cljc b/src/cljc/youyesyet/validation.cljc index fd01d8f..7c293dd 100644 --- a/src/cljc/youyesyet/validation.cljc +++ b/src/cljc/youyesyet/validation.cljc @@ -1,3 +1,26 @@ (ns youyesyet.validation (:require [bouncer.core :as b] [bouncer.validators :as v])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.validation: +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/cljs/youyesyet/ajax.cljs b/src/cljs/youyesyet/ajax.cljs index 07cf00d..ef3a3cb 100644 --- a/src/cljs/youyesyet/ajax.cljs +++ b/src/cljs/youyesyet/ajax.cljs @@ -1,6 +1,30 @@ (ns youyesyet.ajax (:require [ajax.core :as ajax])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.ajax: transciever for ajax packets. +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (defn local-uri? [{:keys [uri]}] (not (re-find #"^\w+?://" uri))) From 19e7b2493353ebf0eed6f682f4dc29f02566acd5 Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 4 Apr 2017 09:33:24 +0100 Subject: [PATCH 02/20] Added lots of queries (not yet complete). --- resources/sql/queries.sql | 221 +++++++++++++++++++++++++++++++++++--- 1 file changed, 206 insertions(+), 15 deletions(-) diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index ab15d8e..6dbcae4 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -1,22 +1,213 @@ --- :name create-user! :! :n --- :doc creates a new user record -INSERT INTO users -(id, first_name, last_name, email, pass) -VALUES (:id, :first_name, :last_name, :email, :pass) +-- :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) --- :name update-user! :! :n --- :doc update an existing user record -UPDATE users -SET first_name = :first_name, last_name = :last_name, email = :email +-- :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-user :? :1 --- :doc retrieve a user given the id. -SELECT * FROM users +-- :name get-address :? :1 +-- :doc retrieve a address given the id. +SELECT * FROM addresses WHERE id = :id --- :name delete-user! :! :n --- :doc delete a user given the id -DELETE FROM users +-- :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) + +-- :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 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, address_id, phone, email, authority_id, authorised) +VALUES (:username, :fullname, :elector_id, :address_id, :phone, :email, :authority_id, :authorised) + +-- :name update-canvasser! :! :n +-- :doc update an existing canvasser record +UPDATE canvassers +SET username = :username, fullname = :fullname, elector_id = :elector_id, address_id = :address_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 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) + +-- :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 create-elector! :! :n +-- :doc creates a new elector record +INSERT INTO electors +(name, address_id, phone, email) +VALUES (:name, :address_id, :phone, :email) + +-- :name update-elector! :! :n +-- :doc update an existing elector record +UPDATE electors +SET name = :name, address_id = :address_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) + +-- 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) + +-- 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) + +-- :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) + +-- :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 +(address_id, canvasser_id) +VALUES (:address_id, :canvasser_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. From 95507002c7fb5e824e0446e0dadef02a59a78da3 Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 5 Apr 2017 09:56:27 +0100 Subject: [PATCH 03/20] Very rough beginning of making the database stuff work. --- resources/sql/queries.sql | 28 ++++++++++++++++++++++ src/clj/youyesyet/routes/authenticated.clj | 23 ++++++++++++------ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index 6dbcae4..29a9d67 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -1,3 +1,31 @@ +------------------------------------------------------------------------------; +---- +---- youyesyet.routes.authenticated: routes and pages for authenticated users. +---- +---- 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 +---- +------------------------------------------------------------------------------; + +-- 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") +-- the functions then appeare in the youyesyet.db.core namespace. + -- :name create-address! :! :n -- :doc creates a new address record INSERT INTO addresses diff --git a/src/clj/youyesyet/routes/authenticated.clj b/src/clj/youyesyet/routes/authenticated.clj index b5c2644..7e38955 100644 --- a/src/clj/youyesyet/routes/authenticated.clj +++ b/src/clj/youyesyet/routes/authenticated.clj @@ -1,12 +1,12 @@ (ns youyesyet.routes.authenticated - (:require [clojure.walk :refer [keywordize-keys]] + (:require [clojure.java.io :as io] + [clojure.walk :refer [keywordize-keys]] + [compojure.core :refer [defroutes GET POST]] [noir.response :as nresponse] [noir.util.route :as route] - [youyesyet.layout :as layout] - [youyesyet.db.core :as db-core] - [compojure.core :refer [defroutes GET POST]] [ring.util.http-response :as response] - [clojure.java.io :as io])) + [youyesyet.layout :as layout] + [youyesyet.db.core :as db])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; @@ -31,5 +31,14 @@ ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; (defn roles-page [request] -;; (if +;;; This code adapted from http://www.luminusweb.net/docs#accessing_the_database + +(defn canvassers-page + [request] + (if + (:params request) + (let [params (:params request)] + (if (:id params) + (db/update-canvasser! params) + (db/create-canvasser! params))))) + From d534302fd862417732c7dbae0d5b6751ba9b80eb Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 15 Apr 2017 11:57:17 +0100 Subject: [PATCH 04/20] #5: Progress, but nothing working yet --- .../20161014170335-basic-setup.up.sql | 21 ++++--- ...170401115900-basic-reference-data-down.sql | 0 ...170401115900-basic-reference-data.down.sql | 17 ++++++ ...0170401115900-basic-reference-data.up.sql} | 2 +- .../20170415102900-core-views.down.sql | 11 ++++ .../20170415102900-core-views.up.sql | 59 +++++++++++++++++++ resources/sql/queries.sql | 34 +++++++++++ src/clj/youyesyet/handler.clj | 2 + src/clj/youyesyet/routes/authenticated.clj | 14 ++++- 9 files changed, 148 insertions(+), 12 deletions(-) delete mode 100644 resources/migrations/20170401115900-basic-reference-data-down.sql create mode 100644 resources/migrations/20170401115900-basic-reference-data.down.sql rename resources/migrations/{20170401115900-basic-reference-data-up.sql => 20170401115900-basic-reference-data.up.sql} (97%) create mode 100644 resources/migrations/20170415102900-core-views.down.sql create mode 100644 resources/migrations/20170415102900-core-views.up.sql diff --git a/resources/migrations/20161014170335-basic-setup.up.sql b/resources/migrations/20161014170335-basic-setup.up.sql index 0719e57..b7b3277 100644 --- a/resources/migrations/20161014170335-basic-setup.up.sql +++ b/resources/migrations/20161014170335-basic-setup.up.sql @@ -118,15 +118,16 @@ ALTER TABLE public.authorities OWNER TO youyesyet; -- 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, - authorised boolean + 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 ); --;; @@ -534,6 +535,8 @@ 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 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 e69de29..0000000 diff --git a/resources/migrations/20170401115900-basic-reference-data.down.sql b/resources/migrations/20170401115900-basic-reference-data.down.sql new file mode 100644 index 0000000..a0b3aa1 --- /dev/null +++ b/resources/migrations/20170401115900-basic-reference-data.down.sql @@ -0,0 +1,17 @@ +-- 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 similarity index 97% rename from resources/migrations/20170401115900-basic-reference-data-up.sql rename to resources/migrations/20170401115900-basic-reference-data.up.sql index 6ca3028..26d3bf1 100644 --- a/resources/migrations/20170401115900-basic-reference-data-up.sql +++ b/resources/migrations/20170401115900-basic-reference-data.up.sql @@ -37,7 +37,7 @@ alter table issues add column content varchar(1024); -- 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 default false; +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.', diff --git a/resources/migrations/20170415102900-core-views.down.sql b/resources/migrations/20170415102900-core-views.down.sql new file mode 100644 index 0000000..c576947 --- /dev/null +++ b/resources/migrations/20170415102900-core-views.down.sql @@ -0,0 +1,11 @@ +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 new file mode 100644 index 0000000..53346a6 --- /dev/null +++ b/resources/migrations/20170415102900-core-views.up.sql @@ -0,0 +1,59 @@ + +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/sql/queries.sql b/resources/sql/queries.sql index 29a9d67..0689475 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -239,3 +239,37 @@ 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-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 + diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index ad5462d..e25b53c 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -1,6 +1,7 @@ (ns youyesyet.handler (:require [compojure.core :refer [routes wrap-routes]] [youyesyet.layout :refer [error-page]] + [youyesyet.routes.authenticated :refer [authenticated-routes]] [youyesyet.routes.home :refer [home-routes]] [youyesyet.routes.oauth :refer [oauth-routes]] [compojure.route :as route] @@ -61,6 +62,7 @@ (wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-formats)) #'oauth-routes + #'authenticated-routes (route/not-found (:body (error-page {:status 404 diff --git a/src/clj/youyesyet/routes/authenticated.clj b/src/clj/youyesyet/routes/authenticated.clj index 7e38955..d0f4d3f 100644 --- a/src/clj/youyesyet/routes/authenticated.clj +++ b/src/clj/youyesyet/routes/authenticated.clj @@ -33,12 +33,22 @@ ;;; This code adapted from http://www.luminusweb.net/docs#accessing_the_database -(defn canvassers-page +(defn canvasser-page [request] (if (:params request) (let [params (:params request)] (if (:id params) (db/update-canvasser! params) - (db/create-canvasser! params))))) + (db/create-canvasser! params)) + ))) +(defn routing-page + "Render the routing page, which offers routes according to the user's roles" + [] + (layout/render "routing.html")) + +(defroutes authenticated-routes + (GET "/edit-canvasser" request (canvasser-page request)) + (POST "/edit-canvasser" request (canvasser-page request)) + (GET "/routing" [] (routing-page))) From be324e9d064ad273b0be84b8f74a5ac34435d884 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 15 Jul 2017 17:49:32 +0100 Subject: [PATCH 05/20] Beginnings of a working role-routing page (also the first actual database reads) --- resources/sql/queries.sql | 14 +++++ resources/templates/canvasser.html | 60 ++++++++++++++++++++++ src/clj/youyesyet/routes/authenticated.clj | 29 +++++++++-- src/clj/youyesyet/routes/home.clj | 35 ++++++++++--- 4 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 resources/templates/canvasser.html diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index 0689475..e708a7b 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -66,6 +66,10 @@ WHERE id = :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 @@ -89,6 +93,16 @@ WHERE id = :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 diff --git a/resources/templates/canvasser.html b/resources/templates/canvasser.html new file mode 100644 index 0000000..b223313 --- /dev/null +++ b/resources/templates/canvasser.html @@ -0,0 +1,60 @@ +{% 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/src/clj/youyesyet/routes/authenticated.clj b/src/clj/youyesyet/routes/authenticated.clj index d0f4d3f..e8b1b7b 100644 --- a/src/clj/youyesyet/routes/authenticated.clj +++ b/src/clj/youyesyet/routes/authenticated.clj @@ -33,15 +33,34 @@ ;;; This code adapted from http://www.luminusweb.net/docs#accessing_the_database -(defn canvasser-page +(defn post? + "Return true if the argument is a ring request which is a post request" [request] - (if + true) + +(defn canvasser-page + "Process this canvasser request, and render the canvasser page" + [request] + (let [canvasser (if (:params request) (let [params (:params request)] (if (:id params) - (db/update-canvasser! params) - (db/create-canvasser! params)) - ))) + (if (post? request) + (db/update-canvasser! params) + (db/create-canvasser! params)) + (db/get-canvasser (:id params))) + ))] + (layout/render + "canvasser.html" + {:title (if canvasser + (str + "Edit canvasser " + (:fullname canvasser) + " " + (:email canvasser)) + "Add new canvasser") + :canvasser canvasser + :address (if (:address_id canvasser) (db/get-address (:address_id canvasser)))}))) (defn routing-page "Render the routing page, which offers routes according to the user's roles" diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index 90f2f12..77ef2f0 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -48,28 +48,51 @@ ;; TODO: Issues need to be fetched from the database :concerns nil}))) + +(defn roles-page [request] + (let + [session (:session request) + username (:user session) + user (if username (db-core/get-canvasser-by-username db-core/*db* {:username username})) + roles (if user (db-core/get-roles-by-canvasser db-core/*db* {:canvasser (:id user)}))] + (cond + roles (layout/render "roles.html" + {:title (str "Welcome " (:fullname user)) + :roles roles}) + true (assoc (response/found "/login") :session (dissoc session :user)) + ))) + + (defn home-page [] (layout/render "home.html" {:title "You Yes Yet?"})) + (defn login-page "This is very temporary. We're going to do authentication by oauth." [request] (let [params (keywordize-keys (:form-params request)) session (:session request) username (:username params) + user (if username (db-core/get-canvasser-by-username db-core/*db* {:username username})) password (:password params) - redirect-to (or (:redirect-to params) "app")] - (if - (and (= username "test") (= password "test")) - (do - (assoc (response/found redirect-to) :session (assoc session :user username))) - (layout/render "login.html" {:title "Please log in" :redirect-to redirect-to})))) + redirect-to (or (:redirect-to params) "roles")] + (cond + ;; this is obviously, ABSURDLY, insecure. I don't want to put just-about-good-enough, + ;; it-will-do-for-now security in place; instead, I want this to be test code only + ;; until we have o-auth properly working. + (and user (= username password)) + (assoc (response/found redirect-to) :session (assoc session :user username)) + user + (layout/render "login.html" {:title (str "User " username " is unknown") :redirect-to redirect-to}) + true + (layout/render "login.html" {:title "Please log in" :redirect-to redirect-to})))) (defroutes home-routes (GET "/" [] (home-page)) (GET "/home" [] (home-page)) (GET "/about" [] (about-page)) + (GET "/roles" request (route/restricted (roles-page request))) (GET "/app" [] (route/restricted (app-page))) (GET "/call-me" [] (call-me-page nil)) (POST "/call-me" request (call-me-page request)) From 4213f6159c5f2b2513300fd791c0fd653081d085 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 15 Jul 2017 17:51:28 +0100 Subject: [PATCH 06/20] #28: only the very beginning of the beginning Don't even know if this is worth doing - this specification language doesn't yet mean much to me. --- doc/specification/api.v1.raml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 doc/specification/api.v1.raml 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: From 2ce44cc0b89bd118d34526271679941b9bc56729 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 15 Jul 2017 19:36:37 +0100 Subject: [PATCH 07/20] Progress... --- resources/public/css/yyy-site.css | 1 + resources/templates/app.html | 40 ++++++------------- resources/templates/base-authenticated.html | 44 ++++++++++++++++++--- resources/templates/login.html | 5 --- resources/templates/roles.html | 5 ++- src/clj/youyesyet/routes/home.clj | 6 ++- 6 files changed, 61 insertions(+), 40 deletions(-) diff --git a/resources/public/css/yyy-site.css b/resources/public/css/yyy-site.css index 5387c7a..6f9b521 100644 --- a/resources/public/css/yyy-site.css +++ b/resources/public/css/yyy-site.css @@ -46,6 +46,7 @@ #nav-menu { margin: 0; padding: 0; + width: 100%; } #nav menu li { diff --git a/resources/templates/app.html b/resources/templates/app.html index 5214806..024aa39 100644 --- a/resources/templates/app.html +++ b/resources/templates/app.html @@ -1,16 +1,5 @@ - - - - - - - - - - You Yes Yet? - - - +{% extends "base-authenticated.html" %} +{% block whole-page %}
@@ -33,21 +22,16 @@ You must enable JavaScript to use the You Yes Yet app.

- - - - - - - - - - {% script "/js/app.js" %} - - +{% endblock %} +{% block extra-script %} + + + + + + +{% script "/js/app.js" %} +{% endblock %} diff --git a/resources/templates/base-authenticated.html b/resources/templates/base-authenticated.html index 7b2d418..7da389b 100644 --- a/resources/templates/base-authenticated.html +++ b/resources/templates/base-authenticated.html @@ -3,12 +3,14 @@ - + + {{title}} + {% block whole-page %}
@@ -28,12 +34,40 @@
+ {% block big-links %} + {% endblock %} +
- {{content}} + {% block content %} + {% endblock %} +
+
+ + {% endblock %} + + {% block extra-script %} + {% endblock %} diff --git a/resources/templates/login.html b/resources/templates/login.html index f3e8731..5ee3706 100644 --- a/resources/templates/login.html +++ b/resources/templates/login.html @@ -1,9 +1,4 @@ {% extends "base-unauthenticated.html" %} -{% block big-links %} - -{% endblock %} {% block content %}

We're not going to do login in the long term; we're going to use oauth. diff --git a/resources/templates/roles.html b/resources/templates/roles.html index 1c61866..6b49d2e 100644 --- a/resources/templates/roles.html +++ b/resources/templates/roles.html @@ -1,5 +1,6 @@ {% extends "base-authenticated.html" %} {% block title %} + {{ user }} {% endblock %} {% block big-links %}

{% for role in roles %} {% endfor %} {% endblock %} +{% block content %} +{% endblock %} diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index 77ef2f0..9e27942 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -34,9 +34,11 @@ (defn app-page [] (layout/render "app.html")) + (defn about-page [] (layout/render "about.html")) + (defn call-me-page [request] (if request @@ -57,8 +59,10 @@ roles (if user (db-core/get-roles-by-canvasser db-core/*db* {:canvasser (:id user)}))] (cond roles (layout/render "roles.html" - {:title (str "Welcome " (:fullname user)) + {:title (str "Welcome " (:fullname user) ", what do you want to do?") + :user user :roles roles}) + (empty? roles)(response/found "/app") true (assoc (response/found "/login") :session (dissoc session :user)) ))) From e77d5d0393cc1ef972a9bb3f3980a7418226047d Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 16 Jul 2017 13:03:24 +0100 Subject: [PATCH 08/20] Documentation and disambiguation Separate package name hierarchies for clj and cljs parts of the system, so that it's unambiguous what package (e.g. 'youyesyet.core' is. --- env/dev/cljs/youyesyet/dev.cljs | 4 +-- env/prod/cljs/youyesyet/prod.cljs | 2 +- project.clj | 2 +- resources/sql/queries.sql | 18 +++++++++++++ src/clj/youyesyet/config.clj | 4 ++- src/clj/youyesyet/db/core.clj | 4 ++- src/clj/youyesyet/db/schema.clj | 3 ++- src/clj/youyesyet/handler.clj | 4 ++- src/clj/youyesyet/layout.clj | 6 ++++- src/clj/youyesyet/middleware.clj | 4 ++- src/clj/youyesyet/routes/administrator.clj | 11 ++++++++ src/clj/youyesyet/routes/authenticated.clj | 4 ++- src/clj/youyesyet/routes/home.clj | 3 ++- src/clj/youyesyet/routes/oauth.clj | 3 ++- src/clj/youyesyet/routes/services.clj | 3 ++- src/cljc/youyesyet/outqueue.cljc | 17 +++++++++--- .../youyesyet/{ => canvasser_app}/ajax.cljs | 6 +++-- .../youyesyet/{ => canvasser_app}/core.cljs | 26 ++++++++++--------- .../{ => canvasser_app}/handlers.cljs | 6 +++-- .../{db.cljs => canvasser_app/state.cljs} | 6 +++-- .../{ => canvasser_app}/subscriptions.cljs | 4 ++- .../{ => canvasser_app}/ui_utils.cljs | 4 ++- .../{ => canvasser_app}/views/about.cljs | 8 +++--- .../{ => canvasser_app}/views/building.cljs | 6 +++-- .../{ => canvasser_app}/views/electors.cljs | 8 +++--- .../{ => canvasser_app}/views/followup.cljs | 8 +++--- .../{ => canvasser_app}/views/issue.cljs | 8 +++--- .../{ => canvasser_app}/views/issues.cljs | 8 +++--- .../{ => canvasser_app}/views/map.cljs | 6 +++-- .../{ => canvasser_app}/core_test.cljs | 4 +-- .../youyesyet/canvasser_app/doo_runner.cljs | 6 +++++ test/cljs/youyesyet/doo_runner.cljs | 6 ----- 32 files changed, 148 insertions(+), 64 deletions(-) create mode 100644 src/clj/youyesyet/routes/administrator.clj rename src/cljs/youyesyet/{ => canvasser_app}/ajax.cljs (89%) rename src/cljs/youyesyet/{ => canvasser_app}/core.cljs (84%) rename src/cljs/youyesyet/{ => canvasser_app}/handlers.cljs (96%) rename src/cljs/youyesyet/{db.cljs => canvasser_app/state.cljs} (96%) rename src/cljs/youyesyet/{ => canvasser_app}/subscriptions.cljs (93%) rename src/cljs/youyesyet/{ => canvasser_app}/ui_utils.cljs (95%) rename src/cljs/youyesyet/{ => canvasser_app}/views/about.cljs (91%) rename src/cljs/youyesyet/{ => canvasser_app}/views/building.cljs (87%) rename src/cljs/youyesyet/{ => canvasser_app}/views/electors.cljs (94%) rename src/cljs/youyesyet/{ => canvasser_app}/views/followup.cljs (92%) rename src/cljs/youyesyet/{ => canvasser_app}/views/issue.cljs (88%) rename src/cljs/youyesyet/{ => canvasser_app}/views/issues.cljs (88%) rename src/cljs/youyesyet/{ => canvasser_app}/views/map.cljs (96%) rename test/cljs/youyesyet/{ => canvasser_app}/core_test.cljs (65%) create mode 100644 test/cljs/youyesyet/canvasser_app/doo_runner.cljs delete mode 100644 test/cljs/youyesyet/doo_runner.cljs diff --git a/env/dev/cljs/youyesyet/dev.cljs b/env/dev/cljs/youyesyet/dev.cljs index a246523..6394c48 100644 --- a/env/dev/cljs/youyesyet/dev.cljs +++ b/env/dev/cljs/youyesyet/dev.cljs @@ -1,5 +1,5 @@ -(ns ^:figwheel-no-load youyesyet.app - (:require [youyesyet.core :as core] +(ns ^:figwheel-no-load youyesyet.canvasser-app.app + (:require [youyesyet.canvasser-app.core :as core] [devtools.core :as devtools] [figwheel.client :as figwheel :include-macros true])) diff --git a/env/prod/cljs/youyesyet/prod.cljs b/env/prod/cljs/youyesyet/prod.cljs index ca12fd2..8cc0292 100644 --- a/env/prod/cljs/youyesyet/prod.cljs +++ b/env/prod/cljs/youyesyet/prod.cljs @@ -1,5 +1,5 @@ (ns youyesyet.app - (:require [youyesyet.core :as core])) + (:require [youyesyet.canvasser-app.core :as core])) ;;ignore println statements in prod (set! *print-fn* (fn [& _])) diff --git a/project.clj b/project.clj index 6108563..9a06d8c 100644 --- a/project.clj +++ b/project.clj @@ -121,7 +121,7 @@ {:app {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"] :compiler - {:main "youyesyet.app" + {:main "youyesyet.canvasser-app.app" :asset-path "/js/out" :output-to "target/cljsbuild/public/js/app.js" :output-dir "target/cljsbuild/public/js/out" diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index e708a7b..4d866db 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -31,6 +31,7 @@ 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 @@ -54,6 +55,7 @@ WHERE id = :id INSERT INTO authorities (id) VALUES (:id) +RETURNING id -- :name update-authority! :! :n -- :doc update an existing authority record @@ -81,6 +83,7 @@ WHERE id = :id INSERT INTO canvassers (username, fullname, elector_id, address_id, phone, email, authority_id, authorised) VALUES (:username, :fullname, :elector_id, :address_id, :phone, :email, :authority_id, :authorised) +RETURNING id -- :name update-canvasser! :! :n -- :doc update an existing canvasser record @@ -114,6 +117,7 @@ WHERE id = :id INSERT INTO districts (id, name) VALUES (:id, :name) +RETURNING id -- :name update-district! :! :n -- :doc update an existing district record @@ -137,6 +141,7 @@ WHERE id = :id INSERT INTO electors (name, address_id, phone, email) VALUES (:name, :address_id, :phone, :email) +RETURNING id -- :name update-elector! :! :n -- :doc update an existing elector record @@ -160,6 +165,7 @@ WHERE id = :id 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. @@ -179,6 +185,7 @@ WHERE id = :id 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. @@ -195,6 +202,7 @@ WHERE id = :id 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 @@ -219,6 +227,8 @@ WHERE id = :id INSERT INTO issues (id, url, content, current) VALUES (:id, :url, :content, :current) +RETURNING id + -- :name update-issue! :! :n -- :doc update an existing issue record @@ -244,6 +254,7 @@ WHERE id = :id INSERT INTO visits (address_id, canvasser_id) VALUES (:address_id, :canvasser_id) +RETURNING id -- visits is audit data; we don't update it. @@ -277,6 +288,13 @@ select * from canvassers_by_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 diff --git a/src/clj/youyesyet/config.clj b/src/clj/youyesyet/config.clj index d3f80e0..2de50fa 100644 --- a/src/clj/youyesyet/config.clj +++ b/src/clj/youyesyet/config.clj @@ -1,4 +1,6 @@ -(ns youyesyet.config +(ns ^{:doc "Read configuration." + :author "Simon Brooke"} + youyesyet.config (:require [cprop.core :refer [load-config]] [cprop.source :as source] [mount.core :refer [args defstate]])) diff --git a/src/clj/youyesyet/db/core.clj b/src/clj/youyesyet/db/core.clj index 696e40b..a57fa50 100644 --- a/src/clj/youyesyet/db/core.clj +++ b/src/clj/youyesyet/db/core.clj @@ -1,4 +1,6 @@ -(ns youyesyet.db.core +(ns ^{:doc "Database access functions." + :author "Simon Brooke"} + youyesyet.db.core (:require [cheshire.core :refer [generate-string parse-string]] [clojure.java.jdbc :as jdbc] diff --git a/src/clj/youyesyet/db/schema.clj b/src/clj/youyesyet/db/schema.clj index 7a6c45b..7571251 100644 --- a/src/clj/youyesyet/db/schema.clj +++ b/src/clj/youyesyet/db/schema.clj @@ -1,4 +1,5 @@ -(ns youyesyet.db.schema +(ns ^{:doc "Korma-flavour database setup, now obsolete but retained for documentation." + :author "Simon Brooke"} youyesyet.db.schema (:require [clojure.java.jdbc :as sql] [korma.core :as kc] [youyesyet.db.core :as yyydb])) diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index e25b53c..173497e 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -1,4 +1,6 @@ -(ns youyesyet.handler +(ns ^{:doc "Handlers for starting and stopping the webapp." + :author "Simon Brooke"} + youyesyet.handler (:require [compojure.core :refer [routes wrap-routes]] [youyesyet.layout :refer [error-page]] [youyesyet.routes.authenticated :refer [authenticated-routes]] diff --git a/src/clj/youyesyet/layout.clj b/src/clj/youyesyet/layout.clj index aaec434..d131cb7 100644 --- a/src/clj/youyesyet/layout.clj +++ b/src/clj/youyesyet/layout.clj @@ -1,4 +1,6 @@ -(ns youyesyet.layout +(ns^{:doc "Render web pages using Selmer tamplating markup." + :author "Simon Brooke"} + youyesyet.layout (:require [selmer.parser :as parser] [selmer.filters :as filters] [markdown.core :refer [md-to-html-string]] @@ -12,6 +14,7 @@ (parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) (filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)])) + (defn render "renders the HTML template located relative to resources/templates" [template & [params]] @@ -26,6 +29,7 @@ :version (System/getProperty "youyesyet.version")))) "text/html; charset=utf-8")) + (defn error-page "error-details should be a map containing the following keys: :status - error status diff --git a/src/clj/youyesyet/middleware.clj b/src/clj/youyesyet/middleware.clj index 1775cb9..2288720 100644 --- a/src/clj/youyesyet/middleware.clj +++ b/src/clj/youyesyet/middleware.clj @@ -1,4 +1,6 @@ -(ns youyesyet.middleware +(ns ^{:doc "Plumbing, mainly boilerplate from Luminus." + :author "Simon Brooke"} + youyesyet.middleware (:require [youyesyet.env :refer [defaults]] [clojure.tools.logging :as log] [youyesyet.layout :refer [*app-context* error-page]] diff --git a/src/clj/youyesyet/routes/administrator.clj b/src/clj/youyesyet/routes/administrator.clj new file mode 100644 index 0000000..7a08b36 --- /dev/null +++ b/src/clj/youyesyet/routes/administrator.clj @@ -0,0 +1,11 @@ +(ns ^{:doc "Routes/pages available to administrators, only." + :author "Simon Brooke"} + youyesyet.routes.administrator + (:require [clojure.java.io :as io] + [clojure.walk :refer [keywordize-keys]] + [compojure.core :refer [defroutes GET POST]] + [noir.response :as nresponse] + [noir.util.route :as route] + [ring.util.http-response :as response] + [youyesyet.layout :as layout] + [youyesyet.db.core :as db])) diff --git a/src/clj/youyesyet/routes/authenticated.clj b/src/clj/youyesyet/routes/authenticated.clj index e8b1b7b..b02c512 100644 --- a/src/clj/youyesyet/routes/authenticated.clj +++ b/src/clj/youyesyet/routes/authenticated.clj @@ -1,4 +1,6 @@ -(ns youyesyet.routes.authenticated +(ns ^{:doc "Routes/pages available to all authenticated users." + :author "Simon Brooke"} + youyesyet.routes.authenticated (:require [clojure.java.io :as io] [clojure.walk :refer [keywordize-keys]] [compojure.core :refer [defroutes GET POST]] diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index 9e27942..9fbc094 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -1,4 +1,5 @@ -(ns youyesyet.routes.home +(ns ^{:doc "Routes/pages available to unauthenticated users." + :author "Simon Brooke"} youyesyet.routes.home (:require [clojure.walk :refer [keywordize-keys]] [noir.response :as nresponse] [noir.util.route :as route] diff --git a/src/clj/youyesyet/routes/oauth.clj b/src/clj/youyesyet/routes/oauth.clj index d916e10..a7c7e46 100644 --- a/src/clj/youyesyet/routes/oauth.clj +++ b/src/clj/youyesyet/routes/oauth.clj @@ -1,4 +1,5 @@ -(ns youyesyet.routes.oauth +(ns ^{:doc "OAuth authentication routes - not finished, does not work yet." + :author "Simon Brooke"} youyesyet.routes.oauth (:require [compojure.core :refer [defroutes GET]] [ring.util.http-response :refer [ok found]] [clojure.java.io :as io] diff --git a/src/clj/youyesyet/routes/services.clj b/src/clj/youyesyet/routes/services.clj index c0de2b5..43347c7 100644 --- a/src/clj/youyesyet/routes/services.clj +++ b/src/clj/youyesyet/routes/services.clj @@ -1,4 +1,5 @@ -(ns youyesyet.routes.services +(ns ^{:doc "REST API." + :author "Simon Brooke"} youyesyet.routes.services (:require [clj-http.client :as client] [ring.util.http-response :refer :all] [compojure.api.sweet :refer :all] diff --git a/src/cljc/youyesyet/outqueue.cljc b/src/cljc/youyesyet/outqueue.cljc index f2f0df3..8386d4a 100644 --- a/src/cljc/youyesyet/outqueue.cljc +++ b/src/cljc/youyesyet/outqueue.cljc @@ -1,4 +1,6 @@ -(ns youyesyet.outqueue +(ns ^{:doc "Queue of messages waiting to be sent to the server." + :author "Simon Brooke"} + youyesyet.outqueue (:require #?(:clj [clojure.core] :cljs [reagent.core :refer [atom]]))) @@ -41,6 +43,7 @@ (reverse items) (list items))}))) + (defn add! "Add this item to the queue." [q item] @@ -49,7 +52,9 @@ (assoc a :items (cons item (:items a)))))) -(defn q? + +(defn queue? + "True if x is a queue, else false." [x] (try (let [q (deref x) @@ -61,21 +66,25 @@ #?(:clj (print (.getMessage any)) :cljs (js/console.log (str any)))))) + (defn peek "Look at the next item which could be removed from the queue." [q] (last (:items (deref q)))) + (defn locked? [q] (:locked (deref q))) + (defn unlock! ([q ] (unlock! q true)) ([q value] (swap! q (fn [a] (assoc a :locked (not (true? value))))))) + (defn lock! [q] (unlock! q false)) @@ -86,6 +95,7 @@ [q] (count (deref q))) + (defn take! "Return the first item from the queue, rebind the queue to the remaining items. If the queue is empty return nil." @@ -97,12 +107,13 @@ (assoc (assoc a :items new-queue) :v item)))) (:v (deref q))) + (defn maybe-process-next "Apply this process, assumed to be a function of one argument, to the next item in the queue, if the queue is not currently locked; return the value returned by process." [q process] - (if (and (q? q)(not (locked? q))) + (if (and (queue? q)(not (locked? q))) (try (lock! q) (let [v (apply process (list (peek q)))] diff --git a/src/cljs/youyesyet/ajax.cljs b/src/cljs/youyesyet/canvasser_app/ajax.cljs similarity index 89% rename from src/cljs/youyesyet/ajax.cljs rename to src/cljs/youyesyet/canvasser_app/ajax.cljs index ef3a3cb..3db48fc 100644 --- a/src/cljs/youyesyet/ajax.cljs +++ b/src/cljs/youyesyet/canvasser_app/ajax.cljs @@ -1,9 +1,11 @@ -(ns youyesyet.ajax +(ns ^{:doc "Canvasser app transciever for ajax packets." + :author "Simon Brooke"} + youyesyet.canvasser-app.ajax (:require [ajax.core :as ajax])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.ajax: transciever for ajax packets. +;;;; youyesyet.canvasser-app.ajax: transciever for ajax packets. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/core.cljs b/src/cljs/youyesyet/canvasser_app/core.cljs similarity index 84% rename from src/cljs/youyesyet/core.cljs rename to src/cljs/youyesyet/canvasser_app/core.cljs index 27e831c..971261e 100644 --- a/src/cljs/youyesyet/core.cljs +++ b/src/cljs/youyesyet/canvasser_app/core.cljs @@ -1,4 +1,6 @@ -(ns youyesyet.core +(ns ^{:doc "Canvasser app navigation and routing." + :author "Simon Brooke"} + youyesyet.canvasser-app.core (:require cljsjs.react-leaflet [ajax.core :refer [GET POST]] [goog.events :as events] @@ -7,21 +9,21 @@ [reagent.core :as r] [re-frame.core :as rf] [secretary.core :as secretary] - [youyesyet.ajax :refer [load-interceptors!]] - [youyesyet.handlers] - [youyesyet.subscriptions] - [youyesyet.ui-utils :as ui] - [youyesyet.views.about :as about] - [youyesyet.views.electors :as electors] - [youyesyet.views.followup :as followup] - [youyesyet.views.issue :as issue] - [youyesyet.views.issues :as issues] - [youyesyet.views.map :as maps]) + [youyesyet.canvasser-app.ajax :refer [load-interceptors!]] + [youyesyet.canvasser-app.handlers] + [youyesyet.canvasser-app.subscriptions] + [youyesyet.canvasser-app.ui-utils :as ui] + [youyesyet.canvasser-app.views.about :as about] + [youyesyet.canvasser-app.views.electors :as electors] + [youyesyet.canvasser-app.views.followup :as followup] + [youyesyet.canvasser-app.views.issue :as issue] + [youyesyet.canvasser-app.views.issues :as issues] + [youyesyet.canvasser-app.views.map :as maps]) (:import goog.History)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.core: core of the app. +;;;; youyesyet.canvasser-app.core: core of the app. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/handlers.cljs b/src/cljs/youyesyet/canvasser_app/handlers.cljs similarity index 96% rename from src/cljs/youyesyet/handlers.cljs rename to src/cljs/youyesyet/canvasser_app/handlers.cljs index deb11f4..e94d1b2 100644 --- a/src/cljs/youyesyet/handlers.cljs +++ b/src/cljs/youyesyet/canvasser_app/handlers.cljs @@ -1,7 +1,9 @@ -(ns youyesyet.handlers +(ns ^{:doc "Canvasser app event handlers." + :author "Simon Brooke"} + youyesyet.canvasser-app.handlers (:require [cljs.reader :refer [read-string]] [re-frame.core :refer [dispatch reg-event-db]] - [youyesyet.db :as db] + [youyesyet.canvasser-app.state :as db] )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/cljs/youyesyet/db.cljs b/src/cljs/youyesyet/canvasser_app/state.cljs similarity index 96% rename from src/cljs/youyesyet/db.cljs rename to src/cljs/youyesyet/canvasser_app/state.cljs index 9659c44..711413e 100644 --- a/src/cljs/youyesyet/db.cljs +++ b/src/cljs/youyesyet/canvasser_app/state.cljs @@ -1,8 +1,10 @@ -(ns youyesyet.db) +(ns ^{:doc "Canvasser app client state." + :author "Simon Brooke"} + youyesyet.canvasser-app.state) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.db: the state of the app. +;;;; youyesyet.canvasser-app.state: the state of the app. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/subscriptions.cljs b/src/cljs/youyesyet/canvasser_app/subscriptions.cljs similarity index 93% rename from src/cljs/youyesyet/subscriptions.cljs rename to src/cljs/youyesyet/canvasser_app/subscriptions.cljs index b1aa09c..c3d18e5 100644 --- a/src/cljs/youyesyet/subscriptions.cljs +++ b/src/cljs/youyesyet/canvasser_app/subscriptions.cljs @@ -1,4 +1,6 @@ -(ns youyesyet.subscriptions +(ns ^{:doc "Canvasser app event subscriptions." + :author "Simon Brooke"} + youyesyet.canvasser-app.subscriptions (:require [re-frame.core :refer [reg-sub]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/cljs/youyesyet/ui_utils.cljs b/src/cljs/youyesyet/canvasser_app/ui_utils.cljs similarity index 95% rename from src/cljs/youyesyet/ui_utils.cljs rename to src/cljs/youyesyet/canvasser_app/ui_utils.cljs index 719090b..068bad6 100644 --- a/src/cljs/youyesyet/ui_utils.cljs +++ b/src/cljs/youyesyet/canvasser_app/ui_utils.cljs @@ -1,4 +1,6 @@ -(ns youyesyet.ui-utils +(ns ^{:doc "Canvasser app user interface widgets." + :author "Simon Brooke"} + youyesyet.canvasser-app.ui-utils (:require [reagent.core :as r] [re-frame.core :as rf])) diff --git a/src/cljs/youyesyet/views/about.cljs b/src/cljs/youyesyet/canvasser_app/views/about.cljs similarity index 91% rename from src/cljs/youyesyet/views/about.cljs rename to src/cljs/youyesyet/canvasser_app/views/about.cljs index 4ca4a4a..6b4d9ca 100644 --- a/src/cljs/youyesyet/views/about.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/about.cljs @@ -1,11 +1,13 @@ -(ns youyesyet.views.about +(ns ^{:doc "Canvasser app about panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.about (:require [re-frame.core :refer [reg-sub subscribe]] [markdown.core :refer [md->html]] - [youyesyet.ui-utils :as ui])) + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.about: about/credits view for youyesyet. +;;;; youyesyet.canvasser-app.views.about: about/credits 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 diff --git a/src/cljs/youyesyet/views/building.cljs b/src/cljs/youyesyet/canvasser_app/views/building.cljs similarity index 87% rename from src/cljs/youyesyet/views/building.cljs rename to src/cljs/youyesyet/canvasser_app/views/building.cljs index 01d3dd6..40e5d0e 100644 --- a/src/cljs/youyesyet/views/building.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/building.cljs @@ -1,9 +1,11 @@ -(ns youyesyet.views.building +(ns ^{:doc "Canvasser app households in building panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.building (:require [re-frame.core :refer [reg-sub]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.building: building view for youyesyet. +;;;; youyesyet.canvasser-app.views.building: building 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 diff --git a/src/cljs/youyesyet/views/electors.cljs b/src/cljs/youyesyet/canvasser_app/views/electors.cljs similarity index 94% rename from src/cljs/youyesyet/views/electors.cljs rename to src/cljs/youyesyet/canvasser_app/views/electors.cljs index 05f7af4..aee2d4d 100644 --- a/src/cljs/youyesyet/views/electors.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/electors.cljs @@ -1,11 +1,13 @@ -(ns youyesyet.views.electors +(ns ^{:doc "Canvasser app electors in household panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.electors (:require [reagent.core :refer [atom]] [re-frame.core :refer [reg-sub subscribe dispatch]] - [youyesyet.ui-utils :as ui])) + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.electors: electors view for youyesyet. +;;;; youyesyet.canvasser-app.views.electors: electors 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 diff --git a/src/cljs/youyesyet/views/followup.cljs b/src/cljs/youyesyet/canvasser_app/views/followup.cljs similarity index 92% rename from src/cljs/youyesyet/views/followup.cljs rename to src/cljs/youyesyet/canvasser_app/views/followup.cljs index 78c0c04..b81ff58 100644 --- a/src/cljs/youyesyet/views/followup.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/followup.cljs @@ -1,10 +1,12 @@ -(ns youyesyet.views.followup +(ns ^{:doc "Canvasser followup request form panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.followup (:require [re-frame.core :refer [reg-sub subscribe dispatch]] - [youyesyet.ui-utils :as ui])) + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.followup-request: followup-request view for youyesyet. +;;;; youyesyet.canvasser-app.views.followup-request: 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 diff --git a/src/cljs/youyesyet/views/issue.cljs b/src/cljs/youyesyet/canvasser_app/views/issue.cljs similarity index 88% rename from src/cljs/youyesyet/views/issue.cljs rename to src/cljs/youyesyet/canvasser_app/views/issue.cljs index 20620d6..34afe10 100644 --- a/src/cljs/youyesyet/views/issue.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issue.cljs @@ -1,8 +1,10 @@ -(ns youyesyet.views.issue +(ns ^{:doc "Canvasser app current issue detail panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.issue (:require [re-frame.core :refer [reg-sub subscribe]] [markdown.core :refer [md->html]] - [youyesyet.ui-utils :as ui] - [youyesyet.views.issues :as issues])) + [youyesyet.canvasser-app.ui-utils :as ui] + [youyesyet.canvasser-app.views.issues :as issues])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; diff --git a/src/cljs/youyesyet/views/issues.cljs b/src/cljs/youyesyet/canvasser_app/views/issues.cljs similarity index 88% rename from src/cljs/youyesyet/views/issues.cljs rename to src/cljs/youyesyet/canvasser_app/views/issues.cljs index ea8f383..8aa1744 100644 --- a/src/cljs/youyesyet/views/issues.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issues.cljs @@ -1,10 +1,12 @@ -(ns youyesyet.views.issues +(ns ^{:doc "Canvasser app current issues list panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.issues (:require [re-frame.core :refer [reg-sub subscribe]] - [youyesyet.ui-utils :as ui])) + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.issues: issues view for youyesyet. +;;;; youyesyet.canvasser-app.views.issues: issues 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 diff --git a/src/cljs/youyesyet/views/map.cljs b/src/cljs/youyesyet/canvasser_app/views/map.cljs similarity index 96% rename from src/cljs/youyesyet/views/map.cljs rename to src/cljs/youyesyet/canvasser_app/views/map.cljs index c725531..6df73dd 100644 --- a/src/cljs/youyesyet/views/map.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/map.cljs @@ -1,10 +1,12 @@ -(ns youyesyet.views.map +(ns ^{:doc "Canvasser app map view panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.map (:require [re-frame.core :refer [reg-sub subscribe dispatch]] [reagent.core :as reagent])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.map: map view for youyesyet. +;;;; youyesyet.canvasser-app.views.map: map 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 diff --git a/test/cljs/youyesyet/core_test.cljs b/test/cljs/youyesyet/canvasser_app/core_test.cljs similarity index 65% rename from test/cljs/youyesyet/core_test.cljs rename to test/cljs/youyesyet/canvasser_app/core_test.cljs index 7de65c8..d0f5e40 100644 --- a/test/cljs/youyesyet/core_test.cljs +++ b/test/cljs/youyesyet/canvasser_app/core_test.cljs @@ -1,7 +1,7 @@ -(ns youyesyet.core-test +(ns youyesyet.canvasser-app.core-test (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]] [reagent.core :as reagent :refer [atom]] - [youyesyet.core :as rc])) + [youyesyet.canvasser-app.core :as rc])) (deftest test-home (is (= true true))) diff --git a/test/cljs/youyesyet/canvasser_app/doo_runner.cljs b/test/cljs/youyesyet/canvasser_app/doo_runner.cljs new file mode 100644 index 0000000..f6c89dd --- /dev/null +++ b/test/cljs/youyesyet/canvasser_app/doo_runner.cljs @@ -0,0 +1,6 @@ +(ns youyesyet.canvasser-app.doo-runner + (:require [doo.runner :refer-macros [doo-tests]] + [youyesyet.canvasser-app.core-test])) + +(doo-tests 'youyesyet.canvasser-app.canvasser-app.core-test) + diff --git a/test/cljs/youyesyet/doo_runner.cljs b/test/cljs/youyesyet/doo_runner.cljs deleted file mode 100644 index 486a282..0000000 --- a/test/cljs/youyesyet/doo_runner.cljs +++ /dev/null @@ -1,6 +0,0 @@ -(ns youyesyet.doo-runner - (:require [doo.runner :refer-macros [doo-tests]] - [youyesyet.core-test])) - -(doo-tests 'youyesyet.core-test) - From c87efe9a662d3014e59633f7b294bf2e3672e6fb Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 21 Jul 2017 08:46:50 +0100 Subject: [PATCH 09/20] Documentation changes associated with introdoction of codox --- doc/specification/scaling.md | 4 ++-- doc/specification/userspec.md | 2 +- project.clj | 7 ++++++- resources/sql/queries.sql | 2 ++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/doc/specification/scaling.md b/doc/specification/scaling.md index bfc16de..922c473 100644 --- a/doc/specification/scaling.md +++ b/doc/specification/scaling.md @@ -22,7 +22,7 @@ Database reads are probably more infrequent. Each client will obviously need to Mobile phones typically can have intermittent network access. The client must be able to buffer a queue of records to be stored, and must not prevent the user from moving on to the next doorstep just because the data from the last visit has not yet been stored. There should probably be some on-screen indication of when there is unsent buffered data. -### Pattern of canvassing +### Pattern of canvassing Canvassing takes place typically between 6:30pm and 9:00pm on a weekday evening. There will be some canvassing outside this period, but not enough to create significant load. Canvassing will be higher on dry nights than on wet ones, and will probably ramp up through the campaign. @@ -40,7 +40,7 @@ This means that the maximum number of transactions per second across Scotland is 700 transactions per second is not a very large number. We should be able to support this level of load on a single server. But what if we can't? -## Spreading the load +## Spreading the load ### Caching and memoizing diff --git a/doc/specification/userspec.md b/doc/specification/userspec.md index 3f494b1..efa9f59 100644 --- a/doc/specification/userspec.md +++ b/doc/specification/userspec.md @@ -1,4 +1,4 @@ -### YouYesYet: User-oriented specification +# YouYesYet: User-oriented specification ## Overview diff --git a/project.clj b/project.clj index 9a06d8c..e248722 100644 --- a/project.clj +++ b/project.clj @@ -55,12 +55,17 @@ [lein-cljsbuild "1.1.4"] [lein-uberwar "0.2.0"] [lein-bower "0.5.1"] - [lein-less "1.7.5"]] + [lein-less "1.7.5"] + [lein-codox "0.10.3"]] :bower-dependencies [[leaflet "0.7.3"]] :cucumber-feature-paths ["test/clj/features"] + :codox {:metadata {:doc "FIXME: write docs"} + :languages [:clojure :clojurescript] + :source-paths ["src/clj" "src/cljc" "src/cljs"]} + :uberwar {:handler youyesyet.handler/app :init youyesyet.handler/init diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index 4d866db..5309965 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -44,6 +44,8 @@ WHERE id = :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 From fc1b17b7a5f76372f010bf2025c869f13f503515 Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 21 Jul 2017 12:18:42 +0100 Subject: [PATCH 10/20] #41 Updated ERD; added and tested migrations. --- .../entity-relationship-diagram.svg | 475 ++++++++++-------- .../20170721084900-dwellings.down.sql | 69 +++ .../20170721084900-dwellings.up.sql | 87 ++++ 3 files changed, 434 insertions(+), 197 deletions(-) create mode 100644 resources/migrations/20170721084900-dwellings.down.sql create mode 100644 resources/migrations/20170721084900-dwellings.up.sql diff --git a/doc/specification/entity-relationship-diagram.svg b/doc/specification/entity-relationship-diagram.svg index 0de6cf0..b20898a 100644 --- a/doc/specification/entity-relationship-diagram.svg +++ b/doc/specification/entity-relationship-diagram.svg @@ -25,16 +25,16 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.979899" - inkscape:cx="833.70674" - inkscape:cy="324.89697" + inkscape:zoom="0.98994949" + inkscape:cx="455.50968" + inkscape:cy="346.1212" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="1920" - inkscape:window-height="1058" - inkscape:window-x="1920" - inkscape:window-y="0" + inkscape:window-height="1031" + inkscape:window-x="0" + inkscape:window-y="27" inkscape:window-maximized="1"> + x="8.484766" + y="312.36221" + sodipodi:insensitive="true" /> + y="354.3468" /> YouYesYet: Entity Relationship Diagram + + + District + - District - Addresss + sodipodi:role="line">Dwelling + y="672.276" /> Elector Canvasser + y="553.07794" /> Authority + y="553.07794" /> Visit Intention @@ -236,26 +241,26 @@ width="100" height="60" x="732.33264" - y="775.31158" /> + y="794.50446" /> IssueExpertise Issue FollowupRequest @@ -303,27 +308,27 @@ width="100" height="60" x="220.18532" - y="896.52985" /> + y="915.72272" /> FollowupAction + transform="translate(170.18532,48.487322)"> + transform="translate(682.33266,290.92393)"> + id="g4369" + transform="translate(0,19.192898)"> @@ -402,7 +408,7 @@ + transform="translate(170.18532,48.487322)"> + id="g4402" + transform="translate(-168.69547,-222.23356)"> @@ -442,7 +449,7 @@ + transform="translate(169.17517,171.72593)"> + transform="translate(-170.71578,139.40105)"> @@ -604,146 +611,146 @@ xml:space="preserve" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="472.0152" - y="394.48404" + y="413.67694" id="text4361" sodipodi:linespacing="125%">Version: 0.2Version: 0.3Date: 20170315Date: 20170401Author: Simon BrookeCopyright: (c) 2016 Simon Brooke for Radical Independence Campaign Introduced Visited Recorded + y="651.55505" + 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">Recorded Raised + x="49.187798" + y="391.35205" + style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Bold Italic';text-align:start;writing-mode:lr-tb;text-anchor:start">Raised Authenticates + y="641.55505" + 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">Authenticates Has + y="761.55505" + 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">Has About + y="881.55505" + 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">About About @@ -751,89 +758,94 @@ xml:space="preserve" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="280" - y="872.36218" + y="891.55505" id="text4417" sodipodi:linespacing="125%">Responded to + y="891.55505" + 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">Responded to Expressed + x="161.30452" + y="480.12863" + 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">Contains Contains + y="531.55505" + 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">Contains Resides at + y="651.55505" + 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">Resides at Requested + y="761.55505" + 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">Requested Performed + y="1021.5551" + 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">Performed To + y="571.55505" + 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">To + y="672.276" /> Team + y="792.48413" /> + y="581.55505" /> Organiser-ship TeamTeamMembership Has @@ -978,41 +990,41 @@ xml:space="preserve" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="879.90356" - y="621.14386" + y="640.33673" id="text4344" sodipodi:linespacing="125%">Has of of + transform="matrix(1,0,0,-1,171.72593,1625.9208)"> @@ -1037,13 +1049,13 @@ xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="450" - y="742.36218" + y="761.55505" id="text4386" sodipodi:linespacing="125%">For + y="672.276" /> RoleRoleMembership + transform="translate(0,-222.73864)"> Role + id="g4338" + transform="translate(0,19.192898)"> Is Includes + transform="translate(341.43156,-222.73861)"> @@ -1159,5 +1172,73 @@ inkscape:connector-curvature="0" /> + + + FollowupMethod + + + + + + + Address diff --git a/resources/migrations/20170721084900-dwellings.down.sql b/resources/migrations/20170721084900-dwellings.down.sql new file mode 100644 index 0000000..674fb1b --- /dev/null +++ b/resources/migrations/20170721084900-dwellings.down.sql @@ -0,0 +1,69 @@ +-------------------------------------------------------------------------------- +---- +---- 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 new file mode 100644 index 0000000..da4a125 --- /dev/null +++ b/resources/migrations/20170721084900-dwellings.up.sql @@ -0,0 +1,87 @@ +-------------------------------------------------------------------------------- +---- +---- 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, + 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; +--;; From 2e81c7b6647fbd26749e312931af32b66fd0632f Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 21 Jul 2017 14:18:22 +0100 Subject: [PATCH 11/20] #41 Updated client to cope with dwelling changs --- resources/sql/queries.sql | 51 ++++++++-- src/cljs/youyesyet/canvasser_app/core.cljs | 11 ++- .../youyesyet/canvasser_app/handlers.cljs | 24 ++++- src/cljs/youyesyet/canvasser_app/state.cljs | 96 ++++++++++--------- .../canvasser_app/subscriptions.cljs | 5 + .../canvasser_app/views/building.cljs | 21 +++- .../canvasser_app/views/electors.cljs | 16 ++-- .../youyesyet/canvasser_app/views/issues.cljs | 2 +- .../youyesyet/canvasser_app/views/map.cljs | 11 ++- 9 files changed, 165 insertions(+), 72 deletions(-) diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index 5309965..e2170b8 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -83,14 +83,14 @@ WHERE id = :id -- :name create-canvasser! :! :n -- :doc creates a new canvasser record INSERT INTO canvassers -(username, fullname, elector_id, address_id, phone, email, authority_id, authorised) -VALUES (:username, :fullname, :elector_id, :address_id, :phone, :email, :authority_id, :authorised) +(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, address_id = :address_id, phone = :phone, email = :email, authority_id = :authority_id, authorised = :authorised +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 @@ -138,17 +138,52 @@ 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, address_id, phone, email) -VALUES (:name, :address_id, :phone, :email) +(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, address_id = :address_id, phone = :phone, email = :email +SET name = :name, dwelling_id = :dwelling_id, phone = :phone, email = :email WHERE id = :id -- :name get-elector :? :1 @@ -254,8 +289,8 @@ WHERE id = :id -- :name create-visit! :! :n -- :doc creates a new visit record INSERT INTO visits -(address_id, canvasser_id) -VALUES (:address_id, :canvasser_id) +(dwelling_id, canvasser_id) +VALUES (:dwelling_id, :canvasser_id) RETURNING id -- visits is audit data; we don't update it. diff --git a/src/cljs/youyesyet/canvasser_app/core.cljs b/src/cljs/youyesyet/canvasser_app/core.cljs index 971261e..e492a35 100644 --- a/src/cljs/youyesyet/canvasser_app/core.cljs +++ b/src/cljs/youyesyet/canvasser_app/core.cljs @@ -14,6 +14,7 @@ [youyesyet.canvasser-app.subscriptions] [youyesyet.canvasser-app.ui-utils :as ui] [youyesyet.canvasser-app.views.about :as about] + [youyesyet.canvasser-app.views.building :as building] [youyesyet.canvasser-app.views.electors :as electors] [youyesyet.canvasser-app.views.followup :as followup] [youyesyet.canvasser-app.views.issue :as issue] @@ -50,6 +51,9 @@ (defn about-page [] (about/panel)) +(defn building-page [] + (building/panel)) + (defn electors-page [] (electors/panel)) @@ -67,6 +71,7 @@ (def pages {:about #'about-page + :building #'building-page :electors #'electors-page :followup #'followup-page :issues #'issues-page @@ -105,10 +110,10 @@ (secretary/defroute "/about" [] (rf/dispatch [:set-active-page :about])) -(secretary/defroute "/electors" [] - (rf/dispatch [:set-active-page :electors])) +(secretary/defroute "/electors/:dwelling" {dwelling-id :dwelling} + (rf/dispatch [:set-dwelling dwelling-id])) -(secretary/defroute "/electors/:address" {address-id :address} +(secretary/defroute "/building/:address" {address-id :address} (rf/dispatch [:set-address address-id])) (secretary/defroute "/followup" [] diff --git a/src/cljs/youyesyet/canvasser_app/handlers.cljs b/src/cljs/youyesyet/canvasser_app/handlers.cljs index e94d1b2..813ec99 100644 --- a/src/cljs/youyesyet/canvasser_app/handlers.cljs +++ b/src/cljs/youyesyet/canvasser_app/handlers.cljs @@ -112,7 +112,29 @@ (fn [db [_ address-id]] (let [id (read-string address-id) address (first (remove nil? (map #(if (= id (:id %)) %) (:addresses db))))] - (assoc (clear-messages db) :address address :page :electors)))) + (if + (= (count (:dwellings address)) 1) + (assoc (clear-messages db) + :address address + :dwelling (first (:dwellings address)) + :page :electors) + (assoc (clear-messages db) + :address address + :dwelling nil + :page :building))))) + + +(reg-event-db + :set-dwelling + (fn [db [_ dwelling-id]] + (let [id (read-string dwelling-id) + dwelling (first + (remove + nil? + (map + #(if (= id (:id %)) %) + (mapcat :dwellings (:addresses db)))))] + (assoc (clear-messages db) :dwelling dwelling :page :electors)))) (reg-event-db diff --git a/src/cljs/youyesyet/canvasser_app/state.cljs b/src/cljs/youyesyet/canvasser_app/state.cljs index 711413e..c967f7c 100644 --- a/src/cljs/youyesyet/canvasser_app/state.cljs +++ b/src/cljs/youyesyet/canvasser_app/state.cljs @@ -33,49 +33,53 @@ (def default-db {;;; the currently selected address, if any. - :address {:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2569057 - :electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no} - {:id 2 :name "Ann Anderson" :gender :female} - {:id 3 :name "Alex Anderson" :gender :fluid :intention :yes} - {:id 4 :name "Andy Anderson" :intention :yes}]} - ;;; a list of the addresses in the current location at which there - ;;; are electors registered. - :addresses [{:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2570944 - :electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no} - {:id 2 :name "Ann Anderson" :gender :female} - {:id 3 :name "Alex Anderson" :gender :fluid :intention :yes} - {:id 4 :name "Andy Anderson" :intention :yes}]} - {:id 2 :address "15 Imaginary Terrace, IM1 3TE" :latitude 55.8252354 :longitude -4.2572778 - :electors [{:id 1 :name "Beryl Brown" :gender :female} - {:id 2 :name "Betty Black" :gender :female}]} - {:id 3 :address "17 Imaginary Terrace, IM1 3TE" :latitude 55.825166 :longitude -4.257026 - :electors [{:id 1 :name "Catriona Crathie" :gender :female :intention :yes} - {:id 2 :name "Colin Caruthers" :gender :male :intention :yes} - {:id 3 :name "Calum Crathie" :intention :yes}]} - {:id 4 :address "19 Imaginary Terrace, IM1 3TE" :latitude 55.82506950000001 :longitude -4.2570239 - :electors [{:id 1 :name "David Dewar" :gender :male :intention :no}]}] - ;;; electors at the currently selected address - :electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no} - {:id 2 :name "Ann Anderson" :gender :female} - {:id 3 :name "Alex Anderson" :gender :fluid :intention :yes} - {:id 4 :name "Andy Anderson" :intention :yes}] - ;;; any error to display - :error nil - ;;; the issue from among the issues which is currently selected. - ;;; any confirmation message to display - :feedback nil - ;;; the currently selected issue - :issue "Currency" - ;;; the issues selected for the issues page on this day. - :issues {"Currency" "Scotland could keep the Pound, or use the Euro. But we could also set up a new currency of our own. Yada yada yada" - "Monarchy" "Scotland could keep the Queen. This is an issue to be decided after independence. Yada yada yada" - "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"} - ;;; message of the day - :motd "This is a test version only. There is no real data." - ;;; the options from among which electors can select. - :options [{:id :yes :description "Yes"} {:id :no :description "No"}] - ;;; the queue of items waiting to be transmitted. - :outqueue () - ;;; the currently displayed page within the app. - :page :home - }) + :address {:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2569057 + :dwellings [{:id 1 + :electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no} + {:id 2 :name "Ann Anderson" :gender :female} + {:id 3 :name "Alex Anderson" :gender :fluid :intention :yes} + {:id 4 :name "Andy Anderson" :intention :yes}]}]} + ;;; a list of the addresses in the current location at which there + ;;; are electors registered. + :addresses [{:id 1 :address "13 Imaginary Terrace, IM1 3TE" :latitude 55.8253043 :longitude -4.2570944 + :dwellings [{:id 1 + :electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no} + {:id 2 :name "Ann Anderson" :gender :female} + {:id 3 :name "Alex Anderson" :gender :fluid :intention :yes} + {:id 4 :name "Andy Anderson" :intention :yes}]}]} + {:id 2 :address "15 Imaginary Terrace, IM1 3TE" :latitude 55.8252354 :longitude -4.2572778 + :dwellings [{:id 2 + :electors [{:id 1 :name "Beryl Brown" :gender :female} + {:id 2 :name "Betty Black" :gender :female}]}]} + + {:id 3 :address "17 Imaginary Terrace, IM1 3TE" :latitude 55.825166 :longitude -4.257026 + :dwellings [{:id 3 :sub-address "Flat 1" + :electors [{:id 1 :name "Catriona Crathie" :gender :female :intention :yes} + {:id 2 :name "Colin Caruthers" :gender :male :intention :yes} + {:id 3 :name "Calum Crathie" :intention :yes}]} + {:id 4 :sub-address "Flat 2" + :electors [{:id 1 :name "David Dewar" :gender :male :intention :no}]}]}] + ;;; electors at the currently selected dwelling + :electors [{:id 1 :name "Alan Anderson" :gender :male :intention :no} + {:id 2 :name "Ann Anderson" :gender :female} + {:id 3 :name "Alex Anderson" :gender :fluid :intention :yes} + {:id 4 :name "Andy Anderson" :intention :yes}] + ;;; any error to display + :error nil + ;;; the issue from among the issues which is currently selected. + ;;; any confirmation message to display + :feedback nil + ;;; the currently selected issue + :issue "Currency" + ;;; the issues selected for the issues page on this day. + :issues {"Currency" "Scotland could keep the Pound, or use the Euro. But we could also set up a new currency of our own. Yada yada yada" + "Monarchy" "Scotland could keep the Queen. This is an issue to be decided after independence. Yada yada yada" + "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"} + ;;; message of the day + :motd "This is a test version only. There is no real data." + ;;; the options from among which electors can select. + :options [{:id :yes :description "Yes"} {:id :no :description "No"}] + ;;; the queue of items waiting to be transmitted. + :outqueue () + ;;; the currently displayed page within the app. + :page :home}) diff --git a/src/cljs/youyesyet/canvasser_app/subscriptions.cljs b/src/cljs/youyesyet/canvasser_app/subscriptions.cljs index c3d18e5..3f87dee 100644 --- a/src/cljs/youyesyet/canvasser_app/subscriptions.cljs +++ b/src/cljs/youyesyet/canvasser_app/subscriptions.cljs @@ -46,6 +46,11 @@ (fn [db _] (:changes db))) +(reg-sub + :dwelling + (fn [db _] + (:dwelling db))) + (reg-sub :elector (fn [db _] diff --git a/src/cljs/youyesyet/canvasser_app/views/building.cljs b/src/cljs/youyesyet/canvasser_app/views/building.cljs index 40e5d0e..5e9e935 100644 --- a/src/cljs/youyesyet/canvasser_app/views/building.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/building.cljs @@ -1,7 +1,8 @@ (ns ^{:doc "Canvasser app households in building panel." :author "Simon Brooke"} youyesyet.canvasser-app.views.building - (:require [re-frame.core :refer [reg-sub]])) + (:require [re-frame.core :refer [reg-sub subscribe]] + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; @@ -30,10 +31,22 @@ ;;; 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 building view. - +;;; I propose to follow this pattern. This file will provide the building view. (defn panel "Generate the building panel." [] - []) + (let [address @(subscribe [:address]) + dwellings (:dwellings address)] + [:div + [:h1 (str "Flats at " (:address address))] + [:div.container {:id "main-container"} + (ui/back-link) + [:div {:id "dwelling-list"} + (map + (fn + [dwelling] + (ui/big-link + (:sub-address dwelling) + (str "#/electors/" (:id dwelling))) ) + (:dwellings address))]]])) diff --git a/src/cljs/youyesyet/canvasser_app/views/electors.cljs b/src/cljs/youyesyet/canvasser_app/views/electors.cljs index aee2d4d..482d21a 100644 --- a/src/cljs/youyesyet/canvasser_app/views/electors.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/electors.cljs @@ -32,7 +32,7 @@ ;;; 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 electors view. +;;; I propose to follow this pattern. This file will provide the electors view. ;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#electors-view @@ -107,14 +107,16 @@ (defn panel "Generate the electors panel." [] - (let [address @(subscribe [:address]) - addresses @(subscribe [:addresses]) - electors (sort-by :id (:electors address)) - options @(subscribe [:options]) - changes @(subscribe [:changes])] + (let [dwelling @(subscribe [:dwelling]) + address @(subscribe [:address]) + sub-address (:sub-address dwelling) + electors (sort-by :id (:electors dwelling)) + options @(subscribe [:options])] (if address [:div - [:h1 (:address address)] + [:h1 (if sub-address + (str sub-address ", " (:address address)) + (:address address))] [:div.container {:id "main-container"} [:table [:tbody diff --git a/src/cljs/youyesyet/canvasser_app/views/issues.cljs b/src/cljs/youyesyet/canvasser_app/views/issues.cljs index 8aa1744..8b2ca47 100644 --- a/src/cljs/youyesyet/canvasser_app/views/issues.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issues.cljs @@ -31,7 +31,7 @@ ;;; 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 issues view. +;;; I propose to follow this pattern. This file will provide the issues view. ;;; See https://github.com/simon-brooke/youyesyet/blob/master/doc/specification/userspec.md#issues-view diff --git a/src/cljs/youyesyet/canvasser_app/views/map.cljs b/src/cljs/youyesyet/canvasser_app/views/map.cljs index 6df73dd..a0a7097 100644 --- a/src/cljs/youyesyet/canvasser_app/views/map.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/map.cljs @@ -57,7 +57,14 @@ (defn pin-image "select the name of a suitable pin image for this address" [address] - (let [intentions (set (remove nil? (map #(:intention %) (:electors address))))] + (let [intentions + (set + (remove + nil? + (map + #(:intention %) + (map :electors + (:dwellings address)))))] (case (count intentions) 0 "unknown-pin" 1 (str (name (first intentions)) "-pin") @@ -70,7 +77,7 @@ so back links work." [id] (js/console.log (str "Click handler for address #" id)) - (set! window.location.href (str "#electors/" id))) + (set! window.location.href (str "#building/" id))) ;; This way is probably more idiomatic React, but history doesn't work: ;; (defn map-pin-click-handler ;; [id] From 3a18cacf0fc761868a523dbb05e4ec30213c7942 Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 21 Jul 2017 15:51:16 +0100 Subject: [PATCH 12/20] #43 Zoom and lat/long now captured in state. --- .../youyesyet/canvasser_app/handlers.cljs | 26 +++++++++++++++++++ src/cljs/youyesyet/canvasser_app/state.cljs | 6 ++++- .../canvasser_app/subscriptions.cljs | 19 ++++++++++++++ .../youyesyet/canvasser_app/views/map.cljs | 13 ++++++++-- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/cljs/youyesyet/canvasser_app/handlers.cljs b/src/cljs/youyesyet/canvasser_app/handlers.cljs index 813ec99..a916231 100644 --- a/src/cljs/youyesyet/canvasser_app/handlers.cljs +++ b/src/cljs/youyesyet/canvasser_app/handlers.cljs @@ -169,8 +169,34 @@ (assoc (clear-messages db) :issue issue))) +(reg-event-db + :set-latitude + (fn [db [_ issue]] + (assoc db :latitude issue))) + + +(reg-event-db + :set-longitude + (fn [db [_ issue]] + (assoc db :longitude issue))) + + (reg-event-db :set-telephone (fn [db [_ telephone]] (js/console.log (str "Setting telephone to " telephone)) (assoc (clear-messages db) :telephone telephone))) + + +(reg-event-db + :set-view + (fn [db [_ view]] + (assoc db :view view))) + + +(reg-event-db + :set-zoom + (fn [db [_ zoom]] + (if (integer? zoom) + (assoc db :zoom zoom) + db))) diff --git a/src/cljs/youyesyet/canvasser_app/state.cljs b/src/cljs/youyesyet/canvasser_app/state.cljs index c967f7c..241752e 100644 --- a/src/cljs/youyesyet/canvasser_app/state.cljs +++ b/src/cljs/youyesyet/canvasser_app/state.cljs @@ -82,4 +82,8 @@ ;;; the queue of items waiting to be transmitted. :outqueue () ;;; the currently displayed page within the app. - :page :home}) + :page :home + :view nil + :latitude 55.82 + :longitude -4.25 + :zoom 12}) diff --git a/src/cljs/youyesyet/canvasser_app/subscriptions.cljs b/src/cljs/youyesyet/canvasser_app/subscriptions.cljs index 3f87dee..8fd4218 100644 --- a/src/cljs/youyesyet/canvasser_app/subscriptions.cljs +++ b/src/cljs/youyesyet/canvasser_app/subscriptions.cljs @@ -76,6 +76,16 @@ (fn [db _] (:issues db))) +(reg-sub + :latitude + (fn [db _] + (:latitude db))) + +(reg-sub + :longitude + (fn [db _] + (:longitude db))) + (reg-sub :page (fn [db _] @@ -91,3 +101,12 @@ (fn [db _] (:outqueue db))) +(reg-sub + :view + (fn [db _] + (:view db))) + +(reg-sub + :zoom + (fn [db _] + (:zoom db))) diff --git a/src/cljs/youyesyet/canvasser_app/views/map.cljs b/src/cljs/youyesyet/canvasser_app/views/map.cljs index a0a7097..f60887d 100644 --- a/src/cljs/youyesyet/canvasser_app/views/map.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/map.cljs @@ -77,6 +77,11 @@ so back links work." [id] (js/console.log (str "Click handler for address #" id)) + (let [view @(subscribe [:view]) + centre (.getCenter view)] + (dispatch [:set-zoom (.getZoom view)]) + (dispatch [:set-latitude (.-lat centre)]) + (dispatch [:set-longitude (.-lng centre)])) (set! window.location.href (str "#building/" id))) ;; This way is probably more idiomatic React, but history doesn't work: ;; (defn map-pin-click-handler @@ -121,7 +126,10 @@ (defn map-did-mount-osm "Did-mount function loading map tile data from Open Street Map." [] - (let [view (.setView (.map js/L "map" (clj->js {:zoomControl false})) #js [55.82 -4.25] 13) + (let [view (.setView + (.map js/L "map" (clj->js {:zoomControl false})) + #js [@(subscribe [:latitude]) @(subscribe [:longitude])] + @(subscribe [:zoom])) addresses @(subscribe [:addresses])] (js/console.log (str "Adding " (count addresses) " pins")) (doall (map #(add-map-pin % view) addresses)) @@ -129,7 +137,8 @@ (clj->js {:attribution osm-attrib :maxZoom 18})) view) - )) + (dispatch [:set-view view]) + view)) (defn map-did-mount From 89745e3a831e87aef648f951304b9f0008f0c0c0 Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 21 Jul 2017 16:39:59 +0100 Subject: [PATCH 13/20] Minor UI fixes --- .../youyesyet/canvasser_app/handlers.cljs | 76 ++++++++++++------- .../canvasser_app/views/building.cljs | 4 +- .../youyesyet/canvasser_app/views/map.cljs | 6 +- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/cljs/youyesyet/canvasser_app/handlers.cljs b/src/cljs/youyesyet/canvasser_app/handlers.cljs index a916231..a010699 100644 --- a/src/cljs/youyesyet/canvasser_app/handlers.cljs +++ b/src/cljs/youyesyet/canvasser_app/handlers.cljs @@ -57,32 +57,56 @@ (reg-event-db - :send-intention - (fn [db [_ args]] - (let [intention (:intention args) - elector-id (:elector-id args) - elector - (first - (remove nil? - (map - #(if (= elector-id (:id %)) %) - (:electors (:address db))))) - old-address (:address db) - new-address (assoc old-address :electors (cons (assoc elector :intention intention) (remove #(= % elector) (:electors old-address))))] - (cond - (nil? elector) - (assoc db :error "No elector found; not setting intention") - (= intention (:intention elector)) (do (js/console.log "Elector's intention hasn't changed; not setting intention") db) - true - (do - (js/console.log (str "Setting intention of elector " elector " to " intention)) - (merge - (clear-messages db) - {:addresses - (cons new-address (remove old-address (:addresses db))) - :address new-address - :elector elector - :outqueue (cons (assoc args :action :set-intention) (:outqueue db))})))))) + :send-intention + (fn [db [_ args]] + (let [intention (:intention args) + elector-id (:elector-id args) + old-elector (first + (remove nil? + (map + #(if (= elector-id (:id %)) %) + (:electors (:dwelling db))))) + new-elector (assoc old-elector :intention intention) + old-dwelling (:dwelling db) + new-dwelling (assoc + old-dwelling + :electors + (cons + new-elector + (remove + #(= % old-elector) + (:electors old-dwelling)))) + old-address (:address db) + new-address (assoc + old-address + :dwellings + (cons + new-dwelling + (remove + #(= % old-dwelling) + (:dwellings old-address))))] + (cond + (nil? old-elector) + (assoc db :error "No elector found; not setting intention") + (= intention (:intention old-elector)) + (do + (js/console.log "Elector's intention hasn't changed; not setting intention") + db) + true + (do + (js/console.log (str "Setting intention of elector " old-elector " to " intention)) + (merge + (clear-messages db) + {:addresses + (cons + new-address + (remove #(= % old-address) (:addresses db))) + :address new-address + :dwelling new-dwelling + :elector new-elector + :outqueue (cons + (assoc args :action :set-intention) + (:outqueue db))})))))) (reg-event-db diff --git a/src/cljs/youyesyet/canvasser_app/views/building.cljs b/src/cljs/youyesyet/canvasser_app/views/building.cljs index 5e9e935..54a1ed2 100644 --- a/src/cljs/youyesyet/canvasser_app/views/building.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/building.cljs @@ -49,4 +49,6 @@ (ui/big-link (:sub-address dwelling) (str "#/electors/" (:id dwelling))) ) - (:dwellings address))]]])) + (sort + #(< (:sub-address %1) (:sub-address %2)) + (:dwellings address)))]]])) diff --git a/src/cljs/youyesyet/canvasser_app/views/map.cljs b/src/cljs/youyesyet/canvasser_app/views/map.cljs index f60887d..257e404 100644 --- a/src/cljs/youyesyet/canvasser_app/views/map.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/map.cljs @@ -62,9 +62,9 @@ (remove nil? (map - #(:intention %) - (map :electors - (:dwellings address)))))] + :intention + (mapcat :electors + (:dwellings address)))))] (case (count intentions) 0 "unknown-pin" 1 (str (name (first intentions)) "-pin") From e300d0c4c5bb7fc919c5a0915d011e34627e3c9b Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 22 Jul 2017 11:04:59 +0100 Subject: [PATCH 14/20] #28 Changed the way I'm doing REST, for simplicity --- src/clj/youyesyet/routes/rest.clj | 49 +++++++++++++++++++++++++++ src/clj/youyesyet/routes/services.clj | 5 ++- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/clj/youyesyet/routes/rest.clj diff --git a/src/clj/youyesyet/routes/rest.clj b/src/clj/youyesyet/routes/rest.clj new file mode 100644 index 0000000..6ca9b3f --- /dev/null +++ b/src/clj/youyesyet/routes/rest.clj @@ -0,0 +1,49 @@ +(ns ^{:doc "Routes which handle data transfer to/from the canvasser app." + :author "Simon Brooke"} youyesyet.routes.rest + (:require [clojure.walk :refer [keywordize-keys]] + [noir.response :as nresponse] + [noir.util.route :as route] + [youyesyet.db.core :as db-core] + [compojure.core :refer [defroutes GET POST]] + [ring.util.http-response :as response] + [clojure.java.io :as io])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.routes.rest: Routes which handle data transfer to/from the +;;;; canvasser app. +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn get-local-data + "Get data local to the user of the canvasser app. Expects arguments `lat` and + `long`. Returns a block of data for that locality" + [request] + ) + +(defn get-issues + "Get current issues. No arguments expected." + [request] + +(defroutes rest-routes + (GET "/rest/get-local-data" request (route/restricted (get-local-data request))) + (GET "/rest/get-issues" request (route/restricted (get-issues request))) + (GET "/rest/set-intention" request (route/restricted (set-intention request))) + (GET "/rest/request-followup" request (route/restricted (request-followup request)))) diff --git a/src/clj/youyesyet/routes/services.clj b/src/clj/youyesyet/routes/services.clj index c0de2b5..5698b0c 100644 --- a/src/clj/youyesyet/routes/services.clj +++ b/src/clj/youyesyet/routes/services.clj @@ -1,4 +1,7 @@ -(ns youyesyet.routes.services +;;;; This is probably the right way to do the API, but I don't understand it. + +(ns ^{:doc "REST API." + :author "Simon Brooke"} youyesyet.routes.services (:require [clj-http.client :as client] [ring.util.http-response :refer :all] [compojure.api.sweet :refer :all] From 9157871cc15f6dbbacc9a2840c6882ed8693cd29 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 24 Jul 2017 19:46:52 +0100 Subject: [PATCH 15/20] Added a note on locality indexing. --- doc/specification/scaling.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/doc/specification/scaling.md b/doc/specification/scaling.md index bfc16de..da3687c 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. From 604e7f485d2eb9e8642091c7dba7fce13247c53e Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sat, 17 Mar 2018 18:33:06 +0000 Subject: [PATCH 16/20] Squirrel-parse generated routes and queries are now working Routes and queries auto-generated from migrations by [squirrel-parse]() are now actually working! --- .../entity-relationship-diagram.svg | 10 +- .../20161014170335-basic-setup.up.sql | 4 +- ...0316110100-intentions-and-options.down.sql | 24 + ...180316110100-intentions-and-options.up.sql | 30 + .../migrations/20180317170000-gender.down.sql | 3 + .../migrations/20180317170000-gender.up.sql | 11 + .../20180317170907-reference-data.down.sql | 10 + .../20180317170907-reference-data.up.sql | 10 + .../20180317175047-test-data.down.sql | 3 + .../20180317175047-test-data.up.sql | 41 + resources/sql/queries.auto.sql | 735 ++++++++++++ src/clj/youyesyet/db/core.clj | 14 +- src/clj/youyesyet/handler.clj | 7 +- src/clj/youyesyet/routes/auto_json_routes.clj | 1035 +++++++++++++++++ src/clj/youyesyet/routes/rest.clj | 2 +- 15 files changed, 1923 insertions(+), 16 deletions(-) create mode 100644 resources/migrations/20180316110100-intentions-and-options.down.sql create mode 100644 resources/migrations/20180316110100-intentions-and-options.up.sql create mode 100644 resources/migrations/20180317170000-gender.down.sql create mode 100644 resources/migrations/20180317170000-gender.up.sql create mode 100644 resources/migrations/20180317170907-reference-data.down.sql create mode 100644 resources/migrations/20180317170907-reference-data.up.sql create mode 100644 resources/migrations/20180317175047-test-data.down.sql create mode 100644 resources/migrations/20180317175047-test-data.up.sql create mode 100644 resources/sql/queries.auto.sql create mode 100644 src/clj/youyesyet/routes/auto_json_routes.clj diff --git a/doc/specification/entity-relationship-diagram.svg b/doc/specification/entity-relationship-diagram.svg index 0de6cf0..dbb5475 100644 --- a/doc/specification/entity-relationship-diagram.svg +++ b/doc/specification/entity-relationship-diagram.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.979899" - inkscape:cx="833.70674" - inkscape:cy="324.89697" + inkscape:zoom="0.9899495" + inkscape:cx="472.36875" + inkscape:cy="325.73865" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -62,8 +62,8 @@ id="rect4428" width="1030" height="730" - x="232.23355" - y="376.0018" /> + x="16.060907" + y="312.36218" /> sql-date (.getTime) (java.util.Date.))) diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index f898fe5..bc67a8c 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -3,6 +3,7 @@ [youyesyet.layout :refer [error-page]] [youyesyet.routes.home :refer [home-routes]] [youyesyet.routes.oauth :refer [oauth-routes]] + [youyesyet.routes.auto-json-routes :refer [auto-rest-routes]] [compojure.route :as route] [youyesyet.env :refer [defaults]] [mount.core :as mount] @@ -37,6 +38,9 @@ (-> #'home-routes (wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-formats)) + (-> #'auto-rest-routes + (wrap-routes middleware/wrap-csrf) + (wrap-routes middleware/wrap-formats)) #'oauth-routes (route/not-found (:body @@ -44,4 +48,5 @@ :title "page not found"}))))) -(def app (middleware/wrap-base #'app-routes)) +(def app #'app-routes) + ;;(middleware/wrap-base #'app-routes)) diff --git a/src/clj/youyesyet/routes/auto_json_routes.clj b/src/clj/youyesyet/routes/auto_json_routes.clj new file mode 100644 index 0000000..8d3d707 --- /dev/null +++ b/src/clj/youyesyet/routes/auto_json_routes.clj @@ -0,0 +1,1035 @@ +(ns + youyesyet.routes.auto-json-routes + (require + [noir.response :as nresponse] + [noir.util.route :as route] + [compojure.core :refer [defroutes GET POST]] + [ring.util.http-response :as response] + [clojure.java.io :as io] + [hugsql.core :as hugsql] + [youyesyet.db.core :as db])) + + +(declare + create-addresse + create-authority + create-canvasser + create-district + create-elector + create-followupaction + create-followupmethod + create-followuprequest + create-intention + create-issue + create-issueexpertise + create-option + create-role + create-rolemembership + create-schema-migration + create-team + create-teammembership + create-teamorganisership + create-visit + delete-addresse + delete-authority + delete-canvasser + delete-district + delete-elector + delete-followupaction + delete-followupmethod + delete-followuprequest + delete-issue + delete-option + delete-visit + get-addresse + get-authority + get-canvasser + get-district + get-elector + get-followupaction + get-followupmethod + get-followuprequest + get-issue + get-option + get-visit + list-addresses + list-addresses-by-district + list-authorities + list-canvassers + list-canvassers-by-addresse + list-canvassers-by-authoritie + list-canvassers-by-elector + list-districts + list-electors + list-electors-by-addresse + list-followupactions + list-followupactions-by-canvasser + list-followupactions-by-followuprequest + list-followupmethods + list-followuprequests + list-followuprequests-by-elector + list-followuprequests-by-followupmethod + list-followuprequests-by-issue + list-followuprequests-by-visit + list-intentions-electors-by-option + list-intentions-electors-by-visit + list-intentions-options-by-elector + list-intentions-options-by-visit + list-intentions-visits-by-elector + list-intentions-visits-by-option + list-issueexpertise-canvassers-by-followupmethod + list-issueexpertise-canvassers-by-issue + list-issueexpertise-followupmethods-by-canvasser + list-issueexpertise-followupmethods-by-issue + list-issueexpertise-issues-by-canvasser + list-issueexpertise-issues-by-followupmethod + list-issues + list-options + list-rolememberships-canvassers-by-role + list-rolememberships-roles-by-canvasser + list-roles + list-schemamigrations + list-teammemberships-canvassers-by-team + list-teammemberships-teams-by-canvasser + list-teamorganiserships-canvassers-by-team + list-teamorganiserships-teams-by-canvasser + list-teams + list-teams-by-district + list-visits + list-visits-by-addresse + list-visits-by-canvasser + update-addresse + update-canvasser + update-district + update-elector + update-followupaction + update-followuprequest + update-issue + update-visit) + + +(defroutes + auto-rest-routes + (POST "/json/auto/create-addresse" request (create-addresse request)) + (POST + "/json/auto/create-authority" + request + (create-authority request)) + (POST + "/json/auto/create-canvasser" + request + (create-canvasser request)) + (POST "/json/auto/create-district" request (create-district request)) + (POST "/json/auto/create-elector" request (create-elector request)) + (POST + "/json/auto/create-followupaction" + request + (create-followupaction request)) + (POST + "/json/auto/create-followupmethod" + request + (create-followupmethod request)) + (POST + "/json/auto/create-followuprequest" + request + (create-followuprequest request)) + (POST + "/json/auto/create-intention" + request + (create-intention request)) + (POST "/json/auto/create-issue" request (create-issue request)) + (POST + "/json/auto/create-issueexpertise" + request + (create-issueexpertise request)) + (POST "/json/auto/create-option" request (create-option request)) + (POST "/json/auto/create-role" request (create-role request)) + (POST + "/json/auto/create-rolemembership" + request + (create-rolemembership request)) + (POST + "/json/auto/create-schema-migration" + request + (create-schema-migration request)) + (POST "/json/auto/create-team" request (create-team request)) + (POST + "/json/auto/create-teammembership" + request + (create-teammembership request)) + (POST + "/json/auto/create-teamorganisership" + request + (create-teamorganisership request)) + (POST "/json/auto/create-visit" request (create-visit request)) + (POST "/json/auto/delete-addresse" request (delete-addresse request)) + (POST + "/json/auto/delete-authority" + request + (delete-authority request)) + (POST + "/json/auto/delete-canvasser" + request + (delete-canvasser request)) + (POST "/json/auto/delete-district" request (delete-district request)) + (POST "/json/auto/delete-elector" request (delete-elector request)) + (POST + "/json/auto/delete-followupaction" + request + (delete-followupaction request)) + (POST + "/json/auto/delete-followupmethod" + request + (delete-followupmethod request)) + (POST + "/json/auto/delete-followuprequest" + request + (delete-followuprequest request)) + (POST "/json/auto/delete-issue" request (delete-issue request)) + (POST "/json/auto/delete-option" request (delete-option request)) + (POST "/json/auto/delete-visit" request (delete-visit request)) + (POST "/json/auto/get-addresse" request (get-addresse request)) + (POST "/json/auto/get-authority" request (get-authority request)) + (POST "/json/auto/get-canvasser" request (get-canvasser request)) + (POST "/json/auto/get-district" request (get-district request)) + (POST "/json/auto/get-elector" request (get-elector request)) + (POST + "/json/auto/get-followupaction" + request + (get-followupaction request)) + (POST + "/json/auto/get-followupmethod" + request + (get-followupmethod request)) + (POST + "/json/auto/get-followuprequest" + request + (get-followuprequest request)) + (POST "/json/auto/get-issue" request (get-issue request)) + (POST "/json/auto/get-option" request (get-option request)) + (POST "/json/auto/get-visit" request (get-visit request)) + (GET "/json/auto/list-addresses" request (list-addresses request)) + (GET + "/json/auto/list-addresses-by-district" + request + (list-addresses-by-district request)) + (GET "/json/auto/list-authorities" request (list-authorities request)) + (GET "/json/auto/list-canvassers" request (list-canvassers request)) + (GET + "/json/auto/list-canvassers-by-addresse" + request + (list-canvassers-by-addresse request)) + (GET + "/json/auto/list-canvassers-by-authoritie" + request + (list-canvassers-by-authoritie request)) + (GET + "/json/auto/list-canvassers-by-elector" + request + (list-canvassers-by-elector request)) + (GET "/json/auto/list-districts" request (list-districts request)) + (GET "/json/auto/list-electors" request (list-electors request)) + (GET + "/json/auto/list-electors-by-addresse" + request + (list-electors-by-addresse request)) + (GET + "/json/auto/list-followupactions" + request + (list-followupactions request)) + (GET + "/json/auto/list-followupactions-by-canvasser" + request + (list-followupactions-by-canvasser request)) + (GET + "/json/auto/list-followupactions-by-followuprequest" + request + (list-followupactions-by-followuprequest request)) + (GET + "/json/auto/list-followupmethods" + request + (list-followupmethods request)) + (GET + "/json/auto/list-followuprequests" + request + (list-followuprequests request)) + (GET + "/json/auto/list-followuprequests-by-elector" + request + (list-followuprequests-by-elector request)) + (GET + "/json/auto/list-followuprequests-by-followupmethod" + request + (list-followuprequests-by-followupmethod request)) + (GET + "/json/auto/list-followuprequests-by-issue" + request + (list-followuprequests-by-issue request)) + (GET + "/json/auto/list-followuprequests-by-visit" + request + (list-followuprequests-by-visit request)) + (GET + "/json/auto/list-intentions-electors-by-option" + request + (list-intentions-electors-by-option request)) + (GET + "/json/auto/list-intentions-electors-by-visit" + request + (list-intentions-electors-by-visit request)) + (GET + "/json/auto/list-intentions-options-by-elector" + request + (list-intentions-options-by-elector request)) + (GET + "/json/auto/list-intentions-options-by-visit" + request + (list-intentions-options-by-visit request)) + (GET + "/json/auto/list-intentions-visits-by-elector" + request + (list-intentions-visits-by-elector request)) + (GET + "/json/auto/list-intentions-visits-by-option" + request + (list-intentions-visits-by-option request)) + (GET + "/json/auto/list-issueexpertise-canvassers-by-followupmethod" + request + (list-issueexpertise-canvassers-by-followupmethod request)) + (GET + "/json/auto/list-issueexpertise-canvassers-by-issue" + request + (list-issueexpertise-canvassers-by-issue request)) + (GET + "/json/auto/list-issueexpertise-followupmethods-by-canvasser" + request + (list-issueexpertise-followupmethods-by-canvasser request)) + (GET + "/json/auto/list-issueexpertise-followupmethods-by-issue" + request + (list-issueexpertise-followupmethods-by-issue request)) + (GET + "/json/auto/list-issueexpertise-issues-by-canvasser" + request + (list-issueexpertise-issues-by-canvasser request)) + (GET + "/json/auto/list-issueexpertise-issues-by-followupmethod" + request + (list-issueexpertise-issues-by-followupmethod request)) + (GET "/json/auto/list-issues" request (list-issues request)) + (GET "/json/auto/list-options" request (list-options request)) + (GET + "/json/auto/list-rolememberships-canvassers-by-role" + request + (list-rolememberships-canvassers-by-role request)) + (GET + "/json/auto/list-rolememberships-roles-by-canvasser" + request + (list-rolememberships-roles-by-canvasser request)) + (GET "/json/auto/list-roles" request (list-roles request)) + (GET + "/json/auto/list-schemamigrations" + request + (list-schemamigrations request)) + (GET + "/json/auto/list-teammemberships-canvassers-by-team" + request + (list-teammemberships-canvassers-by-team request)) + (GET + "/json/auto/list-teammemberships-teams-by-canvasser" + request + (list-teammemberships-teams-by-canvasser request)) + (GET + "/json/auto/list-teamorganiserships-canvassers-by-team" + request + (list-teamorganiserships-canvassers-by-team request)) + (GET + "/json/auto/list-teamorganiserships-teams-by-canvasser" + request + (list-teamorganiserships-teams-by-canvasser request)) + (GET "/json/auto/list-teams" request (list-teams request)) + (GET + "/json/auto/list-teams-by-district" + request + (list-teams-by-district request)) + (GET "/json/auto/list-visits" request (list-visits request)) + (GET + "/json/auto/list-visits-by-addresse" + request + (list-visits-by-addresse request)) + (GET + "/json/auto/list-visits-by-canvasser" + request + (list-visits-by-canvasser request)) + (POST "/json/auto/update-addresse" request (update-addresse request)) + (POST + "/json/auto/update-canvasser" + request + (update-canvasser request)) + (POST "/json/auto/update-district" request (update-district request)) + (POST "/json/auto/update-elector" request (update-elector request)) + (POST + "/json/auto/update-followupaction" + request + (update-followupaction request)) + (POST + "/json/auto/update-followuprequest" + request + (update-followuprequest request)) + (POST "/json/auto/update-issue" request (update-issue request)) + (POST "/json/auto/update-visit" request (update-visit request))) + + +(defn + create-addresse + "Auto-generated method to insert one record to the addresses table. Expects the following key(s) to be present in `params`: (:id :address :postcode :phone :district_id :latitude :longitude). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-addresse! params))) + + +(defn + create-authority + "Auto-generated method to insert one record to the authorities table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-authority! params))) + + +(defn + create-canvasser + "Auto-generated method to insert one record to the canvassers table. Expects the following key(s) to be present in `params`: (:id :username :fullname :elector_id :address_id :phone :email :authority_id :authorised). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-canvasser! params))) + + +(defn + create-district + "Auto-generated method to insert one record to the districts table. Expects the following key(s) to be present in `params`: (:id :name). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-district! params))) + + +(defn + create-elector + "Auto-generated method to insert one record to the electors table. Expects the following key(s) to be present in `params`: (:id :name :address_id :phone :email). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-elector! params))) + + +(defn + create-followupaction + "Auto-generated method to insert one record to the followupactions table. Expects the following key(s) to be present in `params`: (:id :request_id :actor :date :notes :closed). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-followupaction! params))) + + +(defn + create-followupmethod + "Auto-generated method to insert one record to the followupmethods table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-followupmethod! params))) + + +(defn + create-followuprequest + "Auto-generated method to insert one record to the followuprequests table. Expects the following key(s) to be present in `params`: (:id :elector_id :visit_id :issue_id :method_id). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-followuprequest! params))) + + +(defn + create-intention + "Auto-generated method to insert one record to the intentions table. Expects the following key(s) to be present in `params`: (:visit_id :elector_id :option_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-intention! params))) + + +(defn + create-issue + "Auto-generated method to insert one record to the issues table. Expects the following key(s) to be present in `params`: (:id :url). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-issue! params))) + + +(defn + create-issueexpertise + "Auto-generated method to insert one record to the issueexpertise table. Expects the following key(s) to be present in `params`: (:canvasser_id :issue_id :method_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-issueexpertise! params))) + + +(defn + create-option + "Auto-generated method to insert one record to the options table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-option! params))) + + +(defn + create-role + "Auto-generated method to insert one record to the roles table. Expects the following key(s) to be present in `params`: (:id :name). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-role! params))) + + +(defn + create-rolemembership + "Auto-generated method to insert one record to the rolememberships table. Expects the following key(s) to be present in `params`: (:role_id :canvasser_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-rolemembership! params))) + + +(defn + create-schema-migration + "Auto-generated method to insert one record to the schema_migrations table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-schema-migration! params))) + + +(defn + create-team + "Auto-generated method to insert one record to the teams table. Expects the following key(s) to be present in `params`: (:id :name :district_id :latitude :longitude). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-team! params))) + + +(defn + create-teammembership + "Auto-generated method to insert one record to the teammemberships table. Expects the following key(s) to be present in `params`: (:team_id :canvasser_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-teammembership! params))) + + +(defn + create-teamorganisership + "Auto-generated method to insert one record to the teamorganiserships table. Expects the following key(s) to be present in `params`: (:team_id :canvasser_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-teamorganisership! params))) + + +(defn + create-visit + "Auto-generated method to insert one record to the visits table. Expects the following key(s) to be present in `params`: (:id :address_id :canvasser_id :date). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-visit! params))) + + +(defn + delete-addresse + "Auto-generated method to delete one record from the addresses table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-addresse! params)) + (response/found "/")) + + +(defn + delete-authority + "Auto-generated method to delete one record from the authorities table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-authority! params)) + (response/found "/")) + + +(defn + delete-canvasser + "Auto-generated method to delete one record from the canvassers table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-canvasser! params)) + (response/found "/")) + + +(defn + delete-district + "Auto-generated method to delete one record from the districts table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-district! params)) + (response/found "/")) + + +(defn + delete-elector + "Auto-generated method to delete one record from the electors table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-elector! params)) + (response/found "/")) + + +(defn + delete-followupaction + "Auto-generated method to delete one record from the followupactions table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-followupaction! params)) + (response/found "/")) + + +(defn + delete-followupmethod + "Auto-generated method to delete one record from the followupmethods table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-followupmethod! params)) + (response/found "/")) + + +(defn + delete-followuprequest + "Auto-generated method to delete one record from the followuprequests table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-followuprequest! params)) + (response/found "/")) + + +(defn + delete-issue + "Auto-generated method to delete one record from the issues table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-issue! params)) + (response/found "/")) + + +(defn + delete-option + "Auto-generated method to delete one record from the options table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-option! params)) + (response/found "/")) + + +(defn + delete-visit + "Auto-generated method to delete one record from the visits table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-visit! params)) + (response/found "/")) + + +(defn + get-addresse + "Auto-generated method to select one record from the addresses table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:address :district_id :id :latitude :longitude :phone :postcode)." + [{:keys [params]}] + (do (db/get-addresse params))) + + +(defn + get-authority + "Auto-generated method to select one record from the authorities table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id)." + [{:keys [params]}] + (do (db/get-authority params))) + + +(defn + get-canvasser + "Auto-generated method to select one record from the canvassers table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:address_id :authorised :authority_id :elector_id :email :fullname :id :phone :username)." + [{:keys [params]}] + (do (db/get-canvasser params))) + + +(defn + get-district + "Auto-generated method to select one record from the districts table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id :name)." + [{:keys [params]}] + (do (db/get-district params))) + + +(defn + get-elector + "Auto-generated method to select one record from the electors table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:address_id :email :id :name :phone)." + [{:keys [params]}] + (do (db/get-elector params))) + + +(defn + get-followupaction + "Auto-generated method to select one record from the followupactions table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:actor :closed :date :id :notes :request_id)." + [{:keys [params]}] + (do (db/get-followupaction params))) + + +(defn + get-followupmethod + "Auto-generated method to select one record from the followupmethods table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id)." + [{:keys [params]}] + (do (db/get-followupmethod params))) + + +(defn + get-followuprequest + "Auto-generated method to select one record from the followuprequests table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:elector_id :id :issue_id :method_id :visit_id)." + [{:keys [params]}] + (do (db/get-followuprequest params))) + + +(defn + get-issue + "Auto-generated method to select one record from the issues table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id :url)." + [{:keys [params]}] + (do (db/get-issue params))) + + +(defn + get-option + "Auto-generated method to select one record from the options table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id)." + [{:keys [params]}] + (do (db/get-option params))) + + +(defn + get-visit + "Auto-generated method to select one record from the visits table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:address_id :canvasser_id :date :id)." + [{:keys [params]}] + (do (db/get-visit params))) + + +(defn + list-addresses + "Auto-generated method to select all records from the addresses table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:address :district_id :id :latitude :longitude :phone :postcode)." + [{:keys [params]}] + (do (db/list-addresses params))) + + +(defn + list-addresses-by-district + [{:keys [params]}] + (do (db/list-addresses-by-district params))) + + +(defn + list-authorities + "Auto-generated method to select all records from the authorities table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id)." + [{:keys [params]}] + (do (db/list-authorities params))) + + +(defn + list-canvassers + "Auto-generated method to select all records from the canvassers table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:address_id :authorised :authority_id :elector_id :email :fullname :id :phone :username)." + [{:keys [params]}] + (do (db/list-canvassers params))) + + +(defn + list-canvassers-by-addresse + [{:keys [params]}] + (do (db/list-canvassers-by-addresse params))) + + +(defn + list-canvassers-by-authoritie + [{:keys [params]}] + (do (db/list-canvassers-by-authoritie params))) + + +(defn + list-canvassers-by-elector + [{:keys [params]}] + (do (db/list-canvassers-by-elector params))) + + +(defn + list-districts + "Auto-generated method to select all records from the districts table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id :name)." + [{:keys [params]}] + (do (db/list-districts params))) + + +(defn + list-electors + "Auto-generated method to select all records from the electors table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:address_id :email :id :name :phone)." + [{:keys [params]}] + (do (db/list-electors params))) + + +(defn + list-electors-by-addresse + [{:keys [params]}] + (do (db/list-electors-by-addresse params))) + + +(defn + list-followupactions + "Auto-generated method to select all records from the followupactions table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:actor :closed :date :id :notes :request_id)." + [{:keys [params]}] + (do (db/list-followupactions params))) + + +(defn + list-followupactions-by-canvasser + [{:keys [params]}] + (do (db/list-followupactions-by-canvasser params))) + + +(defn + list-followupactions-by-followuprequest + [{:keys [params]}] + (do (db/list-followupactions-by-followuprequest params))) + + +(defn + list-followupmethods + "Auto-generated method to select all records from the followupmethods table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id)." + [{:keys [params]}] + (do (db/list-followupmethods params))) + + +(defn + list-followuprequests + "Auto-generated method to select all records from the followuprequests table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:elector_id :id :issue_id :method_id :visit_id)." + [{:keys [params]}] + (do (db/list-followuprequests params))) + + +(defn + list-followuprequests-by-elector + [{:keys [params]}] + (do (db/list-followuprequests-by-elector params))) + + +(defn + list-followuprequests-by-followupmethod + [{:keys [params]}] + (do (db/list-followuprequests-by-followupmethod params))) + + +(defn + list-followuprequests-by-issue + [{:keys [params]}] + (do (db/list-followuprequests-by-issue params))) + + +(defn + list-followuprequests-by-visit + [{:keys [params]}] + (do (db/list-followuprequests-by-visit params))) + + +(defn + list-intentions-electors-by-option + [{:keys [params]}] + (do (db/list-intentions-electors-by-option params))) + + +(defn + list-intentions-electors-by-visit + [{:keys [params]}] + (do (db/list-intentions-electors-by-visit params))) + + +(defn + list-intentions-options-by-elector + [{:keys [params]}] + (do (db/list-intentions-options-by-elector params))) + + +(defn + list-intentions-options-by-visit + [{:keys [params]}] + (do (db/list-intentions-options-by-visit params))) + + +(defn + list-intentions-visits-by-elector + [{:keys [params]}] + (do (db/list-intentions-visits-by-elector params))) + + +(defn + list-intentions-visits-by-option + [{:keys [params]}] + (do (db/list-intentions-visits-by-option params))) + + +(defn + list-issueexpertise-canvassers-by-followupmethod + [{:keys [params]}] + (do (db/list-issueexpertise-canvassers-by-followupmethod params))) + + +(defn + list-issueexpertise-canvassers-by-issue + [{:keys [params]}] + (do (db/list-issueexpertise-canvassers-by-issue params))) + + +(defn + list-issueexpertise-followupmethods-by-canvasser + [{:keys [params]}] + (do (db/list-issueexpertise-followupmethods-by-canvasser params))) + + +(defn + list-issueexpertise-followupmethods-by-issue + [{:keys [params]}] + (do (db/list-issueexpertise-followupmethods-by-issue params))) + + +(defn + list-issueexpertise-issues-by-canvasser + [{:keys [params]}] + (do (db/list-issueexpertise-issues-by-canvasser params))) + + +(defn + list-issueexpertise-issues-by-followupmethod + [{:keys [params]}] + (do (db/list-issueexpertise-issues-by-followupmethod params))) + + +(defn + list-issues + "Auto-generated method to select all records from the issues table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id :url)." + [{:keys [params]}] + (do (db/list-issues params))) + + +(defn + list-options + "Auto-generated method to select all records from the options table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id)." + [{:keys [params]}] + (do (db/list-options params))) + + +(defn + list-rolememberships-canvassers-by-role + [{:keys [params]}] + (do (db/list-rolememberships-canvassers-by-role params))) + + +(defn + list-rolememberships-roles-by-canvasser + [{:keys [params]}] + (do (db/list-rolememberships-roles-by-canvasser params))) + + +(defn + list-roles + "Auto-generated method to select all records from the roles table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id :name)." + [{:keys [params]}] + (do (db/list-roles params))) + + +(defn + list-schemamigrations + "Auto-generated method to select all records from the schema_migrations table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id)." + [{:keys [params]}] + (do (db/list-schema_migrations params))) + + +(defn + list-teammemberships-canvassers-by-team + [{:keys [params]}] + (do (db/list-teammemberships-canvassers-by-team params))) + + +(defn + list-teammemberships-teams-by-canvasser + [{:keys [params]}] + (do (db/list-teammemberships-teams-by-canvasser params))) + + +(defn + list-teamorganiserships-canvassers-by-team + [{:keys [params]}] + (do (db/list-teamorganiserships-canvassers-by-team params))) + + +(defn + list-teamorganiserships-teams-by-canvasser + [{:keys [params]}] + (do (db/list-teamorganiserships-teams-by-canvasser params))) + + +(defn + list-teams + "Auto-generated method to select all records from the teams table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:district_id :id :latitude :longitude :name)." + [{:keys [params]}] + (do (db/list-teams params))) + + +(defn + list-teams-by-district + [{:keys [params]}] + (do (db/list-teams-by-district params))) + + +(defn + list-visits + "Auto-generated method to select all records from the visits table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:address_id :canvasser_id :date :id)." + [{:keys [params]}] + (do (db/list-visits params))) + + +(defn + list-visits-by-addresse + [{:keys [params]}] + (do (db/list-visits-by-addresse params))) + + +(defn + list-visits-by-canvasser + [{:keys [params]}] + (do (db/list-visits-by-canvasser params))) + + +(defn + update-addresse + "Auto-generated method to update one record in the addresses table. Expects the following key(s) to be present in `params`: (:address :district_id :id :latitude :longitude :phone :postcode)." + [{:keys [params]}] + (do (db/update-addresse! params)) + (response/found "/")) + + +(defn + update-canvasser + "Auto-generated method to update one record in the canvassers table. Expects the following key(s) to be present in `params`: (:address_id :authorised :authority_id :elector_id :email :fullname :id :phone :username)." + [{:keys [params]}] + (do (db/update-canvasser! params)) + (response/found "/")) + + +(defn + update-district + "Auto-generated method to update one record in the districts table. Expects the following key(s) to be present in `params`: (:id :name)." + [{:keys [params]}] + (do (db/update-district! params)) + (response/found "/")) + + +(defn + update-elector + "Auto-generated method to update one record in the electors table. Expects the following key(s) to be present in `params`: (:address_id :email :id :name :phone)." + [{:keys [params]}] + (do (db/update-elector! params)) + (response/found "/")) + + +(defn + update-followupaction + "Auto-generated method to update one record in the followupactions table. Expects the following key(s) to be present in `params`: (:actor :closed :date :id :notes :request_id)." + [{:keys [params]}] + (do (db/update-followupaction! params)) + (response/found "/")) + + +(defn + update-followuprequest + "Auto-generated method to update one record in the followuprequests table. Expects the following key(s) to be present in `params`: (:elector_id :id :issue_id :method_id :visit_id)." + [{:keys [params]}] + (do (db/update-followuprequest! params)) + (response/found "/")) + + +(defn + update-issue + "Auto-generated method to update one record in the issues table. Expects the following key(s) to be present in `params`: (:id :url)." + [{:keys [params]}] + (do (db/update-issue! params)) + (response/found "/")) + + +(defn + update-visit + "Auto-generated method to update one record in the visits table. Expects the following key(s) to be present in `params`: (:address_id :canvasser_id :date :id)." + [{:keys [params]}] + (do (db/update-visit! params)) + (response/found "/")) + + diff --git a/src/clj/youyesyet/routes/rest.clj b/src/clj/youyesyet/routes/rest.clj index 6ca9b3f..c164b9b 100644 --- a/src/clj/youyesyet/routes/rest.clj +++ b/src/clj/youyesyet/routes/rest.clj @@ -40,7 +40,7 @@ (defn get-issues "Get current issues. No arguments expected." - [request] + [request]) (defroutes rest-routes (GET "/rest/get-local-data" request (route/restricted (get-local-data request))) From f23e52a65bebb2ec08146fbc484ccbb1c8671c67 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sat, 17 Mar 2018 18:33:06 +0000 Subject: [PATCH 17/20] Squirrel-parse generated routes and queries are now working Routes and queries auto-generated from migrations by [squirrel-parse](https://github.com/simon-brooke/squirrel-parse) are now actually working! --- .../entity-relationship-diagram.svg | 10 +- .../20161014170335-basic-setup.up.sql | 4 +- ...0316110100-intentions-and-options.down.sql | 24 + ...180316110100-intentions-and-options.up.sql | 30 + .../migrations/20180317170000-gender.down.sql | 3 + .../migrations/20180317170000-gender.up.sql | 11 + .../20180317170907-reference-data.down.sql | 10 + .../20180317170907-reference-data.up.sql | 10 + .../20180317175047-test-data.down.sql | 3 + .../20180317175047-test-data.up.sql | 41 + resources/sql/queries.auto.sql | 735 ++++++++++++ src/clj/youyesyet/db/core.clj | 14 +- src/clj/youyesyet/handler.clj | 7 +- src/clj/youyesyet/routes/auto_json_routes.clj | 1035 +++++++++++++++++ src/clj/youyesyet/routes/rest.clj | 2 +- 15 files changed, 1923 insertions(+), 16 deletions(-) create mode 100644 resources/migrations/20180316110100-intentions-and-options.down.sql create mode 100644 resources/migrations/20180316110100-intentions-and-options.up.sql create mode 100644 resources/migrations/20180317170000-gender.down.sql create mode 100644 resources/migrations/20180317170000-gender.up.sql create mode 100644 resources/migrations/20180317170907-reference-data.down.sql create mode 100644 resources/migrations/20180317170907-reference-data.up.sql create mode 100644 resources/migrations/20180317175047-test-data.down.sql create mode 100644 resources/migrations/20180317175047-test-data.up.sql create mode 100644 resources/sql/queries.auto.sql create mode 100644 src/clj/youyesyet/routes/auto_json_routes.clj diff --git a/doc/specification/entity-relationship-diagram.svg b/doc/specification/entity-relationship-diagram.svg index 0de6cf0..dbb5475 100644 --- a/doc/specification/entity-relationship-diagram.svg +++ b/doc/specification/entity-relationship-diagram.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.979899" - inkscape:cx="833.70674" - inkscape:cy="324.89697" + inkscape:zoom="0.9899495" + inkscape:cx="472.36875" + inkscape:cy="325.73865" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -62,8 +62,8 @@ id="rect4428" width="1030" height="730" - x="232.23355" - y="376.0018" /> + x="16.060907" + y="312.36218" /> sql-date (.getTime) (java.util.Date.))) diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index f898fe5..bc67a8c 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -3,6 +3,7 @@ [youyesyet.layout :refer [error-page]] [youyesyet.routes.home :refer [home-routes]] [youyesyet.routes.oauth :refer [oauth-routes]] + [youyesyet.routes.auto-json-routes :refer [auto-rest-routes]] [compojure.route :as route] [youyesyet.env :refer [defaults]] [mount.core :as mount] @@ -37,6 +38,9 @@ (-> #'home-routes (wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-formats)) + (-> #'auto-rest-routes + (wrap-routes middleware/wrap-csrf) + (wrap-routes middleware/wrap-formats)) #'oauth-routes (route/not-found (:body @@ -44,4 +48,5 @@ :title "page not found"}))))) -(def app (middleware/wrap-base #'app-routes)) +(def app #'app-routes) + ;;(middleware/wrap-base #'app-routes)) diff --git a/src/clj/youyesyet/routes/auto_json_routes.clj b/src/clj/youyesyet/routes/auto_json_routes.clj new file mode 100644 index 0000000..8d3d707 --- /dev/null +++ b/src/clj/youyesyet/routes/auto_json_routes.clj @@ -0,0 +1,1035 @@ +(ns + youyesyet.routes.auto-json-routes + (require + [noir.response :as nresponse] + [noir.util.route :as route] + [compojure.core :refer [defroutes GET POST]] + [ring.util.http-response :as response] + [clojure.java.io :as io] + [hugsql.core :as hugsql] + [youyesyet.db.core :as db])) + + +(declare + create-addresse + create-authority + create-canvasser + create-district + create-elector + create-followupaction + create-followupmethod + create-followuprequest + create-intention + create-issue + create-issueexpertise + create-option + create-role + create-rolemembership + create-schema-migration + create-team + create-teammembership + create-teamorganisership + create-visit + delete-addresse + delete-authority + delete-canvasser + delete-district + delete-elector + delete-followupaction + delete-followupmethod + delete-followuprequest + delete-issue + delete-option + delete-visit + get-addresse + get-authority + get-canvasser + get-district + get-elector + get-followupaction + get-followupmethod + get-followuprequest + get-issue + get-option + get-visit + list-addresses + list-addresses-by-district + list-authorities + list-canvassers + list-canvassers-by-addresse + list-canvassers-by-authoritie + list-canvassers-by-elector + list-districts + list-electors + list-electors-by-addresse + list-followupactions + list-followupactions-by-canvasser + list-followupactions-by-followuprequest + list-followupmethods + list-followuprequests + list-followuprequests-by-elector + list-followuprequests-by-followupmethod + list-followuprequests-by-issue + list-followuprequests-by-visit + list-intentions-electors-by-option + list-intentions-electors-by-visit + list-intentions-options-by-elector + list-intentions-options-by-visit + list-intentions-visits-by-elector + list-intentions-visits-by-option + list-issueexpertise-canvassers-by-followupmethod + list-issueexpertise-canvassers-by-issue + list-issueexpertise-followupmethods-by-canvasser + list-issueexpertise-followupmethods-by-issue + list-issueexpertise-issues-by-canvasser + list-issueexpertise-issues-by-followupmethod + list-issues + list-options + list-rolememberships-canvassers-by-role + list-rolememberships-roles-by-canvasser + list-roles + list-schemamigrations + list-teammemberships-canvassers-by-team + list-teammemberships-teams-by-canvasser + list-teamorganiserships-canvassers-by-team + list-teamorganiserships-teams-by-canvasser + list-teams + list-teams-by-district + list-visits + list-visits-by-addresse + list-visits-by-canvasser + update-addresse + update-canvasser + update-district + update-elector + update-followupaction + update-followuprequest + update-issue + update-visit) + + +(defroutes + auto-rest-routes + (POST "/json/auto/create-addresse" request (create-addresse request)) + (POST + "/json/auto/create-authority" + request + (create-authority request)) + (POST + "/json/auto/create-canvasser" + request + (create-canvasser request)) + (POST "/json/auto/create-district" request (create-district request)) + (POST "/json/auto/create-elector" request (create-elector request)) + (POST + "/json/auto/create-followupaction" + request + (create-followupaction request)) + (POST + "/json/auto/create-followupmethod" + request + (create-followupmethod request)) + (POST + "/json/auto/create-followuprequest" + request + (create-followuprequest request)) + (POST + "/json/auto/create-intention" + request + (create-intention request)) + (POST "/json/auto/create-issue" request (create-issue request)) + (POST + "/json/auto/create-issueexpertise" + request + (create-issueexpertise request)) + (POST "/json/auto/create-option" request (create-option request)) + (POST "/json/auto/create-role" request (create-role request)) + (POST + "/json/auto/create-rolemembership" + request + (create-rolemembership request)) + (POST + "/json/auto/create-schema-migration" + request + (create-schema-migration request)) + (POST "/json/auto/create-team" request (create-team request)) + (POST + "/json/auto/create-teammembership" + request + (create-teammembership request)) + (POST + "/json/auto/create-teamorganisership" + request + (create-teamorganisership request)) + (POST "/json/auto/create-visit" request (create-visit request)) + (POST "/json/auto/delete-addresse" request (delete-addresse request)) + (POST + "/json/auto/delete-authority" + request + (delete-authority request)) + (POST + "/json/auto/delete-canvasser" + request + (delete-canvasser request)) + (POST "/json/auto/delete-district" request (delete-district request)) + (POST "/json/auto/delete-elector" request (delete-elector request)) + (POST + "/json/auto/delete-followupaction" + request + (delete-followupaction request)) + (POST + "/json/auto/delete-followupmethod" + request + (delete-followupmethod request)) + (POST + "/json/auto/delete-followuprequest" + request + (delete-followuprequest request)) + (POST "/json/auto/delete-issue" request (delete-issue request)) + (POST "/json/auto/delete-option" request (delete-option request)) + (POST "/json/auto/delete-visit" request (delete-visit request)) + (POST "/json/auto/get-addresse" request (get-addresse request)) + (POST "/json/auto/get-authority" request (get-authority request)) + (POST "/json/auto/get-canvasser" request (get-canvasser request)) + (POST "/json/auto/get-district" request (get-district request)) + (POST "/json/auto/get-elector" request (get-elector request)) + (POST + "/json/auto/get-followupaction" + request + (get-followupaction request)) + (POST + "/json/auto/get-followupmethod" + request + (get-followupmethod request)) + (POST + "/json/auto/get-followuprequest" + request + (get-followuprequest request)) + (POST "/json/auto/get-issue" request (get-issue request)) + (POST "/json/auto/get-option" request (get-option request)) + (POST "/json/auto/get-visit" request (get-visit request)) + (GET "/json/auto/list-addresses" request (list-addresses request)) + (GET + "/json/auto/list-addresses-by-district" + request + (list-addresses-by-district request)) + (GET "/json/auto/list-authorities" request (list-authorities request)) + (GET "/json/auto/list-canvassers" request (list-canvassers request)) + (GET + "/json/auto/list-canvassers-by-addresse" + request + (list-canvassers-by-addresse request)) + (GET + "/json/auto/list-canvassers-by-authoritie" + request + (list-canvassers-by-authoritie request)) + (GET + "/json/auto/list-canvassers-by-elector" + request + (list-canvassers-by-elector request)) + (GET "/json/auto/list-districts" request (list-districts request)) + (GET "/json/auto/list-electors" request (list-electors request)) + (GET + "/json/auto/list-electors-by-addresse" + request + (list-electors-by-addresse request)) + (GET + "/json/auto/list-followupactions" + request + (list-followupactions request)) + (GET + "/json/auto/list-followupactions-by-canvasser" + request + (list-followupactions-by-canvasser request)) + (GET + "/json/auto/list-followupactions-by-followuprequest" + request + (list-followupactions-by-followuprequest request)) + (GET + "/json/auto/list-followupmethods" + request + (list-followupmethods request)) + (GET + "/json/auto/list-followuprequests" + request + (list-followuprequests request)) + (GET + "/json/auto/list-followuprequests-by-elector" + request + (list-followuprequests-by-elector request)) + (GET + "/json/auto/list-followuprequests-by-followupmethod" + request + (list-followuprequests-by-followupmethod request)) + (GET + "/json/auto/list-followuprequests-by-issue" + request + (list-followuprequests-by-issue request)) + (GET + "/json/auto/list-followuprequests-by-visit" + request + (list-followuprequests-by-visit request)) + (GET + "/json/auto/list-intentions-electors-by-option" + request + (list-intentions-electors-by-option request)) + (GET + "/json/auto/list-intentions-electors-by-visit" + request + (list-intentions-electors-by-visit request)) + (GET + "/json/auto/list-intentions-options-by-elector" + request + (list-intentions-options-by-elector request)) + (GET + "/json/auto/list-intentions-options-by-visit" + request + (list-intentions-options-by-visit request)) + (GET + "/json/auto/list-intentions-visits-by-elector" + request + (list-intentions-visits-by-elector request)) + (GET + "/json/auto/list-intentions-visits-by-option" + request + (list-intentions-visits-by-option request)) + (GET + "/json/auto/list-issueexpertise-canvassers-by-followupmethod" + request + (list-issueexpertise-canvassers-by-followupmethod request)) + (GET + "/json/auto/list-issueexpertise-canvassers-by-issue" + request + (list-issueexpertise-canvassers-by-issue request)) + (GET + "/json/auto/list-issueexpertise-followupmethods-by-canvasser" + request + (list-issueexpertise-followupmethods-by-canvasser request)) + (GET + "/json/auto/list-issueexpertise-followupmethods-by-issue" + request + (list-issueexpertise-followupmethods-by-issue request)) + (GET + "/json/auto/list-issueexpertise-issues-by-canvasser" + request + (list-issueexpertise-issues-by-canvasser request)) + (GET + "/json/auto/list-issueexpertise-issues-by-followupmethod" + request + (list-issueexpertise-issues-by-followupmethod request)) + (GET "/json/auto/list-issues" request (list-issues request)) + (GET "/json/auto/list-options" request (list-options request)) + (GET + "/json/auto/list-rolememberships-canvassers-by-role" + request + (list-rolememberships-canvassers-by-role request)) + (GET + "/json/auto/list-rolememberships-roles-by-canvasser" + request + (list-rolememberships-roles-by-canvasser request)) + (GET "/json/auto/list-roles" request (list-roles request)) + (GET + "/json/auto/list-schemamigrations" + request + (list-schemamigrations request)) + (GET + "/json/auto/list-teammemberships-canvassers-by-team" + request + (list-teammemberships-canvassers-by-team request)) + (GET + "/json/auto/list-teammemberships-teams-by-canvasser" + request + (list-teammemberships-teams-by-canvasser request)) + (GET + "/json/auto/list-teamorganiserships-canvassers-by-team" + request + (list-teamorganiserships-canvassers-by-team request)) + (GET + "/json/auto/list-teamorganiserships-teams-by-canvasser" + request + (list-teamorganiserships-teams-by-canvasser request)) + (GET "/json/auto/list-teams" request (list-teams request)) + (GET + "/json/auto/list-teams-by-district" + request + (list-teams-by-district request)) + (GET "/json/auto/list-visits" request (list-visits request)) + (GET + "/json/auto/list-visits-by-addresse" + request + (list-visits-by-addresse request)) + (GET + "/json/auto/list-visits-by-canvasser" + request + (list-visits-by-canvasser request)) + (POST "/json/auto/update-addresse" request (update-addresse request)) + (POST + "/json/auto/update-canvasser" + request + (update-canvasser request)) + (POST "/json/auto/update-district" request (update-district request)) + (POST "/json/auto/update-elector" request (update-elector request)) + (POST + "/json/auto/update-followupaction" + request + (update-followupaction request)) + (POST + "/json/auto/update-followuprequest" + request + (update-followuprequest request)) + (POST "/json/auto/update-issue" request (update-issue request)) + (POST "/json/auto/update-visit" request (update-visit request))) + + +(defn + create-addresse + "Auto-generated method to insert one record to the addresses table. Expects the following key(s) to be present in `params`: (:id :address :postcode :phone :district_id :latitude :longitude). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-addresse! params))) + + +(defn + create-authority + "Auto-generated method to insert one record to the authorities table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-authority! params))) + + +(defn + create-canvasser + "Auto-generated method to insert one record to the canvassers table. Expects the following key(s) to be present in `params`: (:id :username :fullname :elector_id :address_id :phone :email :authority_id :authorised). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-canvasser! params))) + + +(defn + create-district + "Auto-generated method to insert one record to the districts table. Expects the following key(s) to be present in `params`: (:id :name). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-district! params))) + + +(defn + create-elector + "Auto-generated method to insert one record to the electors table. Expects the following key(s) to be present in `params`: (:id :name :address_id :phone :email). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-elector! params))) + + +(defn + create-followupaction + "Auto-generated method to insert one record to the followupactions table. Expects the following key(s) to be present in `params`: (:id :request_id :actor :date :notes :closed). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-followupaction! params))) + + +(defn + create-followupmethod + "Auto-generated method to insert one record to the followupmethods table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-followupmethod! params))) + + +(defn + create-followuprequest + "Auto-generated method to insert one record to the followuprequests table. Expects the following key(s) to be present in `params`: (:id :elector_id :visit_id :issue_id :method_id). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-followuprequest! params))) + + +(defn + create-intention + "Auto-generated method to insert one record to the intentions table. Expects the following key(s) to be present in `params`: (:visit_id :elector_id :option_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-intention! params))) + + +(defn + create-issue + "Auto-generated method to insert one record to the issues table. Expects the following key(s) to be present in `params`: (:id :url). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-issue! params))) + + +(defn + create-issueexpertise + "Auto-generated method to insert one record to the issueexpertise table. Expects the following key(s) to be present in `params`: (:canvasser_id :issue_id :method_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-issueexpertise! params))) + + +(defn + create-option + "Auto-generated method to insert one record to the options table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-option! params))) + + +(defn + create-role + "Auto-generated method to insert one record to the roles table. Expects the following key(s) to be present in `params`: (:id :name). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-role! params))) + + +(defn + create-rolemembership + "Auto-generated method to insert one record to the rolememberships table. Expects the following key(s) to be present in `params`: (:role_id :canvasser_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-rolemembership! params))) + + +(defn + create-schema-migration + "Auto-generated method to insert one record to the schema_migrations table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-schema-migration! params))) + + +(defn + create-team + "Auto-generated method to insert one record to the teams table. Expects the following key(s) to be present in `params`: (:id :name :district_id :latitude :longitude). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-team! params))) + + +(defn + create-teammembership + "Auto-generated method to insert one record to the teammemberships table. Expects the following key(s) to be present in `params`: (:team_id :canvasser_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-teammembership! params))) + + +(defn + create-teamorganisership + "Auto-generated method to insert one record to the teamorganiserships table. Expects the following key(s) to be present in `params`: (:team_id :canvasser_id). Returns a map containing the keys nil identifying the record created." + [{:keys [params]}] + (do (db/create-teamorganisership! params))) + + +(defn + create-visit + "Auto-generated method to insert one record to the visits table. Expects the following key(s) to be present in `params`: (:id :address_id :canvasser_id :date). Returns a map containing the keys (:id) identifying the record created." + [{:keys [params]}] + (do (db/create-visit! params))) + + +(defn + delete-addresse + "Auto-generated method to delete one record from the addresses table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-addresse! params)) + (response/found "/")) + + +(defn + delete-authority + "Auto-generated method to delete one record from the authorities table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-authority! params)) + (response/found "/")) + + +(defn + delete-canvasser + "Auto-generated method to delete one record from the canvassers table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-canvasser! params)) + (response/found "/")) + + +(defn + delete-district + "Auto-generated method to delete one record from the districts table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-district! params)) + (response/found "/")) + + +(defn + delete-elector + "Auto-generated method to delete one record from the electors table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-elector! params)) + (response/found "/")) + + +(defn + delete-followupaction + "Auto-generated method to delete one record from the followupactions table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-followupaction! params)) + (response/found "/")) + + +(defn + delete-followupmethod + "Auto-generated method to delete one record from the followupmethods table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-followupmethod! params)) + (response/found "/")) + + +(defn + delete-followuprequest + "Auto-generated method to delete one record from the followuprequests table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-followuprequest! params)) + (response/found "/")) + + +(defn + delete-issue + "Auto-generated method to delete one record from the issues table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-issue! params)) + (response/found "/")) + + +(defn + delete-option + "Auto-generated method to delete one record from the options table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-option! params)) + (response/found "/")) + + +(defn + delete-visit + "Auto-generated method to delete one record from the visits table. Expects the following key(s) to be present in `params`: (:id)." + [{:keys [params]}] + (do (db/delete-visit! params)) + (response/found "/")) + + +(defn + get-addresse + "Auto-generated method to select one record from the addresses table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:address :district_id :id :latitude :longitude :phone :postcode)." + [{:keys [params]}] + (do (db/get-addresse params))) + + +(defn + get-authority + "Auto-generated method to select one record from the authorities table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id)." + [{:keys [params]}] + (do (db/get-authority params))) + + +(defn + get-canvasser + "Auto-generated method to select one record from the canvassers table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:address_id :authorised :authority_id :elector_id :email :fullname :id :phone :username)." + [{:keys [params]}] + (do (db/get-canvasser params))) + + +(defn + get-district + "Auto-generated method to select one record from the districts table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id :name)." + [{:keys [params]}] + (do (db/get-district params))) + + +(defn + get-elector + "Auto-generated method to select one record from the electors table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:address_id :email :id :name :phone)." + [{:keys [params]}] + (do (db/get-elector params))) + + +(defn + get-followupaction + "Auto-generated method to select one record from the followupactions table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:actor :closed :date :id :notes :request_id)." + [{:keys [params]}] + (do (db/get-followupaction params))) + + +(defn + get-followupmethod + "Auto-generated method to select one record from the followupmethods table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id)." + [{:keys [params]}] + (do (db/get-followupmethod params))) + + +(defn + get-followuprequest + "Auto-generated method to select one record from the followuprequests table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:elector_id :id :issue_id :method_id :visit_id)." + [{:keys [params]}] + (do (db/get-followuprequest params))) + + +(defn + get-issue + "Auto-generated method to select one record from the issues table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id :url)." + [{:keys [params]}] + (do (db/get-issue params))) + + +(defn + get-option + "Auto-generated method to select one record from the options table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:id)." + [{:keys [params]}] + (do (db/get-option params))) + + +(defn + get-visit + "Auto-generated method to select one record from the visits table. Expects the following key(s) to be present in `params`: (:id). Returns a map containing the following keys: (:address_id :canvasser_id :date :id)." + [{:keys [params]}] + (do (db/get-visit params))) + + +(defn + list-addresses + "Auto-generated method to select all records from the addresses table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:address :district_id :id :latitude :longitude :phone :postcode)." + [{:keys [params]}] + (do (db/list-addresses params))) + + +(defn + list-addresses-by-district + [{:keys [params]}] + (do (db/list-addresses-by-district params))) + + +(defn + list-authorities + "Auto-generated method to select all records from the authorities table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id)." + [{:keys [params]}] + (do (db/list-authorities params))) + + +(defn + list-canvassers + "Auto-generated method to select all records from the canvassers table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:address_id :authorised :authority_id :elector_id :email :fullname :id :phone :username)." + [{:keys [params]}] + (do (db/list-canvassers params))) + + +(defn + list-canvassers-by-addresse + [{:keys [params]}] + (do (db/list-canvassers-by-addresse params))) + + +(defn + list-canvassers-by-authoritie + [{:keys [params]}] + (do (db/list-canvassers-by-authoritie params))) + + +(defn + list-canvassers-by-elector + [{:keys [params]}] + (do (db/list-canvassers-by-elector params))) + + +(defn + list-districts + "Auto-generated method to select all records from the districts table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id :name)." + [{:keys [params]}] + (do (db/list-districts params))) + + +(defn + list-electors + "Auto-generated method to select all records from the electors table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:address_id :email :id :name :phone)." + [{:keys [params]}] + (do (db/list-electors params))) + + +(defn + list-electors-by-addresse + [{:keys [params]}] + (do (db/list-electors-by-addresse params))) + + +(defn + list-followupactions + "Auto-generated method to select all records from the followupactions table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:actor :closed :date :id :notes :request_id)." + [{:keys [params]}] + (do (db/list-followupactions params))) + + +(defn + list-followupactions-by-canvasser + [{:keys [params]}] + (do (db/list-followupactions-by-canvasser params))) + + +(defn + list-followupactions-by-followuprequest + [{:keys [params]}] + (do (db/list-followupactions-by-followuprequest params))) + + +(defn + list-followupmethods + "Auto-generated method to select all records from the followupmethods table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id)." + [{:keys [params]}] + (do (db/list-followupmethods params))) + + +(defn + list-followuprequests + "Auto-generated method to select all records from the followuprequests table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:elector_id :id :issue_id :method_id :visit_id)." + [{:keys [params]}] + (do (db/list-followuprequests params))) + + +(defn + list-followuprequests-by-elector + [{:keys [params]}] + (do (db/list-followuprequests-by-elector params))) + + +(defn + list-followuprequests-by-followupmethod + [{:keys [params]}] + (do (db/list-followuprequests-by-followupmethod params))) + + +(defn + list-followuprequests-by-issue + [{:keys [params]}] + (do (db/list-followuprequests-by-issue params))) + + +(defn + list-followuprequests-by-visit + [{:keys [params]}] + (do (db/list-followuprequests-by-visit params))) + + +(defn + list-intentions-electors-by-option + [{:keys [params]}] + (do (db/list-intentions-electors-by-option params))) + + +(defn + list-intentions-electors-by-visit + [{:keys [params]}] + (do (db/list-intentions-electors-by-visit params))) + + +(defn + list-intentions-options-by-elector + [{:keys [params]}] + (do (db/list-intentions-options-by-elector params))) + + +(defn + list-intentions-options-by-visit + [{:keys [params]}] + (do (db/list-intentions-options-by-visit params))) + + +(defn + list-intentions-visits-by-elector + [{:keys [params]}] + (do (db/list-intentions-visits-by-elector params))) + + +(defn + list-intentions-visits-by-option + [{:keys [params]}] + (do (db/list-intentions-visits-by-option params))) + + +(defn + list-issueexpertise-canvassers-by-followupmethod + [{:keys [params]}] + (do (db/list-issueexpertise-canvassers-by-followupmethod params))) + + +(defn + list-issueexpertise-canvassers-by-issue + [{:keys [params]}] + (do (db/list-issueexpertise-canvassers-by-issue params))) + + +(defn + list-issueexpertise-followupmethods-by-canvasser + [{:keys [params]}] + (do (db/list-issueexpertise-followupmethods-by-canvasser params))) + + +(defn + list-issueexpertise-followupmethods-by-issue + [{:keys [params]}] + (do (db/list-issueexpertise-followupmethods-by-issue params))) + + +(defn + list-issueexpertise-issues-by-canvasser + [{:keys [params]}] + (do (db/list-issueexpertise-issues-by-canvasser params))) + + +(defn + list-issueexpertise-issues-by-followupmethod + [{:keys [params]}] + (do (db/list-issueexpertise-issues-by-followupmethod params))) + + +(defn + list-issues + "Auto-generated method to select all records from the issues table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id :url)." + [{:keys [params]}] + (do (db/list-issues params))) + + +(defn + list-options + "Auto-generated method to select all records from the options table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id)." + [{:keys [params]}] + (do (db/list-options params))) + + +(defn + list-rolememberships-canvassers-by-role + [{:keys [params]}] + (do (db/list-rolememberships-canvassers-by-role params))) + + +(defn + list-rolememberships-roles-by-canvasser + [{:keys [params]}] + (do (db/list-rolememberships-roles-by-canvasser params))) + + +(defn + list-roles + "Auto-generated method to select all records from the roles table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id :name)." + [{:keys [params]}] + (do (db/list-roles params))) + + +(defn + list-schemamigrations + "Auto-generated method to select all records from the schema_migrations table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:id)." + [{:keys [params]}] + (do (db/list-schema_migrations params))) + + +(defn + list-teammemberships-canvassers-by-team + [{:keys [params]}] + (do (db/list-teammemberships-canvassers-by-team params))) + + +(defn + list-teammemberships-teams-by-canvasser + [{:keys [params]}] + (do (db/list-teammemberships-teams-by-canvasser params))) + + +(defn + list-teamorganiserships-canvassers-by-team + [{:keys [params]}] + (do (db/list-teamorganiserships-canvassers-by-team params))) + + +(defn + list-teamorganiserships-teams-by-canvasser + [{:keys [params]}] + (do (db/list-teamorganiserships-teams-by-canvasser params))) + + +(defn + list-teams + "Auto-generated method to select all records from the teams table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:district_id :id :latitude :longitude :name)." + [{:keys [params]}] + (do (db/list-teams params))) + + +(defn + list-teams-by-district + [{:keys [params]}] + (do (db/list-teams-by-district params))) + + +(defn + list-visits + "Auto-generated method to select all records from the visits table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: (:address_id :canvasser_id :date :id)." + [{:keys [params]}] + (do (db/list-visits params))) + + +(defn + list-visits-by-addresse + [{:keys [params]}] + (do (db/list-visits-by-addresse params))) + + +(defn + list-visits-by-canvasser + [{:keys [params]}] + (do (db/list-visits-by-canvasser params))) + + +(defn + update-addresse + "Auto-generated method to update one record in the addresses table. Expects the following key(s) to be present in `params`: (:address :district_id :id :latitude :longitude :phone :postcode)." + [{:keys [params]}] + (do (db/update-addresse! params)) + (response/found "/")) + + +(defn + update-canvasser + "Auto-generated method to update one record in the canvassers table. Expects the following key(s) to be present in `params`: (:address_id :authorised :authority_id :elector_id :email :fullname :id :phone :username)." + [{:keys [params]}] + (do (db/update-canvasser! params)) + (response/found "/")) + + +(defn + update-district + "Auto-generated method to update one record in the districts table. Expects the following key(s) to be present in `params`: (:id :name)." + [{:keys [params]}] + (do (db/update-district! params)) + (response/found "/")) + + +(defn + update-elector + "Auto-generated method to update one record in the electors table. Expects the following key(s) to be present in `params`: (:address_id :email :id :name :phone)." + [{:keys [params]}] + (do (db/update-elector! params)) + (response/found "/")) + + +(defn + update-followupaction + "Auto-generated method to update one record in the followupactions table. Expects the following key(s) to be present in `params`: (:actor :closed :date :id :notes :request_id)." + [{:keys [params]}] + (do (db/update-followupaction! params)) + (response/found "/")) + + +(defn + update-followuprequest + "Auto-generated method to update one record in the followuprequests table. Expects the following key(s) to be present in `params`: (:elector_id :id :issue_id :method_id :visit_id)." + [{:keys [params]}] + (do (db/update-followuprequest! params)) + (response/found "/")) + + +(defn + update-issue + "Auto-generated method to update one record in the issues table. Expects the following key(s) to be present in `params`: (:id :url)." + [{:keys [params]}] + (do (db/update-issue! params)) + (response/found "/")) + + +(defn + update-visit + "Auto-generated method to update one record in the visits table. Expects the following key(s) to be present in `params`: (:address_id :canvasser_id :date :id)." + [{:keys [params]}] + (do (db/update-visit! params)) + (response/found "/")) + + diff --git a/src/clj/youyesyet/routes/rest.clj b/src/clj/youyesyet/routes/rest.clj index 6ca9b3f..c164b9b 100644 --- a/src/clj/youyesyet/routes/rest.clj +++ b/src/clj/youyesyet/routes/rest.clj @@ -40,7 +40,7 @@ (defn get-issues "Get current issues. No arguments expected." - [request] + [request]) (defroutes rest-routes (GET "/rest/get-local-data" request (route/restricted (get-local-data request))) From 0eac2f558122e286ff04daac4f86a535fb309843 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sat, 12 May 2018 14:26:22 +0100 Subject: [PATCH 18/20] Added migration for reference data, canonical ADL. --- .../20180408124500-reference-data.down.sql | 1 + .../20180408124500-reference-data.up.sql | 2 + youyesyet.canonical.adl.xml | 356 ++++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 resources/migrations/20180408124500-reference-data.down.sql create mode 100644 resources/migrations/20180408124500-reference-data.up.sql create mode 100644 youyesyet.canonical.adl.xml diff --git a/resources/migrations/20180408124500-reference-data.down.sql b/resources/migrations/20180408124500-reference-data.down.sql new file mode 100644 index 0000000..68bada9 --- /dev/null +++ b/resources/migrations/20180408124500-reference-data.down.sql @@ -0,0 +1 @@ +alter table issues drop column current; diff --git a/resources/migrations/20180408124500-reference-data.up.sql b/resources/migrations/20180408124500-reference-data.up.sql new file mode 100644 index 0000000..aaae234 --- /dev/null +++ b/resources/migrations/20180408124500-reference-data.up.sql @@ -0,0 +1,2 @@ +alter table issues add column current boolean default true; + diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml new file mode 100644 index 0000000..7d8ca0e --- /dev/null +++ b/youyesyet.canonical.adl.xml @@ -0,0 +1,356 @@ + + + + + + + + + See + https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/488478/Bulk_Data_Transfer_-_additional_validation_valid_from_12_November_2015.pdf, + section 3 + A valid postcode. + + + All users + + + All users of the canvasser app Able to read and add canvassing data in a limited + radius around their current position. + + + Organisers of canvassing teams Able to see and modify data on the canvassers in + the team(s) they organise; able to add canvassers to their team; able to update canvassers in + their team, including resetting passwords and locking accounts; able to see canvass data over + the whole area in which their team operates. + + + People expert on particular issues. Able to read followup requests, and the electors to which they + relate; able to access (read/write) the issues wiki; able to write followuop action records. + + + + Users entitled to see an overview of the canvassing data collected. Able to read canvassing data over the whole map, including historical + data. + + + Users responsible for determining what issues should be current at any time. + Able to set current issues; able to add issues. + + + Able to read and update canvasser records, team membership records, team + organisership records, issue expertise records; able to add and update reference data + generally. + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Issues believed to be of interest to electors, about which they may have questions. + + + + + + + + + + + + + + + + Link table. + + + + + + + + + + + + Primary users of the system: those actually interviewing electors. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + But only their own record + + + But only canvassers in their own team. + + + All canvassers + + + + Requests for a followup with an issue expert + + + + + + + + + + + + + + + + + + + + Link table + + + + + + + + + A role (essentially, the same as a group, but application layer rather than database layer) of which a user may be a member. + + + + + + + + + + + Teams of canvassers who work together under common leadership. + + + + + + + + + + + + + + + + + + + + But only their own group(s) + + + All groups + + + + Electoral districts + + + + + + + + + + + + + Link table + + + + + + + + + Actions taken on followup requests. + + + + + + + + + + + + + + + + + + + + + + + But only for electors in their immediate vicinity + + + + + Link table + + + + + + + + + + + + Options in the election or referendum being canvassed on + + + + + + + + Link table + + + + + + + + + + + + + + + From 162274c23fd410d1e35f485ec3af2cc7caa4c4e4 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sun, 13 May 2018 18:36:17 +0100 Subject: [PATCH 19/20] Getting serious autogenerated files now! --- resources/sql/queries.auto.sql | 1008 +++++++++++--------------------- youyesyet.canonical.adl.xml | 6 +- 2 files changed, 340 insertions(+), 674 deletions(-) diff --git a/resources/sql/queries.auto.sql b/resources/sql/queries.auto.sql index b8f5451..5f63d1e 100644 --- a/resources/sql/queries.auto.sql +++ b/resources/sql/queries.auto.sql @@ -1,104 +1,104 @@ --- :name delete-elector! :! :n --- :doc updates an existing elector record -DELETE FROM electors -WHERE electors.id = :id +-- File queries.sql +-- autogenerated by adl.to-hugsql-queries at +-- 2018-05-13T16:47:04.188Z +-- See [Application Description Language](https://github.com/simon-brooke/adl). --- :name get-district :? :1 --- :doc selects an existing district record -SELECT * FROM districts -WHERE districts.id = :id -ORDER BY districts.id --- :name update-addresse! :! :n --- :doc updates an existing addresse record -UPDATE addresses -SET id = :id, - address = :address, - postcode = :postcode, - phone = :phone, - district_id = :district_id, - latitude = :latitude, - longitude = :longitude -WHERE addresses.id = :id --- :name list-teamorganiserships-teams-by-canvasser :? :* --- :doc lists all existing teams records related through teamorganiserships to a given canvasser -SELECT teams.* -FROM teams, teamorganiserships -WHERE teams. = teamorganiserships.team_id - AND teamorganiserships.canvasser_id = :id -ORDER BY teams. +-- :name create-address! :! :n +-- :doc creates a new address record +INSERT INTO addresses (address, + postcode, + phone, + district_id, + latitude, + longitude) +VALUES (:address, + :postcode, + :phone, + :district_id, + :latitude, + :longitude) -- :name create-authority! :! :n -- :doc creates a new authority record INSERT INTO authorities (id) VALUES (:id) -returning id --- :name list-canvassers :? :* --- :doc lists all existing canvasser records -SELECT * FROM canvassers -ORDER BY canvassers.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") +-- :name create-canvasser! :! :n +-- :doc creates a new canvasser record +INSERT INTO canvassers (username, + fullname, + elector_id, + address_id, + phone, + email, + authority_id, + authorised) +VALUES (:username, + :fullname, + :elector_id, + :address_id, + :phone, + :email, + :authority_id, + :authorised) --- :name delete-canvasser! :! :n --- :doc updates an existing canvasser record -DELETE FROM canvassers -WHERE canvassers.id = :id +-- :name create-district! :! :n +-- :doc creates a new district record +INSERT INTO districts (name) +VALUES (:name) --- :name get-followupmethod :? :1 --- :doc selects an existing followupmethod record -SELECT * FROM followupmethods -WHERE followupmethods.id = :id -ORDER BY followupmethods.id +-- :name create-elector! :! :n +-- :doc creates a new elector record +INSERT INTO electors (name, + address_id, + phone, + email, + gender) +VALUES (:name, + :address_id, + :phone, + :email, + :gender) --- :name get-canvasser :? :1 --- :doc selects an existing canvasser record -SELECT * FROM canvassers -WHERE canvassers.id = :id -ORDER BY canvassers.id +-- :name create-followupaction! :! :n +-- :doc creates a new followupaction record +INSERT INTO followupactions (request_id, + actor, + date, + notes, + closed, + id) +VALUES (:request_id, + :actor, + :date, + :notes, + :closed, + :id) --- :name create-role! :! :n --- :doc creates a new role record -INSERT INTO roles (id, - name) -VALUES (:id, - :name) +-- :name create-followupmethod! :! :n +-- :doc creates a new followupmethod record +INSERT INTO followupmethods (id) +VALUES (:id) --- :name list-issueexpertise-issues-by-canvasser :? :* --- :doc lists all existing issues records related through issueexpertise to a given canvasser -SELECT issues.* -FROM issues, issueexpertise -WHERE issues.id = issueexpertise.issue_id - AND issueexpertise.canvasser_id = :id -ORDER BY issues.id +-- :name create-followuprequest! :! :n +-- :doc creates a new followuprequest record +INSERT INTO followuprequests (elector_id, + visit_id, + issue_id, + method_id, + id) +VALUES (:elector_id, + :visit_id, + :issue_id, + :method_id, + :id) --- :name update-followupaction! :! :n --- :doc updates an existing followupaction record -UPDATE followupactions -SET id = :id, - request_id = :request_id, - actor = :actor, - date = :date, - notes = :notes, - closed = :closed -WHERE followupactions.id = :id - --- :name list-teammemberships-teams-by-canvasser :? :* --- :doc lists all existing teams records related through teammemberships to a given canvasser -SELECT teams.* -FROM teams, teammemberships -WHERE teams. = teammemberships.team_id - AND teammemberships.canvasser_id = :id -ORDER BY teams. - --- :name list-authorities :? :* --- :doc lists all existing authority records -SELECT * FROM authorities -ORDER BY authorities.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") +-- :name create-gender! :! :n +-- :doc creates a new gender record +INSERT INTO genders (id) +VALUES (:id) -- :name create-intention! :! :n -- :doc creates a new intention record @@ -109,313 +109,33 @@ VALUES (:visit_id, :elector_id, :option_id) --- :name delete-issue! :! :n --- :doc updates an existing issue record -DELETE FROM issues -WHERE issues.id = :id - --- :name list-followuprequests-by-visit :? :* --- :doc lists all existing followuprequest records related to a given visit -SELECT * -FROM followuprequests -WHERE followuprequests.visit_id = :id -ORDER BY followuprequests.id - --- :name list-canvassers-by-addresse :? :* --- :doc lists all existing canvasser records related to a given addresse -SELECT * -FROM canvassers -WHERE canvassers.address_id = :id -ORDER BY canvassers.id - --- :name list-intentions-options-by-elector :? :* --- :doc lists all existing options records related through intentions to a given elector -SELECT options.* -FROM options, intentions -WHERE options.id = intentions.option_id - AND intentions.elector_id = :id -ORDER BY options.id - --- :name create-followupmethod! :! :n --- :doc creates a new followupmethod record -INSERT INTO followupmethods (id) -VALUES (:id) -returning id - --- :name list-canvassers-by-elector :? :* --- :doc lists all existing canvasser records related to a given elector -SELECT * -FROM canvassers -WHERE canvassers.elector_id = :id -ORDER BY canvassers.id - --- :name create-district! :! :n --- :doc creates a new district record -INSERT INTO districts (id, - name) -VALUES (:id, - :name) -returning id - --- :name delete-followupaction! :! :n --- :doc updates an existing followupaction record -DELETE FROM followupactions -WHERE followupactions.id = :id - --- :name create-followupaction! :! :n --- :doc creates a new followupaction record -INSERT INTO followupactions (id, - request_id, - actor, - date, - notes, - closed) -VALUES (:id, - :request_id, - :actor, - :date, - :notes, - :closed) -returning id - --- :name list-canvassers-by-authoritie :? :* --- :doc lists all existing canvasser records related to a given authoritie -SELECT * -FROM canvassers -WHERE canvassers.authority_id = :id -ORDER BY canvassers.id - --- :name list-followuprequests :? :* --- :doc lists all existing followuprequest records -SELECT * FROM followuprequests -ORDER BY followuprequests.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name list-followupactions :? :* --- :doc lists all existing followupaction records -SELECT * FROM followupactions -ORDER BY followupactions.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name create-followuprequest! :! :n --- :doc creates a new followuprequest record -INSERT INTO followuprequests (id, - elector_id, - visit_id, - issue_id, - method_id) -VALUES (:id, - :elector_id, - :visit_id, - :issue_id, - :method_id) -returning id - --- :name update-issue! :! :n --- :doc updates an existing issue record -UPDATE issues -SET id = :id, - url = :url -WHERE issues.id = :id - --- :name get-option :? :1 --- :doc selects an existing option record -SELECT * FROM options -WHERE options.id = :id -ORDER BY options.id - --- :name list-issueexpertise-followupmethods-by-issue :? :* --- :doc lists all existing followupmethods records related through issueexpertise to a given issue -SELECT followupmethods.* -FROM followupmethods, issueexpertise -WHERE followupmethods.id = issueexpertise.method_id - AND issueexpertise.issue_id = :id -ORDER BY followupmethods.id - --- :name list-intentions-visits-by-option :? :* --- :doc lists all existing visits records related through intentions to a given option -SELECT visits.* -FROM visits, intentions -WHERE visits.id = intentions.visit_id - AND intentions.option_id = :id -ORDER BY visits.id - --- :name list-teams :? :* --- :doc lists all existing team records -SELECT * FROM teams -ORDER BY teams. ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name list-schema_migrations :? :* --- :doc lists all existing schema-migration records -SELECT * FROM schema_migrations -ORDER BY schema_migrations. ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name create-elector! :! :n --- :doc creates a new elector record -INSERT INTO electors (id, - name, - address_id, - phone, - email) -VALUES (:id, - :name, - :address_id, - :phone, - :email) -returning id - --- :name delete-addresse! :! :n --- :doc updates an existing addresse record -DELETE FROM addresses -WHERE addresses.id = :id - --- :name delete-followuprequest! :! :n --- :doc updates an existing followuprequest record -DELETE FROM followuprequests -WHERE followuprequests.id = :id - --- :name list-options :? :* --- :doc lists all existing option records -SELECT * FROM options -ORDER BY options.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name get-followupaction :? :1 --- :doc selects an existing followupaction record -SELECT * FROM followupactions -WHERE followupactions.id = :id -ORDER BY followupactions.id - --- :name list-followupactions-by-canvasser :? :* --- :doc lists all existing followupaction records related to a given canvasser -SELECT * -FROM followupactions -WHERE followupactions.actor = :id -ORDER BY followupactions.id - --- :name get-issue :? :1 --- :doc selects an existing issue record -SELECT * FROM issues -WHERE issues.id = :id -ORDER BY issues.id - --- :name create-teamorganisership! :! :n --- :doc creates a new teamorganisership record -INSERT INTO teamorganiserships (team_id, - canvasser_id) -VALUES (:team_id, - :canvasser_id) - --- :name get-visit :? :1 --- :doc selects an existing visit record -SELECT * FROM visits -WHERE visits.id = :id -ORDER BY visits.id - --- :name list-addresses :? :* --- :doc lists all existing addresse records -SELECT * FROM addresses -ORDER BY addresses.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name create-team! :! :n --- :doc creates a new team record -INSERT INTO teams (id, - name, - district_id, - latitude, - longitude) -VALUES (:id, - :name, - :district_id, - :latitude, - :longitude) - --- :name list-addresses-by-district :? :* --- :doc lists all existing addresse records related to a given district -SELECT * -FROM addresses -WHERE addresses.district_id = :id -ORDER BY addresses.id - -- :name create-issue! :! :n -- :doc creates a new issue record -INSERT INTO issues (id, - url) -VALUES (:id, - :url) -returning id +INSERT INTO issues (url, + current, + id) +VALUES (:url, + :current, + :id) --- :name delete-authority! :! :n --- :doc updates an existing authority record -DELETE FROM authorities -WHERE authorities.id = :id +-- :name create-issueexpertis! :! :n +-- :doc creates a new issueexpertis record +INSERT INTO issueexpertise (canvasser_id, + issue_id, + method_id) +VALUES (:canvasser_id, + :issue_id, + :method_id) --- :name create-canvasser! :! :n --- :doc creates a new canvasser record -INSERT INTO canvassers (id, - username, - fullname, - elector_id, - address_id, - phone, - email, - authority_id, - authorised) -VALUES (:id, - :username, - :fullname, - :elector_id, - :address_id, - :phone, - :email, - :authority_id, - :authorised) -returning id +-- :name create-option! :! :n +-- :doc creates a new option record +INSERT INTO options (id) +VALUES (:id) --- :name list-visits-by-addresse :? :* --- :doc lists all existing visit records related to a given addresse -SELECT * -FROM visits -WHERE visits.address_id = :id -ORDER BY visits.id - --- :name delete-district! :! :n --- :doc updates an existing district record -DELETE FROM districts -WHERE districts.id = :id - --- :name get-addresse :? :1 --- :doc selects an existing addresse record -SELECT * FROM addresses -WHERE addresses.id = :id -ORDER BY addresses.id - --- :name create-addresse! :! :n --- :doc creates a new addresse record -INSERT INTO addresses (id, - address, - postcode, - phone, - district_id, - latitude, - longitude) -VALUES (:id, - :address, - :postcode, - :phone, - :district_id, - :latitude, - :longitude) -returning id +-- :name create-role! :! :n +-- :doc creates a new role record +INSERT INTO roles (name) +VALUES (:name) -- :name create-rolemembership! :! :n -- :doc creates a new rolemembership record @@ -424,19 +144,16 @@ INSERT INTO rolememberships (role_id, VALUES (:role_id, :canvasser_id) --- :name list-issueexpertise-followupmethods-by-canvasser :? :* --- :doc lists all existing followupmethods records related through issueexpertise to a given canvasser -SELECT followupmethods.* -FROM followupmethods, issueexpertise -WHERE followupmethods.id = issueexpertise.method_id - AND issueexpertise.canvasser_id = :id -ORDER BY followupmethods.id - --- :name get-followuprequest :? :1 --- :doc selects an existing followuprequest record -SELECT * FROM followuprequests -WHERE followuprequests.id = :id -ORDER BY followuprequests.id +-- :name create-team! :! :n +-- :doc creates a new team record +INSERT INTO teams (name, + district_id, + latitude, + longitude) +VALUES (:name, + :district_id, + :latitude, + :longitude) -- :name create-teammembership! :! :n -- :doc creates a new teammembership record @@ -445,291 +162,240 @@ INSERT INTO teammemberships (team_id, VALUES (:team_id, :canvasser_id) --- :name delete-option! :! :n --- :doc updates an existing option record -DELETE FROM options -WHERE options.id = :id +-- :name create-teamorganisership! :! :n +-- :doc creates a new teamorganisership record +INSERT INTO teamorganiserships (team_id, + canvasser_id) +VALUES (:team_id, + :canvasser_id) --- :name list-teammemberships-canvassers-by-team :? :* --- :doc lists all existing canvassers records related through teammemberships to a given team -SELECT canvassers.* -FROM canvassers, teammemberships -WHERE canvassers.id = teammemberships.canvasser_id - AND teammemberships.team_id = :id -ORDER BY canvassers.id +-- :name create-visit! :! :n +-- :doc creates a new visit record +INSERT INTO visits (address_id, + canvasser_id, + date) +VALUES (:address_id, + :canvasser_id, + :date) --- :name list-issueexpertise-canvassers-by-issue :? :* --- :doc lists all existing canvassers records related through issueexpertise to a given issue -SELECT canvassers.* -FROM canvassers, issueexpertise -WHERE canvassers.id = issueexpertise.canvasser_id - AND issueexpertise.issue_id = :id -ORDER BY canvassers.id +-- :name list-addresses :? :* +-- :doc lists all existing address records +SELECT * FROM addresses +ORDER BY addresses.address, + addresses.postcode +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") --- :name delete-followupmethod! :! :n --- :doc updates an existing followupmethod record -DELETE FROM followupmethods -WHERE followupmethods.id = :id +-- :name list-addresses-by-district :? :* +-- :doc lists all existing address records related to a given district +SELECT * +FROM addresses +WHERE addresses.district_id = :id +ORDER BY addresses.address, + addresses.postcode + +-- :name list-canvassers :? :* +-- :doc lists all existing canvasser records +SELECT * FROM canvassers +ORDER BY canvassers.username, + canvassers.fullname, + canvassers.email +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-canvassers-by-address :? :* +-- :doc lists all existing canvasser records related to a given address +SELECT * +FROM canvassers +WHERE canvassers.address_id = :id +ORDER BY canvassers.username, + canvassers.fullname, + canvassers.email + +-- :name list-canvassers-by-authority :? :* +-- :doc lists all existing canvasser records related to a given authority +SELECT * +FROM canvassers +WHERE canvassers.authority_id = :id +ORDER BY canvassers.username, + canvassers.fullname, + canvassers.email + +-- :name list-canvassers-by-elector :? :* +-- :doc lists all existing canvasser records related to a given elector +SELECT * +FROM canvassers +WHERE canvassers.elector_id = :id +ORDER BY canvassers.username, + canvassers.fullname, + canvassers.email -- :name list-districts :? :* -- :doc lists all existing district records SELECT * FROM districts -ORDER BY districts.id +ORDER BY districts.name --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") --- :name list-rolememberships-canvassers-by-role :? :* --- :doc lists all existing canvassers records related through rolememberships to a given role -SELECT canvassers.* -FROM canvassers, rolememberships -WHERE canvassers.id = rolememberships.canvasser_id - AND rolememberships.role_id = :id -ORDER BY canvassers.id - --- :name get-authority :? :1 --- :doc selects an existing authority record -SELECT * FROM authorities -WHERE authorities.id = :id -ORDER BY authorities.id - --- :name create-option! :! :n --- :doc creates a new option record -INSERT INTO options (id) -VALUES (:id) -returning id - --- :name list-visits :? :* --- :doc lists all existing visit records -SELECT * FROM visits -ORDER BY visits.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name list-teamorganiserships-canvassers-by-team :? :* --- :doc lists all existing canvassers records related through teamorganiserships to a given team -SELECT canvassers.* -FROM canvassers, teamorganiserships -WHERE canvassers.id = teamorganiserships.canvasser_id - AND teamorganiserships.team_id = :id -ORDER BY canvassers.id - --- :name get-elector :? :1 --- :doc selects an existing elector record -SELECT * FROM electors -WHERE electors.id = :id -ORDER BY electors.id - --- :name create-visit! :! :n --- :doc creates a new visit record -INSERT INTO visits (id, - address_id, - canvasser_id, - date) -VALUES (:id, - :address_id, - :canvasser_id, - :date) -returning id - --- :name list-roles :? :* --- :doc lists all existing role records -SELECT * FROM roles -ORDER BY roles. ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name update-visit! :! :n --- :doc updates an existing visit record -UPDATE visits -SET id = :id, - address_id = :address_id, - canvasser_id = :canvasser_id, - date = :date -WHERE visits.id = :id - --- :name update-district! :! :n --- :doc updates an existing district record -UPDATE districts -SET id = :id, - name = :name -WHERE districts.id = :id - --- :name list-followupactions-by-followuprequest :? :* --- :doc lists all existing followupaction records related to a given followuprequest -SELECT * -FROM followupactions -WHERE followupactions.request_id = :id -ORDER BY followupactions.id - --- :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) - --- :name list-issueexpertise-canvassers-by-followupmethod :? :* --- :doc lists all existing canvassers records related through issueexpertise to a given followupmethod -SELECT canvassers.* -FROM canvassers, issueexpertise -WHERE canvassers.id = issueexpertise.canvasser_id - AND issueexpertise.method_id = :id -ORDER BY canvassers.id - --- :name update-elector! :! :n --- :doc updates an existing elector record -UPDATE electors -SET id = :id, - name = :name, - address_id = :address_id, - phone = :phone, - email = :email -WHERE electors.id = :id - --- :name list-followupmethods :? :* --- :doc lists all existing followupmethod records -SELECT * FROM followupmethods -ORDER BY followupmethods.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name delete-visit! :! :n --- :doc updates an existing visit record -DELETE FROM visits -WHERE visits.id = :id - --- :name list-intentions-electors-by-option :? :* --- :doc lists all existing electors records related through intentions to a given option -SELECT electors.* -FROM electors, intentions -WHERE electors.id = intentions.elector_id - AND intentions.option_id = :id -ORDER BY electors.id - --- :name create-schema-migration! :! :n --- :doc creates a new schema-migration record -INSERT INTO schema_migrations (id) -VALUES (:id) - --- :name update-canvasser! :! :n --- :doc updates an existing canvasser record -UPDATE canvassers -SET id = :id, - username = :username, - fullname = :fullname, - elector_id = :elector_id, - address_id = :address_id, - phone = :phone, - email = :email, - authority_id = :authority_id, - authorised = :authorised -WHERE canvassers.id = :id - --- :name list-intentions-options-by-visit :? :* --- :doc lists all existing options records related through intentions to a given visit -SELECT options.* -FROM options, intentions -WHERE options.id = intentions.option_id - AND intentions.visit_id = :id -ORDER BY options.id - --- :name list-followuprequests-by-issue :? :* --- :doc lists all existing followuprequest records related to a given issue -SELECT * -FROM followuprequests -WHERE followuprequests.issue_id = :id -ORDER BY followuprequests.id - --- :name list-followuprequests-by-followupmethod :? :* --- :doc lists all existing followuprequest records related to a given followupmethod -SELECT * -FROM followuprequests -WHERE followuprequests.method_id = :id -ORDER BY followuprequests.id - --- :name list-intentions-visits-by-elector :? :* --- :doc lists all existing visits records related through intentions to a given elector -SELECT visits.* -FROM visits, intentions -WHERE visits.id = intentions.visit_id - AND intentions.elector_id = :id -ORDER BY visits.id - --- :name list-rolememberships-roles-by-canvasser :? :* --- :doc lists all existing roles records related through rolememberships to a given canvasser -SELECT roles.* -FROM roles, rolememberships -WHERE roles. = rolememberships.role_id - AND rolememberships.canvasser_id = :id -ORDER BY roles. - --- :name list-intentions-electors-by-visit :? :* --- :doc lists all existing electors records related through intentions to a given visit -SELECT electors.* -FROM electors, intentions -WHERE electors.id = intentions.elector_id - AND intentions.visit_id = :id -ORDER BY electors.id - --- :name list-issueexpertise-issues-by-followupmethod :? :* --- :doc lists all existing issues records related through issueexpertise to a given followupmethod -SELECT issues.* -FROM issues, issueexpertise -WHERE issues.id = issueexpertise.issue_id - AND issueexpertise.method_id = :id -ORDER BY issues.id - --- :name list-teams-by-district :? :* --- :doc lists all existing team records related to a given district -SELECT * -FROM teams -WHERE teams.district_id = :id -ORDER BY teams. - --- :name list-issues :? :* --- :doc lists all existing issue records -SELECT * FROM issues -ORDER BY issues.id ---~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") - --- :name list-electors-by-addresse :? :* --- :doc lists all existing elector records related to a given addresse -SELECT * -FROM electors -WHERE electors.address_id = :id -ORDER BY electors.id - --- :name list-visits-by-canvasser :? :* --- :doc lists all existing visit records related to a given canvasser -SELECT * -FROM visits -WHERE visits.canvasser_id = :id -ORDER BY visits.id - -- :name list-electors :? :* -- :doc lists all existing elector records SELECT * FROM electors -ORDER BY electors.id +ORDER BY electors.name, + electors.phone, + electors.email --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") --- :name list-followuprequests-by-elector :? :* --- :doc lists all existing followuprequest records related to a given elector -SELECT * -FROM followuprequests -WHERE followuprequests.elector_id = :id -ORDER BY followuprequests.id +-- :name list-electors-by-address :? :* +-- :doc lists all existing elector records related to a given address +SELECT * +FROM electors +WHERE electors.address_id = :id +ORDER BY electors.name, + electors.phone, + electors.email --- :name update-followuprequest! :! :n --- :doc updates an existing followuprequest record -UPDATE followuprequests -SET id = :id, - elector_id = :elector_id, - visit_id = :visit_id, - issue_id = :issue_id, - method_id = :method_id -WHERE followuprequests.id = :id +-- :name list-electors-by-gender :? :* +-- :doc lists all existing elector records related to a given gender +SELECT * +FROM electors +WHERE electors.gender = :id +ORDER BY electors.name, + electors.phone, + electors.email +-- :name list-followupactions :? :* +-- :doc lists all existing followupaction records +SELECT * FROM followupactions +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-followupactions-by-canvasser :? :* +-- :doc lists all existing followupaction records related to a given canvasser +SELECT * +FROM followupactions +WHERE followupactions.actor = :id + +-- :name list-followupactions-by-followuprequest :? :* +-- :doc lists all existing followupaction records related to a given followuprequest +SELECT * +FROM followupactions +WHERE followupactions.request_id = :id + +-- :name list-issues :? :* +-- :doc lists all existing issue records +SELECT * FROM issues +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-roles :? :* +-- :doc lists all existing role records +SELECT * FROM roles +ORDER BY roles.name +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-teams :? :* +-- :doc lists all existing team records +SELECT * FROM teams +ORDER BY teams.name +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-teams-by-district :? :* +-- :doc lists all existing team records related to a given district +SELECT * +FROM teams +WHERE teams.district_id = :id +ORDER BY teams.name + +-- :name list-visits :? :* +-- :doc lists all existing visit records +SELECT * FROM visits +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-visits-by-address :? :* +-- :doc lists all existing visit records related to a given address +SELECT * +FROM visits +WHERE visits.address_id = :id + +-- :name list-visits-by-canvasser :? :* +-- :doc lists all existing visit records related to a given canvasser +SELECT * +FROM visits +WHERE visits.canvasser_id = :id + +-- :name search-strings-address :? :1 +-- :doc selects existing address records having any string field matching `:pattern` by substring match +SELECT * FROM addresses +WHERE +address LIKE '%:pattern%' + OR phone LIKE '%:pattern%' +ORDER BY addresses.address, + addresses.postcode +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-canvasser :? :1 +-- :doc selects existing canvasser records having any string field matching `:pattern` by substring match +SELECT * FROM canvassers +WHERE +username LIKE '%:pattern%' + OR fullname LIKE '%:pattern%' + OR phone LIKE '%:pattern%' + OR email LIKE '%:pattern%' +ORDER BY canvassers.username, + canvassers.fullname, + canvassers.email +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-district :? :1 +-- :doc selects existing district records having any string field matching `:pattern` by substring match +SELECT * FROM districts +WHERE +name LIKE '%:pattern%' +ORDER BY districts.name +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-elector :? :1 +-- :doc selects existing elector records having any string field matching `:pattern` by substring match +SELECT * FROM electors +WHERE +name LIKE '%:pattern%' + OR phone LIKE '%:pattern%' + OR email LIKE '%:pattern%' +ORDER BY electors.name, + electors.phone, + electors.email +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-issue :? :1 +-- :doc selects existing issue records having any string field matching `:pattern` by substring match +SELECT * FROM issues +WHERE +url LIKE '%:pattern%' +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-role :? :1 +-- :doc selects existing role records having any string field matching `:pattern` by substring match +SELECT * FROM roles +WHERE +name LIKE '%:pattern%' +ORDER BY roles.name +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-team :? :1 +-- :doc selects existing team records having any string field matching `:pattern` by substring match +SELECT * FROM teams +WHERE +name LIKE '%:pattern%' +ORDER BY teams.name +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") \ No newline at end of file diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 7d8ca0e..0fa1f0a 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -45,7 +45,7 @@ - + @@ -82,13 +82,13 @@ - + - + From f86c5cffe6b8b10a31d595d668b6b31858c41a66 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sat, 26 May 2018 19:23:21 +0100 Subject: [PATCH 20/20] Mainly, added the dwellings table, implied by the revised ERD. Some other tidy up. --- .../entity-relationship-diagram.svg | 313 +++++----- project.clj | 1 + .../20180526162051-dwellings.down.sql | 8 + .../20180526162051-dwellings.up.sql | 11 + resources/sql/queries.auto.sql | 539 ++++++++++++++++-- src/clj/youyesyet/routes/auto_json_routes.clj | 28 +- youyesyet.adl.xml | 392 +++++++++++++ youyesyet.canonical.adl.xml | 240 ++++++-- 8 files changed, 1258 insertions(+), 274 deletions(-) create mode 100644 resources/migrations/20180526162051-dwellings.down.sql create mode 100644 resources/migrations/20180526162051-dwellings.up.sql create mode 100644 youyesyet.adl.xml diff --git a/doc/specification/entity-relationship-diagram.svg b/doc/specification/entity-relationship-diagram.svg index dbb5475..2e51c1e 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"> @@ -32,7 +32,7 @@ inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="1920" - inkscape:window-height="1058" + inkscape:window-height="1043" inkscape:window-x="1920" inkscape:window-y="0" inkscape:window-maximized="1"> @@ -66,27 +66,26 @@ y="312.36218" /> + y="335.1539" + style="font-size:20px;line-height:1.25">  YouYesYet: Entity Relationship Diagram + 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 + 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">District Addresss + sodipodi:role="line">Address Elector + 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   Authority + 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 + 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 IssueIssueExpertise Followup FollowupFollowupAction + 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 @@ -528,13 +517,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.2Date: 20170315Author: Simon BrookeCopyright: (c) 2016 Simon Brooke for Radical Independence Campaign Introduced Visited Recorded + 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">Recorded Raised + style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Bold Italic';text-align:start;writing-mode:lr-tb;text-anchor:start">Raised Authenticates + 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">Authenticates Has + 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">Has About + 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">About About Responded to + 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">Responded to Expressed + 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 + 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">Contains Resides at + 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">Resides at Requested + 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">Requested Performed + 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">Performed To + 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">To + y="562.36218" + style="font-size:20px;line-height:1.25">  Organiser-Organiser-ship TeamTeamMembership + id="tspan4385" + style="font-size:15px;line-height:1.25">Membership Has Has of + style="font-size:10px;line-height:1.25">of of + style="font-size:10px;line-height:1.25">of @@ -1035,16 +1002,15 @@ For + style="font-size:10px;line-height:1.25">For RoleRoleMembership + id="tspan4383" + style="font-size:15px;line-height:1.25">Membership @@ -1078,16 +1045,16 @@ x="561.61688" y="774.30139" /> Role + sodipodi:role="line" + style="font-size:15px;line-height:1.25">Role @@ -1114,28 +1081,26 @@ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> Is + style="font-size:10px;line-height:1.25">Is Includes + style="font-size:10px;line-height:1.25">Includes diff --git a/project.clj b/project.clj index 6108563..cd9bd48 100644 --- a/project.clj +++ b/project.clj @@ -53,6 +53,7 @@ [migratus-lein "0.4.2"] [org.clojars.punkisdead/lein-cucumber "1.0.5"] [lein-cljsbuild "1.1.4"] + [lein-codox "0.10.3"] [lein-uberwar "0.2.0"] [lein-bower "0.5.1"] [lein-less "1.7.5"]] diff --git a/resources/migrations/20180526162051-dwellings.down.sql b/resources/migrations/20180526162051-dwellings.down.sql new file mode 100644 index 0000000..ab91769 --- /dev/null +++ b/resources/migrations/20180526162051-dwellings.down.sql @@ -0,0 +1,8 @@ +alter table electors + add column address_id references addresses on delete no action; + +update electors + set address_id = + (select address_id + from dwellings + where dwellings.id electors.dwelling_id); diff --git a/resources/migrations/20180526162051-dwellings.up.sql b/resources/migrations/20180526162051-dwellings.up.sql new file mode 100644 index 0000000..41e1a6e --- /dev/null +++ b/resources/migrations/20180526162051-dwellings.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE dwellings +( + id INT NOT NULL PRIMARY KEY, + address_id INT NOT NULL references addresses on delete no action, + sub_address VARCHAR( 32) +); + +alter table electors + add column dwelling_id int references dwellings on delete no action; + +alter table electors drop column address_id; diff --git a/resources/sql/queries.auto.sql b/resources/sql/queries.auto.sql index 5f63d1e..a80bf0f 100644 --- a/resources/sql/queries.auto.sql +++ b/resources/sql/queries.auto.sql @@ -1,6 +1,6 @@ -- File queries.sql -- autogenerated by adl.to-hugsql-queries at --- 2018-05-13T16:47:04.188Z +-- 2018-05-26T15:03:25.295Z -- See [Application Description Language](https://github.com/simon-brooke/adl). @@ -19,11 +19,13 @@ VALUES (:address, :district_id, :latitude, :longitude) +returning id -- :name create-authority! :! :n -- :doc creates a new authority record INSERT INTO authorities (id) VALUES (:id) +returning id -- :name create-canvasser! :! :n -- :doc creates a new canvasser record @@ -43,24 +45,35 @@ VALUES (:username, :email, :authority_id, :authorised) +returning id -- :name create-district! :! :n -- :doc creates a new district record INSERT INTO districts (name) VALUES (:name) +returning id + +-- :name create-dwelling! :! :n +-- :doc creates a new dwelling record +INSERT INTO dwellings (address_id, + sub-address) +VALUES (:address_id, + :sub-address) +returning id -- :name create-elector! :! :n -- :doc creates a new elector record INSERT INTO electors (name, - address_id, + dwelling_id, phone, email, gender) VALUES (:name, - :address_id, + :dwelling_id, :phone, :email, :gender) +returning id -- :name create-followupaction! :! :n -- :doc creates a new followupaction record @@ -68,37 +81,37 @@ INSERT INTO followupactions (request_id, actor, date, notes, - closed, - id) + closed) VALUES (:request_id, :actor, :date, :notes, - :closed, - :id) + :closed) +returning id -- :name create-followupmethod! :! :n -- :doc creates a new followupmethod record INSERT INTO followupmethods (id) VALUES (:id) +returning id -- :name create-followuprequest! :! :n -- :doc creates a new followuprequest record INSERT INTO followuprequests (elector_id, visit_id, issue_id, - method_id, - id) + method_id) VALUES (:elector_id, :visit_id, :issue_id, - :method_id, - :id) + :method_id) +returning id -- :name create-gender! :! :n -- :doc creates a new gender record INSERT INTO genders (id) VALUES (:id) +returning id -- :name create-intention! :! :n -- :doc creates a new intention record @@ -108,6 +121,7 @@ INSERT INTO intentions (visit_id, VALUES (:visit_id, :elector_id, :option_id) +returning Id -- :name create-issue! :! :n -- :doc creates a new issue record @@ -117,6 +131,7 @@ INSERT INTO issues (url, VALUES (:url, :current, :id) +returning id -- :name create-issueexpertis! :! :n -- :doc creates a new issueexpertis record @@ -126,16 +141,19 @@ INSERT INTO issueexpertise (canvasser_id, VALUES (:canvasser_id, :issue_id, :method_id) +returning Id -- :name create-option! :! :n -- :doc creates a new option record INSERT INTO options (id) VALUES (:id) +returning id -- :name create-role! :! :n -- :doc creates a new role record INSERT INTO roles (name) VALUES (:name) +returning id -- :name create-rolemembership! :! :n -- :doc creates a new rolemembership record @@ -143,6 +161,7 @@ INSERT INTO rolememberships (role_id, canvasser_id) VALUES (:role_id, :canvasser_id) +returning Id -- :name create-team! :! :n -- :doc creates a new team record @@ -154,6 +173,7 @@ VALUES (:name, :district_id, :latitude, :longitude) +returning id -- :name create-teammembership! :! :n -- :doc creates a new teammembership record @@ -161,6 +181,7 @@ INSERT INTO teammemberships (team_id, canvasser_id) VALUES (:team_id, :canvasser_id) +returning Id -- :name create-teamorganisership! :! :n -- :doc creates a new teamorganisership record @@ -168,6 +189,7 @@ INSERT INTO teamorganiserships (team_id, canvasser_id) VALUES (:team_id, :canvasser_id) +returning Id -- :name create-visit! :! :n -- :doc creates a new visit record @@ -177,92 +199,338 @@ INSERT INTO visits (address_id, VALUES (:address_id, :canvasser_id, :date) +returning id + +-- :name delete-address! :! :n +-- :doc updates an existing address record +DELETE FROM addresses +WHERE addresses.id = :id + +-- :name delete-authority! :! :n +-- :doc updates an existing authority record +DELETE FROM authorities +WHERE authorities.id = :id + +-- :name delete-canvasser! :! :n +-- :doc updates an existing canvasser record +DELETE FROM canvassers +WHERE canvassers.id = :id + +-- :name delete-district! :! :n +-- :doc updates an existing district record +DELETE FROM districts +WHERE districts.id = :id + +-- :name delete-dwelling! :! :n +-- :doc updates an existing dwelling record +DELETE FROM dwellings +WHERE dwellings.id = :id + +-- :name delete-elector! :! :n +-- :doc updates an existing elector record +DELETE FROM electors +WHERE electors.id = :id + +-- :name delete-followupaction! :! :n +-- :doc updates an existing followupaction record +DELETE FROM followupactions +WHERE followupactions.id = :id + +-- :name delete-followupmethod! :! :n +-- :doc updates an existing followupmethod record +DELETE FROM followupmethods +WHERE followupmethods.id = :id + +-- :name delete-followuprequest! :! :n +-- :doc updates an existing followuprequest record +DELETE FROM followuprequests +WHERE followuprequests.id = :id + +-- :name delete-gender! :! :n +-- :doc updates an existing gender record +DELETE FROM genders +WHERE genders.id = :id + +-- :name delete-intention! :! :n +-- :doc updates an existing intention record +DELETE FROM intentions +WHERE intentions.Id = :Id + +-- :name delete-issue! :! :n +-- :doc updates an existing issue record +DELETE FROM issues +WHERE issues.id = :id + +-- :name delete-issueexpertis! :! :n +-- :doc updates an existing issueexpertis record +DELETE FROM issueexpertise +WHERE issueexpertise.Id = :Id + +-- :name delete-option! :! :n +-- :doc updates an existing option record +DELETE FROM options +WHERE options.id = :id + +-- :name delete-role! :! :n +-- :doc updates an existing role record +DELETE FROM roles +WHERE roles.id = :id + +-- :name delete-rolemembership! :! :n +-- :doc updates an existing rolemembership record +DELETE FROM rolememberships +WHERE rolememberships.Id = :Id + +-- :name delete-team! :! :n +-- :doc updates an existing team record +DELETE FROM teams +WHERE teams.id = :id + +-- :name delete-teammembership! :! :n +-- :doc updates an existing teammembership record +DELETE FROM teammemberships +WHERE teammemberships.Id = :Id + +-- :name delete-teamorganisership! :! :n +-- :doc updates an existing teamorganisership record +DELETE FROM teamorganiserships +WHERE teamorganiserships.Id = :Id + +-- :name delete-visit! :! :n +-- :doc updates an existing visit record +DELETE FROM visits +WHERE visits.id = :id + +-- :name get-address :? :1 +-- :doc selects an existing address record +SELECT * FROM addresses +WHERE addresses.id = :id +ORDER BY addresses.address, + addresses.postcode, + addresses.id + +-- :name get-authority :? :1 +-- :doc selects an existing authority record +SELECT * FROM authorities +WHERE authorities.id = :id + +-- :name get-canvasser :? :1 +-- :doc selects an existing canvasser record +SELECT * FROM canvassers +WHERE canvassers.id = :id +ORDER BY canvassers.username, + canvassers.fullname, + canvassers.email, + canvassers.id + +-- :name get-district :? :1 +-- :doc selects an existing district record +SELECT * FROM districts +WHERE districts.id = :id +ORDER BY districts.name, + districts.id + +-- :name get-dwelling :? :1 +-- :doc selects an existing dwelling record +SELECT * FROM dwellings +WHERE dwellings.id = :id + +-- :name get-elector :? :1 +-- :doc selects an existing elector record +SELECT * FROM electors +WHERE electors.id = :id +ORDER BY electors.name, + electors.phone, + electors.email, + electors.id + +-- :name get-followupaction :? :1 +-- :doc selects an existing followupaction record +SELECT * FROM followupactions +WHERE followupactions.id = :id + +-- :name get-followupmethod :? :1 +-- :doc selects an existing followupmethod record +SELECT * FROM followupmethods +WHERE followupmethods.id = :id + +-- :name get-followuprequest :? :1 +-- :doc selects an existing followuprequest record +SELECT * FROM followuprequests +WHERE followuprequests.id = :id + +-- :name get-gender :? :1 +-- :doc selects an existing gender record +SELECT * FROM genders +WHERE genders.id = :id + +-- :name get-intention :? :1 +-- :doc selects an existing intention record +SELECT * FROM intentions +WHERE intentions.Id = :Id + +-- :name get-issue :? :1 +-- :doc selects an existing issue record +SELECT * FROM issues +WHERE issues.id = :id + +-- :name get-issueexpertis :? :1 +-- :doc selects an existing issueexpertis record +SELECT * FROM issueexpertise +WHERE issueexpertise.Id = :Id + +-- :name get-option :? :1 +-- :doc selects an existing option record +SELECT * FROM options +WHERE options.id = :id + +-- :name get-role :? :1 +-- :doc selects an existing role record +SELECT * FROM roles +WHERE roles.id = :id +ORDER BY roles.name, + roles.id + +-- :name get-rolemembership :? :1 +-- :doc selects an existing rolemembership record +SELECT * FROM rolememberships +WHERE rolememberships.Id = :Id + +-- :name get-team :? :1 +-- :doc selects an existing team record +SELECT * FROM teams +WHERE teams.id = :id +ORDER BY teams.name, + teams.id + +-- :name get-teammembership :? :1 +-- :doc selects an existing teammembership record +SELECT * FROM teammemberships +WHERE teammemberships.Id = :Id + +-- :name get-teamorganisership :? :1 +-- :doc selects an existing teamorganisership record +SELECT * FROM teamorganiserships +WHERE teamorganiserships.Id = :Id + +-- :name get-visit :? :1 +-- :doc selects an existing visit record +SELECT * FROM visits +WHERE visits.id = :id -- :name list-addresses :? :* -- :doc lists all existing address records SELECT * FROM addresses ORDER BY addresses.address, - addresses.postcode + addresses.postcode, + addresses.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name list-addresses-by-district :? :* -- :doc lists all existing address records related to a given district -SELECT * +SELECT * FROM addresses WHERE addresses.district_id = :id ORDER BY addresses.address, - addresses.postcode + addresses.postcode, + addresses.id + +-- :name list-authorities :? :* +-- :doc lists all existing authority records +SELECT * FROM authorities +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name list-canvassers :? :* -- :doc lists all existing canvasser records SELECT * FROM canvassers ORDER BY canvassers.username, canvassers.fullname, - canvassers.email + canvassers.email, + canvassers.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name list-canvassers-by-address :? :* -- :doc lists all existing canvasser records related to a given address -SELECT * +SELECT * FROM canvassers WHERE canvassers.address_id = :id ORDER BY canvassers.username, canvassers.fullname, - canvassers.email + canvassers.email, + canvassers.id -- :name list-canvassers-by-authority :? :* -- :doc lists all existing canvasser records related to a given authority -SELECT * +SELECT * FROM canvassers WHERE canvassers.authority_id = :id ORDER BY canvassers.username, canvassers.fullname, - canvassers.email + canvassers.email, + canvassers.id -- :name list-canvassers-by-elector :? :* -- :doc lists all existing canvasser records related to a given elector -SELECT * +SELECT * FROM canvassers WHERE canvassers.elector_id = :id ORDER BY canvassers.username, canvassers.fullname, - canvassers.email + canvassers.email, + canvassers.id -- :name list-districts :? :* -- :doc lists all existing district records SELECT * FROM districts -ORDER BY districts.name +ORDER BY districts.name, + districts.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") +-- :name list-dwellings :? :* +-- :doc lists all existing dwelling records +SELECT * FROM dwellings +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-dwellings-by-addres :? :* +-- :doc lists all existing dwelling records related to a given addres +SELECT * +FROM dwellings +WHERE dwellings.address_id = :id + -- :name list-electors :? :* -- :doc lists all existing elector records SELECT * FROM electors ORDER BY electors.name, electors.phone, - electors.email + electors.email, + electors.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") --- :name list-electors-by-address :? :* --- :doc lists all existing elector records related to a given address -SELECT * +-- :name list-electors-by-dwelling :? :* +-- :doc lists all existing elector records related to a given dwelling +SELECT * FROM electors -WHERE electors.address_id = :id +WHERE electors.dwelling_id = :id ORDER BY electors.name, electors.phone, - electors.email + electors.email, + electors.id -- :name list-electors-by-gender :? :* -- :doc lists all existing elector records related to a given gender -SELECT * +SELECT * FROM electors WHERE electors.gender = :id ORDER BY electors.name, electors.phone, - electors.email + electors.email, + electors.id -- :name list-followupactions :? :* -- :doc lists all existing followupaction records @@ -272,42 +540,195 @@ SELECT * FROM followupactions -- :name list-followupactions-by-canvasser :? :* -- :doc lists all existing followupaction records related to a given canvasser -SELECT * +SELECT * FROM followupactions WHERE followupactions.actor = :id -- :name list-followupactions-by-followuprequest :? :* -- :doc lists all existing followupaction records related to a given followuprequest -SELECT * +SELECT * FROM followupactions WHERE followupactions.request_id = :id +-- :name list-followupmethods :? :* +-- :doc lists all existing followupmethod records +SELECT * FROM followupmethods +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-followuprequests :? :* +-- :doc lists all existing followuprequest records +SELECT * FROM followuprequests +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-followuprequests-by-elector :? :* +-- :doc lists all existing followuprequest records related to a given elector +SELECT * +FROM followuprequests +WHERE followuprequests.elector_id = :id + +-- :name list-followuprequests-by-followupmethod :? :* +-- :doc lists all existing followuprequest records related to a given followupmethod +SELECT * +FROM followuprequests +WHERE followuprequests.method_id = :id + +-- :name list-followuprequests-by-issue :? :* +-- :doc lists all existing followuprequest records related to a given issue +SELECT * +FROM followuprequests +WHERE followuprequests.issue_id = :id + +-- :name list-followuprequests-by-visit :? :* +-- :doc lists all existing followuprequest records related to a given visit +SELECT * +FROM followuprequests +WHERE followuprequests.visit_id = :id + +-- :name list-genders :? :* +-- :doc lists all existing gender records +SELECT * FROM genders +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-intentions :? :* +-- :doc lists all existing intention records +SELECT * FROM intentions +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-intentions-by-elector :? :* +-- :doc lists all existing intention records related to a given elector +SELECT * +FROM intentions +WHERE intentions.elector_id = :id + +-- :name list-intentions-by-option :? :* +-- :doc lists all existing intention records related to a given option +SELECT * +FROM intentions +WHERE intentions.option_id = :id + +-- :name list-intentions-by-visit :? :* +-- :doc lists all existing intention records related to a given visit +SELECT * +FROM intentions +WHERE intentions.visit_id = :id + +-- :name list-issueexpertise :? :* +-- :doc lists all existing issueexpertis records +SELECT * FROM issueexpertise +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-issueexpertise-by-canvasser :? :* +-- :doc lists all existing issueexpertis records related to a given canvasser +SELECT * +FROM issueexpertise +WHERE issueexpertise.canvasser_id = :id + +-- :name list-issueexpertise-by-followupmethod :? :* +-- :doc lists all existing issueexpertis records related to a given followupmethod +SELECT * +FROM issueexpertise +WHERE issueexpertise.method_id = :id + +-- :name list-issueexpertise-by-issue :? :* +-- :doc lists all existing issueexpertis records related to a given issue +SELECT * +FROM issueexpertise +WHERE issueexpertise.issue_id = :id + -- :name list-issues :? :* -- :doc lists all existing issue records SELECT * FROM issues --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") +-- :name list-options :? :* +-- :doc lists all existing option records +SELECT * FROM options +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-rolememberships :? :* +-- :doc lists all existing rolemembership records +SELECT * FROM rolememberships +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-rolememberships-by-canvasser :? :* +-- :doc lists all existing rolemembership records related to a given canvasser +SELECT * +FROM rolememberships +WHERE rolememberships.canvasser_id = :id + +-- :name list-rolememberships-by-role :? :* +-- :doc lists all existing rolemembership records related to a given role +SELECT * +FROM rolememberships +WHERE rolememberships.role_id = :id + -- :name list-roles :? :* -- :doc lists all existing role records SELECT * FROM roles -ORDER BY roles.name +ORDER BY roles.name, + roles.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") +-- :name list-teammemberships :? :* +-- :doc lists all existing teammembership records +SELECT * FROM teammemberships +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-teammemberships-by-canvasser :? :* +-- :doc lists all existing teammembership records related to a given canvasser +SELECT * +FROM teammemberships +WHERE teammemberships.canvasser_id = :id + +-- :name list-teammemberships-by-team :? :* +-- :doc lists all existing teammembership records related to a given team +SELECT * +FROM teammemberships +WHERE teammemberships.team_id = :id + +-- :name list-teamorganiserships :? :* +-- :doc lists all existing teamorganisership records +SELECT * FROM teamorganiserships +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name list-teamorganiserships-by-canvasser :? :* +-- :doc lists all existing teamorganisership records related to a given canvasser +SELECT * +FROM teamorganiserships +WHERE teamorganiserships.canvasser_id = :id + +-- :name list-teamorganiserships-by-team :? :* +-- :doc lists all existing teamorganisership records related to a given team +SELECT * +FROM teamorganiserships +WHERE teamorganiserships.team_id = :id + -- :name list-teams :? :* -- :doc lists all existing team records SELECT * FROM teams -ORDER BY teams.name +ORDER BY teams.name, + teams.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name list-teams-by-district :? :* -- :doc lists all existing team records related to a given district -SELECT * +SELECT * FROM teams WHERE teams.district_id = :id -ORDER BY teams.name +ORDER BY teams.name, + teams.id -- :name list-visits :? :* -- :doc lists all existing visit records @@ -317,67 +738,79 @@ SELECT * FROM visits -- :name list-visits-by-address :? :* -- :doc lists all existing visit records related to a given address -SELECT * +SELECT * FROM visits WHERE visits.address_id = :id -- :name list-visits-by-canvasser :? :* -- :doc lists all existing visit records related to a given canvasser -SELECT * +SELECT * FROM visits WHERE visits.canvasser_id = :id -- :name search-strings-address :? :1 -- :doc selects existing address records having any string field matching `:pattern` by substring match SELECT * FROM addresses -WHERE +WHERE address LIKE '%:pattern%' OR phone LIKE '%:pattern%' ORDER BY addresses.address, - addresses.postcode + addresses.postcode, + addresses.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name search-strings-canvasser :? :1 -- :doc selects existing canvasser records having any string field matching `:pattern` by substring match SELECT * FROM canvassers -WHERE +WHERE username LIKE '%:pattern%' OR fullname LIKE '%:pattern%' OR phone LIKE '%:pattern%' OR email LIKE '%:pattern%' ORDER BY canvassers.username, canvassers.fullname, - canvassers.email + canvassers.email, + canvassers.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name search-strings-district :? :1 -- :doc selects existing district records having any string field matching `:pattern` by substring match SELECT * FROM districts -WHERE +WHERE name LIKE '%:pattern%' -ORDER BY districts.name +ORDER BY districts.name, + districts.id +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-dwelling :? :1 +-- :doc selects existing dwelling records having any string field matching `:pattern` by substring match +SELECT * FROM dwellings +WHERE +sub-address LIKE '%:pattern%' --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name search-strings-elector :? :1 -- :doc selects existing elector records having any string field matching `:pattern` by substring match SELECT * FROM electors -WHERE +WHERE name LIKE '%:pattern%' OR phone LIKE '%:pattern%' OR email LIKE '%:pattern%' ORDER BY electors.name, electors.phone, - electors.email + electors.email, + electors.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name search-strings-issue :? :1 -- :doc selects existing issue records having any string field matching `:pattern` by substring match SELECT * FROM issues -WHERE +WHERE url LIKE '%:pattern%' --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") @@ -385,17 +818,19 @@ url LIKE '%:pattern%' -- :name search-strings-role :? :1 -- :doc selects existing role records having any string field matching `:pattern` by substring match SELECT * FROM roles -WHERE +WHERE name LIKE '%:pattern%' -ORDER BY roles.name +ORDER BY roles.name, + roles.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name search-strings-team :? :1 -- :doc selects existing team records having any string field matching `:pattern` by substring match SELECT * FROM teams -WHERE +WHERE name LIKE '%:pattern%' -ORDER BY teams.name +ORDER BY teams.name, + teams.id --~ (if (:offset params) "OFFSET :offset ") ---~ (if (:limit params) "LIMIT :limit" "LIMIT 100") \ No newline at end of file +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") diff --git a/src/clj/youyesyet/routes/auto_json_routes.clj b/src/clj/youyesyet/routes/auto_json_routes.clj index 8d3d707..c9732cd 100644 --- a/src/clj/youyesyet/routes/auto_json_routes.clj +++ b/src/clj/youyesyet/routes/auto_json_routes.clj @@ -11,7 +11,7 @@ (declare - create-addresse + create-address create-authority create-canvasser create-district @@ -30,7 +30,7 @@ create-teammembership create-teamorganisership create-visit - delete-addresse + delete-address delete-authority delete-canvasser delete-district @@ -41,7 +41,7 @@ delete-issue delete-option delete-visit - get-addresse + get-address get-authority get-canvasser get-district @@ -61,7 +61,7 @@ list-canvassers-by-elector list-districts list-electors - list-electors-by-addresse + list-electors-by-address list-followupactions list-followupactions-by-canvasser list-followupactions-by-followuprequest @@ -96,9 +96,9 @@ list-teams list-teams-by-district list-visits - list-visits-by-addresse + list-visits-by-address list-visits-by-canvasser - update-addresse + update-address update-canvasser update-district update-elector @@ -110,7 +110,7 @@ (defroutes auto-rest-routes - (POST "/json/auto/create-addresse" request (create-addresse request)) + (POST "/json/auto/create-addresse" request (create-address request)) (POST "/json/auto/create-authority" request @@ -162,7 +162,7 @@ request (create-teamorganisership request)) (POST "/json/auto/create-visit" request (create-visit request)) - (POST "/json/auto/delete-addresse" request (delete-addresse request)) + (POST "/json/auto/delete-addresse" request (delete-address request)) (POST "/json/auto/delete-authority" request @@ -382,10 +382,10 @@ (defn - create-addresse + create-address "Auto-generated method to insert one record to the addresses table. Expects the following key(s) to be present in `params`: (:id :address :postcode :phone :district_id :latitude :longitude). Returns a map containing the keys (:id) identifying the record created." [{:keys [params]}] - (do (db/create-addresse! params))) + (do (db/create-address! params))) (defn @@ -515,10 +515,10 @@ (defn - delete-addresse + delete-address "Auto-generated method to delete one record from the addresses table. Expects the following key(s) to be present in `params`: (:id)." [{:keys [params]}] - (do (db/delete-addresse! params)) + (do (db/delete-address! params)) (response/found "/")) @@ -970,10 +970,10 @@ (defn - update-addresse + update-address "Auto-generated method to update one record in the addresses table. Expects the following key(s) to be present in `params`: (:address :district_id :id :latitude :longitude :phone :postcode)." [{:keys [params]}] - (do (db/update-addresse! params)) + (do (db/update-address! params)) (response/found "/")) diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml new file mode 100644 index 0000000..54e6214 --- /dev/null +++ b/youyesyet.adl.xml @@ -0,0 +1,392 @@ + + + + + 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. + + + + + + + + See + https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/488478/Bulk_Data_Transfer_-_additional_validation_valid_from_12_November_2015.pdf, + section 3 + A valid postcode. + + + All users + + + All users of the canvasser app Able to read and add canvassing data in a limited + radius around their current position. + + + Organisers of canvassing teams Able to see and modify data on the canvassers in + the team(s) they organise; able to add canvassers to their team; able to update canvassers in + their team, including resetting passwords and locking accounts; able to see canvass data over + the whole area in which their team operates. + + + People expert on particular issues. Able to read followup requests, and the electors to which they + relate; able to access (read/write) the issues wiki; able to write followuop action records. + + + + Users entitled to see an overview of the canvassing data collected. Able to read canvassing data over the whole map, including historical + data. + + + Users responsible for determining what issues should be current at any time. + Able to set current issues; able to add issues. + + + Able to read and update canvasser records, team membership records, team + organisership records, issue expertise records; able to add and update reference data + generally. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Issues believed to be of interest to electors, about which they may have questions. + + + + + + + + + + + + + + + + Link table. + + + + + + + + + + + + Primary users of the system: those actually interviewing electors. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + But only their own record + + + But only canvassers in their own team. + + + All canvassers + + + + Requests for a followup with an issue expert + + + + + + + + + + + + + + + + + + + + Link table + + + + + + + + + A role (essentially, the same as a group, but application layer rather than database layer) of which a user may be a member. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + But only their own group(s) + + + All groups + + + + Electoral districts + + + + + + + + + + + + + Link table + + + + + + + + + Actions taken on followup requests. + + + + + + + + + + + + + + + + + + + + + + + But only for electors in their immediate vicinity + + + + + Link table + + + + + + + + + + + + Options in the election or referendum being canvassed on + + + + + + + + Link table + + + + + + + + + + + + + + + diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 0fa1f0a..4bdbeee 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -1,11 +1,33 @@ - + + + + + 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. + - + See https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/488478/Bulk_Data_Transfer_-_additional_validation_valid_from_12_November_2015.pdf, section 3 @@ -18,7 +40,7 @@ All users of the canvasser app Able to read and add canvassing data in a limited radius around their current position. - + Organisers of canvassing teams Able to see and modify data on the canvassers in the team(s) they organise; able to add canvassers to their team; able to update canvassers in their team, including resetting passwords and locking accounts; able to see canvass data over @@ -42,30 +64,32 @@ organisership records, issue expertise records; able to add and update reference data generally. - + - + - - + + - + - + - - + + @@ -73,9 +97,35 @@ - -
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + @@ -96,16 +146,19 @@ - + dwe - -
+ +
+ @@ -124,6 +177,9 @@ + @@ -131,6 +187,9 @@ + Issues believed to be of interest to electors, about which they may have questions. @@ -144,21 +203,42 @@ - -
+ +
- + + Link table. + + + + + Auto-generated abstract primary key + + + - + + + + + + + + + Primary users of the system: those actually interviewing electors. @@ -184,7 +264,7 @@ - + @@ -200,35 +280,59 @@ All canvassers + Requests for a followup with an issue expert - + - + - + - + - + + Link table + + + + + Auto-generated abstract primary key + + + + + + + + + + + A role (essentially, the same as a group, but application layer rather than database layer) of which a user may be a member. @@ -240,8 +344,10 @@ + - Teams of canvassers who work together under common leadership. @@ -267,6 +373,9 @@ All groups + Electoral districts @@ -280,19 +389,40 @@ - + + Link table + + + + + Auto-generated abstract primary key + + + + + + + + + + + Actions taken on followup requests. - + @@ -317,18 +447,39 @@ - + + Link table - + + + + + Auto-generated abstract primary key + + + + - + + + + + + + + + Options in the election or referendum being canvassed on @@ -337,18 +488,39 @@ - + + Link table + + + + + Auto-generated abstract primary key + + + + + + + + + + + - +