From 4213f6159c5f2b2513300fd791c0fd653081d085 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 15 Jul 2017 17:51:28 +0100 Subject: [PATCH 01/51] #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 e300d0c4c5bb7fc919c5a0915d011e34627e3c9b Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 22 Jul 2017 11:04:59 +0100 Subject: [PATCH 02/51] #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 03/51] 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 04/51] 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 05/51] 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 06/51] 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 07/51] 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 08/51] 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 + + + + + + + + + + + - + From 79bf3ed7eb93c43a0bb3692153fb0e5ff81c13cb Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Tue, 5 Jun 2018 09:26:36 +0100 Subject: [PATCH 09/51] More work on ADL. --- youyesyet.adl.xml | 62 +++++++++++++++++++++---------------- youyesyet.canonical.adl.xml | 57 +++++++++++++++++----------------- 2 files changed, 65 insertions(+), 54 deletions(-) diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml index 54e6214..18884fd 100644 --- a/youyesyet.adl.xml +++ b/youyesyet.adl.xml @@ -1,9 +1,9 @@ - + + xmlns="http://bowyer.journeyman.cc/adl/1.4.1/" + xmlns:adl="http://bowyer.journeyman.cc/adl/1.4.1/"> A web-app intended to be used by canvassers campaigning for a 'Yes' vote in the second independence referendum. @@ -55,7 +55,7 @@ generally. - + @@ -64,9 +64,8 @@ - - + + @@ -81,7 +80,7 @@
- + @@ -90,7 +89,18 @@ - + + + + + + + + + + + + @@ -120,7 +130,7 @@ - + @@ -140,14 +150,14 @@ - + - + Issues believed to be of interest to electors, about which they may have questions. @@ -163,7 +173,7 @@ - + Link table. @@ -178,7 +188,7 @@ - + Primary users of the system: those actually interviewing electors. @@ -221,7 +231,7 @@ All canvassers - + Requests for a followup with an issue expert @@ -245,7 +255,7 @@ - + Link table @@ -256,7 +266,7 @@ - + A role (essentially, the same as a group, but application layer rather than database layer) of which a user may be a member. @@ -267,7 +277,7 @@ - + @@ -294,7 +304,7 @@ All groups - + Electoral districts @@ -307,7 +317,7 @@ - + Link table @@ -318,7 +328,7 @@ - + Actions taken on followup requests. @@ -348,7 +358,7 @@ - + Link table @@ -363,7 +373,7 @@ - + Options in the election or referendum being canvassed on @@ -371,7 +381,7 @@ - + Link table @@ -382,7 +392,7 @@ - + diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 4bdbeee..9939cad 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -1,12 +1,11 @@ - - + + - + @@ -88,10 +87,10 @@ - - + +
- + @@ -111,8 +110,10 @@ - - + + @@ -126,7 +127,7 @@ - + @@ -146,7 +147,7 @@ - dwe + @@ -159,7 +160,7 @@ - + @@ -180,7 +181,7 @@ - + @@ -190,7 +191,7 @@ - + Issues believed to be of interest to electors, about which they may have questions. @@ -209,7 +210,7 @@ - + Link table. @@ -239,7 +240,7 @@ - + Primary users of the system: those actually interviewing electors. @@ -283,7 +284,7 @@ - + Requests for a followup with an issue expert @@ -306,7 +307,7 @@ - + Link table @@ -333,7 +334,7 @@ - + A role (essentially, the same as a group, but application layer rather than database layer) of which a user may be a member. @@ -347,7 +348,7 @@ - + @@ -376,7 +377,7 @@ - + Electoral districts @@ -392,7 +393,7 @@ - + Link table @@ -419,7 +420,7 @@ - + Actions taken on followup requests. @@ -450,7 +451,7 @@ - + Link table @@ -480,7 +481,7 @@ - + Options in the election or referendum being canvassed on @@ -491,7 +492,7 @@ - + Link table @@ -518,7 +519,7 @@ - + From 85b54fbd607861fde48863cfe11c0174ac1a25be Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sun, 10 Jun 2018 22:31:12 +0100 Subject: [PATCH 10/51] #47, #48, #49, #50: Auto-generated admin system largely done This work is closely coupled with work on [ADL](https://github.com/simon-brooke/adl), q.v. At present ADL largely supports this project. --- .gitignore | 2 +- resources/sql/queries.auto.sql | 314 ++++++++- .../templates/auto/application-index.html | 204 ++++++ .../auto/form-addresses-Address.html | 232 +++++++ .../auto/form-canvassers-Canvasser.html | 317 +++++++++ .../auto/form-districts-District.html | 82 +++ .../auto/form-dwellings-Dwelling.html | 120 ++++ .../templates/auto/form-electors-Elector.html | 212 ++++++ .../form-followupactions-Followupaction.html | 204 ++++++ .../templates/auto/form-issues-Issue.html | 123 ++++ resources/templates/auto/form-roles-Role.html | 82 +++ resources/templates/auto/form-teams-Team.html | 195 ++++++ .../templates/auto/form-visits-Visit.html | 148 ++++ .../auto/list-addresses-Addresses.html | 115 ++++ .../auto/list-canvassers-Canvassers.html | 133 ++++ .../auto/list-districts-Districts.html | 70 ++ .../auto/list-dwellings-Dwellings.html | 79 +++ .../auto/list-electors-Electors.html | 106 +++ .../list-followupactions-Followupactions.html | 106 +++ .../templates/auto/list-issues-Issues.html | 79 +++ .../templates/auto/list-roles-Roles.html | 70 ++ .../templates/auto/list-teams-Teams.html | 97 +++ .../templates/auto/list-visits-Visits.html | 88 +++ src/clj/youyesyet/authorisation.clj | 4 + src/clj/youyesyet/routes/auto.clj | 639 ++++++++++++++++++ youyesyet.adl.xml | 38 +- youyesyet.canonical.adl.xml | 82 ++- 27 files changed, 3894 insertions(+), 47 deletions(-) create mode 100644 resources/templates/auto/application-index.html create mode 100644 resources/templates/auto/form-addresses-Address.html create mode 100644 resources/templates/auto/form-canvassers-Canvasser.html create mode 100644 resources/templates/auto/form-districts-District.html create mode 100644 resources/templates/auto/form-dwellings-Dwelling.html create mode 100644 resources/templates/auto/form-electors-Elector.html create mode 100644 resources/templates/auto/form-followupactions-Followupaction.html create mode 100644 resources/templates/auto/form-issues-Issue.html create mode 100644 resources/templates/auto/form-roles-Role.html create mode 100644 resources/templates/auto/form-teams-Team.html create mode 100644 resources/templates/auto/form-visits-Visit.html create mode 100644 resources/templates/auto/list-addresses-Addresses.html create mode 100644 resources/templates/auto/list-canvassers-Canvassers.html create mode 100644 resources/templates/auto/list-districts-Districts.html create mode 100644 resources/templates/auto/list-dwellings-Dwellings.html create mode 100644 resources/templates/auto/list-electors-Electors.html create mode 100644 resources/templates/auto/list-followupactions-Followupactions.html create mode 100644 resources/templates/auto/list-issues-Issues.html create mode 100644 resources/templates/auto/list-roles-Roles.html create mode 100644 resources/templates/auto/list-teams-Teams.html create mode 100644 resources/templates/auto/list-visits-Visits.html create mode 100644 src/clj/youyesyet/authorisation.clj create mode 100644 src/clj/youyesyet/routes/auto.clj diff --git a/.gitignore b/.gitignore index e65e38a..bf93d93 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,4 @@ pom.xml.asc *-init.clj profiles\.clj .bowerrc -bower.json \ No newline at end of file +bower.json diff --git a/resources/sql/queries.auto.sql b/resources/sql/queries.auto.sql index a80bf0f..b199ca0 100644 --- a/resources/sql/queries.auto.sql +++ b/resources/sql/queries.auto.sql @@ -1,11 +1,11 @@ -- File queries.sql -- autogenerated by adl.to-hugsql-queries at --- 2018-05-26T15:03:25.295Z +-- 2018-06-05T12:33:53.043Z -- See [Application Description Language](https://github.com/simon-brooke/adl). --- :name create-address! :! :n +-- :name create-address! :! :n -- :doc creates a new address record INSERT INTO addresses (address, postcode, @@ -21,13 +21,13 @@ VALUES (:address, :longitude) returning id --- :name create-authority! :! :n +-- :name create-authority! :! :n -- :doc creates a new authority record INSERT INTO authorities (id) VALUES (:id) returning id --- :name create-canvasser! :! :n +-- :name create-canvasser! :! :n -- :doc creates a new canvasser record INSERT INTO canvassers (username, fullname, @@ -36,7 +36,8 @@ INSERT INTO canvassers (username, phone, email, authority_id, - authorised) + authorised, + roles) VALUES (:username, :fullname, :elector_id, @@ -44,16 +45,17 @@ VALUES (:username, :phone, :email, :authority_id, - :authorised) + :authorised, + :roles) returning id --- :name create-district! :! :n +-- :name create-district! :! :n -- :doc creates a new district record INSERT INTO districts (name) VALUES (:name) returning id --- :name create-dwelling! :! :n +-- :name create-dwelling! :! :n -- :doc creates a new dwelling record INSERT INTO dwellings (address_id, sub-address) @@ -61,7 +63,7 @@ VALUES (:address_id, :sub-address) returning id --- :name create-elector! :! :n +-- :name create-elector! :! :n -- :doc creates a new elector record INSERT INTO electors (name, dwelling_id, @@ -75,7 +77,7 @@ VALUES (:name, :gender) returning id --- :name create-followupaction! :! :n +-- :name create-followupaction! :! :n -- :doc creates a new followupaction record INSERT INTO followupactions (request_id, actor, @@ -89,13 +91,13 @@ VALUES (:request_id, :closed) returning id --- :name create-followupmethod! :! :n +-- :name create-followupmethod! :! :n -- :doc creates a new followupmethod record INSERT INTO followupmethods (id) VALUES (:id) returning id --- :name create-followuprequest! :! :n +-- :name create-followuprequest! :! :n -- :doc creates a new followuprequest record INSERT INTO followuprequests (elector_id, visit_id, @@ -107,13 +109,13 @@ VALUES (:elector_id, :method_id) returning id --- :name create-gender! :! :n +-- :name create-gender! :! :n -- :doc creates a new gender record INSERT INTO genders (id) VALUES (:id) returning id --- :name create-intention! :! :n +-- :name create-intention! :! :n -- :doc creates a new intention record INSERT INTO intentions (visit_id, elector_id, @@ -123,7 +125,7 @@ VALUES (:visit_id, :option_id) returning Id --- :name create-issue! :! :n +-- :name create-issue! :! :n -- :doc creates a new issue record INSERT INTO issues (url, current, @@ -133,8 +135,8 @@ VALUES (:url, :id) returning id --- :name create-issueexpertis! :! :n --- :doc creates a new issueexpertis record +-- :name create-issueexpertise! :! :n +-- :doc creates a new issueexpertise record INSERT INTO issueexpertise (canvasser_id, issue_id, method_id) @@ -143,19 +145,19 @@ VALUES (:canvasser_id, :method_id) returning Id --- :name create-option! :! :n +-- :name create-option! :! :n -- :doc creates a new option record INSERT INTO options (id) VALUES (:id) returning id --- :name create-role! :! :n +-- :name create-role! :! :n -- :doc creates a new role record INSERT INTO roles (name) VALUES (:name) returning id --- :name create-rolemembership! :! :n +-- :name create-rolemembership! :! :n -- :doc creates a new rolemembership record INSERT INTO rolememberships (role_id, canvasser_id) @@ -163,7 +165,7 @@ VALUES (:role_id, :canvasser_id) returning Id --- :name create-team! :! :n +-- :name create-team! :! :n -- :doc creates a new team record INSERT INTO teams (name, district_id, @@ -175,7 +177,7 @@ VALUES (:name, :longitude) returning id --- :name create-teammembership! :! :n +-- :name create-teammembership! :! :n -- :doc creates a new teammembership record INSERT INTO teammemberships (team_id, canvasser_id) @@ -183,7 +185,7 @@ VALUES (:team_id, :canvasser_id) returning Id --- :name create-teamorganisership! :! :n +-- :name create-teamorganisership! :! :n -- :doc creates a new teamorganisership record INSERT INTO teamorganiserships (team_id, canvasser_id) @@ -191,7 +193,7 @@ VALUES (:team_id, :canvasser_id) returning Id --- :name create-visit! :! :n +-- :name create-visit! :! :n -- :doc creates a new visit record INSERT INTO visits (address_id, canvasser_id, @@ -261,8 +263,8 @@ WHERE intentions.Id = :Id DELETE FROM issues WHERE issues.id = :id --- :name delete-issueexpertis! :! :n --- :doc updates an existing issueexpertis record +-- :name delete-issueexpertise! :! :n +-- :doc updates an existing issueexpertise record DELETE FROM issueexpertise WHERE issueexpertise.Id = :Id @@ -323,6 +325,15 @@ ORDER BY canvassers.username, canvassers.email, canvassers.id +-- :name get-canvasser-by-username :? :1 +-- :doc selects an existing canvasser record +SELECT * FROM canvassers +WHERE canvassers.username = :username +ORDER BY canvassers.username, + canvassers.fullname, + canvassers.email, + canvassers.id + -- :name get-district :? :1 -- :doc selects an existing district record SELECT * FROM districts @@ -374,8 +385,8 @@ WHERE intentions.Id = :Id SELECT * FROM issues WHERE issues.id = :id --- :name get-issueexpertis :? :1 --- :doc selects an existing issueexpertis record +-- :name get-issueexpertise :? :1 +-- :doc selects an existing issueexpertise record SELECT * FROM issueexpertise WHERE issueexpertise.Id = :Id @@ -482,6 +493,16 @@ ORDER BY canvassers.username, canvassers.email, canvassers.id +-- :name list-canvassers-by-role :? :* +-- :doc lists all existing canvasser records related to a given role +SELECT * +FROM canvassers +WHERE canvassers.roles = :id +ORDER BY canvassers.username, + canvassers.fullname, + canvassers.email, + canvassers.id + -- :name list-districts :? :* -- :doc lists all existing district records SELECT * FROM districts @@ -496,8 +517,8 @@ 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 +-- :name list-dwellings-by-address :? :* +-- :doc lists all existing dwelling records related to a given address SELECT * FROM dwellings WHERE dwellings.address_id = :id @@ -617,25 +638,25 @@ FROM intentions WHERE intentions.visit_id = :id -- :name list-issueexpertise :? :* --- :doc lists all existing issueexpertis records +-- :doc lists all existing issueexpertise 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 +-- :doc lists all existing issueexpertise 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 +-- :doc lists all existing issueexpertise 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 +-- :doc lists all existing issueexpertise records related to a given issue SELECT * FROM issueexpertise WHERE issueexpertise.issue_id = :id @@ -760,6 +781,14 @@ ORDER BY addresses.address, --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") +-- :name search-strings-authority :? :1 +-- :doc selects existing authority records having any string field matching `:pattern` by substring match +SELECT * FROM authorities +WHERE +id LIKE '%:pattern%' +--~ (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 @@ -807,11 +836,65 @@ ORDER BY electors.name, --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") +-- :name search-strings-followupaction :? :1 +-- :doc selects existing followupaction records having any string field matching `:pattern` by substring match +SELECT * FROM followupactions +WHERE +notes LIKE '%:pattern%' +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-followupmethod :? :1 +-- :doc selects existing followupmethod records having any string field matching `:pattern` by substring match +SELECT * FROM followupmethods +WHERE +id LIKE '%:pattern%' +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-followuprequest :? :1 +-- :doc selects existing followuprequest records having any string field matching `:pattern` by substring match +SELECT * FROM followuprequests +WHERE +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-gender :? :1 +-- :doc selects existing gender records having any string field matching `:pattern` by substring match +SELECT * FROM genders +WHERE +id LIKE '%:pattern%' +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-intention :? :1 +-- :doc selects existing intention records having any string field matching `:pattern` by substring match +SELECT * FROM intentions +WHERE +--~ (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%' + OR id LIKE '%:pattern%' +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-issueexpertise :? :1 +-- :doc selects existing issueexpertise records having any string field matching `:pattern` by substring match +SELECT * FROM issueexpertise +WHERE +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-option :? :1 +-- :doc selects existing option records having any string field matching `:pattern` by substring match +SELECT * FROM options +WHERE +id LIKE '%:pattern%' --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") @@ -825,6 +908,13 @@ ORDER BY roles.name, --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") +-- :name search-strings-rolemembership :? :1 +-- :doc selects existing rolemembership records having any string field matching `:pattern` by substring match +SELECT * FROM rolememberships +WHERE +--~ (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 @@ -834,3 +924,159 @@ ORDER BY teams.name, teams.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-teammembership :? :1 +-- :doc selects existing teammembership records having any string field matching `:pattern` by substring match +SELECT * FROM teammemberships +WHERE +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-teamorganisership :? :1 +-- :doc selects existing teamorganisership records having any string field matching `:pattern` by substring match +SELECT * FROM teamorganiserships +WHERE +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name search-strings-visit :? :1 +-- :doc selects existing visit records having any string field matching `:pattern` by substring match +SELECT * FROM visits +WHERE +--~ (if (:offset params) "OFFSET :offset ") +--~ (if (:limit params) "LIMIT :limit" "LIMIT 100") + +-- :name update-address! :! :n +-- :doc updates an existing address record +UPDATE addresses +SET address = :address, + postcode = :postcode, + phone = :phone, + district_id = :district_id, + latitude = :latitude, + longitude = :longitude +WHERE addresses.id = :id + +-- :name update-canvasser! :! :n +-- :doc updates 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, + roles = :roles +WHERE canvassers.id = :id + +-- :name update-district! :! :n +-- :doc updates an existing district record +UPDATE districts +SET name = :name +WHERE districts.id = :id + +-- :name update-dwelling! :! :n +-- :doc updates an existing dwelling record +UPDATE dwellings +SET address_id = :address_id, + sub-address = :sub-address +WHERE dwellings.id = :id + +-- :name update-elector! :! :n +-- :doc updates an existing elector record +UPDATE electors +SET name = :name, + dwelling_id = :dwelling_id, + phone = :phone, + email = :email, + gender = :gender +WHERE electors.id = :id + +-- :name update-followupaction! :! :n +-- :doc updates an existing followupaction record +UPDATE followupactions +SET request_id = :request_id, + actor = :actor, + date = :date, + notes = :notes, + closed = :closed +WHERE followupactions.id = :id + +-- :name update-followuprequest! :! :n +-- :doc updates an existing followuprequest record +UPDATE followuprequests +SET elector_id = :elector_id, + visit_id = :visit_id, + issue_id = :issue_id, + method_id = :method_id +WHERE followuprequests.id = :id + +-- :name update-intention! :! :n +-- :doc updates an existing intention record +UPDATE intentions +SET visit_id = :visit_id, + elector_id = :elector_id, + option_id = :option_id +WHERE intentions.Id = :Id + +-- :name update-issue! :! :n +-- :doc updates an existing issue record +UPDATE issues +SET url = :url, + current = :current, + id = :id +WHERE issues.id = :id + +-- :name update-issueexpertise! :! :n +-- :doc updates an existing issueexpertise record +UPDATE issueexpertise +SET canvasser_id = :canvasser_id, + issue_id = :issue_id, + method_id = :method_id +WHERE issueexpertise.Id = :Id + +-- :name update-role! :! :n +-- :doc updates an existing role record +UPDATE roles +SET name = :name +WHERE roles.id = :id + +-- :name update-rolemembership! :! :n +-- :doc updates an existing rolemembership record +UPDATE rolememberships +SET role_id = :role_id, + canvasser_id = :canvasser_id +WHERE rolememberships.Id = :Id + +-- :name update-team! :! :n +-- :doc updates an existing team record +UPDATE teams +SET name = :name, + district_id = :district_id, + latitude = :latitude, + longitude = :longitude +WHERE teams.id = :id + +-- :name update-teammembership! :! :n +-- :doc updates an existing teammembership record +UPDATE teammemberships +SET team_id = :team_id, + canvasser_id = :canvasser_id +WHERE teammemberships.Id = :Id + +-- :name update-teamorganisership! :! :n +-- :doc updates an existing teamorganisership record +UPDATE teamorganiserships +SET team_id = :team_id, + canvasser_id = :canvasser_id +WHERE teamorganiserships.Id = :Id + +-- :name update-visit! :! :n +-- :doc updates an existing visit record +UPDATE visits +SET address_id = :address_id, + canvasser_id = :canvasser_id, + date = :date +WHERE visits.id = :id diff --git a/resources/templates/auto/application-index.html b/resources/templates/auto/application-index.html new file mode 100644 index 0000000..ace787c --- /dev/null +++ b/resources/templates/auto/application-index.html @@ -0,0 +1,204 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+ +Elector + +
+
+

+All electors known to the system; electors are people believed to be entitled to vote in the current campaign. +

+
+
+ +Gender + +
+
+

+All genders which may be assigned to electors. +

+
+
+ +Dwelling + +
+
+

+All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. +

+
+
+ +Address + +
+
+

+Addresses of all buildings which contain dwellings. +

+
+
+ +Visit + +
+
+

+All visits made by canvassers to dwellings in which opinions were recorded. +

+
+
+ +Authority + +
+
+

+Authorities which may authenticate canvassers to the system. +

+
+
+ +Issue + +
+
+

+Issues believed to be of interest to electors, about which they may have questions. +

+
+
+ +Intention + +
+
+

+Link table. +

+
+
+ +Canvasser + +
+
+

+Primary users of the system: those actually interviewing electors. +

+
+
+ +Followuprequest + +
+
+

+Requests for a followup with an issue expert +

+
+
+ +Rolemembership + +
+
+

+Link table +

+
+
+ +Role + +
+
+

+A role (essentially, the same as a group, but application layer rather than database layer) of which a user may be a member. +

+
+
+ +Team + +
+
+
+
+ +District + +
+
+

+Electoral districts +

+
+
+ +Teamorganisership + +
+
+

+Link table +

+
+
+ +Followupaction + +
+
+

+Actions taken on followup requests. +

+
+
+ +Issueexpertise + +
+
+

+Link table +

+
+
+ +Option + +
+
+

+Options in the election or referendum being canvassed on +

+
+
+ +Teammembership + +
+
+

+Link table +

+
+
+ +Followupmethod + +
+
+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html new file mode 100644 index 0000000..b07e39b --- /dev/null +++ b/resources/templates/auto/form-addresses-Address.html @@ -0,0 +1,232 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable addresses %} + +{% else %} +{% ifreadable addresses %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses address %} + +{% else %} +{% ifreadable addresses address%} + +{{record.address}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses postcode %} + +{% else %} +{% ifreadable addresses postcode%} + +{{record.postcode}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses phone %} + +{% else %} +{% ifreadable addresses phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses district_id %} +

+ + +
+{% else %} +{% ifreadable addresses district_id%} + +{{record.district_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses latitude %} + +{% else %} +{% ifreadable addresses latitude%} + +{{record.latitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses longitude %} + +{% else %} +{% ifreadable addresses longitude%} + +{{record.longitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses %} + +{% else %} +{% ifreadable addresses %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses address %} + +{% else %} +{% ifreadable addresses address%} + +{{record.address}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses postcode %} + +{% else %} +{% ifreadable addresses postcode%} + +{{record.postcode}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses phone %} + +{% else %} +{% ifreadable addresses phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses district_id %} +

+ + +
+{% else %} +{% ifreadable addresses district_id%} + +{{record.district_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses latitude %} + +{% else %} +{% ifreadable addresses latitude%} + +{{record.latitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses longitude %} + +{% else %} +{% ifreadable addresses longitude%} + +{{record.longitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-canvassers-Canvasser.html b/resources/templates/auto/form-canvassers-Canvasser.html new file mode 100644 index 0000000..97cbc37 --- /dev/null +++ b/resources/templates/auto/form-canvassers-Canvasser.html @@ -0,0 +1,317 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable canvassers %} + +{% else %} +{% ifreadable canvassers %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers username %} + +{% else %} +{% ifreadable canvassers username%} + +{{record.username}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers fullname %} + +{% else %} +{% ifreadable canvassers fullname%} + +{{record.fullname}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers elector_id %} +

+ + +
+{% else %} +{% ifreadable canvassers elector_id%} + +{{record.elector_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers address_id %} + +{% else %} +{% ifreadable canvassers address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers phone %} + +{% else %} +{% ifreadable canvassers phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers email %} + +{% else %} +{% ifreadable canvassers email%} + +{{record.email}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers authority_id %} +

+ + +
+{% else %} +{% ifreadable canvassers authority_id%} + +{{record.authority_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers authorised %} + +{% else %} +{% ifreadable canvassers authorised%} + +{{record.authorised}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers %} + +{% else %} +{% ifreadable canvassers %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers username %} + +{% else %} +{% ifreadable canvassers username%} + +{{record.username}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers fullname %} + +{% else %} +{% ifreadable canvassers fullname%} + +{{record.fullname}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers elector_id %} +

+ + +
+{% else %} +{% ifreadable canvassers elector_id%} + +{{record.elector_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers address_id %} + +{% else %} +{% ifreadable canvassers address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers phone %} + +{% else %} +{% ifreadable canvassers phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers email %} + +{% else %} +{% ifreadable canvassers email%} + +{{record.email}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers authority_id %} +

+ + +
+{% else %} +{% ifreadable canvassers authority_id%} + +{{record.authority_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers authorised %} + +{% else %} +{% ifreadable canvassers authorised%} + +{{record.authorised}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers roles %} +

+ + +
+{% else %} +{% ifreadable canvassers roles%} + +{{record.roles}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-districts-District.html b/resources/templates/auto/form-districts-District.html new file mode 100644 index 0000000..a70e74b --- /dev/null +++ b/resources/templates/auto/form-districts-District.html @@ -0,0 +1,82 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable districts %} + +{% else %} +{% ifreadable districts %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable districts name %} + +{% else %} +{% ifreadable districts name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable districts %} + +{% else %} +{% ifreadable districts %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable districts name %} + +{% else %} +{% ifreadable districts name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-dwellings-Dwelling.html b/resources/templates/auto/form-dwellings-Dwelling.html new file mode 100644 index 0000000..ea520e7 --- /dev/null +++ b/resources/templates/auto/form-dwellings-Dwelling.html @@ -0,0 +1,120 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable dwellings %} + +{% else %} +{% ifreadable dwellings %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings address_id %} +

+ + +
+{% else %} +{% ifreadable dwellings address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings sub-address %} + +{% else %} +{% ifreadable dwellings sub-address%} + +{{record.sub-address}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings %} + +{% else %} +{% ifreadable dwellings %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings address_id %} +

+ + +
+{% else %} +{% ifreadable dwellings address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings sub-address %} + +{% else %} +{% ifreadable dwellings sub-address%} + +{{record.sub-address}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-electors-Elector.html b/resources/templates/auto/form-electors-Elector.html new file mode 100644 index 0000000..7b0b158 --- /dev/null +++ b/resources/templates/auto/form-electors-Elector.html @@ -0,0 +1,212 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable electors %} + +{% else %} +{% ifreadable electors %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors name %} + +{% else %} +{% ifreadable electors name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors dwelling_id %} +

+ + +
+{% else %} +{% ifreadable electors dwelling_id%} + +{{record.dwelling_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors phone %} + +{% else %} +{% ifreadable electors phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors email %} + +{% else %} +{% ifreadable electors email%} + +{{record.email}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors gender %} +

+ +
+{% else %} +{% ifreadable electors gender%} + +{{record.gender}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors %} + +{% else %} +{% ifreadable electors %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors name %} + +{% else %} +{% ifreadable electors name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors dwelling_id %} +

+ + +
+{% else %} +{% ifreadable electors dwelling_id%} + +{{record.dwelling_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors phone %} + +{% else %} +{% ifreadable electors phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors email %} + +{% else %} +{% ifreadable electors email%} + +{{record.email}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors gender %} +

+ +
+{% else %} +{% ifreadable electors gender%} + +{{record.gender}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-followupactions-Followupaction.html b/resources/templates/auto/form-followupactions-Followupaction.html new file mode 100644 index 0000000..8143647 --- /dev/null +++ b/resources/templates/auto/form-followupactions-Followupaction.html @@ -0,0 +1,204 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable followupactions %} + +{% else %} +{% ifreadable followupactions %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions request_id %} +

+ + +
+{% else %} +{% ifreadable followupactions request_id%} + +{{record.request_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions actor %} + +{% else %} +{% ifreadable followupactions actor%} + +{{record.actor}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions date %} + +{% else %} +{% ifreadable followupactions date%} + +{{record.date}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions notes %} + +{% else %} +{% ifreadable followupactions notes%} + +{{record.notes}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions closed %} + +{% else %} +{% ifreadable followupactions closed%} + +{{record.closed}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions %} + +{% else %} +{% ifreadable followupactions %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions request_id %} +

+ + +
+{% else %} +{% ifreadable followupactions request_id%} + +{{record.request_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions actor %} + +{% else %} +{% ifreadable followupactions actor%} + +{{record.actor}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions date %} + +{% else %} +{% ifreadable followupactions date%} + +{{record.date}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions notes %} + +{% else %} +{% ifreadable followupactions notes%} + +{{record.notes}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions closed %} + +{% else %} +{% ifreadable followupactions closed%} + +{{record.closed}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-issues-Issue.html b/resources/templates/auto/form-issues-Issue.html new file mode 100644 index 0000000..7bec673 --- /dev/null +++ b/resources/templates/auto/form-issues-Issue.html @@ -0,0 +1,123 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} +

+ +{% ifwritable issues id %} + +{% else %} +{% ifreadable issues id%} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues %} + +{% else %} +{% ifreadable issues %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues url %} + +{% else %} +{% ifreadable issues url%} + +{{record.url}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues current %} + +{% else %} +{% ifreadable issues current%} + +{{record.current}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues %} + +{% else %} +{% ifreadable issues %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues url %} + +{% else %} +{% ifreadable issues url%} + +{{record.url}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues current %} + +{% else %} +{% ifreadable issues current%} + +{{record.current}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-roles-Role.html b/resources/templates/auto/form-roles-Role.html new file mode 100644 index 0000000..029740f --- /dev/null +++ b/resources/templates/auto/form-roles-Role.html @@ -0,0 +1,82 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable roles %} + +{% else %} +{% ifreadable roles %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable roles name %} + +{% else %} +{% ifreadable roles name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable roles %} + +{% else %} +{% ifreadable roles %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable roles name %} + +{% else %} +{% ifreadable roles name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-teams-Team.html b/resources/templates/auto/form-teams-Team.html new file mode 100644 index 0000000..f81dbc7 --- /dev/null +++ b/resources/templates/auto/form-teams-Team.html @@ -0,0 +1,195 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable teams %} + +{% else %} +{% ifreadable teams %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams name %} + +{% else %} +{% ifreadable teams name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams district_id %} +

+ + +
+{% else %} +{% ifreadable teams district_id%} + +{{record.district_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams latitude %} + +{% else %} +{% ifreadable teams latitude%} + +{{record.latitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams longitude %} + +{% else %} +{% ifreadable teams longitude%} + +{{record.longitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams %} + +{% else %} +{% ifreadable teams %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams name %} + +{% else %} +{% ifreadable teams name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams district_id %} +

+ + +
+{% else %} +{% ifreadable teams district_id%} + +{{record.district_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams latitude %} + +{% else %} +{% ifreadable teams latitude%} + +{{record.latitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams members %} +

+ + +
+{% else %} +{% ifreadable teams members%} + +{{record.members}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams longitude %} + +{% else %} +{% ifreadable teams longitude%} + +{{record.longitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html new file mode 100644 index 0000000..8b6c093 --- /dev/null +++ b/resources/templates/auto/form-visits-Visit.html @@ -0,0 +1,148 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable visits %} + +{% else %} +{% ifreadable visits %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits address_id %} + +{% else %} +{% ifreadable visits address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits canvasser_id %} +

+ + +
+{% else %} +{% ifreadable visits canvasser_id%} + +{{record.canvasser_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits date %} + +{% else %} +{% ifreadable visits date%} + +{{record.date}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits %} + +{% else %} +{% ifreadable visits %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits address_id %} + +{% else %} +{% ifreadable visits address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits canvasser_id %} +

+ + +
+{% else %} +{% ifreadable visits canvasser_id%} + +{{record.canvasser_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits date %} + +{% else %} +{% ifreadable visits date%} + +{{record.date}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html new file mode 100644 index 0000000..0a62aa0 --- /dev/null +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -0,0 +1,115 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + + + +{% endfor %} + + +
+id + +address + +postcode + +phone + +district_id + +latitude + +longitude +
+ + + + + + + + + + + + + +
+{{ record.id }} + +{{ record.address }} + +{{ record.postcode }} + +{{ record.phone }} + +{{ record.district_id }} + +{{ record.latitude }} + +{{ record.longitude }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html new file mode 100644 index 0000000..1b78cb8 --- /dev/null +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -0,0 +1,133 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + + + + + +{% endfor %} + + +
+id + +username + +fullname + +elector_id + +address_id + +phone + +email + +authority_id + +authorised +
+ + + + + + + + + + + + + + + + + +
+{{ record.id }} + +{{ record.username }} + +{{ record.fullname }} + +{{ record.elector_id }} + +{{ record.address_id }} + +{{ record.phone }} + +{{ record.email }} + +{{ record.authority_id }} + +{{ record.authorised }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html new file mode 100644 index 0000000..9b41182 --- /dev/null +++ b/resources/templates/auto/list-districts-Districts.html @@ -0,0 +1,70 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + +{% for record in %records% %} + + + + + +{% endfor %} + + +
+id + +name +
+ + + +
+{{ record.id }} + +{{ record.name }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html new file mode 100644 index 0000000..c8e5600 --- /dev/null +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -0,0 +1,79 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + +{% endfor %} + + +
+id + +address_id + +sub-address +
+ + + + + +
+{{ record.id }} + +{{ record.address_id }} + +{{ record.sub-address }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html new file mode 100644 index 0000000..ea6102b --- /dev/null +++ b/resources/templates/auto/list-electors-Electors.html @@ -0,0 +1,106 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + + +{% endfor %} + + +
+id + +name + +dwelling_id + +phone + +email + +gender +
+ + + + + + + + + + + +
+{{ record.id }} + +{{ record.name }} + +{{ record.dwelling_id }} + +{{ record.phone }} + +{{ record.email }} + +{{ record.gender }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html new file mode 100644 index 0000000..d6ad6fb --- /dev/null +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -0,0 +1,106 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + + +{% endfor %} + + +
+id + +request_id + +actor + +date + +notes + +closed +
+ + + + + + + + + + + +
+{{ record.id }} + +{{ record.request_id }} + +{{ record.actor }} + +{{ record.date }} + +{{ record.notes }} + +{{ record.closed }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html new file mode 100644 index 0000000..f72dafb --- /dev/null +++ b/resources/templates/auto/list-issues-Issues.html @@ -0,0 +1,79 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + +{% endfor %} + + +
+id + +url + +current +
+ + + + + +
+{{ record.id }} + +{{ record.url }} + +{{ record.current }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html new file mode 100644 index 0000000..65b7e89 --- /dev/null +++ b/resources/templates/auto/list-roles-Roles.html @@ -0,0 +1,70 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + +{% for record in %records% %} + + + + + +{% endfor %} + + +
+id + +name +
+ + + +
+{{ record.id }} + +{{ record.name }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html new file mode 100644 index 0000000..a9b805f --- /dev/null +++ b/resources/templates/auto/list-teams-Teams.html @@ -0,0 +1,97 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + +{% endfor %} + + +
+id + +name + +district_id + +latitude + +longitude +
+ + + + + + + + + +
+{{ record.id }} + +{{ record.name }} + +{{ record.district_id }} + +{{ record.latitude }} + +{{ record.longitude }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html new file mode 100644 index 0000000..7faee46 --- /dev/null +++ b/resources/templates/auto/list-visits-Visits.html @@ -0,0 +1,88 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + +{% endfor %} + + +
+id + +address_id + +canvasser_id + +date +
+ + + + + + + +
+{{ record.id }} + +{{ record.address_id }} + +{{ record.canvasser_id }} + +{{ record.date }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/src/clj/youyesyet/authorisation.clj b/src/clj/youyesyet/authorisation.clj new file mode 100644 index 0000000..2ca529b --- /dev/null +++ b/src/clj/youyesyet/authorisation.clj @@ -0,0 +1,4 @@ +(ns ^{:doc "Field-level authorisation. Messy." + :author "Simon Brooke"} + youyesyet.authorisation + (:require [youyesyet.env :refer [defaults]])) diff --git a/src/clj/youyesyet/routes/auto.clj b/src/clj/youyesyet/routes/auto.clj new file mode 100644 index 0000000..82d865d --- /dev/null +++ b/src/clj/youyesyet/routes/auto.clj @@ -0,0 +1,639 @@ +(ns + youyesyet.routes.auto + "JSON routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180610T194402.765Z" + (: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.routes.manual :as m])) + +(defn + raw-resolve-template + [n] + (if + (.exists (io/as-file (str "resources/templates/" n))) + n + (str "auto/n"))) + +(def resolve-template (memoise raw-resolve-template)) + +(defn + list-electors-Electors + [r] + (layout/render + (resolve-template "list-electors-Electors.html") + {:title "Elector"})) + +(defn + form-electors-Elector + [r] + (layout/render + (resolve-template "form-electors-Elector.html") + {:title "Elector"})) + +(defn + list-genders-Genders + [r] + (layout/render + (resolve-template "list-genders-Genders.html") + {:title "Gender"})) + +(defn + form-genders-Gender + [r] + (layout/render + (resolve-template "form-genders-Gender.html") + {:title "Gender"})) + +(defn + list-dwellings-Dwellings + [r] + (layout/render + (resolve-template "list-dwellings-Dwellings.html") + {:title "Dwelling"})) + +(defn + form-dwellings-Dwelling + [r] + (layout/render + (resolve-template "form-dwellings-Dwelling.html") + {:title "Dwelling"})) + +(defn + list-addresses-Addresses + [r] + (layout/render + (resolve-template "list-addresses-Addresses.html") + {:title "Address"})) + +(defn + form-addresses-Address + [r] + (layout/render + (resolve-template "form-addresses-Address.html") + {:title "Address"})) + +(defn + list-visits-Visits + [r] + (layout/render + (resolve-template "list-visits-Visits.html") + {:title "Visit"})) + +(defn + form-visits-Visit + [r] + (layout/render + (resolve-template "form-visits-Visit.html") + {:title "Visit"})) + +(defn + list-authorities-Authorities + [r] + (layout/render + (resolve-template "list-authorities-Authorities.html") + {:title "Authority"})) + +(defn + form-authorities-Authority + [r] + (layout/render + (resolve-template "form-authorities-Authority.html") + {:title "Authority"})) + +(defn + list-issues-Issues + [r] + (layout/render + (resolve-template "list-issues-Issues.html") + {:title "Issue"})) + +(defn + form-issues-Issue + [r] + (layout/render + (resolve-template "form-issues-Issue.html") + {:title "Issue"})) + +(defn + list-intentions-Intentions + [r] + (layout/render + (resolve-template "list-intentions-Intentions.html") + {:title "Intention"})) + +(defn + form-intentions-Intention + [r] + (layout/render + (resolve-template "form-intentions-Intention.html") + {:title "Intention"})) + +(defn + list-canvassers-Canvassers + [r] + (layout/render + (resolve-template "list-canvassers-Canvassers.html") + {:title "Canvasser"})) + +(defn + form-canvassers-Canvasser + [r] + (layout/render + (resolve-template "form-canvassers-Canvasser.html") + {:title "Canvasser"})) + +(defn + list-followuprequests-Followuprequests + [r] + (layout/render + (resolve-template "list-followuprequests-Followuprequests.html") + {:title "Followuprequest"})) + +(defn + form-followuprequests-Followuprequest + [r] + (layout/render + (resolve-template "form-followuprequests-Followuprequest.html") + {:title "Followuprequest"})) + +(defn + list-roles-Roles + [r] + (layout/render + (resolve-template "list-roles-Roles.html") + {:title "Role"})) + +(defn + form-roles-Role + [r] + (layout/render + (resolve-template "form-roles-Role.html") + {:title "Role"})) + +(defn + list-teams-Teams + [r] + (layout/render + (resolve-template "list-teams-Teams.html") + {:title "Team"})) + +(defn + form-teams-Team + [r] + (layout/render + (resolve-template "form-teams-Team.html") + {:title "Team"})) + +(defn + list-districts-Districts + [r] + (layout/render + (resolve-template "list-districts-Districts.html") + {:title "District"})) + +(defn + form-districts-District + [r] + (layout/render + (resolve-template "form-districts-District.html") + {:title "District"})) + +(defn + list-followupactions-Followupactions + [r] + (layout/render + (resolve-template "list-followupactions-Followupactions.html") + {:title "Followupaction"})) + +(defn + form-followupactions-Followupaction + [r] + (layout/render + (resolve-template "form-followupactions-Followupaction.html") + {:title "Followupaction"})) + +(defn + list-options-Options + [r] + (layout/render + (resolve-template "list-options-Options.html") + {:title "Option"})) + +(defn + form-options-Option + [r] + (layout/render + (resolve-template "form-options-Option.html") + {:title "Option"})) + +(defn + list-followupmethods-Followupmethods + [r] + (layout/render + (resolve-template "list-followupmethods-Followupmethods.html") + {:title "Followupmethod"})) + +(defn + form-followupmethods-Followupmethod + [r] + (layout/render + (resolve-template "form-followupmethods-Followupmethod.html") + {:title "Followupmethod"})) + +(defn + raw-resolve-handler + "Prefer the manually-written version of the handler with name `n`, if it exists, to the automatically generated one" + [n] + (let + [s (symbol (str "m." n))] + (if (bound? s) (eval s) (eval (symbol n))))) + +(def resolve-handler (memoize raw-resolve-handler)) + +(defroutes + auto-selmer-routes + (GET + "/form-addresses-Address" + request + (route/restricted + (apply (resolve-handler "form-addresses-Address") (list request)))) + (POST + "/form-addresses-Address" + request + (route/restricted + (apply (resolve-handler "form-addresses-Address") (list request)))) + (GET + "/form-authorities-Authority" + request + (route/restricted + (apply + (resolve-handler "form-authorities-Authority") + (list request)))) + (POST + "/form-authorities-Authority" + request + (route/restricted + (apply + (resolve-handler "form-authorities-Authority") + (list request)))) + (GET + "/form-canvassers-Canvasser" + request + (route/restricted + (apply + (resolve-handler "form-canvassers-Canvasser") + (list request)))) + (POST + "/form-canvassers-Canvasser" + request + (route/restricted + (apply + (resolve-handler "form-canvassers-Canvasser") + (list request)))) + (GET + "/form-districts-District" + request + (route/restricted + (apply (resolve-handler "form-districts-District") (list request)))) + (POST + "/form-districts-District" + request + (route/restricted + (apply (resolve-handler "form-districts-District") (list request)))) + (GET + "/form-dwellings-Dwelling" + request + (route/restricted + (apply (resolve-handler "form-dwellings-Dwelling") (list request)))) + (POST + "/form-dwellings-Dwelling" + request + (route/restricted + (apply (resolve-handler "form-dwellings-Dwelling") (list request)))) + (GET + "/form-electors-Elector" + request + (route/restricted + (apply (resolve-handler "form-electors-Elector") (list request)))) + (POST + "/form-electors-Elector" + request + (route/restricted + (apply (resolve-handler "form-electors-Elector") (list request)))) + (GET + "/form-followupactions-Followupaction" + request + (route/restricted + (apply + (resolve-handler "form-followupactions-Followupaction") + (list request)))) + (POST + "/form-followupactions-Followupaction" + request + (route/restricted + (apply + (resolve-handler "form-followupactions-Followupaction") + (list request)))) + (GET + "/form-followupmethods-Followupmethod" + request + (route/restricted + (apply + (resolve-handler "form-followupmethods-Followupmethod") + (list request)))) + (POST + "/form-followupmethods-Followupmethod" + request + (route/restricted + (apply + (resolve-handler "form-followupmethods-Followupmethod") + (list request)))) + (GET + "/form-followuprequests-Followuprequest" + request + (route/restricted + (apply + (resolve-handler "form-followuprequests-Followuprequest") + (list request)))) + (POST + "/form-followuprequests-Followuprequest" + request + (route/restricted + (apply + (resolve-handler "form-followuprequests-Followuprequest") + (list request)))) + (GET + "/form-genders-Gender" + request + (route/restricted + (apply (resolve-handler "form-genders-Gender") (list request)))) + (POST + "/form-genders-Gender" + request + (route/restricted + (apply (resolve-handler "form-genders-Gender") (list request)))) + (GET + "/form-intentions-Intention" + request + (route/restricted + (apply + (resolve-handler "form-intentions-Intention") + (list request)))) + (POST + "/form-intentions-Intention" + request + (route/restricted + (apply + (resolve-handler "form-intentions-Intention") + (list request)))) + (GET + "/form-issues-Issue" + request + (route/restricted + (apply (resolve-handler "form-issues-Issue") (list request)))) + (POST + "/form-issues-Issue" + request + (route/restricted + (apply (resolve-handler "form-issues-Issue") (list request)))) + (GET + "/form-options-Option" + request + (route/restricted + (apply (resolve-handler "form-options-Option") (list request)))) + (POST + "/form-options-Option" + request + (route/restricted + (apply (resolve-handler "form-options-Option") (list request)))) + (GET + "/form-roles-Role" + request + (route/restricted + (apply (resolve-handler "form-roles-Role") (list request)))) + (POST + "/form-roles-Role" + request + (route/restricted + (apply (resolve-handler "form-roles-Role") (list request)))) + (GET + "/form-teams-Team" + request + (route/restricted + (apply (resolve-handler "form-teams-Team") (list request)))) + (POST + "/form-teams-Team" + request + (route/restricted + (apply (resolve-handler "form-teams-Team") (list request)))) + (GET + "/form-visits-Visit" + request + (route/restricted + (apply (resolve-handler "form-visits-Visit") (list request)))) + (POST + "/form-visits-Visit" + request + (route/restricted + (apply (resolve-handler "form-visits-Visit") (list request)))) + (GET + "/list-addresses-Addresses" + request + (route/restricted + (apply + (resolve-handler "list-addresses-Addresses") + (list request)))) + (POST + "/list-addresses-Addresses" + request + (route/restricted + (apply + (resolve-handler "list-addresses-Addresses") + (list request)))) + (GET + "/list-authorities-Authorities" + request + (route/restricted + (apply + (resolve-handler "list-authorities-Authorities") + (list request)))) + (POST + "/list-authorities-Authorities" + request + (route/restricted + (apply + (resolve-handler "list-authorities-Authorities") + (list request)))) + (GET + "/list-canvassers-Canvassers" + request + (route/restricted + (apply + (resolve-handler "list-canvassers-Canvassers") + (list request)))) + (POST + "/list-canvassers-Canvassers" + request + (route/restricted + (apply + (resolve-handler "list-canvassers-Canvassers") + (list request)))) + (GET + "/list-districts-Districts" + request + (route/restricted + (apply + (resolve-handler "list-districts-Districts") + (list request)))) + (POST + "/list-districts-Districts" + request + (route/restricted + (apply + (resolve-handler "list-districts-Districts") + (list request)))) + (GET + "/list-dwellings-Dwellings" + request + (route/restricted + (apply + (resolve-handler "list-dwellings-Dwellings") + (list request)))) + (POST + "/list-dwellings-Dwellings" + request + (route/restricted + (apply + (resolve-handler "list-dwellings-Dwellings") + (list request)))) + (GET + "/list-electors-Electors" + request + (route/restricted + (apply (resolve-handler "list-electors-Electors") (list request)))) + (POST + "/list-electors-Electors" + request + (route/restricted + (apply (resolve-handler "list-electors-Electors") (list request)))) + (GET + "/list-followupactions-Followupactions" + request + (route/restricted + (apply + (resolve-handler "list-followupactions-Followupactions") + (list request)))) + (POST + "/list-followupactions-Followupactions" + request + (route/restricted + (apply + (resolve-handler "list-followupactions-Followupactions") + (list request)))) + (GET + "/list-followupmethods-Followupmethods" + request + (route/restricted + (apply + (resolve-handler "list-followupmethods-Followupmethods") + (list request)))) + (POST + "/list-followupmethods-Followupmethods" + request + (route/restricted + (apply + (resolve-handler "list-followupmethods-Followupmethods") + (list request)))) + (GET + "/list-followuprequests-Followuprequests" + request + (route/restricted + (apply + (resolve-handler "list-followuprequests-Followuprequests") + (list request)))) + (POST + "/list-followuprequests-Followuprequests" + request + (route/restricted + (apply + (resolve-handler "list-followuprequests-Followuprequests") + (list request)))) + (GET + "/list-genders-Genders" + request + (route/restricted + (apply (resolve-handler "list-genders-Genders") (list request)))) + (POST + "/list-genders-Genders" + request + (route/restricted + (apply (resolve-handler "list-genders-Genders") (list request)))) + (GET + "/list-intentions-Intentions" + request + (route/restricted + (apply + (resolve-handler "list-intentions-Intentions") + (list request)))) + (POST + "/list-intentions-Intentions" + request + (route/restricted + (apply + (resolve-handler "list-intentions-Intentions") + (list request)))) + (GET + "/list-issues-Issues" + request + (route/restricted + (apply (resolve-handler "list-issues-Issues") (list request)))) + (POST + "/list-issues-Issues" + request + (route/restricted + (apply (resolve-handler "list-issues-Issues") (list request)))) + (GET + "/list-options-Options" + request + (route/restricted + (apply (resolve-handler "list-options-Options") (list request)))) + (POST + "/list-options-Options" + request + (route/restricted + (apply (resolve-handler "list-options-Options") (list request)))) + (GET + "/list-roles-Roles" + request + (route/restricted + (apply (resolve-handler "list-roles-Roles") (list request)))) + (POST + "/list-roles-Roles" + request + (route/restricted + (apply (resolve-handler "list-roles-Roles") (list request)))) + (GET + "/list-teams-Teams" + request + (route/restricted + (apply (resolve-handler "list-teams-Teams") (list request)))) + (POST + "/list-teams-Teams" + request + (route/restricted + (apply (resolve-handler "list-teams-Teams") (list request)))) + (GET + "/list-visits-Visits" + request + (route/restricted + (apply (resolve-handler "list-visits-Visits") (list request)))) + (POST + "/list-visits-Visits" + request + (route/restricted + (apply (resolve-handler "list-visits-Visits") (list request))))) + diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml index 18884fd..98b0593 100644 --- a/youyesyet.adl.xml +++ b/youyesyet.adl.xml @@ -56,6 +56,7 @@ + All electors known to the system; electors are people believed to be entitled to vote in the current campaign. @@ -64,7 +65,7 @@ - + @@ -81,6 +82,7 @@
+ All genders which may be assigned to electors. @@ -90,6 +92,7 @@ + All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. @@ -99,8 +102,11 @@ + + + Addresses of all buildings which contain dwellings. @@ -131,6 +137,7 @@ + All visits made by canvassers to dwellings in which opinions were recorded. @@ -149,13 +156,18 @@ + + + Authorities which may authenticate canvassers to the system. + + Issues believed to be of interest to electors, about which they may have questions. @@ -187,6 +199,8 @@ column="option_id"> + + Primary users of the system: those actually interviewing electors. @@ -221,6 +235,9 @@ + + + But only their own record @@ -230,6 +247,8 @@ All canvassers + + Requests for a followup with an issue expert @@ -254,6 +273,8 @@ entity="followupmethods" farkey="id"> + + Link table @@ -276,6 +297,8 @@ + + @@ -293,6 +316,9 @@ + + + @@ -303,6 +329,8 @@ All groups + + Electoral districts @@ -316,6 +344,8 @@ + + Link table @@ -357,6 +387,8 @@ But only for electors in their immediate vicinity + + Link table @@ -380,6 +412,8 @@ + + Link table @@ -398,5 +432,7 @@ + + diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 9939cad..18a2de3 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -67,6 +67,7 @@ entity electors already has a key - not generating one --> + All electors known to the system; electors are people believed to be entitled to vote in the current campaign. @@ -75,7 +76,7 @@ - + @@ -87,10 +88,11 @@ - - + +
+ All genders which may be assigned to electors. @@ -104,16 +106,21 @@ - + + +
- + + +
+ All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. @@ -123,11 +130,14 @@ + +
+ Addresses of all buildings which contain dwellings. @@ -154,13 +164,14 @@ - -
+ +
+ All visits made by canvassers to dwellings in which opinions were recorded. @@ -177,16 +188,21 @@ + +
+ Authorities which may authenticate canvassers to the system. + +
- + Link table. @@ -236,6 +252,28 @@ + + + + + + + + + + + +
+ + + + + + + + + +
- + Link table @@ -488,6 +544,8 @@ + +
+ +{% block content %} +
+
+ +Elector + +
+
+

+All electors known to the system; electors are people believed to be entitled to vote in the current campaign. +

+
+
+ +Gender + +
+
+

+All genders which may be assigned to electors. +

+
+
+ +Dwelling + +
+
+

+All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. +

+
+
+ +Address + +
+
+

+Addresses of all buildings which contain dwellings. +

+
+
+ +Visit + +
+
+

+All visits made by canvassers to dwellings in which opinions were recorded. +

+
+
+ +Authority + +
+
+

+Authorities which may authenticate canvassers to the system. +

+
+
+ +Issue + +
+
+

+Issues believed to be of interest to electors, about which they may have questions. +

+
+
+ +Intention + +
+
+

+Link table. +

+
+
+ +Canvasser + +
+
+

+Primary users of the system: those actually interviewing electors. +

+
+
+ +Followuprequest + +
+
+

+Requests for a followup with an issue expert +

+
+
+ +Rolemembership + +
+
+

+Link table +

+
+
+ +Role + +
+
+

+A role (essentially, the same as a group, but application layer rather than database layer) of which a user may be a member. +

+
+
+ +Team + +
+
+
+
+ +District + +
+
+

+Electoral districts +

+
+
+ +Teamorganisership + +
+
+

+Link table +

+
+
+ +Followupaction + +
+
+

+Actions taken on followup requests. +

+
+
+ +Issueexpertise + +
+
+

+Link table +

+
+
+ +Option + +
+
+

+Options in the election or referendum being canvassed on +

+
+
+ +Teammembership + +
+
+

+Link table +

+
+
+ +Followupmethod + +
+
+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html new file mode 100644 index 0000000..b07e39b --- /dev/null +++ b/resources/templates/auto/form-addresses-Address.html @@ -0,0 +1,232 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable addresses %} + +{% else %} +{% ifreadable addresses %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses address %} + +{% else %} +{% ifreadable addresses address%} + +{{record.address}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses postcode %} + +{% else %} +{% ifreadable addresses postcode%} + +{{record.postcode}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses phone %} + +{% else %} +{% ifreadable addresses phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses district_id %} +

+ + +
+{% else %} +{% ifreadable addresses district_id%} + +{{record.district_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses latitude %} + +{% else %} +{% ifreadable addresses latitude%} + +{{record.latitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses longitude %} + +{% else %} +{% ifreadable addresses longitude%} + +{{record.longitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses %} + +{% else %} +{% ifreadable addresses %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses address %} + +{% else %} +{% ifreadable addresses address%} + +{{record.address}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses postcode %} + +{% else %} +{% ifreadable addresses postcode%} + +{{record.postcode}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses phone %} + +{% else %} +{% ifreadable addresses phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses district_id %} +

+ + +
+{% else %} +{% ifreadable addresses district_id%} + +{{record.district_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses latitude %} + +{% else %} +{% ifreadable addresses latitude%} + +{{record.latitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable addresses longitude %} + +{% else %} +{% ifreadable addresses longitude%} + +{{record.longitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-canvassers-Canvasser.html b/resources/templates/auto/form-canvassers-Canvasser.html new file mode 100644 index 0000000..97cbc37 --- /dev/null +++ b/resources/templates/auto/form-canvassers-Canvasser.html @@ -0,0 +1,317 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable canvassers %} + +{% else %} +{% ifreadable canvassers %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers username %} + +{% else %} +{% ifreadable canvassers username%} + +{{record.username}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers fullname %} + +{% else %} +{% ifreadable canvassers fullname%} + +{{record.fullname}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers elector_id %} +

+ + +
+{% else %} +{% ifreadable canvassers elector_id%} + +{{record.elector_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers address_id %} + +{% else %} +{% ifreadable canvassers address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers phone %} + +{% else %} +{% ifreadable canvassers phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers email %} + +{% else %} +{% ifreadable canvassers email%} + +{{record.email}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers authority_id %} +

+ + +
+{% else %} +{% ifreadable canvassers authority_id%} + +{{record.authority_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers authorised %} + +{% else %} +{% ifreadable canvassers authorised%} + +{{record.authorised}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers %} + +{% else %} +{% ifreadable canvassers %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers username %} + +{% else %} +{% ifreadable canvassers username%} + +{{record.username}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers fullname %} + +{% else %} +{% ifreadable canvassers fullname%} + +{{record.fullname}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers elector_id %} +

+ + +
+{% else %} +{% ifreadable canvassers elector_id%} + +{{record.elector_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers address_id %} + +{% else %} +{% ifreadable canvassers address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers phone %} + +{% else %} +{% ifreadable canvassers phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers email %} + +{% else %} +{% ifreadable canvassers email%} + +{{record.email}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers authority_id %} +

+ + +
+{% else %} +{% ifreadable canvassers authority_id%} + +{{record.authority_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers authorised %} + +{% else %} +{% ifreadable canvassers authorised%} + +{{record.authorised}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable canvassers roles %} +

+ + +
+{% else %} +{% ifreadable canvassers roles%} + +{{record.roles}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-districts-District.html b/resources/templates/auto/form-districts-District.html new file mode 100644 index 0000000..a70e74b --- /dev/null +++ b/resources/templates/auto/form-districts-District.html @@ -0,0 +1,82 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable districts %} + +{% else %} +{% ifreadable districts %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable districts name %} + +{% else %} +{% ifreadable districts name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable districts %} + +{% else %} +{% ifreadable districts %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable districts name %} + +{% else %} +{% ifreadable districts name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-dwellings-Dwelling.html b/resources/templates/auto/form-dwellings-Dwelling.html new file mode 100644 index 0000000..ea520e7 --- /dev/null +++ b/resources/templates/auto/form-dwellings-Dwelling.html @@ -0,0 +1,120 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable dwellings %} + +{% else %} +{% ifreadable dwellings %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings address_id %} +

+ + +
+{% else %} +{% ifreadable dwellings address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings sub-address %} + +{% else %} +{% ifreadable dwellings sub-address%} + +{{record.sub-address}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings %} + +{% else %} +{% ifreadable dwellings %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings address_id %} +

+ + +
+{% else %} +{% ifreadable dwellings address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable dwellings sub-address %} + +{% else %} +{% ifreadable dwellings sub-address%} + +{{record.sub-address}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-electors-Elector.html b/resources/templates/auto/form-electors-Elector.html new file mode 100644 index 0000000..7b0b158 --- /dev/null +++ b/resources/templates/auto/form-electors-Elector.html @@ -0,0 +1,212 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable electors %} + +{% else %} +{% ifreadable electors %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors name %} + +{% else %} +{% ifreadable electors name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors dwelling_id %} +

+ + +
+{% else %} +{% ifreadable electors dwelling_id%} + +{{record.dwelling_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors phone %} + +{% else %} +{% ifreadable electors phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors email %} + +{% else %} +{% ifreadable electors email%} + +{{record.email}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors gender %} +

+ +
+{% else %} +{% ifreadable electors gender%} + +{{record.gender}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors %} + +{% else %} +{% ifreadable electors %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors name %} + +{% else %} +{% ifreadable electors name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors dwelling_id %} +

+ + +
+{% else %} +{% ifreadable electors dwelling_id%} + +{{record.dwelling_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors phone %} + +{% else %} +{% ifreadable electors phone%} + +{{record.phone}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors email %} + +{% else %} +{% ifreadable electors email%} + +{{record.email}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable electors gender %} +

+ +
+{% else %} +{% ifreadable electors gender%} + +{{record.gender}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-followupactions-Followupaction.html b/resources/templates/auto/form-followupactions-Followupaction.html new file mode 100644 index 0000000..8143647 --- /dev/null +++ b/resources/templates/auto/form-followupactions-Followupaction.html @@ -0,0 +1,204 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable followupactions %} + +{% else %} +{% ifreadable followupactions %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions request_id %} +

+ + +
+{% else %} +{% ifreadable followupactions request_id%} + +{{record.request_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions actor %} + +{% else %} +{% ifreadable followupactions actor%} + +{{record.actor}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions date %} + +{% else %} +{% ifreadable followupactions date%} + +{{record.date}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions notes %} + +{% else %} +{% ifreadable followupactions notes%} + +{{record.notes}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions closed %} + +{% else %} +{% ifreadable followupactions closed%} + +{{record.closed}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions %} + +{% else %} +{% ifreadable followupactions %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions request_id %} +

+ + +
+{% else %} +{% ifreadable followupactions request_id%} + +{{record.request_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions actor %} + +{% else %} +{% ifreadable followupactions actor%} + +{{record.actor}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions date %} + +{% else %} +{% ifreadable followupactions date%} + +{{record.date}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions notes %} + +{% else %} +{% ifreadable followupactions notes%} + +{{record.notes}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupactions closed %} + +{% else %} +{% ifreadable followupactions closed%} + +{{record.closed}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-issues-Issue.html b/resources/templates/auto/form-issues-Issue.html new file mode 100644 index 0000000..7bec673 --- /dev/null +++ b/resources/templates/auto/form-issues-Issue.html @@ -0,0 +1,123 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} +

+ +{% ifwritable issues id %} + +{% else %} +{% ifreadable issues id%} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues %} + +{% else %} +{% ifreadable issues %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues url %} + +{% else %} +{% ifreadable issues url%} + +{{record.url}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues current %} + +{% else %} +{% ifreadable issues current%} + +{{record.current}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues %} + +{% else %} +{% ifreadable issues %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues url %} + +{% else %} +{% ifreadable issues url%} + +{{record.url}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable issues current %} + +{% else %} +{% ifreadable issues current%} + +{{record.current}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-roles-Role.html b/resources/templates/auto/form-roles-Role.html new file mode 100644 index 0000000..029740f --- /dev/null +++ b/resources/templates/auto/form-roles-Role.html @@ -0,0 +1,82 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable roles %} + +{% else %} +{% ifreadable roles %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable roles name %} + +{% else %} +{% ifreadable roles name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable roles %} + +{% else %} +{% ifreadable roles %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable roles name %} + +{% else %} +{% ifreadable roles name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-teams-Team.html b/resources/templates/auto/form-teams-Team.html new file mode 100644 index 0000000..f81dbc7 --- /dev/null +++ b/resources/templates/auto/form-teams-Team.html @@ -0,0 +1,195 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable teams %} + +{% else %} +{% ifreadable teams %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams name %} + +{% else %} +{% ifreadable teams name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams district_id %} +

+ + +
+{% else %} +{% ifreadable teams district_id%} + +{{record.district_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams latitude %} + +{% else %} +{% ifreadable teams latitude%} + +{{record.latitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams longitude %} + +{% else %} +{% ifreadable teams longitude%} + +{{record.longitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams %} + +{% else %} +{% ifreadable teams %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams name %} + +{% else %} +{% ifreadable teams name%} + +{{record.name}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams district_id %} +

+ + +
+{% else %} +{% ifreadable teams district_id%} + +{{record.district_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams latitude %} + +{% else %} +{% ifreadable teams latitude%} + +{{record.latitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams members %} +

+ + +
+{% else %} +{% ifreadable teams members%} + +{{record.members}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable teams longitude %} + +{% else %} +{% ifreadable teams longitude%} + +{{record.longitude}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html new file mode 100644 index 0000000..8b6c093 --- /dev/null +++ b/resources/templates/auto/form-visits-Visit.html @@ -0,0 +1,148 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable visits %} + +{% else %} +{% ifreadable visits %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits address_id %} + +{% else %} +{% ifreadable visits address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits canvasser_id %} +

+ + +
+{% else %} +{% ifreadable visits canvasser_id%} + +{{record.canvasser_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits date %} + +{% else %} +{% ifreadable visits date%} + +{{record.date}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits %} + +{% else %} +{% ifreadable visits %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits address_id %} + +{% else %} +{% ifreadable visits address_id%} + +{{record.address_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits canvasser_id %} +

+ + +
+{% else %} +{% ifreadable visits canvasser_id%} + +{{record.canvasser_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable visits date %} + +{% else %} +{% ifreadable visits date%} + +{{record.date}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html new file mode 100644 index 0000000..0a62aa0 --- /dev/null +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -0,0 +1,115 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + + + +{% endfor %} + + +
+id + +address + +postcode + +phone + +district_id + +latitude + +longitude +
+ + + + + + + + + + + + + +
+{{ record.id }} + +{{ record.address }} + +{{ record.postcode }} + +{{ record.phone }} + +{{ record.district_id }} + +{{ record.latitude }} + +{{ record.longitude }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html new file mode 100644 index 0000000..1b78cb8 --- /dev/null +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -0,0 +1,133 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + + + + + +{% endfor %} + + +
+id + +username + +fullname + +elector_id + +address_id + +phone + +email + +authority_id + +authorised +
+ + + + + + + + + + + + + + + + + +
+{{ record.id }} + +{{ record.username }} + +{{ record.fullname }} + +{{ record.elector_id }} + +{{ record.address_id }} + +{{ record.phone }} + +{{ record.email }} + +{{ record.authority_id }} + +{{ record.authorised }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html new file mode 100644 index 0000000..9b41182 --- /dev/null +++ b/resources/templates/auto/list-districts-Districts.html @@ -0,0 +1,70 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + +{% for record in %records% %} + + + + + +{% endfor %} + + +
+id + +name +
+ + + +
+{{ record.id }} + +{{ record.name }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html new file mode 100644 index 0000000..c8e5600 --- /dev/null +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -0,0 +1,79 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + +{% endfor %} + + +
+id + +address_id + +sub-address +
+ + + + + +
+{{ record.id }} + +{{ record.address_id }} + +{{ record.sub-address }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html new file mode 100644 index 0000000..ea6102b --- /dev/null +++ b/resources/templates/auto/list-electors-Electors.html @@ -0,0 +1,106 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + + +{% endfor %} + + +
+id + +name + +dwelling_id + +phone + +email + +gender +
+ + + + + + + + + + + +
+{{ record.id }} + +{{ record.name }} + +{{ record.dwelling_id }} + +{{ record.phone }} + +{{ record.email }} + +{{ record.gender }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html new file mode 100644 index 0000000..d6ad6fb --- /dev/null +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -0,0 +1,106 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + + +{% endfor %} + + +
+id + +request_id + +actor + +date + +notes + +closed +
+ + + + + + + + + + + +
+{{ record.id }} + +{{ record.request_id }} + +{{ record.actor }} + +{{ record.date }} + +{{ record.notes }} + +{{ record.closed }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html new file mode 100644 index 0000000..f72dafb --- /dev/null +++ b/resources/templates/auto/list-issues-Issues.html @@ -0,0 +1,79 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + +{% endfor %} + + +
+id + +url + +current +
+ + + + + +
+{{ record.id }} + +{{ record.url }} + +{{ record.current }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html new file mode 100644 index 0000000..65b7e89 --- /dev/null +++ b/resources/templates/auto/list-roles-Roles.html @@ -0,0 +1,70 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + +{% for record in %records% %} + + + + + +{% endfor %} + + +
+id + +name +
+ + + +
+{{ record.id }} + +{{ record.name }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html new file mode 100644 index 0000000..a9b805f --- /dev/null +++ b/resources/templates/auto/list-teams-Teams.html @@ -0,0 +1,97 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + +{% endfor %} + + +
+id + +name + +district_id + +latitude + +longitude +
+ + + + + + + + + +
+{{ record.id }} + +{{ record.name }} + +{{ record.district_id }} + +{{ record.latitude }} + +{{ record.longitude }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html new file mode 100644 index 0000000..7faee46 --- /dev/null +++ b/resources/templates/auto/list-visits-Visits.html @@ -0,0 +1,88 @@ +{% extends "templates/base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + +{% endfor %} + + +
+id + +address_id + +canvasser_id + +date +
+ + + + + + + +
+{{ record.id }} + +{{ record.address_id }} + +{{ record.canvasser_id }} + +{{ record.date }} + + +View + +
+{% if offset > 0 %} + +{% endif %} + + +
+ +{% endblock %} diff --git a/src/clj/youyesyet/authorisation.clj b/src/clj/youyesyet/authorisation.clj new file mode 100644 index 0000000..2ca529b --- /dev/null +++ b/src/clj/youyesyet/authorisation.clj @@ -0,0 +1,4 @@ +(ns ^{:doc "Field-level authorisation. Messy." + :author "Simon Brooke"} + youyesyet.authorisation + (:require [youyesyet.env :refer [defaults]])) diff --git a/src/clj/youyesyet/routes/auto.clj b/src/clj/youyesyet/routes/auto.clj new file mode 100644 index 0000000..b1951c3 --- /dev/null +++ b/src/clj/youyesyet/routes/auto.clj @@ -0,0 +1,725 @@ +(ns + youyesyet.routes.auto + "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180610T214425.631Z" + (: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] + [youyesyet.routes.manual :as m])) + +(defn + raw-resolve-template + [n] + (if + (.exists (io/as-file (str "resources/templates/" n))) + n + (str "auto/" n))) + +(def resolve-template (memoise raw-resolve-template)) + +(defn + index + [r] + (layout/render + (resolve-template "application-index") + {:title "Administrative menu"})) + +(defn + list-electors-Electors + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-electors-Electors.html") + {:title "Electors", :params p, :records (db/search-elector p)}))) + +(defn + form-electors-Elector + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-electors-Elector.html") + {:title "Elector", :params p, :record (db/get-elector p)}))) + +(defn + list-genders-Genders + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-genders-Genders.html") + {:title "Genders", :params p, :records (db/search-gender p)}))) + +(defn + form-genders-Gender + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-genders-Gender.html") + {:title "Gender", :params p, :record (db/get-gender p)}))) + +(defn + list-dwellings-Dwellings + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-dwellings-Dwellings.html") + {:title "Dwellings", :params p, :records (db/search-dwelling p)}))) + +(defn + form-dwellings-Dwelling + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-dwellings-Dwelling.html") + {:title "Dwelling", :params p, :record (db/get-dwelling p)}))) + +(defn + list-addresses-Addresses + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-addresses-Addresses.html") + {:title "Addresses", :params p, :records (db/search-address p)}))) + +(defn + form-addresses-Address + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-addresses-Address.html") + {:title "Address", :params p, :record (db/get-address p)}))) + +(defn + list-visits-Visits + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-visits-Visits.html") + {:title "Visits", :params p, :records (db/search-visit p)}))) + +(defn + form-visits-Visit + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-visits-Visit.html") + {:title "Visit", :params p, :record (db/get-visit p)}))) + +(defn + list-authorities-Authorities + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-authorities-Authorities.html") + {:title "Authorities", + :params p, + :records (db/search-authority p)}))) + +(defn + form-authorities-Authority + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-authorities-Authority.html") + {:title "Authority", :params p, :record (db/get-authority p)}))) + +(defn + list-issues-Issues + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-issues-Issues.html") + {:title "Issues", :params p, :records (db/search-issue p)}))) + +(defn + form-issues-Issue + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-issues-Issue.html") + {:title "Issue", :params p, :record (db/get-issue p)}))) + +(defn + list-intentions-Intentions + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-intentions-Intentions.html") + {:title "Intentions", :params p, :records (db/search-intention p)}))) + +(defn + form-intentions-Intention + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-intentions-Intention.html") + {:title "Intention", :params p, :record (db/get-intention p)}))) + +(defn + list-canvassers-Canvassers + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-canvassers-Canvassers.html") + {:title "Canvassers", :params p, :records (db/search-canvasser p)}))) + +(defn + form-canvassers-Canvasser + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-canvassers-Canvasser.html") + {:title "Canvasser", :params p, :record (db/get-canvasser p)}))) + +(defn + list-followuprequests-Followuprequests + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-followuprequests-Followuprequests.html") + {:title "Followuprequests", + :params p, + :records (db/search-followuprequest p)}))) + +(defn + form-followuprequests-Followuprequest + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-followuprequests-Followuprequest.html") + {:title "Followuprequest", + :params p, + :record (db/get-followuprequest p)}))) + +(defn + list-roles-Roles + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-roles-Roles.html") + {:title "Roles", :params p, :records (db/search-role p)}))) + +(defn + form-roles-Role + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-roles-Role.html") + {:title "Role", :params p, :record (db/get-role p)}))) + +(defn + list-teams-Teams + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-teams-Teams.html") + {:title "Teams", :params p, :records (db/search-team p)}))) + +(defn + form-teams-Team + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-teams-Team.html") + {:title "Team", :params p, :record (db/get-team p)}))) + +(defn + list-districts-Districts + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-districts-Districts.html") + {:title "Districts", :params p, :records (db/search-district p)}))) + +(defn + form-districts-District + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-districts-District.html") + {:title "District", :params p, :record (db/get-district p)}))) + +(defn + list-followupactions-Followupactions + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-followupactions-Followupactions.html") + {:title "Followupactions", + :params p, + :records (db/search-followupaction p)}))) + +(defn + form-followupactions-Followupaction + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-followupactions-Followupaction.html") + {:title "Followupaction", + :params p, + :record (db/get-followupaction p)}))) + +(defn + list-options-Options + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-options-Options.html") + {:title "Options", :params p, :records (db/search-option p)}))) + +(defn + form-options-Option + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-options-Option.html") + {:title "Option", :params p, :record (db/get-option p)}))) + +(defn + list-followupmethods-Followupmethods + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "list-followupmethods-Followupmethods.html") + {:title "Followupmethods", + :params p, + :records (db/search-followupmethod p)}))) + +(defn + form-followupmethods-Followupmethod + [r] + (let + [p (:form-params r)] + (layout/render + (resolve-template "form-followupmethods-Followupmethod.html") + {:title "Followupmethod", + :params p, + :record (db/get-followupmethod p)}))) + +(defn + raw-resolve-handler + "Prefer the manually-written version of the handler with name `n`, if it exists, to the automatically generated one" + [n] + (let + [s (symbol (str "m." n))] + (if (bound? s) (eval s) (eval (symbol n))))) + +(def resolve-handler (memoize raw-resolve-handler)) + +(defroutes + auto-selmer-routes + (GET + "/form-addresses-Address" + request + (route/restricted + (apply (resolve-handler "form-addresses-Address") (list request)))) + (POST + "/form-addresses-Address" + request + (route/restricted + (apply (resolve-handler "form-addresses-Address") (list request)))) + (GET + "/form-authorities-Authority" + request + (route/restricted + (apply + (resolve-handler "form-authorities-Authority") + (list request)))) + (POST + "/form-authorities-Authority" + request + (route/restricted + (apply + (resolve-handler "form-authorities-Authority") + (list request)))) + (GET + "/form-canvassers-Canvasser" + request + (route/restricted + (apply + (resolve-handler "form-canvassers-Canvasser") + (list request)))) + (POST + "/form-canvassers-Canvasser" + request + (route/restricted + (apply + (resolve-handler "form-canvassers-Canvasser") + (list request)))) + (GET + "/form-districts-District" + request + (route/restricted + (apply (resolve-handler "form-districts-District") (list request)))) + (POST + "/form-districts-District" + request + (route/restricted + (apply (resolve-handler "form-districts-District") (list request)))) + (GET + "/form-dwellings-Dwelling" + request + (route/restricted + (apply (resolve-handler "form-dwellings-Dwelling") (list request)))) + (POST + "/form-dwellings-Dwelling" + request + (route/restricted + (apply (resolve-handler "form-dwellings-Dwelling") (list request)))) + (GET + "/form-electors-Elector" + request + (route/restricted + (apply (resolve-handler "form-electors-Elector") (list request)))) + (POST + "/form-electors-Elector" + request + (route/restricted + (apply (resolve-handler "form-electors-Elector") (list request)))) + (GET + "/form-followupactions-Followupaction" + request + (route/restricted + (apply + (resolve-handler "form-followupactions-Followupaction") + (list request)))) + (POST + "/form-followupactions-Followupaction" + request + (route/restricted + (apply + (resolve-handler "form-followupactions-Followupaction") + (list request)))) + (GET + "/form-followupmethods-Followupmethod" + request + (route/restricted + (apply + (resolve-handler "form-followupmethods-Followupmethod") + (list request)))) + (POST + "/form-followupmethods-Followupmethod" + request + (route/restricted + (apply + (resolve-handler "form-followupmethods-Followupmethod") + (list request)))) + (GET + "/form-followuprequests-Followuprequest" + request + (route/restricted + (apply + (resolve-handler "form-followuprequests-Followuprequest") + (list request)))) + (POST + "/form-followuprequests-Followuprequest" + request + (route/restricted + (apply + (resolve-handler "form-followuprequests-Followuprequest") + (list request)))) + (GET + "/form-genders-Gender" + request + (route/restricted + (apply (resolve-handler "form-genders-Gender") (list request)))) + (POST + "/form-genders-Gender" + request + (route/restricted + (apply (resolve-handler "form-genders-Gender") (list request)))) + (GET + "/form-intentions-Intention" + request + (route/restricted + (apply + (resolve-handler "form-intentions-Intention") + (list request)))) + (POST + "/form-intentions-Intention" + request + (route/restricted + (apply + (resolve-handler "form-intentions-Intention") + (list request)))) + (GET + "/form-issues-Issue" + request + (route/restricted + (apply (resolve-handler "form-issues-Issue") (list request)))) + (POST + "/form-issues-Issue" + request + (route/restricted + (apply (resolve-handler "form-issues-Issue") (list request)))) + (GET + "/form-options-Option" + request + (route/restricted + (apply (resolve-handler "form-options-Option") (list request)))) + (POST + "/form-options-Option" + request + (route/restricted + (apply (resolve-handler "form-options-Option") (list request)))) + (GET + "/form-roles-Role" + request + (route/restricted + (apply (resolve-handler "form-roles-Role") (list request)))) + (POST + "/form-roles-Role" + request + (route/restricted + (apply (resolve-handler "form-roles-Role") (list request)))) + (GET + "/form-teams-Team" + request + (route/restricted + (apply (resolve-handler "form-teams-Team") (list request)))) + (POST + "/form-teams-Team" + request + (route/restricted + (apply (resolve-handler "form-teams-Team") (list request)))) + (GET + "/form-visits-Visit" + request + (route/restricted + (apply (resolve-handler "form-visits-Visit") (list request)))) + (POST + "/form-visits-Visit" + request + (route/restricted + (apply (resolve-handler "form-visits-Visit") (list request)))) + (GET + "/list-addresses-Addresses" + request + (route/restricted + (apply + (resolve-handler "list-addresses-Addresses") + (list request)))) + (POST + "/list-addresses-Addresses" + request + (route/restricted + (apply + (resolve-handler "list-addresses-Addresses") + (list request)))) + (GET + "/list-authorities-Authorities" + request + (route/restricted + (apply + (resolve-handler "list-authorities-Authorities") + (list request)))) + (POST + "/list-authorities-Authorities" + request + (route/restricted + (apply + (resolve-handler "list-authorities-Authorities") + (list request)))) + (GET + "/list-canvassers-Canvassers" + request + (route/restricted + (apply + (resolve-handler "list-canvassers-Canvassers") + (list request)))) + (POST + "/list-canvassers-Canvassers" + request + (route/restricted + (apply + (resolve-handler "list-canvassers-Canvassers") + (list request)))) + (GET + "/list-districts-Districts" + request + (route/restricted + (apply + (resolve-handler "list-districts-Districts") + (list request)))) + (POST + "/list-districts-Districts" + request + (route/restricted + (apply + (resolve-handler "list-districts-Districts") + (list request)))) + (GET + "/list-dwellings-Dwellings" + request + (route/restricted + (apply + (resolve-handler "list-dwellings-Dwellings") + (list request)))) + (POST + "/list-dwellings-Dwellings" + request + (route/restricted + (apply + (resolve-handler "list-dwellings-Dwellings") + (list request)))) + (GET + "/list-electors-Electors" + request + (route/restricted + (apply (resolve-handler "list-electors-Electors") (list request)))) + (POST + "/list-electors-Electors" + request + (route/restricted + (apply (resolve-handler "list-electors-Electors") (list request)))) + (GET + "/list-followupactions-Followupactions" + request + (route/restricted + (apply + (resolve-handler "list-followupactions-Followupactions") + (list request)))) + (POST + "/list-followupactions-Followupactions" + request + (route/restricted + (apply + (resolve-handler "list-followupactions-Followupactions") + (list request)))) + (GET + "/list-followupmethods-Followupmethods" + request + (route/restricted + (apply + (resolve-handler "list-followupmethods-Followupmethods") + (list request)))) + (POST + "/list-followupmethods-Followupmethods" + request + (route/restricted + (apply + (resolve-handler "list-followupmethods-Followupmethods") + (list request)))) + (GET + "/list-followuprequests-Followuprequests" + request + (route/restricted + (apply + (resolve-handler "list-followuprequests-Followuprequests") + (list request)))) + (POST + "/list-followuprequests-Followuprequests" + request + (route/restricted + (apply + (resolve-handler "list-followuprequests-Followuprequests") + (list request)))) + (GET + "/list-genders-Genders" + request + (route/restricted + (apply (resolve-handler "list-genders-Genders") (list request)))) + (POST + "/list-genders-Genders" + request + (route/restricted + (apply (resolve-handler "list-genders-Genders") (list request)))) + (GET + "/list-intentions-Intentions" + request + (route/restricted + (apply + (resolve-handler "list-intentions-Intentions") + (list request)))) + (POST + "/list-intentions-Intentions" + request + (route/restricted + (apply + (resolve-handler "list-intentions-Intentions") + (list request)))) + (GET + "/list-issues-Issues" + request + (route/restricted + (apply (resolve-handler "list-issues-Issues") (list request)))) + (POST + "/list-issues-Issues" + request + (route/restricted + (apply (resolve-handler "list-issues-Issues") (list request)))) + (GET + "/list-options-Options" + request + (route/restricted + (apply (resolve-handler "list-options-Options") (list request)))) + (POST + "/list-options-Options" + request + (route/restricted + (apply (resolve-handler "list-options-Options") (list request)))) + (GET + "/list-roles-Roles" + request + (route/restricted + (apply (resolve-handler "list-roles-Roles") (list request)))) + (POST + "/list-roles-Roles" + request + (route/restricted + (apply (resolve-handler "list-roles-Roles") (list request)))) + (GET + "/list-teams-Teams" + request + (route/restricted + (apply (resolve-handler "list-teams-Teams") (list request)))) + (POST + "/list-teams-Teams" + request + (route/restricted + (apply (resolve-handler "list-teams-Teams") (list request)))) + (GET + "/list-visits-Visits" + request + (route/restricted + (apply (resolve-handler "list-visits-Visits") (list request)))) + (POST + "/list-visits-Visits" + request + (route/restricted + (apply (resolve-handler "list-visits-Visits") (list request))))) + diff --git a/src/clj/youyesyet/routes/manual.clj b/src/clj/youyesyet/routes/manual.clj new file mode 100644 index 0000000..1da2e3e --- /dev/null +++ b/src/clj/youyesyet/routes/manual.clj @@ -0,0 +1,38 @@ +(ns + youyesyet.routes.auto + "Manual overrides for auto-generated 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])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.routes.manual: Manual overrides for auto-generated routes. +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +;;; Any of the routes defined in auto.clj can be overridden in this file. This means that +;;; new autogenerated code will not overwrite manually generated code. diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml index 18884fd..98b0593 100644 --- a/youyesyet.adl.xml +++ b/youyesyet.adl.xml @@ -56,6 +56,7 @@ + All electors known to the system; electors are people believed to be entitled to vote in the current campaign. @@ -64,7 +65,7 @@ - + @@ -81,6 +82,7 @@
+ All genders which may be assigned to electors. @@ -90,6 +92,7 @@ + All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. @@ -99,8 +102,11 @@ + + + Addresses of all buildings which contain dwellings. @@ -131,6 +137,7 @@ + All visits made by canvassers to dwellings in which opinions were recorded. @@ -149,13 +156,18 @@ + + + Authorities which may authenticate canvassers to the system. + + Issues believed to be of interest to electors, about which they may have questions. @@ -187,6 +199,8 @@ column="option_id"> + + Primary users of the system: those actually interviewing electors. @@ -221,6 +235,9 @@ + + + But only their own record @@ -230,6 +247,8 @@ All canvassers + + Requests for a followup with an issue expert @@ -254,6 +273,8 @@ entity="followupmethods" farkey="id"> + + Link table @@ -276,6 +297,8 @@ + + @@ -293,6 +316,9 @@ + + + @@ -303,6 +329,8 @@ All groups + + Electoral districts @@ -316,6 +344,8 @@ + + Link table @@ -357,6 +387,8 @@ But only for electors in their immediate vicinity + + Link table @@ -380,6 +412,8 @@ + + Link table @@ -398,5 +432,7 @@ + + diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 9939cad..18a2de3 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -67,6 +67,7 @@ entity electors already has a key - not generating one --> + All electors known to the system; electors are people believed to be entitled to vote in the current campaign. @@ -75,7 +76,7 @@ - + @@ -87,10 +88,11 @@ - - + +
+ All genders which may be assigned to electors. @@ -104,16 +106,21 @@ - + + +
- + + +
+ All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. @@ -123,11 +130,14 @@ + +
+ Addresses of all buildings which contain dwellings. @@ -154,13 +164,14 @@ - -
+ +
+ All visits made by canvassers to dwellings in which opinions were recorded. @@ -177,16 +188,21 @@ + +
+ Authorities which may authenticate canvassers to the system. + +
- + Link table. @@ -236,6 +252,28 @@ + + + + + + + + + + + +
+ + + + + + + + + +
- + Link table @@ -488,6 +544,8 @@ + +
A web-app intended to be used by canvassers campaigning for a 'Yes' vote in the second independence referendum. @@ -12,9 +13,34 @@ 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. - + + + + + + + + {{site-title}}: {{title}} + - + + + + + + A project of the + Radical Independence Campaign || + Version {{version}} + + + Built with LuminusWeb || + Powered by Clojure || + Find me/fork me on Github || + Licensed under the GNU General Public License version 2.0 + + + + @@ -143,7 +169,7 @@
- @@ -218,7 +244,7 @@ - @@ -369,7 +395,7 @@ entity="followuprequests" farkey="id">
- @@ -392,7 +418,7 @@
Link table - diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 18a2de3..8fb571f 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -1,7 +1,7 @@ - + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -202,3 +219,50 @@ Followupmethod
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html index b07e39b..3fbf3ab 100644 --- a/resources/templates/auto/form-addresses-Address.html +++ b/resources/templates/auto/form-addresses-Address.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -230,3 +247,50 @@ To delete this addresses record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-canvassers-Canvasser.html b/resources/templates/auto/form-canvassers-Canvasser.html index 97cbc37..8a975b9 100644 --- a/resources/templates/auto/form-canvassers-Canvasser.html +++ b/resources/templates/auto/form-canvassers-Canvasser.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -74,7 +91,12 @@ elector_id address_id {% ifwritable canvassers address_id %} - +
+ + +
{% else %} {% ifreadable canvassers address_id%} @@ -210,7 +232,12 @@ elector_id address_id {% ifwritable canvassers address_id %} - +
+ + +
{% else %} {% ifreadable canvassers address_id%} @@ -315,3 +342,50 @@ To delete this canvassers record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-districts-District.html b/resources/templates/auto/form-districts-District.html index a70e74b..6755daa 100644 --- a/resources/templates/auto/form-districts-District.html +++ b/resources/templates/auto/form-districts-District.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -80,3 +97,50 @@ To delete this districts record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-dwellings-Dwelling.html b/resources/templates/auto/form-dwellings-Dwelling.html index ea520e7..ab82225 100644 --- a/resources/templates/auto/form-dwellings-Dwelling.html +++ b/resources/templates/auto/form-dwellings-Dwelling.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -118,3 +135,50 @@ To delete this dwellings record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-electors-Elector.html b/resources/templates/auto/form-electors-Elector.html index 7b0b158..099cd53 100644 --- a/resources/templates/auto/form-electors-Elector.html +++ b/resources/templates/auto/form-electors-Elector.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -210,3 +227,50 @@ To delete this electors record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-followupactions-Followupaction.html b/resources/templates/auto/form-followupactions-Followupaction.html index 8143647..a82cce8 100644 --- a/resources/templates/auto/form-followupactions-Followupaction.html +++ b/resources/templates/auto/form-followupactions-Followupaction.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -46,7 +63,12 @@ request_id actor {% ifwritable followupactions actor %} - +
+ + +
{% else %} {% ifreadable followupactions actor%} @@ -135,7 +157,12 @@ request_id actor {% ifwritable followupactions actor %} - +
+ + +
{% else %} {% ifreadable followupactions actor%} @@ -202,3 +229,50 @@ To delete this followupactions record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-issues-Issue.html b/resources/templates/auto/form-issues-Issue.html index 7bec673..1a33953 100644 --- a/resources/templates/auto/form-issues-Issue.html +++ b/resources/templates/auto/form-issues-Issue.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -121,3 +138,50 @@ To delete this issues record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-roles-Role.html b/resources/templates/auto/form-roles-Role.html index 029740f..1d20938 100644 --- a/resources/templates/auto/form-roles-Role.html +++ b/resources/templates/auto/form-roles-Role.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -64,6 +81,25 @@ name {% endifreadable %} {% endifwritable %}

+

+ +{% ifwritable roles members %} +

+ + +
+{% else %} +{% ifreadable roles members%} + +{{record.members}} + +{% endifreadable %} +{% endifwritable %} +

{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-teams-Team.html b/resources/templates/auto/form-teams-Team.html index f81dbc7..e08105f 100644 --- a/resources/templates/auto/form-teams-Team.html +++ b/resources/templates/auto/form-teams-Team.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -193,3 +210,50 @@ To delete this teams record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html index 8b6c093..2c1ea30 100644 --- a/resources/templates/auto/form-visits-Visit.html +++ b/resources/templates/auto/form-visits-Visit.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -27,7 +44,12 @@ id address_id {% ifwritable visits address_id %} - +
+ + +
{% else %} {% ifreadable visits address_id%} @@ -88,7 +110,12 @@ id address_id {% ifwritable visits address_id %} - +
+ + +
{% else %} {% ifreadable visits address_id%} @@ -146,3 +173,50 @@ To delete this visits record
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html index 0a62aa0..70aa63e 100644 --- a/resources/templates/auto/list-addresses-Addresses.html +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -37,25 +54,28 @@ longitude - + - + - + - + - + - + - + + + + @@ -84,32 +104,69 @@ longitude {{ record.longitude }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html index 1b78cb8..98c8b4a 100644 --- a/resources/templates/auto/list-canvassers-Canvassers.html +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -43,31 +60,34 @@ authorised - + - + - + - + - + - + - + - + - + + + + @@ -102,32 +122,69 @@ authorised {{ record.authorised }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html index 9b41182..3349092 100644 --- a/resources/templates/auto/list-districts-Districts.html +++ b/resources/templates/auto/list-districts-Districts.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -22,10 +39,13 @@ name - + - + + + + @@ -39,32 +59,69 @@ name {{ record.name }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html index c8e5600..7f39626 100644 --- a/resources/templates/auto/list-dwellings-Dwellings.html +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -25,13 +42,16 @@ sub-address - + - + - + + + + @@ -48,32 +68,69 @@ sub-address {{ record.sub-address }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html index ea6102b..da7d082 100644 --- a/resources/templates/auto/list-electors-Electors.html +++ b/resources/templates/auto/list-electors-Electors.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -34,22 +51,25 @@ gender - + - + - + - + - + - + + + + @@ -75,32 +95,69 @@ gender {{ record.gender }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html index d6ad6fb..6c587e6 100644 --- a/resources/templates/auto/list-followupactions-Followupactions.html +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -34,22 +51,25 @@ closed - + - + - + - + - + - + + + + @@ -75,32 +95,69 @@ closed {{ record.closed }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html index f72dafb..d1c9e43 100644 --- a/resources/templates/auto/list-issues-Issues.html +++ b/resources/templates/auto/list-issues-Issues.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -25,13 +42,16 @@ current - + - + - + + + + @@ -48,32 +68,69 @@ current {{ record.current }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html index 65b7e89..720443f 100644 --- a/resources/templates/auto/list-roles-Roles.html +++ b/resources/templates/auto/list-roles-Roles.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -22,10 +39,13 @@ name - + - + + + + @@ -39,32 +59,69 @@ name {{ record.name }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html index a9b805f..d3b6c19 100644 --- a/resources/templates/auto/list-teams-Teams.html +++ b/resources/templates/auto/list-teams-Teams.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -31,19 +48,22 @@ longitude - + - + - + - + - + + + + @@ -66,32 +86,69 @@ longitude {{ record.longitude }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html index 7faee46..fd26cb1 100644 --- a/resources/templates/auto/list-visits-Visits.html +++ b/resources/templates/auto/list-visits-Visits.html @@ -1,7 +1,24 @@ -{% extends "templates/base.html" %} - - +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} {% block content %}
@@ -28,16 +45,19 @@ date - + - + - + - + + + + @@ -57,32 +77,69 @@ date {{ record.date }} - + View {% endfor %} - - -{% if offset > 0 %} + -{% endif %} - + +
{% endblock %} +{% block foot %} + + + + + + A project of the + + +Radical Independence Campaign + + || + Version {{version}} + + + + +Built with + +LuminusWeb + + || + + + Powered by + +Clojure + + || + + +Find me/fork me on + +Github + + || + + +Licensed under the + +GNU General Public License version 2.0 + + + + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/base-authenticated copy.html b/resources/templates/base-authenticated copy.html new file mode 100644 index 0000000..22af08a --- /dev/null +++ b/resources/templates/base-authenticated copy.html @@ -0,0 +1,79 @@ + + + + + + + + + + {{title}} + + + {% block whole-page %} + {% block top %} +
+ + +

+ {{title}} +

+
+ {% endblock %} +
+ +
+ {% block content %} + {% endblock %} +
+ +
+ {% block foot %} + + {% endblock %} + {% endblock %} + + {% block extra-script %} + {% endblock %} + + + + + + diff --git a/resources/templates/base-unauthenticated.html b/resources/templates/base.html similarity index 96% rename from resources/templates/base-unauthenticated.html rename to resources/templates/base.html index 4e41c8f..cfb278f 100644 --- a/resources/templates/base-unauthenticated.html +++ b/resources/templates/base.html @@ -1,5 +1,6 @@ - + diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index 789564f..40f95cf 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -6,7 +6,8 @@ [youyesyet.routes.authenticated :refer [authenticated-routes]] [youyesyet.routes.home :refer [home-routes]] [youyesyet.routes.oauth :refer [oauth-routes]] - [youyesyet.routes.auto-json-routes :refer [auto-rest-routes]] + [youyesyet.routes.auto-json :refer [auto-rest-routes]] + [youyesyet.routes.auto :refer [auto-selmer-routes]] [compojure.route :as route] [youyesyet.env :refer [defaults]] [mount.core :as mount] @@ -67,6 +68,9 @@ (-> #'auto-rest-routes (wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-formats)) + (-> #'auto-selmer-routes + (wrap-routes middleware/wrap-csrf) + (wrap-routes middleware/wrap-formats)) #'oauth-routes #'authenticated-routes (route/not-found @@ -75,5 +79,5 @@ :title "page not found"}))))) -(def app #'app-routes) - ;;(middleware/wrap-base #'app-routes)) +(def app ;; #'app-routes) + (middleware/wrap-base #'app-routes)) diff --git a/src/clj/youyesyet/routes/auto.clj b/src/clj/youyesyet/routes/auto.clj index b1951c3..3781e1d 100644 --- a/src/clj/youyesyet/routes/auto.clj +++ b/src/clj/youyesyet/routes/auto.clj @@ -1,6 +1,6 @@ (ns youyesyet.routes.auto - "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180610T214425.631Z" + "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180611T180322.460Z" (:require [noir.response :as nresponse] [noir.util.route :as route] @@ -8,6 +8,7 @@ [ring.util.http-response :as response] [clojure.java.io :as io] [hugsql.core :as hugsql] + [youyesyet.layout :as l] [youyesyet.db.core :as db] [youyesyet.routes.manual :as m])) @@ -19,13 +20,13 @@ n (str "auto/" n))) -(def resolve-template (memoise raw-resolve-template)) +(def resolve-template (memoize raw-resolve-template)) (defn index [r] - (layout/render - (resolve-template "application-index") + (l/render + (resolve-template "application-index.html") {:title "Administrative menu"})) (defn @@ -33,16 +34,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-electors-Electors.html") - {:title "Electors", :params p, :records (db/search-elector p)}))) + {:title "Electors", + :params p, + :records (db/search-strings-elector p)}))) (defn form-electors-Elector [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-electors-Elector.html") {:title "Elector", :params p, :record (db/get-elector p)}))) @@ -51,16 +54,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-genders-Genders.html") - {:title "Genders", :params p, :records (db/search-gender p)}))) + {:title "Genders", + :params p, + :records (db/search-strings-gender p)}))) (defn form-genders-Gender [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-genders-Gender.html") {:title "Gender", :params p, :record (db/get-gender p)}))) @@ -69,16 +74,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-dwellings-Dwellings.html") - {:title "Dwellings", :params p, :records (db/search-dwelling p)}))) + {:title "Dwellings", + :params p, + :records (db/search-strings-dwelling p)}))) (defn form-dwellings-Dwelling [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-dwellings-Dwelling.html") {:title "Dwelling", :params p, :record (db/get-dwelling p)}))) @@ -87,16 +94,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-addresses-Addresses.html") - {:title "Addresses", :params p, :records (db/search-address p)}))) + {:title "Addresses", + :params p, + :records (db/search-strings-address p)}))) (defn form-addresses-Address [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-addresses-Address.html") {:title "Address", :params p, :record (db/get-address p)}))) @@ -105,16 +114,16 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-visits-Visits.html") - {:title "Visits", :params p, :records (db/search-visit p)}))) + {:title "Visits", :params p, :records (db/search-strings-visit p)}))) (defn form-visits-Visit [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-visits-Visit.html") {:title "Visit", :params p, :record (db/get-visit p)}))) @@ -123,18 +132,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-authorities-Authorities.html") {:title "Authorities", :params p, - :records (db/search-authority p)}))) + :records (db/search-strings-authority p)}))) (defn form-authorities-Authority [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-authorities-Authority.html") {:title "Authority", :params p, :record (db/get-authority p)}))) @@ -143,16 +152,16 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-issues-Issues.html") - {:title "Issues", :params p, :records (db/search-issue p)}))) + {:title "Issues", :params p, :records (db/search-strings-issue p)}))) (defn form-issues-Issue [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-issues-Issue.html") {:title "Issue", :params p, :record (db/get-issue p)}))) @@ -161,16 +170,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-intentions-Intentions.html") - {:title "Intentions", :params p, :records (db/search-intention p)}))) + {:title "Intentions", + :params p, + :records (db/search-strings-intention p)}))) (defn form-intentions-Intention [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-intentions-Intention.html") {:title "Intention", :params p, :record (db/get-intention p)}))) @@ -179,16 +190,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-canvassers-Canvassers.html") - {:title "Canvassers", :params p, :records (db/search-canvasser p)}))) + {:title "Canvassers", + :params p, + :records (db/search-strings-canvasser p)}))) (defn form-canvassers-Canvasser [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-canvassers-Canvasser.html") {:title "Canvasser", :params p, :record (db/get-canvasser p)}))) @@ -197,18 +210,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-followuprequests-Followuprequests.html") {:title "Followuprequests", :params p, - :records (db/search-followuprequest p)}))) + :records (db/search-strings-followuprequest p)}))) (defn form-followuprequests-Followuprequest [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-followuprequests-Followuprequest.html") {:title "Followuprequest", :params p, @@ -219,16 +232,16 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-roles-Roles.html") - {:title "Roles", :params p, :records (db/search-role p)}))) + {:title "Roles", :params p, :records (db/search-strings-role p)}))) (defn form-roles-Role [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-roles-Role.html") {:title "Role", :params p, :record (db/get-role p)}))) @@ -237,16 +250,16 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-teams-Teams.html") - {:title "Teams", :params p, :records (db/search-team p)}))) + {:title "Teams", :params p, :records (db/search-strings-team p)}))) (defn form-teams-Team [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-teams-Team.html") {:title "Team", :params p, :record (db/get-team p)}))) @@ -255,16 +268,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-districts-Districts.html") - {:title "Districts", :params p, :records (db/search-district p)}))) + {:title "Districts", + :params p, + :records (db/search-strings-district p)}))) (defn form-districts-District [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-districts-District.html") {:title "District", :params p, :record (db/get-district p)}))) @@ -273,18 +288,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-followupactions-Followupactions.html") {:title "Followupactions", :params p, - :records (db/search-followupaction p)}))) + :records (db/search-strings-followupaction p)}))) (defn form-followupactions-Followupaction [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-followupactions-Followupaction.html") {:title "Followupaction", :params p, @@ -295,16 +310,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-options-Options.html") - {:title "Options", :params p, :records (db/search-option p)}))) + {:title "Options", + :params p, + :records (db/search-strings-option p)}))) (defn form-options-Option [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-options-Option.html") {:title "Option", :params p, :record (db/get-option p)}))) @@ -313,18 +330,18 @@ [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "list-followupmethods-Followupmethods.html") {:title "Followupmethods", :params p, - :records (db/search-followupmethod p)}))) + :records (db/search-strings-followupmethod p)}))) (defn form-followupmethods-Followupmethod [r] (let [p (:form-params r)] - (layout/render + (l/render (resolve-template "form-followupmethods-Followupmethod.html") {:title "Followupmethod", :params p, @@ -334,14 +351,21 @@ raw-resolve-handler "Prefer the manually-written version of the handler with name `n`, if it exists, to the automatically generated one" [n] - (let - [s (symbol (str "m." n))] - (if (bound? s) (eval s) (eval (symbol n))))) + (try + (eval (symbol (str "youyesyet" ".routes.manual/" n))) + (catch + Exception + _ + (eval (symbol (str "youyesyet" ".routes.auto/" n)))))) (def resolve-handler (memoize raw-resolve-handler)) (defroutes auto-selmer-routes + (GET + "/index" + request + (route/restricted (apply (resolve-handler "index") (list request)))) (GET "/form-addresses-Address" request diff --git a/src/clj/youyesyet/routes/auto_json.clj b/src/clj/youyesyet/routes/auto_json.clj new file mode 100644 index 0000000..9426c90 --- /dev/null +++ b/src/clj/youyesyet/routes/auto_json.clj @@ -0,0 +1,1653 @@ +(ns + youyesyet.routes.auto-json + "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180611T180321.339Z" + (: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-address! + create-authority! + create-canvasser! + create-district! + create-dwelling! + create-elector! + create-followupaction! + create-followupmethod! + create-followuprequest! + create-gender! + create-intention! + create-issue! + create-issueexpertise! + create-option! + create-role! + create-rolemembership! + create-team! + create-teammembership! + create-teamorganisership! + create-visit! + delete-address! + delete-authority! + delete-canvasser! + delete-district! + delete-dwelling! + delete-elector! + delete-followupaction! + delete-followupmethod! + delete-followuprequest! + delete-gender! + delete-intention! + delete-issue! + delete-issueexpertise! + delete-option! + delete-role! + delete-rolemembership! + delete-team! + delete-teammembership! + delete-teamorganisership! + delete-visit! + get-address + get-authority + get-canvasser + get-canvasser-by-username + get-district + get-dwelling + get-elector + get-followupaction + get-followupmethod + get-followuprequest + get-gender + get-intention + get-issue + get-issueexpertise + get-option + get-role + get-rolemembership + get-team + get-teammembership + get-teamorganisership + get-visit + list-addresses + list-addresses-by-district + list-authorities + list-canvassers + list-canvassers-by-address + list-canvassers-by-authority + list-canvassers-by-elector + list-canvassers-by-role + list-districts + list-dwellings + list-dwellings-by-address + list-electors + list-electors-by-dwelling + list-electors-by-gender + 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-genders + list-intentions + list-intentions-by-elector + list-intentions-by-option + list-intentions-by-visit + list-issueexpertise + list-issueexpertise-by-canvasser + list-issueexpertise-by-followupmethod + list-issueexpertise-by-issue + list-issues + list-options + list-rolememberships + list-rolememberships-by-canvasser + list-rolememberships-by-role + list-roles + list-roles-by-canvasser + list-teammemberships + list-teammemberships-by-canvasser + list-teammemberships-by-team + list-teamorganiserships + list-teamorganiserships-by-canvasser + list-teamorganiserships-by-team + list-teams + list-teams-by-canvasser + list-teams-by-district + list-visits + list-visits-by-address + list-visits-by-canvasser + search-strings-address + search-strings-authority + search-strings-canvasser + search-strings-district + search-strings-dwelling + search-strings-elector + search-strings-followupaction + search-strings-followupmethod + search-strings-followuprequest + search-strings-gender + search-strings-intention + search-strings-issue + search-strings-issueexpertise + search-strings-option + search-strings-role + search-strings-rolemembership + search-strings-team + search-strings-teammembership + search-strings-teamorganisership + search-strings-visit + update-address! + update-canvasser! + update-district! + update-dwelling! + update-elector! + update-followupaction! + update-followuprequest! + update-intention! + update-issue! + update-issueexpertise! + update-role! + update-rolemembership! + update-team! + update-teammembership! + update-teamorganisership! + update-visit!) + + +(defroutes + auto-rest-routes + (POST + "/json/auto/create-address" + request + (route/restricted (create-address! request))) + (POST + "/json/auto/create-authority" + request + (route/restricted (create-authority! request))) + (POST + "/json/auto/create-canvasser" + request + (route/restricted (create-canvasser! request))) + (POST + "/json/auto/create-district" + request + (route/restricted (create-district! request))) + (POST + "/json/auto/create-dwelling" + request + (route/restricted (create-dwelling! request))) + (POST + "/json/auto/create-elector" + request + (route/restricted (create-elector! request))) + (POST + "/json/auto/create-followupaction" + request + (route/restricted (create-followupaction! request))) + (POST + "/json/auto/create-followupmethod" + request + (route/restricted (create-followupmethod! request))) + (POST + "/json/auto/create-followuprequest" + request + (route/restricted (create-followuprequest! request))) + (POST + "/json/auto/create-gender" + request + (route/restricted (create-gender! request))) + (POST + "/json/auto/create-intention" + request + (route/restricted (create-intention! request))) + (POST + "/json/auto/create-issue" + request + (route/restricted (create-issue! request))) + (POST + "/json/auto/create-issueexpertise" + request + (route/restricted (create-issueexpertise! request))) + (POST + "/json/auto/create-option" + request + (route/restricted (create-option! request))) + (POST + "/json/auto/create-role" + request + (route/restricted (create-role! request))) + (POST + "/json/auto/create-rolemembership" + request + (route/restricted (create-rolemembership! request))) + (POST + "/json/auto/create-team" + request + (route/restricted (create-team! request))) + (POST + "/json/auto/create-teammembership" + request + (route/restricted (create-teammembership! request))) + (POST + "/json/auto/create-teamorganisership" + request + (route/restricted (create-teamorganisership! request))) + (POST + "/json/auto/create-visit" + request + (route/restricted (create-visit! request))) + (POST + "/json/auto/delete-address" + request + (route/restricted (delete-address! request))) + (POST + "/json/auto/delete-authority" + request + (route/restricted (delete-authority! request))) + (POST + "/json/auto/delete-canvasser" + request + (route/restricted (delete-canvasser! request))) + (POST + "/json/auto/delete-district" + request + (route/restricted (delete-district! request))) + (POST + "/json/auto/delete-dwelling" + request + (route/restricted (delete-dwelling! request))) + (POST + "/json/auto/delete-elector" + request + (route/restricted (delete-elector! request))) + (POST + "/json/auto/delete-followupaction" + request + (route/restricted (delete-followupaction! request))) + (POST + "/json/auto/delete-followupmethod" + request + (route/restricted (delete-followupmethod! request))) + (POST + "/json/auto/delete-followuprequest" + request + (route/restricted (delete-followuprequest! request))) + (POST + "/json/auto/delete-gender" + request + (route/restricted (delete-gender! request))) + (POST + "/json/auto/delete-intention" + request + (route/restricted (delete-intention! request))) + (POST + "/json/auto/delete-issue" + request + (route/restricted (delete-issue! request))) + (POST + "/json/auto/delete-issueexpertise" + request + (route/restricted (delete-issueexpertise! request))) + (POST + "/json/auto/delete-option" + request + (route/restricted (delete-option! request))) + (POST + "/json/auto/delete-role" + request + (route/restricted (delete-role! request))) + (POST + "/json/auto/delete-rolemembership" + request + (route/restricted (delete-rolemembership! request))) + (POST + "/json/auto/delete-team" + request + (route/restricted (delete-team! request))) + (POST + "/json/auto/delete-teammembership" + request + (route/restricted (delete-teammembership! request))) + (POST + "/json/auto/delete-teamorganisership" + request + (route/restricted (delete-teamorganisership! request))) + (POST + "/json/auto/delete-visit" + request + (route/restricted (delete-visit! request))) + (POST + "/json/auto/get-address" + request + (route/restricted (get-address request))) + (POST + "/json/auto/get-authority" + request + (route/restricted (get-authority request))) + (POST + "/json/auto/get-canvasser" + request + (route/restricted (get-canvasser request))) + (POST + "/json/auto/get-canvasser-by-username" + request + (route/restricted (get-canvasser-by-username request))) + (POST + "/json/auto/get-district" + request + (route/restricted (get-district request))) + (POST + "/json/auto/get-dwelling" + request + (route/restricted (get-dwelling request))) + (POST + "/json/auto/get-elector" + request + (route/restricted (get-elector request))) + (POST + "/json/auto/get-followupaction" + request + (route/restricted (get-followupaction request))) + (POST + "/json/auto/get-followupmethod" + request + (route/restricted (get-followupmethod request))) + (POST + "/json/auto/get-followuprequest" + request + (route/restricted (get-followuprequest request))) + (POST + "/json/auto/get-gender" + request + (route/restricted (get-gender request))) + (POST + "/json/auto/get-intention" + request + (route/restricted (get-intention request))) + (POST + "/json/auto/get-issue" + request + (route/restricted (get-issue request))) + (POST + "/json/auto/get-issueexpertise" + request + (route/restricted (get-issueexpertise request))) + (POST + "/json/auto/get-option" + request + (route/restricted (get-option request))) + (POST + "/json/auto/get-role" + request + (route/restricted (get-role request))) + (POST + "/json/auto/get-rolemembership" + request + (route/restricted (get-rolemembership request))) + (POST + "/json/auto/get-team" + request + (route/restricted (get-team request))) + (POST + "/json/auto/get-teammembership" + request + (route/restricted (get-teammembership request))) + (POST + "/json/auto/get-teamorganisership" + request + (route/restricted (get-teamorganisership request))) + (POST + "/json/auto/get-visit" + request + (route/restricted (get-visit request))) + (GET + "/json/auto/list-addresses" + request + (route/restricted (list-addresses request))) + (GET + "/json/auto/list-addresses-by-district" + request + (route/restricted (list-addresses-by-district request))) + (GET + "/json/auto/list-authorities" + request + (route/restricted (list-authorities request))) + (GET + "/json/auto/list-canvassers" + request + (route/restricted (list-canvassers request))) + (GET + "/json/auto/list-canvassers-by-address" + request + (route/restricted (list-canvassers-by-address request))) + (GET + "/json/auto/list-canvassers-by-authority" + request + (route/restricted (list-canvassers-by-authority request))) + (GET + "/json/auto/list-canvassers-by-elector" + request + (route/restricted (list-canvassers-by-elector request))) + (GET + "/json/auto/list-canvassers-by-role" + request + (route/restricted (list-canvassers-by-role request))) + (GET + "/json/auto/list-districts" + request + (route/restricted (list-districts request))) + (GET + "/json/auto/list-dwellings" + request + (route/restricted (list-dwellings request))) + (GET + "/json/auto/list-dwellings-by-address" + request + (route/restricted (list-dwellings-by-address request))) + (GET + "/json/auto/list-electors" + request + (route/restricted (list-electors request))) + (GET + "/json/auto/list-electors-by-dwelling" + request + (route/restricted (list-electors-by-dwelling request))) + (GET + "/json/auto/list-electors-by-gender" + request + (route/restricted (list-electors-by-gender request))) + (GET + "/json/auto/list-followupactions" + request + (route/restricted (list-followupactions request))) + (GET + "/json/auto/list-followupactions-by-canvasser" + request + (route/restricted (list-followupactions-by-canvasser request))) + (GET + "/json/auto/list-followupactions-by-followuprequest" + request + (route/restricted (list-followupactions-by-followuprequest request))) + (GET + "/json/auto/list-followupmethods" + request + (route/restricted (list-followupmethods request))) + (GET + "/json/auto/list-followuprequests" + request + (route/restricted (list-followuprequests request))) + (GET + "/json/auto/list-followuprequests-by-elector" + request + (route/restricted (list-followuprequests-by-elector request))) + (GET + "/json/auto/list-followuprequests-by-followupmethod" + request + (route/restricted (list-followuprequests-by-followupmethod request))) + (GET + "/json/auto/list-followuprequests-by-issue" + request + (route/restricted (list-followuprequests-by-issue request))) + (GET + "/json/auto/list-followuprequests-by-visit" + request + (route/restricted (list-followuprequests-by-visit request))) + (GET + "/json/auto/list-genders" + request + (route/restricted (list-genders request))) + (GET + "/json/auto/list-intentions" + request + (route/restricted (list-intentions request))) + (GET + "/json/auto/list-intentions-by-elector" + request + (route/restricted (list-intentions-by-elector request))) + (GET + "/json/auto/list-intentions-by-option" + request + (route/restricted (list-intentions-by-option request))) + (GET + "/json/auto/list-intentions-by-visit" + request + (route/restricted (list-intentions-by-visit request))) + (GET + "/json/auto/list-issueexpertise" + request + (route/restricted (list-issueexpertise request))) + (GET + "/json/auto/list-issueexpertise-by-canvasser" + request + (route/restricted (list-issueexpertise-by-canvasser request))) + (GET + "/json/auto/list-issueexpertise-by-followupmethod" + request + (route/restricted (list-issueexpertise-by-followupmethod request))) + (GET + "/json/auto/list-issueexpertise-by-issue" + request + (route/restricted (list-issueexpertise-by-issue request))) + (GET + "/json/auto/list-issues" + request + (route/restricted (list-issues request))) + (GET + "/json/auto/list-options" + request + (route/restricted (list-options request))) + (GET + "/json/auto/list-rolememberships" + request + (route/restricted (list-rolememberships request))) + (GET + "/json/auto/list-rolememberships-by-canvasser" + request + (route/restricted (list-rolememberships-by-canvasser request))) + (GET + "/json/auto/list-rolememberships-by-role" + request + (route/restricted (list-rolememberships-by-role request))) + (GET + "/json/auto/list-roles" + request + (route/restricted (list-roles request))) + (GET + "/json/auto/list-roles-by-canvasser" + request + (route/restricted (list-roles-by-canvasser request))) + (GET + "/json/auto/list-teammemberships" + request + (route/restricted (list-teammemberships request))) + (GET + "/json/auto/list-teammemberships-by-canvasser" + request + (route/restricted (list-teammemberships-by-canvasser request))) + (GET + "/json/auto/list-teammemberships-by-team" + request + (route/restricted (list-teammemberships-by-team request))) + (GET + "/json/auto/list-teamorganiserships" + request + (route/restricted (list-teamorganiserships request))) + (GET + "/json/auto/list-teamorganiserships-by-canvasser" + request + (route/restricted (list-teamorganiserships-by-canvasser request))) + (GET + "/json/auto/list-teamorganiserships-by-team" + request + (route/restricted (list-teamorganiserships-by-team request))) + (GET + "/json/auto/list-teams" + request + (route/restricted (list-teams request))) + (GET + "/json/auto/list-teams-by-canvasser" + request + (route/restricted (list-teams-by-canvasser request))) + (GET + "/json/auto/list-teams-by-district" + request + (route/restricted (list-teams-by-district request))) + (GET + "/json/auto/list-visits" + request + (route/restricted (list-visits request))) + (GET + "/json/auto/list-visits-by-address" + request + (route/restricted (list-visits-by-address request))) + (GET + "/json/auto/list-visits-by-canvasser" + request + (route/restricted (list-visits-by-canvasser request))) + (GET + "/json/auto/search-strings-address" + request + (route/restricted (search-strings-address request))) + (GET + "/json/auto/search-strings-authority" + request + (route/restricted (search-strings-authority request))) + (GET + "/json/auto/search-strings-canvasser" + request + (route/restricted (search-strings-canvasser request))) + (GET + "/json/auto/search-strings-district" + request + (route/restricted (search-strings-district request))) + (GET + "/json/auto/search-strings-dwelling" + request + (route/restricted (search-strings-dwelling request))) + (GET + "/json/auto/search-strings-elector" + request + (route/restricted (search-strings-elector request))) + (GET + "/json/auto/search-strings-followupaction" + request + (route/restricted (search-strings-followupaction request))) + (GET + "/json/auto/search-strings-followupmethod" + request + (route/restricted (search-strings-followupmethod request))) + (GET + "/json/auto/search-strings-followuprequest" + request + (route/restricted (search-strings-followuprequest request))) + (GET + "/json/auto/search-strings-gender" + request + (route/restricted (search-strings-gender request))) + (GET + "/json/auto/search-strings-intention" + request + (route/restricted (search-strings-intention request))) + (GET + "/json/auto/search-strings-issue" + request + (route/restricted (search-strings-issue request))) + (GET + "/json/auto/search-strings-issueexpertise" + request + (route/restricted (search-strings-issueexpertise request))) + (GET + "/json/auto/search-strings-option" + request + (route/restricted (search-strings-option request))) + (GET + "/json/auto/search-strings-role" + request + (route/restricted (search-strings-role request))) + (GET + "/json/auto/search-strings-rolemembership" + request + (route/restricted (search-strings-rolemembership request))) + (GET + "/json/auto/search-strings-team" + request + (route/restricted (search-strings-team request))) + (GET + "/json/auto/search-strings-teammembership" + request + (route/restricted (search-strings-teammembership request))) + (GET + "/json/auto/search-strings-teamorganisership" + request + (route/restricted (search-strings-teamorganisership request))) + (GET + "/json/auto/search-strings-visit" + request + (route/restricted (search-strings-visit request))) + (POST + "/json/auto/update-address" + request + (route/restricted (update-address! request))) + (POST + "/json/auto/update-canvasser" + request + (route/restricted (update-canvasser! request))) + (POST + "/json/auto/update-district" + request + (route/restricted (update-district! request))) + (POST + "/json/auto/update-dwelling" + request + (route/restricted (update-dwelling! request))) + (POST + "/json/auto/update-elector" + request + (route/restricted (update-elector! request))) + (POST + "/json/auto/update-followupaction" + request + (route/restricted (update-followupaction! request))) + (POST + "/json/auto/update-followuprequest" + request + (route/restricted (update-followuprequest! request))) + (POST + "/json/auto/update-intention" + request + (route/restricted (update-intention! request))) + (POST + "/json/auto/update-issue" + request + (route/restricted (update-issue! request))) + (POST + "/json/auto/update-issueexpertise" + request + (route/restricted (update-issueexpertise! request))) + (POST + "/json/auto/update-role" + request + (route/restricted (update-role! request))) + (POST + "/json/auto/update-rolemembership" + request + (route/restricted (update-rolemembership! request))) + (POST + "/json/auto/update-team" + request + (route/restricted (update-team! request))) + (POST + "/json/auto/update-teammembership" + request + (route/restricted (update-teammembership! request))) + (POST + "/json/auto/update-teamorganisership" + request + (route/restricted (update-teamorganisership! request))) + (POST + "/json/auto/update-visit" + request + (route/restricted (update-visit! request)))) + + +(defn + create-address! + "Auto-generated method to insert one record to the `addresses` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the keys `nil` identifying the record created." + [{:keys [params]}] + (do (db/create-address! 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`: `nil`. Returns a map containing the keys `nil` 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`: `nil`. Returns a map containing the keys `nil` 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`: `nil`. Returns a map containing the keys `nil` identifying the record created." + [{:keys [params]}] + (do (db/create-district! params))) + +(defn + create-dwelling! + "Auto-generated method to insert one record to the `dwellings` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the keys `nil` identifying the record created." + [{:keys [params]}] + (do (db/create-dwelling! 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`: `nil`. Returns a map containing the keys `nil` 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`: `nil`. Returns a map containing the keys `nil` 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`: `nil`. Returns a map containing the keys `nil` 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`: `nil`. Returns a map containing the keys `nil` identifying the record created." + [{:keys [params]}] + (do (db/create-followuprequest! params))) + +(defn + create-gender! + "Auto-generated method to insert one record to the `genders` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the keys `nil` identifying the record created." + [{:keys [params]}] + (do (db/create-gender! 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`: `nil`. 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`: `nil`. Returns a map containing the keys `nil` 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`: `nil`. 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`: `nil`. Returns a map containing the keys `nil` 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`: `nil`. 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`: `nil`. Returns a map containing the keys `nil` identifying the record created." + [{:keys [params]}] + (do (db/create-rolemembership! 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`: `nil`. 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`: `nil`. 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`: `nil`. 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`: `nil`. Returns a map containing the keys `nil` identifying the record created." + [{:keys [params]}] + (do (db/create-visit! params))) + +(defn + delete-address! + "Auto-generated method to delete one record from the `addresses` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-address! 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`: ``." + [{: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`: ``." + [{: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`: ``." + [{:keys [params]}] + (do (db/delete-district! params)) + (response/found "/")) + +(defn + delete-dwelling! + "Auto-generated method to delete one record from the `dwellings` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-dwelling! 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`: ``." + [{: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`: ``." + [{: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`: ``." + [{: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`: ``." + [{:keys [params]}] + (do (db/delete-followuprequest! params)) + (response/found "/")) + +(defn + delete-gender! + "Auto-generated method to delete one record from the `genders` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-gender! params)) + (response/found "/")) + +(defn + delete-intention! + "Auto-generated method to delete one record from the `intentions` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-intention! 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`: ``." + [{:keys [params]}] + (do (db/delete-issue! params)) + (response/found "/")) + +(defn + delete-issueexpertise! + "Auto-generated method to delete one record from the `issueexpertise` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-issueexpertise! 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`: ``." + [{:keys [params]}] + (do (db/delete-option! params)) + (response/found "/")) + +(defn + delete-role! + "Auto-generated method to delete one record from the `roles` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-role! params)) + (response/found "/")) + +(defn + delete-rolemembership! + "Auto-generated method to delete one record from the `rolememberships` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-rolemembership! params)) + (response/found "/")) + +(defn + delete-team! + "Auto-generated method to delete one record from the `teams` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-team! params)) + (response/found "/")) + +(defn + delete-teammembership! + "Auto-generated method to delete one record from the `teammemberships` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-teammembership! params)) + (response/found "/")) + +(defn + delete-teamorganisership! + "Auto-generated method to delete one record from the `teamorganiserships` table. Expects the following key(s) to be present in `params`: ``." + [{:keys [params]}] + (do (db/delete-teamorganisership! 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`: ``." + [{:keys [params]}] + (do (db/delete-visit! params)) + (response/found "/")) + +(defn + get-address + "Auto-generated method to select one record from the `addresses` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-address 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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{: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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-canvasser params))) + +(defn + get-canvasser-by-username + "Auto-generated method to select one record from the `canvassers` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-canvasser-by-username 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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-district params))) + +(defn + get-dwelling + "Auto-generated method to select one record from the `dwellings` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-dwelling 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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{: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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{: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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{: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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-followuprequest params))) + +(defn + get-gender + "Auto-generated method to select one record from the `genders` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-gender params))) + +(defn + get-intention + "Auto-generated method to select one record from the `intentions` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-intention 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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-issue params))) + +(defn + get-issueexpertise + "Auto-generated method to select one record from the `issueexpertise` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-issueexpertise 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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-option params))) + +(defn + get-role + "Auto-generated method to select one record from the `roles` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-role params))) + +(defn + get-rolemembership + "Auto-generated method to select one record from the `rolememberships` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-rolemembership params))) + +(defn + get-team + "Auto-generated method to select one record from the `teams` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-team params))) + +(defn + get-teammembership + "Auto-generated method to select one record from the `teammemberships` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-teammembership params))) + +(defn + get-teamorganisership + "Auto-generated method to select one record from the `teamorganiserships` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + [{:keys [params]}] + (do (db/get-teamorganisership 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`: `nil`. Returns a map containing the following keys: `(nil)`." + [{: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: `(nil)`." + [{: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: `(nil)`." + [{: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: `(nil)`." + [{:keys [params]}] + (do (db/list-canvassers params))) + +(defn + list-canvassers-by-address + [{:keys [params]}] + (do (db/list-canvassers-by-address params))) + +(defn + list-canvassers-by-authority + [{:keys [params]}] + (do (db/list-canvassers-by-authority params))) + +(defn + list-canvassers-by-elector + [{:keys [params]}] + (do (db/list-canvassers-by-elector params))) + +(defn + list-canvassers-by-role + [{:keys [params]}] + (do (db/list-canvassers-by-role 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: `(nil)`." + [{:keys [params]}] + (do (db/list-districts params))) + +(defn + list-dwellings + "Auto-generated method to select all records from the `dwellings` 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: `(nil)`." + [{:keys [params]}] + (do (db/list-dwellings params))) + +(defn + list-dwellings-by-address + [{:keys [params]}] + (do (db/list-dwellings-by-address 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: `(nil)`." + [{:keys [params]}] + (do (db/list-electors params))) + +(defn + list-electors-by-dwelling + [{:keys [params]}] + (do (db/list-electors-by-dwelling params))) + +(defn + list-electors-by-gender + [{:keys [params]}] + (do (db/list-electors-by-gender 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: `(nil)`." + [{: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: `(nil)`." + [{: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: `(nil)`." + [{: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-genders + "Auto-generated method to select all records from the `genders` 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: `(nil)`." + [{:keys [params]}] + (do (db/list-genders params))) + +(defn + list-intentions + "Auto-generated method to select all records from the `intentions` 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: `(nil)`." + [{:keys [params]}] + (do (db/list-intentions params))) + +(defn + list-intentions-by-elector + [{:keys [params]}] + (do (db/list-intentions-by-elector params))) + +(defn + list-intentions-by-option + [{:keys [params]}] + (do (db/list-intentions-by-option params))) + +(defn + list-intentions-by-visit + [{:keys [params]}] + (do (db/list-intentions-by-visit params))) + +(defn + list-issueexpertise + "Auto-generated method to select all records from the `issueexpertise` 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: `(nil)`." + [{:keys [params]}] + (do (db/list-issueexpertise params))) + +(defn + list-issueexpertise-by-canvasser + [{:keys [params]}] + (do (db/list-issueexpertise-by-canvasser params))) + +(defn + list-issueexpertise-by-followupmethod + [{:keys [params]}] + (do (db/list-issueexpertise-by-followupmethod params))) + +(defn + list-issueexpertise-by-issue + [{:keys [params]}] + (do (db/list-issueexpertise-by-issue 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: `(nil)`." + [{: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: `(nil)`." + [{:keys [params]}] + (do (db/list-options params))) + +(defn + list-rolememberships + "Auto-generated method to select all records from the `rolememberships` 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: `(nil)`." + [{:keys [params]}] + (do (db/list-rolememberships params))) + +(defn + list-rolememberships-by-canvasser + [{:keys [params]}] + (do (db/list-rolememberships-by-canvasser params))) + +(defn + list-rolememberships-by-role + [{:keys [params]}] + (do (db/list-rolememberships-by-role 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: `(nil)`." + [{:keys [params]}] + (do (db/list-roles params))) + +(defn + list-roles-by-canvasser + [{:keys [params]}] + (do (db/list-roles-by-canvasser params))) + +(defn + list-teammemberships + "Auto-generated method to select all records from the `teammemberships` 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: `(nil)`." + [{:keys [params]}] + (do (db/list-teammemberships params))) + +(defn + list-teammemberships-by-canvasser + [{:keys [params]}] + (do (db/list-teammemberships-by-canvasser params))) + +(defn + list-teammemberships-by-team + [{:keys [params]}] + (do (db/list-teammemberships-by-team params))) + +(defn + list-teamorganiserships + "Auto-generated method to select all records from the `teamorganiserships` 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: `(nil)`." + [{:keys [params]}] + (do (db/list-teamorganiserships params))) + +(defn + list-teamorganiserships-by-canvasser + [{:keys [params]}] + (do (db/list-teamorganiserships-by-canvasser params))) + +(defn + list-teamorganiserships-by-team + [{:keys [params]}] + (do (db/list-teamorganiserships-by-team 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: `(nil)`." + [{:keys [params]}] + (do (db/list-teams params))) + +(defn + list-teams-by-canvasser + [{:keys [params]}] + (do (db/list-teams-by-canvasser 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: `(nil)`." + [{:keys [params]}] + (do (db/list-visits params))) + +(defn + list-visits-by-address + [{:keys [params]}] + (do (db/list-visits-by-address params))) + +(defn + list-visits-by-canvasser + [{:keys [params]}] + (do (db/list-visits-by-canvasser params))) + +(defn + search-strings-address + "Auto-generated method to select all records from the `addresses` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-address params))) + +(defn + search-strings-authority + "Auto-generated method to select all records from the `authorities` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-authority params))) + +(defn + search-strings-canvasser + "Auto-generated method to select all records from the `canvassers` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-canvasser params))) + +(defn + search-strings-district + "Auto-generated method to select all records from the `districts` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-district params))) + +(defn + search-strings-dwelling + "Auto-generated method to select all records from the `dwellings` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-dwelling params))) + +(defn + search-strings-elector + "Auto-generated method to select all records from the `electors` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-elector params))) + +(defn + search-strings-followupaction + "Auto-generated method to select all records from the `followupactions` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-followupaction params))) + +(defn + search-strings-followupmethod + "Auto-generated method to select all records from the `followupmethods` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-followupmethod params))) + +(defn + search-strings-followuprequest + "Auto-generated method to select all records from the `followuprequests` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-followuprequest params))) + +(defn + search-strings-gender + "Auto-generated method to select all records from the `genders` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-gender params))) + +(defn + search-strings-intention + "Auto-generated method to select all records from the `intentions` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-intention params))) + +(defn + search-strings-issue + "Auto-generated method to select all records from the `issues` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-issue params))) + +(defn + search-strings-issueexpertise + "Auto-generated method to select all records from the `issueexpertise` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-issueexpertise params))) + +(defn + search-strings-option + "Auto-generated method to select all records from the `options` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-option params))) + +(defn + search-strings-role + "Auto-generated method to select all records from the `roles` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-role params))) + +(defn + search-strings-rolemembership + "Auto-generated method to select all records from the `rolememberships` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-rolemembership params))) + +(defn + search-strings-team + "Auto-generated method to select all records from the `teams` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-team params))) + +(defn + search-strings-teammembership + "Auto-generated method to select all records from the `teammemberships` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-teammembership params))) + +(defn + search-strings-teamorganisership + "Auto-generated method to select all records from the `teamorganiserships` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-teamorganisership params))) + +(defn + search-strings-visit + "Auto-generated method to select all records from the `visits` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." + [{:keys [params]}] + (do (db/search-strings-visit params))) + +(defn + update-address! + "Auto-generated method to update one record in the `addresses` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-address! 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`: `(nil)`." + [{: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`: `(nil)`." + [{:keys [params]}] + (do (db/update-district! params)) + (response/found "/")) + +(defn + update-dwelling! + "Auto-generated method to update one record in the `dwellings` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-dwelling! 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`: `(nil)`." + [{: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`: `(nil)`." + [{: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`: `(nil)`." + [{:keys [params]}] + (do (db/update-followuprequest! params)) + (response/found "/")) + +(defn + update-intention! + "Auto-generated method to update one record in the `intentions` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-intention! 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`: `(nil)`." + [{:keys [params]}] + (do (db/update-issue! params)) + (response/found "/")) + +(defn + update-issueexpertise! + "Auto-generated method to update one record in the `issueexpertise` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-issueexpertise! params)) + (response/found "/")) + +(defn + update-role! + "Auto-generated method to update one record in the `roles` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-role! params)) + (response/found "/")) + +(defn + update-rolemembership! + "Auto-generated method to update one record in the `rolememberships` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-rolemembership! params)) + (response/found "/")) + +(defn + update-team! + "Auto-generated method to update one record in the `teams` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-team! params)) + (response/found "/")) + +(defn + update-teammembership! + "Auto-generated method to update one record in the `teammemberships` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-teammembership! params)) + (response/found "/")) + +(defn + update-teamorganisership! + "Auto-generated method to update one record in the `teamorganiserships` table. Expects the following key(s) to be present in `params`: `(nil)`." + [{:keys [params]}] + (do (db/update-teamorganisership! 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`: `(nil)`." + [{:keys [params]}] + (do (db/update-visit! params)) + (response/found "/")) + diff --git a/src/clj/youyesyet/routes/auto_json_routes.clj b/src/clj/youyesyet/routes/auto_json_routes.clj deleted file mode 100644 index 64efb8f..0000000 --- a/src/clj/youyesyet/routes/auto_json_routes.clj +++ /dev/null @@ -1,1035 +0,0 @@ -(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-address - 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-address - delete-authority - delete-canvasser - delete-district - delete-elector - delete-followupaction - delete-followupmethod - delete-followuprequest - delete-issue - delete-option - delete-visit - get-address - 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-address - list-canvassers-by-authoritie - list-canvassers-by-elector - list-districts - list-electors - list-electors-by-address - 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-address - list-visits-by-canvasser - update-address - update-canvasser - update-district - update-elector - update-followupaction - update-followuprequest - update-issue - update-visit) - - -(defroutes - auto-rest-routes - (POST "/json/auto/create-address" request (create-address 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-address" request (delete-address 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-address" request (get-address 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-address" - request - (list-canvassers-by-address 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-address" - request - (list-electors-by-address 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-address" - request - (list-visits-by-address request)) - (GET - "/json/auto/list-visits-by-canvasser" - request - (list-visits-by-canvasser request)) - (POST "/json/auto/update-address" request (update-address 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-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-address! 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-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-address! 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-address - "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-address 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-address - [{:keys [params]}] - (do (db/list-canvassers-by-address 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-address - [{:keys [params]}] - (do (db/list-electors-by-address 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-address - [{:keys [params]}] - (do (db/list-visits-by-address params))) - - -(defn - list-visits-by-canvasser - [{:keys [params]}] - (do (db/list-visits-by-canvasser params))) - - -(defn - 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-address! 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/manual.clj b/src/clj/youyesyet/routes/manual.clj index 1da2e3e..bf3cf46 100644 --- a/src/clj/youyesyet/routes/manual.clj +++ b/src/clj/youyesyet/routes/manual.clj @@ -1,5 +1,5 @@ (ns - youyesyet.routes.auto + youyesyet.routes.manual "Manual overrides for auto-generated routes" (:require [noir.response :as nresponse] diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml index de657b5..52a6aef 100644 --- a/youyesyet.adl.xml +++ b/youyesyet.adl.xml @@ -323,6 +323,9 @@ + + +
diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 8fb571f..62a75e9 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -414,8 +414,11 @@ + + + -
+
{% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -220,49 +220,49 @@ Followupmethod {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html index 3fbf3ab..30af8fe 100644 --- a/resources/templates/auto/form-addresses-Address.html +++ b/resources/templates/auto/form-addresses-Address.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -248,49 +248,49 @@ To delete this addresses record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-canvassers-Canvasser.html b/resources/templates/auto/form-canvassers-Canvasser.html index 8a975b9..2df601e 100644 --- a/resources/templates/auto/form-canvassers-Canvasser.html +++ b/resources/templates/auto/form-canvassers-Canvasser.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -343,49 +343,49 @@ To delete this canvassers record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-districts-District.html b/resources/templates/auto/form-districts-District.html index 6755daa..108288b 100644 --- a/resources/templates/auto/form-districts-District.html +++ b/resources/templates/auto/form-districts-District.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -98,49 +98,49 @@ To delete this districts record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-dwellings-Dwelling.html b/resources/templates/auto/form-dwellings-Dwelling.html index ab82225..15462c1 100644 --- a/resources/templates/auto/form-dwellings-Dwelling.html +++ b/resources/templates/auto/form-dwellings-Dwelling.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -136,49 +136,49 @@ To delete this dwellings record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-electors-Elector.html b/resources/templates/auto/form-electors-Elector.html index 099cd53..c696650 100644 --- a/resources/templates/auto/form-electors-Elector.html +++ b/resources/templates/auto/form-electors-Elector.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -228,49 +228,49 @@ To delete this electors record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-followupactions-Followupaction.html b/resources/templates/auto/form-followupactions-Followupaction.html index a82cce8..b7b3e1d 100644 --- a/resources/templates/auto/form-followupactions-Followupaction.html +++ b/resources/templates/auto/form-followupactions-Followupaction.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -230,49 +230,49 @@ To delete this followupactions record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-issues-Issue.html b/resources/templates/auto/form-issues-Issue.html index 1a33953..c496dcd 100644 --- a/resources/templates/auto/form-issues-Issue.html +++ b/resources/templates/auto/form-issues-Issue.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -139,49 +139,49 @@ To delete this issues record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-roles-Role.html b/resources/templates/auto/form-roles-Role.html index 1d20938..2215d29 100644 --- a/resources/templates/auto/form-roles-Role.html +++ b/resources/templates/auto/form-roles-Role.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -117,49 +117,49 @@ To delete this roles record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-teams-Team.html b/resources/templates/auto/form-teams-Team.html index e08105f..17a7abf 100644 --- a/resources/templates/auto/form-teams-Team.html +++ b/resources/templates/auto/form-teams-Team.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -211,49 +211,49 @@ To delete this teams record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html index 2c1ea30..eea048a 100644 --- a/resources/templates/auto/form-visits-Visit.html +++ b/resources/templates/auto/form-visits-Visit.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -174,49 +174,49 @@ To delete this visits record {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html index 70aa63e..67f54be 100644 --- a/resources/templates/auto/list-addresses-Addresses.html +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -124,49 +124,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html index 98c8b4a..3208f2c 100644 --- a/resources/templates/auto/list-canvassers-Canvassers.html +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -142,49 +142,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html index 3349092..f7ba9a6 100644 --- a/resources/templates/auto/list-districts-Districts.html +++ b/resources/templates/auto/list-districts-Districts.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -79,49 +79,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html index 7f39626..308fa23 100644 --- a/resources/templates/auto/list-dwellings-Dwellings.html +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -88,49 +88,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html index da7d082..0a512bf 100644 --- a/resources/templates/auto/list-electors-Electors.html +++ b/resources/templates/auto/list-electors-Electors.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -115,49 +115,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html index 6c587e6..bd336e9 100644 --- a/resources/templates/auto/list-followupactions-Followupactions.html +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -115,49 +115,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html index d1c9e43..3eb2726 100644 --- a/resources/templates/auto/list-issues-Issues.html +++ b/resources/templates/auto/list-issues-Issues.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -88,49 +88,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html index 720443f..6fe67e3 100644 --- a/resources/templates/auto/list-roles-Roles.html +++ b/resources/templates/auto/list-roles-Roles.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -79,49 +79,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html index d3b6c19..c456013 100644 --- a/resources/templates/auto/list-teams-Teams.html +++ b/resources/templates/auto/list-teams-Teams.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -106,49 +106,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html index fd26cb1..0eed2f0 100644 --- a/resources/templates/auto/list-visits-Visits.html +++ b/resources/templates/auto/list-visits-Visits.html @@ -1,22 +1,22 @@ {% extends "base.html" %} - {% block head %} - + - + - + - + - + - + - + {{site-title}}: {{title}} -</html:title> + {% endblock %} @@ -97,49 +97,49 @@ View {% endblock %} {% block foot %} - - - - + {% endblock %} \ No newline at end of file diff --git a/resources/templates/base-authenticated copy.html b/resources/templates/base-unauthenticated.html similarity index 66% rename from resources/templates/base-authenticated copy.html rename to resources/templates/base-unauthenticated.html index 22af08a..cfb278f 100644 --- a/resources/templates/base-authenticated copy.html +++ b/resources/templates/base-unauthenticated.html @@ -1,30 +1,22 @@ - + - - + + - - {{title}} + {% block title %}{% endblock %}{{title}} - {% block whole-page %} - {% block top %}
@@ -32,21 +24,17 @@ {{title}}
- {% endblock %} +
+
{% block content %} {% endblock %}
- - {% block foot %}
@@ -63,17 +51,9 @@
- {% endblock %} - {% endblock %} - {% block extra-script %} - {% endblock %} - - - - diff --git a/resources/templates/base.html b/resources/templates/base.html index cfb278f..b490cd1 100644 --- a/resources/templates/base.html +++ b/resources/templates/base.html @@ -1,22 +1,30 @@ - + - - + + + - {% block title %}{% endblock %}{{title}} + {{title}} + {% block whole-page %} + {% block top %}
@@ -24,17 +32,22 @@ {{title}}
- + {% endblock %}
+
{% block content %} {% endblock %}
+
+ + {% block foot %}
@@ -51,9 +64,17 @@
+ {% endblock %} + {% endblock %} + + + + diff --git a/resources/templates/login.html b/resources/templates/login.html index 5ee3706..eebe793 100644 --- a/resources/templates/login.html +++ b/resources/templates/login.html @@ -1,7 +1,8 @@ {% extends "base-unauthenticated.html" %} {% block content %}

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

diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index 40f95cf..5c3d962 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -13,6 +13,7 @@ [mount.core :as mount] [youyesyet.middleware :as middleware] [clojure.tools.logging :as log] + [youyesyet.db.core :as db] [youyesyet.config :refer [env]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -42,6 +43,8 @@ :start ((or (:init defaults) identity)) :stop ((or (:stop defaults) identity))) +;;(mount/start db/*db*) + (defn init "init will be called once when app is deployed as a servlet on diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index eeb3722..af151ba 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -1,8 +1,10 @@ (ns ^{:doc "Routes/pages available to unauthenticated users." :author "Simon Brooke"} youyesyet.routes.home (:require [clojure.walk :refer [keywordize-keys]] + [clojure.java.io :refer [input-stream]] [noir.response :as nresponse] [noir.util.route :as route] + [ring.util.http-response :refer [content-type ok]] [youyesyet.layout :as layout] [youyesyet.db.core :as db-core] [compojure.core :refer [defroutes GET POST]] @@ -95,6 +97,10 @@ (defroutes home-routes (GET "/" [] (home-page)) + (GET "/js/:file" [file] + (-> (input-stream (str "resources/public/js/" file)) + ok + (content-type "text/javascript;charset=UTF-8"))) (GET "/home" [] (home-page)) (GET "/about" [] (about-page)) (GET "/roles" request (route/restricted (roles-page request))) @@ -104,6 +110,6 @@ (GET "/auth" request (login-page request)) (POST "/auth" request (login-page request)) (GET "/notyet" [] (layout/render "notyet.html" - {:title "Can we persuade you?"})) + {:title "Can we persuade you?"})) (GET "/supporter" [] (layout/render "supporter.html" {:title "Have you signed up as a canvasser yet?"}))) diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml index 52a6aef..4c9ae8c 100644 --- a/youyesyet.adl.xml +++ b/youyesyet.adl.xml @@ -14,32 +14,32 @@ - - - - - - - {{site-title}}: {{title}} + + + + + + + {{site-title}}: {{title}} - - - - + - - - - - - - {{site-title}}: {{title}} + + + + + + + {{site-title}}: {{title}} - - - - + From a9fd6617d237e601cd012c69bee49da2872fc14a Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Thu, 14 Jun 2018 00:14:06 +0100 Subject: [PATCH 15/51] A number of small changes to the ADL specification --- youyesyet.adl.xml | 170 +++++++++++++++++++------- youyesyet.canonical.adl.xml | 233 +++++++++++++++++++----------------- 2 files changed, 253 insertions(+), 150 deletions(-) diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml index 52a6aef..e8b8642 100644 --- a/youyesyet.adl.xml +++ b/youyesyet.adl.xml @@ -56,7 +56,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 @@ -106,6 +106,13 @@ + + + + + + + All genders which may be assigned to electors. @@ -116,6 +123,13 @@ + + + + + + + All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. @@ -124,12 +138,19 @@ - + - + + + + + + + + Addresses of all buildings which contain dwellings. @@ -161,6 +182,13 @@ + + + + + + + All visits made by canvassers to dwellings in which opinions were recorded. @@ -170,20 +198,27 @@ + entity="addresses" farkey="id" distinct="user"> - + + + + + + + + Authorities which may authenticate canvassers to the system. @@ -194,6 +229,13 @@ + + + + + + + Issues believed to be of interest to electors, about which they may have questions. @@ -210,6 +252,13 @@ + + + + + + + Link table. @@ -227,6 +276,13 @@ + + + + + + + Primary users of the system: those actually interviewing electors. @@ -261,7 +317,7 @@ - + @@ -275,6 +331,13 @@ + + + + + + + Requests for a followup with an issue expert @@ -284,15 +347,15 @@ + farkey="id" distinct="user"> + farkey="id" distinct="user"> + farkey="id" distinct="user"> - - - 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. @@ -328,6 +387,13 @@ + + + + + + + @@ -360,6 +426,13 @@ + + + + + + + Electoral districts @@ -375,17 +448,13 @@ - - - Link table - - - - - - + + + + + + + Actions taken on followup requests. @@ -418,6 +487,13 @@ + + + + + + + Link table @@ -433,6 +509,13 @@ entity="followupmethods" farkey="id"> + + + + + + + Options in the election or referendum being canvassed on @@ -443,17 +526,13 @@ - - - Link table - - - - - - + + + + + + + @@ -463,5 +542,12 @@ + + + + + + + diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 62a75e9..3333478 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -64,7 +64,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 @@ -115,6 +115,13 @@ + + + + + + +
All genders which may be assigned to electors. @@ -123,13 +130,13 @@
- - - - - - - + + + + + + + @@ -151,12 +158,19 @@
- + - +
+ + + + + + +
- - Link table - - - - - Auto-generated abstract primary key - - - - - - - - - - - - - - - + + + + + + - - Link table - - - - - Auto-generated abstract primary key - - - - - - - - - - - - - - - + + + + + + - - Link table - - - - - Auto-generated abstract primary key - - - - - - - - - - - - - - - + + + + + + {% block head %} @@ -123,16 +123,6 @@ Requests for a followup with an issue expert

- -Rolemembership - -
-
-

-Link table -

-
-
Role @@ -156,17 +146,7 @@ District

-Electoral districts -

-
-
- -Teamorganisership - -
-
-

-Link table +Electoral districts: TODO: Shape (polygon) information will need to be added, for use in maps.

@@ -180,16 +160,6 @@ Actions taken on followup requests.

- -Issueexpertise - -
-
-

-Link table -

-
-
Option @@ -200,16 +170,6 @@ Options in the election or referendum being canvassed on

- -Teammembership - -
-
-

-Link table -

-
-
Followupmethod diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html index 30af8fe..1a69022 100644 --- a/resources/templates/auto/form-addresses-Address.html +++ b/resources/templates/auto/form-addresses-Address.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-authorities-Authority.html b/resources/templates/auto/form-authorities-Authority.html new file mode 100644 index 0000000..b4e7604 --- /dev/null +++ b/resources/templates/auto/form-authorities-Authority.html @@ -0,0 +1,131 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+
+{% csrf-field %} +

+ +{% ifwritable authorities id %} + +{% else %} +{% ifreadable authorities id%} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable authorities %} + +{% else %} +{% ifreadable authorities %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable authorities %} + +{% else %} +{% ifreadable authorities %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-canvassers-Canvasser.html b/resources/templates/auto/form-canvassers-Canvasser.html index 2df601e..59115bc 100644 --- a/resources/templates/auto/form-canvassers-Canvasser.html +++ b/resources/templates/auto/form-canvassers-Canvasser.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -75,7 +75,7 @@ elector_id
{% else %} @@ -216,7 +216,7 @@ elector_id
{% else %} @@ -312,10 +312,9 @@ authorised roles {% ifwritable canvassers roles %} -
- - +{% for record in roles %}{% endfor %}
{% else %} diff --git a/resources/templates/auto/form-districts-District.html b/resources/templates/auto/form-districts-District.html index 108288b..03bd2cb 100644 --- a/resources/templates/auto/form-districts-District.html +++ b/resources/templates/auto/form-districts-District.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-dwellings-Dwelling.html b/resources/templates/auto/form-dwellings-Dwelling.html index 15462c1..1e85f97 100644 --- a/resources/templates/auto/form-dwellings-Dwelling.html +++ b/resources/templates/auto/form-dwellings-Dwelling.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -44,10 +44,10 @@ id address_id {% ifwritable dwellings address_id %} -
+
{% else %} @@ -91,10 +91,10 @@ id address_id {% ifwritable dwellings address_id %} -
+
{% else %} diff --git a/resources/templates/auto/form-electors-Elector.html b/resources/templates/auto/form-electors-Elector.html index c696650..4f21ae4 100644 --- a/resources/templates/auto/form-electors-Elector.html +++ b/resources/templates/auto/form-electors-Elector.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -61,7 +61,7 @@ dwelling_id
{% else %} @@ -154,7 +154,7 @@ dwelling_id
{% else %} diff --git a/resources/templates/auto/form-followupactions-Followupaction.html b/resources/templates/auto/form-followupactions-Followupaction.html index b7b3e1d..371099f 100644 --- a/resources/templates/auto/form-followupactions-Followupaction.html +++ b/resources/templates/auto/form-followupactions-Followupaction.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -47,7 +47,7 @@ request_id
{% else %} @@ -141,7 +141,7 @@ request_id
{% else %} diff --git a/resources/templates/auto/form-followupmethods-Followupmethod.html b/resources/templates/auto/form-followupmethods-Followupmethod.html new file mode 100644 index 0000000..f233b36 --- /dev/null +++ b/resources/templates/auto/form-followupmethods-Followupmethod.html @@ -0,0 +1,131 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+
+{% csrf-field %} +

+ +{% ifwritable followupmethods id %} + +{% else %} +{% ifreadable followupmethods id%} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupmethods %} + +{% else %} +{% ifreadable followupmethods %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followupmethods %} + +{% else %} +{% ifreadable followupmethods %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-followuprequests-Followuprequest.html b/resources/templates/auto/form-followuprequests-Followuprequest.html new file mode 100644 index 0000000..6940865 --- /dev/null +++ b/resources/templates/auto/form-followuprequests-Followuprequest.html @@ -0,0 +1,266 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable followuprequests %} + +{% else %} +{% ifreadable followuprequests %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests elector_id %} +

+ + +
+{% else %} +{% ifreadable followuprequests elector_id%} + +{{record.elector_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests visit_id %} +

+ + +
+{% else %} +{% ifreadable followuprequests visit_id%} + +{{record.visit_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests issue_id %} +

+ +
+{% else %} +{% ifreadable followuprequests issue_id%} + +{{record.issue_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests method_id %} +

+ +
+{% else %} +{% ifreadable followuprequests method_id%} + +{{record.method_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests %} + +{% else %} +{% ifreadable followuprequests %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests elector_id %} +

+ + +
+{% else %} +{% ifreadable followuprequests elector_id%} + +{{record.elector_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests visit_id %} +

+ + +
+{% else %} +{% ifreadable followuprequests visit_id%} + +{{record.visit_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests issue_id %} +

+ +
+{% else %} +{% ifreadable followuprequests issue_id%} + +{{record.issue_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable followuprequests method_id %} +

+ +
+{% else %} +{% ifreadable followuprequests method_id%} + +{{record.method_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-genders-Gender.html b/resources/templates/auto/form-genders-Gender.html new file mode 100644 index 0000000..6ad02c0 --- /dev/null +++ b/resources/templates/auto/form-genders-Gender.html @@ -0,0 +1,131 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+
+{% csrf-field %} +

+ +{% ifwritable genders id %} + +{% else %} +{% ifreadable genders id%} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable genders %} + +{% else %} +{% ifreadable genders %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable genders %} + +{% else %} +{% ifreadable genders %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-intentions-Intention.html b/resources/templates/auto/form-intentions-Intention.html new file mode 100644 index 0000000..0d6a71d --- /dev/null +++ b/resources/templates/auto/form-intentions-Intention.html @@ -0,0 +1,202 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+
+{% csrf-field %} + +

+ +{% ifwritable intentions visit_id %} +

+ + +
+{% else %} +{% ifreadable intentions visit_id%} + +{{record.visit_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable intentions elector_id %} +

+ + +
+{% else %} +{% ifreadable intentions elector_id%} + +{{record.elector_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable intentions option_id %} +

+ +
+{% else %} +{% ifreadable intentions option_id%} + +{{record.option_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable intentions visit_id %} +

+ + +
+{% else %} +{% ifreadable intentions visit_id%} + +{{record.visit_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable intentions elector_id %} +

+ + +
+{% else %} +{% ifreadable intentions elector_id%} + +{{record.elector_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable intentions option_id %} +

+ +
+{% else %} +{% ifreadable intentions option_id%} + +{{record.option_id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-issues-Issue.html b/resources/templates/auto/form-issues-Issue.html index c496dcd..141d785 100644 --- a/resources/templates/auto/form-issues-Issue.html +++ b/resources/templates/auto/form-issues-Issue.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-options-Option.html b/resources/templates/auto/form-options-Option.html new file mode 100644 index 0000000..9837b1e --- /dev/null +++ b/resources/templates/auto/form-options-Option.html @@ -0,0 +1,131 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+
+{% csrf-field %} +

+ +{% ifwritable options id %} + +{% else %} +{% ifreadable options id%} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable options %} + +{% else %} +{% ifreadable options %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ +{% ifwritable options %} + +{% else %} +{% ifreadable options %} + +{{record.id}} + +{% endifreadable %} +{% endifwritable %} +

+

+ + +

+

+ + +

+
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/form-roles-Role.html b/resources/templates/auto/form-roles-Role.html index 2215d29..fc4550f 100644 --- a/resources/templates/auto/form-roles-Role.html +++ b/resources/templates/auto/form-roles-Role.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-teams-Team.html b/resources/templates/auto/form-teams-Team.html index 17a7abf..964e783 100644 --- a/resources/templates/auto/form-teams-Team.html +++ b/resources/templates/auto/form-teams-Team.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -181,6 +181,25 @@ members {% endifwritable %}

+ +{% ifwritable teams organisers %} +

+ + +
+{% else %} +{% ifreadable teams organisers%} + +{{record.organisers}} + +{% endifreadable %} +{% endifwritable %} +

+

diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html index eea048a..80b3d61 100644 --- a/resources/templates/auto/form-visits-Visit.html +++ b/resources/templates/auto/form-visits-Visit.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html index 67f54be..77d4307 100644 --- a/resources/templates/auto/list-addresses-Addresses.html +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-authorities-Authorities.html b/resources/templates/auto/list-authorities-Authorities.html new file mode 100644 index 0000000..161786d --- /dev/null +++ b/resources/templates/auto/list-authorities-Authorities.html @@ -0,0 +1,118 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +

+ + + + + + + + + + + + +{% for record in %records% %} + + + + +{% endfor %} + + + + + +
+id +
+ + + +
+{{ record.id }} + + +View + +
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html index 3208f2c..b9e5b76 100644 --- a/resources/templates/auto/list-canvassers-Canvassers.html +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html index f7ba9a6..89c4394 100644 --- a/resources/templates/auto/list-districts-Districts.html +++ b/resources/templates/auto/list-districts-Districts.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html index 308fa23..f5c31e5 100644 --- a/resources/templates/auto/list-dwellings-Dwellings.html +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html index 0a512bf..95cf89d 100644 --- a/resources/templates/auto/list-electors-Electors.html +++ b/resources/templates/auto/list-electors-Electors.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html index bd336e9..e3e271f 100644 --- a/resources/templates/auto/list-followupactions-Followupactions.html +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followupmethods-Followupmethods.html b/resources/templates/auto/list-followupmethods-Followupmethods.html new file mode 100644 index 0000000..f717b8c --- /dev/null +++ b/resources/templates/auto/list-followupmethods-Followupmethods.html @@ -0,0 +1,118 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+ + + + + + + + + + + + +{% for record in %records% %} + + + + +{% endfor %} + + + + + +
+id +
+ + + +
+{{ record.id }} + + +View + +
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-followuprequests-Followuprequests.html b/resources/templates/auto/list-followuprequests-Followuprequests.html new file mode 100644 index 0000000..8ddb65e --- /dev/null +++ b/resources/templates/auto/list-followuprequests-Followuprequests.html @@ -0,0 +1,154 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + + + +{% endfor %} + + + + + +
+id + +elector_id + +visit_id + +issue_id + +method_id +
+ + + + + + + + + + + +
+{{ record.id }} + +{{ record.elector_id }} + +{{ record.visit_id }} + +{{ record.issue_id }} + +{{ record.method_id }} + + +View + +
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-genders-Genders.html b/resources/templates/auto/list-genders-Genders.html new file mode 100644 index 0000000..7a4778a --- /dev/null +++ b/resources/templates/auto/list-genders-Genders.html @@ -0,0 +1,118 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+ + + + + + + + + + + + +{% for record in %records% %} + + + + +{% endfor %} + + + + + +
+id +
+ + + +
+{{ record.id }} + + +View + +
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-intentions-Intentions.html b/resources/templates/auto/list-intentions-Intentions.html new file mode 100644 index 0000000..45ecdec --- /dev/null +++ b/resources/templates/auto/list-intentions-Intentions.html @@ -0,0 +1,136 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+ + + + + + + + + + + + + + + + +{% for record in %records% %} + + + + + + +{% endfor %} + + + + + +
+visit_id + +elector_id + +option_id +
+ + + + + + + +
+{{ record.visit_id }} + +{{ record.elector_id }} + +{{ record.option_id }} + + +View + +
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html index 3eb2726..b5f2283 100644 --- a/resources/templates/auto/list-issues-Issues.html +++ b/resources/templates/auto/list-issues-Issues.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-options-Options.html b/resources/templates/auto/list-options-Options.html new file mode 100644 index 0000000..6ad9f1f --- /dev/null +++ b/resources/templates/auto/list-options-Options.html @@ -0,0 +1,118 @@ +{% extends "base.html" %} + +{% block head %} + + + + + + + + + + + + + +{{site-title}}: {{title}} + + +{% endblock %} + +{% block content %} +
+ + + + + + + + + + + + +{% for record in %records% %} + + + + +{% endfor %} + + + + + +
+id +
+ + + +
+{{ record.id }} + + +View + +
+
+ +{% endblock %} +{% block foot %} + + +{% endblock %} \ No newline at end of file diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html index 6fe67e3..62b00d7 100644 --- a/resources/templates/auto/list-roles-Roles.html +++ b/resources/templates/auto/list-roles-Roles.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html index c456013..a18045b 100644 --- a/resources/templates/auto/list-teams-Teams.html +++ b/resources/templates/auto/list-teams-Teams.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html index 0eed2f0..1807168 100644 --- a/resources/templates/auto/list-visits-Visits.html +++ b/resources/templates/auto/list-visits-Visits.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/src/clj/youyesyet/db/core.clj b/src/clj/youyesyet/db/core.clj index 6941257..49bbe06 100644 --- a/src/clj/youyesyet/db/core.clj +++ b/src/clj/youyesyet/db/core.clj @@ -5,8 +5,8 @@ [cheshire.core :refer [generate-string parse-string]] [clojure.java.jdbc :as jdbc] [conman.core :as conman] - [youyesyet.config :refer [env]] - [mount.core :refer [defstate]]) + [mount.core :refer [defstate]] + [youyesyet.config :refer [env]]) (:import org.postgresql.util.PGobject java.sql.Array clojure.lang.IPersistentMap diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index 5c3d962..909dea4 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -1,20 +1,19 @@ (ns ^{:doc "Handlers for starting and stopping the webapp." :author "Simon Brooke"} youyesyet.handler - (:require [compojure.core :refer [routes wrap-routes]] + (:require [clojure.tools.logging :as log] + [compojure.core :refer [routes wrap-routes]] + [compojure.route :as route] + [mount.core :as mount] + [youyesyet.config :refer [env]] [youyesyet.layout :refer [error-page]] + [youyesyet.middleware :as middleware] [youyesyet.routes.authenticated :refer [authenticated-routes]] [youyesyet.routes.home :refer [home-routes]] [youyesyet.routes.oauth :refer [oauth-routes]] [youyesyet.routes.auto-json :refer [auto-rest-routes]] [youyesyet.routes.auto :refer [auto-selmer-routes]] - [compojure.route :as route] - [youyesyet.env :refer [defaults]] - [mount.core :as mount] - [youyesyet.middleware :as middleware] - [clojure.tools.logging :as log] - [youyesyet.db.core :as db] - [youyesyet.config :refer [env]])) + [youyesyet.env :refer [defaults]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; @@ -82,5 +81,5 @@ :title "page not found"}))))) -(def app ;; #'app-routes) - (middleware/wrap-base #'app-routes)) +(def app #'app-routes) +;; (middleware/wrap-base #'app-routes)) diff --git a/src/clj/youyesyet/routes/auto.clj b/src/clj/youyesyet/routes/auto.clj index 3781e1d..6fc5733 100644 --- a/src/clj/youyesyet/routes/auto.clj +++ b/src/clj/youyesyet/routes/auto.clj @@ -1,6 +1,6 @@ (ns youyesyet.routes.auto - "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180611T180322.460Z" + "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180614T175643.413Z" (:require [noir.response :as nresponse] [noir.util.route :as route] diff --git a/src/clj/youyesyet/routes/auto_json.clj b/src/clj/youyesyet/routes/auto_json.clj index 9426c90..6a5a5ef 100644 --- a/src/clj/youyesyet/routes/auto_json.clj +++ b/src/clj/youyesyet/routes/auto_json.clj @@ -1,6 +1,6 @@ (ns youyesyet.routes.auto-json - "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180611T180321.339Z" + "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180614T175642.456Z" (:require [noir.response :as nresponse] [noir.util.route :as route] @@ -24,13 +24,9 @@ create-gender! create-intention! create-issue! - create-issueexpertise! create-option! create-role! - create-rolemembership! create-team! - create-teammembership! - create-teamorganisership! create-visit! delete-address! delete-authority! @@ -44,13 +40,9 @@ delete-gender! delete-intention! delete-issue! - delete-issueexpertise! delete-option! delete-role! - delete-rolemembership! delete-team! - delete-teammembership! - delete-teamorganisership! delete-visit! get-address get-authority @@ -65,13 +57,10 @@ get-gender get-intention get-issue - get-issueexpertise get-option get-role - get-rolemembership + get-role-by-name get-team - get-teammembership - get-teamorganisership get-visit list-addresses list-addresses-by-district @@ -101,23 +90,10 @@ list-intentions-by-elector list-intentions-by-option list-intentions-by-visit - list-issueexpertise - list-issueexpertise-by-canvasser - list-issueexpertise-by-followupmethod - list-issueexpertise-by-issue list-issues list-options - list-rolememberships - list-rolememberships-by-canvasser - list-rolememberships-by-role list-roles list-roles-by-canvasser - list-teammemberships - list-teammemberships-by-canvasser - list-teammemberships-by-team - list-teamorganiserships - list-teamorganiserships-by-canvasser - list-teamorganiserships-by-team list-teams list-teams-by-canvasser list-teams-by-district @@ -136,13 +112,9 @@ search-strings-gender search-strings-intention search-strings-issue - search-strings-issueexpertise search-strings-option search-strings-role - search-strings-rolemembership search-strings-team - search-strings-teammembership - search-strings-teamorganisership search-strings-visit update-address! update-canvasser! @@ -153,12 +125,8 @@ update-followuprequest! update-intention! update-issue! - update-issueexpertise! update-role! - update-rolemembership! update-team! - update-teammembership! - update-teamorganisership! update-visit!) @@ -212,10 +180,6 @@ "/json/auto/create-issue" request (route/restricted (create-issue! request))) - (POST - "/json/auto/create-issueexpertise" - request - (route/restricted (create-issueexpertise! request))) (POST "/json/auto/create-option" request @@ -224,22 +188,10 @@ "/json/auto/create-role" request (route/restricted (create-role! request))) - (POST - "/json/auto/create-rolemembership" - request - (route/restricted (create-rolemembership! request))) (POST "/json/auto/create-team" request (route/restricted (create-team! request))) - (POST - "/json/auto/create-teammembership" - request - (route/restricted (create-teammembership! request))) - (POST - "/json/auto/create-teamorganisership" - request - (route/restricted (create-teamorganisership! request))) (POST "/json/auto/create-visit" request @@ -292,10 +244,6 @@ "/json/auto/delete-issue" request (route/restricted (delete-issue! request))) - (POST - "/json/auto/delete-issueexpertise" - request - (route/restricted (delete-issueexpertise! request))) (POST "/json/auto/delete-option" request @@ -304,22 +252,10 @@ "/json/auto/delete-role" request (route/restricted (delete-role! request))) - (POST - "/json/auto/delete-rolemembership" - request - (route/restricted (delete-rolemembership! request))) (POST "/json/auto/delete-team" request (route/restricted (delete-team! request))) - (POST - "/json/auto/delete-teammembership" - request - (route/restricted (delete-teammembership! request))) - (POST - "/json/auto/delete-teamorganisership" - request - (route/restricted (delete-teamorganisership! request))) (POST "/json/auto/delete-visit" request @@ -376,10 +312,6 @@ "/json/auto/get-issue" request (route/restricted (get-issue request))) - (POST - "/json/auto/get-issueexpertise" - request - (route/restricted (get-issueexpertise request))) (POST "/json/auto/get-option" request @@ -389,21 +321,13 @@ request (route/restricted (get-role request))) (POST - "/json/auto/get-rolemembership" + "/json/auto/get-role-by-name" request - (route/restricted (get-rolemembership request))) + (route/restricted (get-role-by-name request))) (POST "/json/auto/get-team" request (route/restricted (get-team request))) - (POST - "/json/auto/get-teammembership" - request - (route/restricted (get-teammembership request))) - (POST - "/json/auto/get-teamorganisership" - request - (route/restricted (get-teamorganisership request))) (POST "/json/auto/get-visit" request @@ -520,22 +444,6 @@ "/json/auto/list-intentions-by-visit" request (route/restricted (list-intentions-by-visit request))) - (GET - "/json/auto/list-issueexpertise" - request - (route/restricted (list-issueexpertise request))) - (GET - "/json/auto/list-issueexpertise-by-canvasser" - request - (route/restricted (list-issueexpertise-by-canvasser request))) - (GET - "/json/auto/list-issueexpertise-by-followupmethod" - request - (route/restricted (list-issueexpertise-by-followupmethod request))) - (GET - "/json/auto/list-issueexpertise-by-issue" - request - (route/restricted (list-issueexpertise-by-issue request))) (GET "/json/auto/list-issues" request @@ -544,18 +452,6 @@ "/json/auto/list-options" request (route/restricted (list-options request))) - (GET - "/json/auto/list-rolememberships" - request - (route/restricted (list-rolememberships request))) - (GET - "/json/auto/list-rolememberships-by-canvasser" - request - (route/restricted (list-rolememberships-by-canvasser request))) - (GET - "/json/auto/list-rolememberships-by-role" - request - (route/restricted (list-rolememberships-by-role request))) (GET "/json/auto/list-roles" request @@ -564,30 +460,6 @@ "/json/auto/list-roles-by-canvasser" request (route/restricted (list-roles-by-canvasser request))) - (GET - "/json/auto/list-teammemberships" - request - (route/restricted (list-teammemberships request))) - (GET - "/json/auto/list-teammemberships-by-canvasser" - request - (route/restricted (list-teammemberships-by-canvasser request))) - (GET - "/json/auto/list-teammemberships-by-team" - request - (route/restricted (list-teammemberships-by-team request))) - (GET - "/json/auto/list-teamorganiserships" - request - (route/restricted (list-teamorganiserships request))) - (GET - "/json/auto/list-teamorganiserships-by-canvasser" - request - (route/restricted (list-teamorganiserships-by-canvasser request))) - (GET - "/json/auto/list-teamorganiserships-by-team" - request - (route/restricted (list-teamorganiserships-by-team request))) (GET "/json/auto/list-teams" request @@ -660,10 +532,6 @@ "/json/auto/search-strings-issue" request (route/restricted (search-strings-issue request))) - (GET - "/json/auto/search-strings-issueexpertise" - request - (route/restricted (search-strings-issueexpertise request))) (GET "/json/auto/search-strings-option" request @@ -672,22 +540,10 @@ "/json/auto/search-strings-role" request (route/restricted (search-strings-role request))) - (GET - "/json/auto/search-strings-rolemembership" - request - (route/restricted (search-strings-rolemembership request))) (GET "/json/auto/search-strings-team" request (route/restricted (search-strings-team request))) - (GET - "/json/auto/search-strings-teammembership" - request - (route/restricted (search-strings-teammembership request))) - (GET - "/json/auto/search-strings-teamorganisership" - request - (route/restricted (search-strings-teamorganisership request))) (GET "/json/auto/search-strings-visit" request @@ -728,30 +584,14 @@ "/json/auto/update-issue" request (route/restricted (update-issue! request))) - (POST - "/json/auto/update-issueexpertise" - request - (route/restricted (update-issueexpertise! request))) (POST "/json/auto/update-role" request (route/restricted (update-role! request))) - (POST - "/json/auto/update-rolemembership" - request - (route/restricted (update-rolemembership! request))) (POST "/json/auto/update-team" request (route/restricted (update-team! request))) - (POST - "/json/auto/update-teammembership" - request - (route/restricted (update-teammembership! request))) - (POST - "/json/auto/update-teamorganisership" - request - (route/restricted (update-teamorganisership! request))) (POST "/json/auto/update-visit" request @@ -830,12 +670,6 @@ [{: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`: `nil`. 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`: `nil`. Returns a map containing the keys `nil` identifying the record created." @@ -848,30 +682,12 @@ [{: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`: `nil`. Returns a map containing the keys `nil` identifying the record created." - [{:keys [params]}] - (do (db/create-rolemembership! 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`: `nil`. 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`: `nil`. 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`: `nil`. 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`: `nil`. Returns a map containing the keys `nil` identifying the record created." @@ -962,13 +778,6 @@ (do (db/delete-issue! params)) (response/found "/")) -(defn - delete-issueexpertise! - "Auto-generated method to delete one record from the `issueexpertise` table. Expects the following key(s) to be present in `params`: ``." - [{:keys [params]}] - (do (db/delete-issueexpertise! 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`: ``." @@ -983,13 +792,6 @@ (do (db/delete-role! params)) (response/found "/")) -(defn - delete-rolemembership! - "Auto-generated method to delete one record from the `rolememberships` table. Expects the following key(s) to be present in `params`: ``." - [{:keys [params]}] - (do (db/delete-rolemembership! params)) - (response/found "/")) - (defn delete-team! "Auto-generated method to delete one record from the `teams` table. Expects the following key(s) to be present in `params`: ``." @@ -997,20 +799,6 @@ (do (db/delete-team! params)) (response/found "/")) -(defn - delete-teammembership! - "Auto-generated method to delete one record from the `teammemberships` table. Expects the following key(s) to be present in `params`: ``." - [{:keys [params]}] - (do (db/delete-teammembership! params)) - (response/found "/")) - -(defn - delete-teamorganisership! - "Auto-generated method to delete one record from the `teamorganiserships` table. Expects the following key(s) to be present in `params`: ``." - [{:keys [params]}] - (do (db/delete-teamorganisership! 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`: ``." @@ -1096,12 +884,6 @@ [{:keys [params]}] (do (db/get-issue params))) -(defn - get-issueexpertise - "Auto-generated method to select one record from the `issueexpertise` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." - [{:keys [params]}] - (do (db/get-issueexpertise 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`: `nil`. Returns a map containing the following keys: `(nil)`." @@ -1115,10 +897,10 @@ (do (db/get-role params))) (defn - get-rolemembership - "Auto-generated method to select one record from the `rolememberships` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." + get-role-by-name + "Auto-generated method to select one record from the `roles` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." [{:keys [params]}] - (do (db/get-rolemembership params))) + (do (db/get-role-by-name params))) (defn get-team @@ -1126,18 +908,6 @@ [{:keys [params]}] (do (db/get-team params))) -(defn - get-teammembership - "Auto-generated method to select one record from the `teammemberships` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." - [{:keys [params]}] - (do (db/get-teammembership params))) - -(defn - get-teamorganisership - "Auto-generated method to select one record from the `teamorganiserships` table. Expects the following key(s) to be present in `params`: `nil`. Returns a map containing the following keys: `(nil)`." - [{:keys [params]}] - (do (db/get-teamorganisership 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`: `nil`. Returns a map containing the following keys: `(nil)`." @@ -1295,27 +1065,6 @@ [{:keys [params]}] (do (db/list-intentions-by-visit params))) -(defn - list-issueexpertise - "Auto-generated method to select all records from the `issueexpertise` 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: `(nil)`." - [{:keys [params]}] - (do (db/list-issueexpertise params))) - -(defn - list-issueexpertise-by-canvasser - [{:keys [params]}] - (do (db/list-issueexpertise-by-canvasser params))) - -(defn - list-issueexpertise-by-followupmethod - [{:keys [params]}] - (do (db/list-issueexpertise-by-followupmethod params))) - -(defn - list-issueexpertise-by-issue - [{:keys [params]}] - (do (db/list-issueexpertise-by-issue 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: `(nil)`." @@ -1328,22 +1077,6 @@ [{:keys [params]}] (do (db/list-options params))) -(defn - list-rolememberships - "Auto-generated method to select all records from the `rolememberships` 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: `(nil)`." - [{:keys [params]}] - (do (db/list-rolememberships params))) - -(defn - list-rolememberships-by-canvasser - [{:keys [params]}] - (do (db/list-rolememberships-by-canvasser params))) - -(defn - list-rolememberships-by-role - [{:keys [params]}] - (do (db/list-rolememberships-by-role 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: `(nil)`." @@ -1355,38 +1088,6 @@ [{:keys [params]}] (do (db/list-roles-by-canvasser params))) -(defn - list-teammemberships - "Auto-generated method to select all records from the `teammemberships` 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: `(nil)`." - [{:keys [params]}] - (do (db/list-teammemberships params))) - -(defn - list-teammemberships-by-canvasser - [{:keys [params]}] - (do (db/list-teammemberships-by-canvasser params))) - -(defn - list-teammemberships-by-team - [{:keys [params]}] - (do (db/list-teammemberships-by-team params))) - -(defn - list-teamorganiserships - "Auto-generated method to select all records from the `teamorganiserships` 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: `(nil)`." - [{:keys [params]}] - (do (db/list-teamorganiserships params))) - -(defn - list-teamorganiserships-by-canvasser - [{:keys [params]}] - (do (db/list-teamorganiserships-by-canvasser params))) - -(defn - list-teamorganiserships-by-team - [{:keys [params]}] - (do (db/list-teamorganiserships-by-team 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: `(nil)`." @@ -1491,12 +1192,6 @@ [{:keys [params]}] (do (db/search-strings-issue params))) -(defn - search-strings-issueexpertise - "Auto-generated method to select all records from the `issueexpertise` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." - [{:keys [params]}] - (do (db/search-strings-issueexpertise params))) - (defn search-strings-option "Auto-generated method to select all records from the `options` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." @@ -1509,30 +1204,12 @@ [{:keys [params]}] (do (db/search-strings-role params))) -(defn - search-strings-rolemembership - "Auto-generated method to select all records from the `rolememberships` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." - [{:keys [params]}] - (do (db/search-strings-rolemembership params))) - (defn search-strings-team "Auto-generated method to select all records from the `teams` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." [{:keys [params]}] (do (db/search-strings-team params))) -(defn - search-strings-teammembership - "Auto-generated method to select all records from the `teammemberships` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." - [{:keys [params]}] - (do (db/search-strings-teammembership params))) - -(defn - search-strings-teamorganisership - "Auto-generated method to select all records from the `teamorganiserships` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." - [{:keys [params]}] - (do (db/search-strings-teamorganisership params))) - (defn search-strings-visit "Auto-generated method to select all records from the `visits` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(nil)`." @@ -1602,13 +1279,6 @@ (do (db/update-issue! params)) (response/found "/")) -(defn - update-issueexpertise! - "Auto-generated method to update one record in the `issueexpertise` table. Expects the following key(s) to be present in `params`: `(nil)`." - [{:keys [params]}] - (do (db/update-issueexpertise! params)) - (response/found "/")) - (defn update-role! "Auto-generated method to update one record in the `roles` table. Expects the following key(s) to be present in `params`: `(nil)`." @@ -1616,13 +1286,6 @@ (do (db/update-role! params)) (response/found "/")) -(defn - update-rolemembership! - "Auto-generated method to update one record in the `rolememberships` table. Expects the following key(s) to be present in `params`: `(nil)`." - [{:keys [params]}] - (do (db/update-rolemembership! params)) - (response/found "/")) - (defn update-team! "Auto-generated method to update one record in the `teams` table. Expects the following key(s) to be present in `params`: `(nil)`." @@ -1630,20 +1293,6 @@ (do (db/update-team! params)) (response/found "/")) -(defn - update-teammembership! - "Auto-generated method to update one record in the `teammemberships` table. Expects the following key(s) to be present in `params`: `(nil)`." - [{:keys [params]}] - (do (db/update-teammembership! params)) - (response/found "/")) - -(defn - update-teamorganisership! - "Auto-generated method to update one record in the `teamorganiserships` table. Expects the following key(s) to be present in `params`: `(nil)`." - [{:keys [params]}] - (do (db/update-teamorganisership! 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`: `(nil)`." diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml index 4c9ae8c..c7237e1 100644 --- a/youyesyet.adl.xml +++ b/youyesyet.adl.xml @@ -56,7 +56,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 @@ -101,13 +101,19 @@ + default="Unknown" distinct="user">
+ + + + + + - + All genders which may be assigned to electors. @@ -116,6 +122,12 @@ + + + + + + All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. @@ -124,12 +136,18 @@ - + - + + + + + + + Addresses of all buildings which contain dwellings. @@ -156,11 +174,46 @@ - + + + Locality indexing; see issue #44. Note that this property should be generated + automatically from the latitude and 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)))) + We'll use a trigger to insert this. I don't think it will ever appear in the user + interface; it's an implementation detail, not of interest to users. + + + + + + + + + + + + + + + + + + + + + + - -
+ + All visits made by canvassers to dwellings in which opinions were recorded. @@ -170,18 +223,24 @@ + entity="addresses" farkey="id" distinct="user"> - + - - + + But only in their immediate area. + + + + + + @@ -194,6 +253,12 @@ + + + + + + Issues believed to be of interest to electors, about which they may have questions. @@ -210,6 +275,12 @@ + + + + + + Link table. @@ -227,6 +298,16 @@ + + But only for intentions in their immediate locality. + + + + + Can see all recorded intentions across the whole of Scotland. + + + Primary users of the system: those actually interviewing electors. @@ -261,13 +342,13 @@ - + But only their own record - + But only canvassers in their own team. @@ -275,6 +356,14 @@ + + But should only be able to edit their own record. + + + + + + Requests for a followup with an issue expert @@ -284,15 +373,15 @@ + farkey="id" distinct="user"> + farkey="id" distinct="user"> + farkey="id" distinct="user"> - - - 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. @@ -320,7 +404,7 @@ - + @@ -328,6 +412,12 @@ + + + + + + @@ -348,13 +438,19 @@ + + + - + But only their own group(s) + + + All groups @@ -362,7 +458,7 @@ - Electoral districts + Electoral districts: TODO: Shape (polygon) information will need to be added, for use in maps. @@ -375,17 +471,12 @@ - - - Link table - - - - - - + + + + + + Actions taken on followup requests. @@ -402,37 +493,25 @@ farkey="id"> - + - + - + + + But only for electors in their immediate vicinity + - - - - - Link table - - - - - - - - - + + Options in the election or referendum being canvassed on @@ -443,17 +522,12 @@ - - - Link table - - - - - - + + + + + + @@ -463,5 +537,11 @@ + + + + + + diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index e907dc8..c269c0f 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -64,7 +64,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 @@ -110,36 +110,36 @@ - + + + + + + +
- + + All genders which may be assigned to electors. - - - - - - - - - - - - -
- - - -
+ +
+ + + + + +
- - Link table - - - - - Auto-generated abstract primary key - - - - - - - - - - - - - - - - + + + + + + - Electoral districts + Electoral districts: TODO: Shape (polygon) information will need to be added, for use in maps. @@ -471,33 +529,12 @@
-
- - - Link table - - - - - Auto-generated abstract primary key - - - - - - - - - - - - - - - - + + + + + + - - Link table - - - - - Auto-generated abstract primary key - - - - - - - - - - - - - - - - - - - + + - - Link table - - - - - Auto-generated abstract primary key - - - - - - - - - - - - - - - - + + + + + + {% block head %} diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html index 1a69022..95f7a5f 100644 --- a/resources/templates/auto/form-addresses-Address.html +++ b/resources/templates/auto/form-addresses-Address.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-authorities-Authority.html b/resources/templates/auto/form-authorities-Authority.html index b4e7604..6b15dfa 100644 --- a/resources/templates/auto/form-authorities-Authority.html +++ b/resources/templates/auto/form-authorities-Authority.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-canvassers-Canvasser.html b/resources/templates/auto/form-canvassers-Canvasser.html index 59115bc..e622a82 100644 --- a/resources/templates/auto/form-canvassers-Canvasser.html +++ b/resources/templates/auto/form-canvassers-Canvasser.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-districts-District.html b/resources/templates/auto/form-districts-District.html index 03bd2cb..4e7f830 100644 --- a/resources/templates/auto/form-districts-District.html +++ b/resources/templates/auto/form-districts-District.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-dwellings-Dwelling.html b/resources/templates/auto/form-dwellings-Dwelling.html index 1e85f97..e5757b4 100644 --- a/resources/templates/auto/form-dwellings-Dwelling.html +++ b/resources/templates/auto/form-dwellings-Dwelling.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-electors-Elector.html b/resources/templates/auto/form-electors-Elector.html index 4f21ae4..227a8a3 100644 --- a/resources/templates/auto/form-electors-Elector.html +++ b/resources/templates/auto/form-electors-Elector.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-followupactions-Followupaction.html b/resources/templates/auto/form-followupactions-Followupaction.html index 371099f..2e78a33 100644 --- a/resources/templates/auto/form-followupactions-Followupaction.html +++ b/resources/templates/auto/form-followupactions-Followupaction.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-followupmethods-Followupmethod.html b/resources/templates/auto/form-followupmethods-Followupmethod.html index f233b36..e1602b6 100644 --- a/resources/templates/auto/form-followupmethods-Followupmethod.html +++ b/resources/templates/auto/form-followupmethods-Followupmethod.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-followuprequests-Followuprequest.html b/resources/templates/auto/form-followuprequests-Followuprequest.html index 6940865..93365b0 100644 --- a/resources/templates/auto/form-followuprequests-Followuprequest.html +++ b/resources/templates/auto/form-followuprequests-Followuprequest.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-genders-Gender.html b/resources/templates/auto/form-genders-Gender.html index 6ad02c0..af41f8e 100644 --- a/resources/templates/auto/form-genders-Gender.html +++ b/resources/templates/auto/form-genders-Gender.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-intentions-Intention.html b/resources/templates/auto/form-intentions-Intention.html index 0d6a71d..f67958c 100644 --- a/resources/templates/auto/form-intentions-Intention.html +++ b/resources/templates/auto/form-intentions-Intention.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-issues-Issue.html b/resources/templates/auto/form-issues-Issue.html index 141d785..bd76684 100644 --- a/resources/templates/auto/form-issues-Issue.html +++ b/resources/templates/auto/form-issues-Issue.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-options-Option.html b/resources/templates/auto/form-options-Option.html index 9837b1e..6267dcb 100644 --- a/resources/templates/auto/form-options-Option.html +++ b/resources/templates/auto/form-options-Option.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-roles-Role.html b/resources/templates/auto/form-roles-Role.html index fc4550f..c8d9200 100644 --- a/resources/templates/auto/form-roles-Role.html +++ b/resources/templates/auto/form-roles-Role.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-teams-Team.html b/resources/templates/auto/form-teams-Team.html index 964e783..5271507 100644 --- a/resources/templates/auto/form-teams-Team.html +++ b/resources/templates/auto/form-teams-Team.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html index 80b3d61..52484fa 100644 --- a/resources/templates/auto/form-visits-Visit.html +++ b/resources/templates/auto/form-visits-Visit.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html index 77d4307..d0530f6 100644 --- a/resources/templates/auto/list-addresses-Addresses.html +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -80,7 +80,7 @@ longitude -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -104,7 +104,7 @@ longitude {{ record.longitude }} - + View diff --git a/resources/templates/auto/list-authorities-Authorities.html b/resources/templates/auto/list-authorities-Authorities.html index 161786d..6cdb8b6 100644 --- a/resources/templates/auto/list-authorities-Authorities.html +++ b/resources/templates/auto/list-authorities-Authorities.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -44,13 +44,13 @@ id -{% for record in %records% %} +{% for record in records %} {{ record.id }} - + View diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html index b9e5b76..e016a0f 100644 --- a/resources/templates/auto/list-canvassers-Canvassers.html +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -92,7 +92,7 @@ authorised -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -122,7 +122,7 @@ authorised {{ record.authorised }} - + View diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html index 89c4394..46ab6e3 100644 --- a/resources/templates/auto/list-districts-Districts.html +++ b/resources/templates/auto/list-districts-Districts.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -50,7 +50,7 @@ name -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -59,7 +59,7 @@ name {{ record.name }} - + View diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html index f5c31e5..5774c07 100644 --- a/resources/templates/auto/list-dwellings-Dwellings.html +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -56,7 +56,7 @@ sub-address -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -68,7 +68,7 @@ sub-address {{ record.sub-address }} - + View diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html index 95cf89d..b2c1885 100644 --- a/resources/templates/auto/list-electors-Electors.html +++ b/resources/templates/auto/list-electors-Electors.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -74,7 +74,7 @@ gender -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -95,7 +95,7 @@ gender {{ record.gender }} - + View diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html index e3e271f..fdc153e 100644 --- a/resources/templates/auto/list-followupactions-Followupactions.html +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -74,7 +74,7 @@ closed -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -95,7 +95,7 @@ closed {{ record.closed }} - + View diff --git a/resources/templates/auto/list-followupmethods-Followupmethods.html b/resources/templates/auto/list-followupmethods-Followupmethods.html index f717b8c..73711fa 100644 --- a/resources/templates/auto/list-followupmethods-Followupmethods.html +++ b/resources/templates/auto/list-followupmethods-Followupmethods.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -44,13 +44,13 @@ id -{% for record in %records% %} +{% for record in records %} {{ record.id }} - + View diff --git a/resources/templates/auto/list-followuprequests-Followuprequests.html b/resources/templates/auto/list-followuprequests-Followuprequests.html index 8ddb65e..74af183 100644 --- a/resources/templates/auto/list-followuprequests-Followuprequests.html +++ b/resources/templates/auto/list-followuprequests-Followuprequests.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -68,7 +68,7 @@ method_id -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -86,7 +86,7 @@ method_id {{ record.method_id }} - + View diff --git a/resources/templates/auto/list-genders-Genders.html b/resources/templates/auto/list-genders-Genders.html index 7a4778a..0aa4a3d 100644 --- a/resources/templates/auto/list-genders-Genders.html +++ b/resources/templates/auto/list-genders-Genders.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -44,13 +44,13 @@ id -{% for record in %records% %} +{% for record in records %} {{ record.id }} - + View diff --git a/resources/templates/auto/list-intentions-Intentions.html b/resources/templates/auto/list-intentions-Intentions.html index 45ecdec..47b3d73 100644 --- a/resources/templates/auto/list-intentions-Intentions.html +++ b/resources/templates/auto/list-intentions-Intentions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -56,7 +56,7 @@ option_id -{% for record in %records% %} +{% for record in records %} {{ record.visit_id }} @@ -68,7 +68,7 @@ option_id {{ record.option_id }} - + View diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html index b5f2283..3365a93 100644 --- a/resources/templates/auto/list-issues-Issues.html +++ b/resources/templates/auto/list-issues-Issues.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -56,7 +56,7 @@ current -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -68,7 +68,7 @@ current {{ record.current }} - + View diff --git a/resources/templates/auto/list-options-Options.html b/resources/templates/auto/list-options-Options.html index 6ad9f1f..aec7f02 100644 --- a/resources/templates/auto/list-options-Options.html +++ b/resources/templates/auto/list-options-Options.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -44,13 +44,13 @@ id -{% for record in %records% %} +{% for record in records %} {{ record.id }} - + View diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html index 62b00d7..4728ce5 100644 --- a/resources/templates/auto/list-roles-Roles.html +++ b/resources/templates/auto/list-roles-Roles.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -50,7 +50,7 @@ name -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -59,7 +59,7 @@ name {{ record.name }} - + View diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html index a18045b..23a5b33 100644 --- a/resources/templates/auto/list-teams-Teams.html +++ b/resources/templates/auto/list-teams-Teams.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -68,7 +68,7 @@ longitude -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -86,7 +86,7 @@ longitude {{ record.longitude }} - + View diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html index 1807168..af8b307 100644 --- a/resources/templates/auto/list-visits-Visits.html +++ b/resources/templates/auto/list-visits-Visits.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -62,7 +62,7 @@ date -{% for record in %records% %} +{% for record in records %} {{ record.id }} @@ -77,7 +77,7 @@ date {{ record.date }} - + View diff --git a/src/clj/youyesyet/routes/auto.clj b/src/clj/youyesyet/routes/auto.clj index 6fc5733..a26a13c 100644 --- a/src/clj/youyesyet/routes/auto.clj +++ b/src/clj/youyesyet/routes/auto.clj @@ -1,13 +1,13 @@ (ns youyesyet.routes.auto - "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180614T175643.413Z" + "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180615T084444.957Z" (:require + [clojure.java.io :as io] + [compojure.core :refer [defroutes GET POST]] + [hugsql.core :as hugsql] [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.layout :as l] [youyesyet.db.core :as db] [youyesyet.routes.manual :as m])) @@ -38,7 +38,11 @@ (resolve-template "list-electors-Electors.html") {:title "Electors", :params p, - :records (db/search-strings-elector p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-elector db/*db* p) + (db/list-electors db/*db* {}))}))) (defn form-electors-Elector @@ -58,7 +62,11 @@ (resolve-template "list-genders-Genders.html") {:title "Genders", :params p, - :records (db/search-strings-gender p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-gender db/*db* p) + (db/list-genders db/*db* {}))}))) (defn form-genders-Gender @@ -78,7 +86,11 @@ (resolve-template "list-dwellings-Dwellings.html") {:title "Dwellings", :params p, - :records (db/search-strings-dwelling p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-dwelling db/*db* p) + (db/list-dwellings db/*db* {}))}))) (defn form-dwellings-Dwelling @@ -98,7 +110,11 @@ (resolve-template "list-addresses-Addresses.html") {:title "Addresses", :params p, - :records (db/search-strings-address p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-address db/*db* p) + (db/list-addresses db/*db* {}))}))) (defn form-addresses-Address @@ -116,7 +132,13 @@ [p (:form-params r)] (l/render (resolve-template "list-visits-Visits.html") - {:title "Visits", :params p, :records (db/search-strings-visit p)}))) + {:title "Visits", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-visit db/*db* p) + (db/list-visits db/*db* {}))}))) (defn form-visits-Visit @@ -136,7 +158,11 @@ (resolve-template "list-authorities-Authorities.html") {:title "Authorities", :params p, - :records (db/search-strings-authority p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-authority db/*db* p) + (db/list-authorities db/*db* {}))}))) (defn form-authorities-Authority @@ -154,7 +180,13 @@ [p (:form-params r)] (l/render (resolve-template "list-issues-Issues.html") - {:title "Issues", :params p, :records (db/search-strings-issue p)}))) + {:title "Issues", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-issue db/*db* p) + (db/list-issues db/*db* {}))}))) (defn form-issues-Issue @@ -174,7 +206,11 @@ (resolve-template "list-intentions-Intentions.html") {:title "Intentions", :params p, - :records (db/search-strings-intention p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-intention db/*db* p) + (db/list-intentions db/*db* {}))}))) (defn form-intentions-Intention @@ -194,7 +230,11 @@ (resolve-template "list-canvassers-Canvassers.html") {:title "Canvassers", :params p, - :records (db/search-strings-canvasser p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-canvasser db/*db* p) + (db/list-canvassers db/*db* {}))}))) (defn form-canvassers-Canvasser @@ -214,7 +254,11 @@ (resolve-template "list-followuprequests-Followuprequests.html") {:title "Followuprequests", :params p, - :records (db/search-strings-followuprequest p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-followuprequest db/*db* p) + (db/list-followuprequests db/*db* {}))}))) (defn form-followuprequests-Followuprequest @@ -234,7 +278,13 @@ [p (:form-params r)] (l/render (resolve-template "list-roles-Roles.html") - {:title "Roles", :params p, :records (db/search-strings-role p)}))) + {:title "Roles", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-role db/*db* p) + (db/list-roles db/*db* {}))}))) (defn form-roles-Role @@ -252,7 +302,13 @@ [p (:form-params r)] (l/render (resolve-template "list-teams-Teams.html") - {:title "Teams", :params p, :records (db/search-strings-team p)}))) + {:title "Teams", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-team db/*db* p) + (db/list-teams db/*db* {}))}))) (defn form-teams-Team @@ -272,7 +328,11 @@ (resolve-template "list-districts-Districts.html") {:title "Districts", :params p, - :records (db/search-strings-district p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-district db/*db* p) + (db/list-districts db/*db* {}))}))) (defn form-districts-District @@ -292,7 +352,11 @@ (resolve-template "list-followupactions-Followupactions.html") {:title "Followupactions", :params p, - :records (db/search-strings-followupaction p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-followupaction db/*db* p) + (db/list-followupactions db/*db* {}))}))) (defn form-followupactions-Followupaction @@ -314,7 +378,11 @@ (resolve-template "list-options-Options.html") {:title "Options", :params p, - :records (db/search-strings-option p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-option db/*db* p) + (db/list-options db/*db* {}))}))) (defn form-options-Option @@ -334,7 +402,11 @@ (resolve-template "list-followupmethods-Followupmethods.html") {:title "Followupmethods", :params p, - :records (db/search-strings-followupmethod p)}))) + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-followupmethod db/*db* p) + (db/list-followupmethods db/*db* {}))}))) (defn form-followupmethods-Followupmethod diff --git a/src/clj/youyesyet/routes/auto_json.clj b/src/clj/youyesyet/routes/auto_json.clj index 6a5a5ef..91f8d08 100644 --- a/src/clj/youyesyet/routes/auto_json.clj +++ b/src/clj/youyesyet/routes/auto_json.clj @@ -1,13 +1,13 @@ (ns youyesyet.routes.auto-json - "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180614T175642.456Z" + "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180615T084443.981Z" (:require + [clojure.java.io :as io] + [compojure.core :refer [defroutes GET POST]] + [hugsql.core :as hugsql] [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])) From 284509fa7bee1c169cc588f94cfa42167d99f62d Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Tue, 19 Jun 2018 08:23:30 +0100 Subject: [PATCH 19/51] Work on getting forms to go. Not yet complete. --- doc/specification/authorisation.md | 17 ++ resources/sql/queries.auto.sql | 2 +- .../templates/auto/application-index.html | 2 +- .../auto/form-addresses-Address.html | 114 ++++++------ .../auto/form-authorities-Authority.html | 26 +-- .../auto/form-canvassers-Canvasser.html | 154 +++++++-------- .../auto/form-districts-District.html | 34 ++-- .../auto/form-dwellings-Dwelling.html | 50 ++--- .../templates/auto/form-electors-Elector.html | 98 +++++----- .../form-followupactions-Followupaction.html | 98 +++++----- .../form-followupmethods-Followupmethod.html | 26 +-- ...form-followuprequests-Followuprequest.html | 82 ++++---- .../templates/auto/form-genders-Gender.html | 26 +-- .../auto/form-intentions-Intention.html | 50 ++--- .../templates/auto/form-issues-Issue.html | 58 +++--- .../templates/auto/form-options-Option.html | 26 +-- resources/templates/auto/form-roles-Role.html | 42 ++--- resources/templates/auto/form-teams-Team.html | 98 +++++----- .../templates/auto/form-visits-Visit.html | 66 +++---- .../auto/list-addresses-Addresses.html | 10 +- .../auto/list-authorities-Authorities.html | 4 +- .../auto/list-canvassers-Canvassers.html | 22 ++- .../auto/list-districts-Districts.html | 4 +- .../auto/list-dwellings-Dwellings.html | 10 +- .../auto/list-electors-Electors.html | 16 +- .../list-followupactions-Followupactions.html | 16 +- .../list-followupmethods-Followupmethods.html | 4 +- ...ist-followuprequests-Followuprequests.html | 28 ++- .../templates/auto/list-genders-Genders.html | 4 +- .../auto/list-intentions-Intentions.html | 22 ++- .../templates/auto/list-issues-Issues.html | 4 +- .../templates/auto/list-options-Options.html | 4 +- .../templates/auto/list-roles-Roles.html | 4 +- .../templates/auto/list-teams-Teams.html | 10 +- .../templates/auto/list-visits-Visits.html | 16 +- src/clj/youyesyet/layout.clj | 97 ++++++---- src/clj/youyesyet/routes/auto.clj | 176 +++++++++++++----- src/clj/youyesyet/routes/auto_json.clj | 2 +- src/clj/youyesyet/tags.clj | 20 ++ youyesyet.adl.xml | 8 +- 40 files changed, 866 insertions(+), 684 deletions(-) create mode 100644 doc/specification/authorisation.md create mode 100644 src/clj/youyesyet/tags.clj diff --git a/doc/specification/authorisation.md b/doc/specification/authorisation.md new file mode 100644 index 0000000..66b006e --- /dev/null +++ b/doc/specification/authorisation.md @@ -0,0 +1,17 @@ +# Security and authorisation + +Essentially we have six levels of authorisation, at essentially increasing levels of sensitivity. + +1. *Canvassers:* Any authenticated user essentially has this level of authorisation. Hence users of the app can all share the same database connections without problem. Therefore there will be one first-class database user for all canvassers, and they will not have individual real database logins. + +2. *Issue experts:* Issue experts respond to followup requests. Therefore they must be able to see the queue of requests and the details of the elector making the request. They don't need to see voter intentions and I don't believe the information they do need to see is particularly sensitive. So they too can share a single database-layer login and connection pool; whether this is the same login as used by the canvassers is an implementation detail but I don't believe that it's critical. + +3. *Issue editors:* Don't need to see much sensitive data (although they do need to see, in aggregate, what issues are being raised by electors in the field), but they do have the power to dictate the initial responses canvassers make to issues raised, so the information they can *write* is pretty sensitive. We need to be very sure that unauthorised users don't have the power to write this data. So I suggest that issue editors probably should have individual first class database logins. + +4. *Team leaders:* Need to be able to monitor the performance of their teams, to invite new users to the system and to block abusive users from the system. Again, these are significant functions which should be well protected from abuse. But we will have at least hundreds, probably thousands of team leaders across Scotland. I would prefer that they each had first class logins, but this may be impractical. But in any case, even if they use a shared login, it should not be the same shared login as used by canvassers. + +5. *Analyists* Need broad authorisation to read, but not write or edit, all sensitive data held by the system. They must have individual first class database logins. + +6. *Admins* Can necessarily read and write everything. They should definitely each have individual first class database logins. + +This means we have a hybrid authentication scheme; for lower levels, application layer security and shared connection pools are adequate. For higher levels, individual connections and database layer authorisation are required. It implies that the routes at the different layers should be separated into separate namespaces with separate authentication functions. diff --git a/resources/sql/queries.auto.sql b/resources/sql/queries.auto.sql index 41337f4..637b92f 100644 --- a/resources/sql/queries.auto.sql +++ b/resources/sql/queries.auto.sql @@ -1,7 +1,7 @@ ------------------------------------------------------------------------ -- File queries.sql -- --- autogenerated by adl.to-hugsql-queries at 2018-06-15T09:22:07.884Z +-- autogenerated by adl.to-hugsql-queries at 2018-06-17T11:05:53.294Z -- -- See [Application Description -- Language](https://github.com/simon-brooke/adl). diff --git a/resources/templates/auto/application-index.html b/resources/templates/auto/application-index.html index 6a420f7..eecae38 100644 --- a/resources/templates/auto/application-index.html +++ b/resources/templates/auto/application-index.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html index 95f7a5f..b834a12 100644 --- a/resources/templates/auto/form-addresses-Address.html +++ b/resources/templates/auto/form-addresses-Address.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,63 +29,63 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> -{% ifwritable addresses %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses address %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses address%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.address}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses postcode %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses postcode%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.postcode}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses phone %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses phone%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.phone}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses district_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable addresses district_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.district_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses latitude %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses latitude%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.latitude}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses longitude %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses longitude%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.longitude}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses address %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses address%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.address}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses postcode %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses postcode%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.postcode}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses phone %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses phone%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.phone}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses district_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable addresses district_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.district_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses latitude %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses latitude%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.latitude}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable addresses longitude %} +{% ifmemberof admin %} {% else %} -{% ifreadable addresses longitude%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.longitude}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable authorities %} +{% ifmemberof admin %} {% else %} -{% ifreadable authorities %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable authorities %} +{% ifmemberof admin %} {% else %} -{% ifreadable authorities %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers username %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers username%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.username}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers fullname %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers fullname%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.fullname}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers elector_id %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %}

{% else %} -{% ifreadable canvassers elector_id%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.elector_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers address_id %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %}

{% else %} -{% ifreadable canvassers address_id%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.address_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers phone %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers phone%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.phone}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers email %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers email%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.email}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers authority_id %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %}

{% else %} -{% ifreadable canvassers authority_id%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.authority_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers authorised %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers authorised%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.authorised}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers username %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers username%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.username}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers fullname %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers fullname%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.fullname}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers elector_id %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %}

{% else %} -{% ifreadable canvassers elector_id%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.elector_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers address_id %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %}

{% else %} -{% ifreadable canvassers address_id%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.address_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers phone %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers phone%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.phone}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers email %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers email%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.email}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers authority_id %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %}

{% else %} -{% ifreadable canvassers authority_id%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.authority_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers authorised %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} {% else %} -{% ifreadable canvassers authorised%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.authorised}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable canvassers roles %} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %}

{% else %} -{% ifreadable canvassers roles%} +{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.roles}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable districts name %} +{% ifmemberof admin admin %} {% else %} -{% ifreadable districts name%} +{% ifmemberof public admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.name}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable districts %} +{% ifmemberof admin admin %} {% else %} -{% ifreadable districts %} +{% ifmemberof public admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable districts name %} +{% ifmemberof admin admin %} {% else %} -{% ifreadable districts name%} +{% ifmemberof public admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.name}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable dwellings address_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable dwellings address_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.address_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable dwellings sub-address %} +{% ifmemberof admin %} {% else %} -{% ifreadable dwellings sub-address%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.sub-address}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable dwellings %} +{% ifmemberof admin %} {% else %} -{% ifreadable dwellings %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable dwellings address_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable dwellings address_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.address_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable dwellings sub-address %} +{% ifmemberof admin %} {% else %} -{% ifreadable dwellings sub-address%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.sub-address}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors name %} +{% ifmemberof admin %} {% else %} -{% ifreadable electors name%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.name}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors dwelling_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable electors dwelling_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.dwelling_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors phone %} +{% ifmemberof admin %} {% else %} -{% ifreadable electors phone%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.phone}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors email %} +{% ifmemberof admin %} {% else %} -{% ifreadable electors email%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.email}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors gender %} +{% ifmemberof admin %}

{% else %} -{% ifreadable electors gender%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.gender}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors %} +{% ifmemberof admin %} {% else %} -{% ifreadable electors %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors name %} +{% ifmemberof admin %} {% else %} -{% ifreadable electors name%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.name}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors dwelling_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable electors dwelling_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.dwelling_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors phone %} +{% ifmemberof admin %} {% else %} -{% ifreadable electors phone%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.phone}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors email %} +{% ifmemberof admin %} {% else %} -{% ifreadable electors email%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.email}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable electors gender %} +{% ifmemberof admin %}

{% else %} -{% ifreadable electors gender%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.gender}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions request_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followupactions request_id%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.request_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions actor %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followupactions actor%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.actor}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions date %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupactions date%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.date}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions notes %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupactions notes%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.notes}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions closed %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupactions closed%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.closed}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupactions %} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions request_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followupactions request_id%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.request_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions actor %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followupactions actor%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.actor}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions date %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupactions date%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.date}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions notes %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupactions notes%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.notes}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupactions closed %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupactions closed%} +{% ifmemberof issueexperts canvassers analysts issueeditors admin %} {{record.closed}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupmethods %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupmethods %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followupmethods %} +{% ifmemberof admin %} {% else %} -{% ifreadable followupmethods %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests elector_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followuprequests elector_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.elector_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests visit_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followuprequests visit_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.visit_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests issue_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followuprequests issue_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.issue_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests method_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followuprequests method_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.method_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests %} +{% ifmemberof admin %} {% else %} -{% ifreadable followuprequests %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests elector_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followuprequests elector_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.elector_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests visit_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followuprequests visit_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.visit_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests issue_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followuprequests issue_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.issue_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable followuprequests method_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable followuprequests method_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.method_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable genders %} +{% ifmemberof admin %} {% else %} -{% ifreadable genders %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable genders %} +{% ifmemberof admin %} {% else %} -{% ifreadable genders %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

{% else %} -{% ifreadable intentions visit_id%} +{% ifmemberof canvassers analysts admin %} {{record.visit_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable intentions elector_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable intentions elector_id%} +{% ifmemberof canvassers analysts admin %} {{record.elector_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable intentions option_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable intentions option_id%} +{% ifmemberof canvassers analysts admin %} {{record.option_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable intentions visit_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable intentions visit_id%} +{% ifmemberof canvassers analysts admin %} {{record.visit_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable intentions elector_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable intentions elector_id%} +{% ifmemberof canvassers analysts admin %} {{record.elector_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable intentions option_id %} +{% ifmemberof admin %}

{% else %} -{% ifreadable intentions option_id%} +{% ifmemberof canvassers analysts admin %} {{record.option_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable issues %} +{% ifmemberof issueeditors admin %} {% else %} -{% ifreadable issues %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable issues url %} +{% ifmemberof issueeditors admin %} {% else %} -{% ifreadable issues url%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.url}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable issues current %} +{% ifmemberof issueeditors admin %} {% else %} -{% ifreadable issues current%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.current}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable issues %} +{% ifmemberof issueeditors admin %} {% else %} -{% ifreadable issues %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable issues url %} +{% ifmemberof issueeditors admin %} {% else %} -{% ifreadable issues url%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.url}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable issues current %} +{% ifmemberof issueeditors admin %} {% else %} -{% ifreadable issues current%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.current}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable options %} +{% ifmemberof admin %} {% else %} -{% ifreadable options %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable options %} +{% ifmemberof admin %} {% else %} -{% ifreadable options %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable roles name %} +{% ifmemberof admin %} {% else %} -{% ifreadable roles name%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.name}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable roles %} +{% ifmemberof admin %} {% else %} -{% ifreadable roles %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable roles name %} +{% ifmemberof admin %} {% else %} -{% ifreadable roles name%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.name}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable roles members %} +{% ifmemberof admin %}

{% else %} -{% ifreadable roles members%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.members}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams name %} +{% ifmemberof teamorganisers admin %} {% else %} -{% ifreadable teams name%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.name}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams district_id %} +{% ifmemberof teamorganisers admin %}

{% else %} -{% ifreadable teams district_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.district_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams latitude %} +{% ifmemberof teamorganisers admin %} {% else %} -{% ifreadable teams latitude%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.latitude}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams longitude %} +{% ifmemberof teamorganisers admin %} {% else %} -{% ifreadable teams longitude%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.longitude}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams %} +{% ifmemberof teamorganisers admin %} {% else %} -{% ifreadable teams %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams name %} +{% ifmemberof teamorganisers admin %} {% else %} -{% ifreadable teams name%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.name}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams district_id %} +{% ifmemberof teamorganisers admin %}

{% else %} -{% ifreadable teams district_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.district_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams latitude %} +{% ifmemberof teamorganisers admin %} {% else %} -{% ifreadable teams latitude%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.latitude}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams members %} +{% ifmemberof teamorganisers admin %}

{% else %} -{% ifreadable teams members%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.members}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams organisers %} +{% ifmemberof teamorganisers admin %}

{% else %} -{% ifreadable teams organisers%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.organisers}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable teams longitude %} +{% ifmemberof teamorganisers admin %} {% else %} -{% ifreadable teams longitude%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.longitude}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable visits address_id %} +{% ifmemberof admin admin %}

{% else %} -{% ifreadable visits address_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.address_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable visits canvasser_id %} +{% ifmemberof admin admin %}

{% else %} -{% ifreadable visits canvasser_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.canvasser_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable visits date %} +{% ifmemberof admin admin %} {% else %} -{% ifreadable visits date%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.date}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable visits %} +{% ifmemberof admin admin %} {% else %} -{% ifreadable visits %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable visits address_id %} +{% ifmemberof admin admin %}

{% else %} -{% ifreadable visits address_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.address_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable visits canvasser_id %} +{% ifmemberof admin admin %}

{% else %} -{% ifreadable visits canvasser_id%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.canvasser_id}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

-{% ifwritable visits date %} +{% ifmemberof admin admin %} {% else %} -{% ifreadable visits date%} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.date}} -{% endifreadable %} -{% endifwritable %} +{% endifmemberof %} +{% endifmemberof %}

From acfaf985fa775de8155ff6f216703f8f8d628022 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Wed, 20 Jun 2018 09:17:53 +0100 Subject: [PATCH 20/51] Much progress, nothing yet works. --- project.clj | 3 +- resources/sql/queries.auto.sql | 61 +- resources/sql/youyesyet.postgres.sql | 902 ++++++++++++++++++ .../templates/auto/application-index.html | 2 +- .../auto/form-addresses-Address.html | 134 +-- .../auto/form-authorities-Authority.html | 34 +- .../auto/form-canvassers-Canvasser.html | 184 +--- .../auto/form-districts-District.html | 39 +- .../auto/form-dwellings-Dwelling.html | 62 +- .../templates/auto/form-electors-Elector.html | 120 +-- .../form-followupactions-Followupaction.html | 121 +-- .../form-followupmethods-Followupmethod.html | 34 +- ...form-followuprequests-Followuprequest.html | 111 +-- .../templates/auto/form-genders-Gender.html | 34 +- .../auto/form-intentions-Intention.html | 71 +- .../templates/auto/form-issues-Issue.html | 66 +- .../templates/auto/form-options-Option.html | 34 +- resources/templates/auto/form-roles-Role.html | 43 +- resources/templates/auto/form-teams-Team.html | 106 +- .../templates/auto/form-visits-Visit.html | 85 +- .../auto/list-addresses-Addresses.html | 2 +- .../auto/list-authorities-Authorities.html | 2 +- .../auto/list-canvassers-Canvassers.html | 2 +- .../auto/list-districts-Districts.html | 2 +- .../auto/list-dwellings-Dwellings.html | 2 +- .../auto/list-electors-Electors.html | 2 +- .../list-followupactions-Followupactions.html | 2 +- .../list-followupmethods-Followupmethods.html | 2 +- ...ist-followuprequests-Followuprequests.html | 2 +- .../templates/auto/list-genders-Genders.html | 2 +- .../auto/list-intentions-Intentions.html | 2 +- .../templates/auto/list-issues-Issues.html | 2 +- .../templates/auto/list-options-Options.html | 2 +- .../templates/auto/list-roles-Roles.html | 2 +- .../templates/auto/list-teams-Teams.html | 2 +- .../templates/auto/list-visits-Visits.html | 2 +- src/clj/youyesyet/db/core.clj | 1 - src/clj/youyesyet/layout.clj | 102 +- src/clj/youyesyet/routes/auto.clj | 704 +++++++------- src/clj/youyesyet/routes/auto_json.clj | 3 +- 40 files changed, 1658 insertions(+), 1430 deletions(-) create mode 100644 resources/sql/youyesyet.postgres.sql diff --git a/project.clj b/project.clj index d671ac7..404b692 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,8 @@ :description "Canvassing tool for referenda" :url "https://github.com/simon-brooke/youyesyet" - :dependencies [[bouncer "1.0.1"] + :dependencies [[adl-support "0.1.0-SNAPSHOT"] + [bouncer "1.0.1"] [ch.qos.logback/logback-classic "1.2.2"] [clj-oauth "1.5.5"] [cljsjs/react-leaflet "0.12.3-4"] diff --git a/resources/sql/queries.auto.sql b/resources/sql/queries.auto.sql index 637b92f..9de72ba 100644 --- a/resources/sql/queries.auto.sql +++ b/resources/sql/queries.auto.sql @@ -1,7 +1,7 @@ ------------------------------------------------------------------------ -- File queries.sql -- --- autogenerated by adl.to-hugsql-queries at 2018-06-17T11:05:53.294Z +-- autogenerated by adl.to-hugsql-queries at 2018-06-19T18:51:50.013Z -- -- See [Application Description -- Language](https://github.com/simon-brooke/adl). @@ -60,9 +60,9 @@ returning id -- :name create-dwelling! :! :n -- :doc creates a new dwelling record INSERT INTO dwellings (address_id, - sub-address) + sub_address) VALUES (:address_id, - :sub-address) + :sub_address) returning id -- :name create-elector! :! :n @@ -294,7 +294,7 @@ ORDER BY districts.name, SELECT * FROM dwellings WHERE dwellings.id = :id ORDER BY dwellings.address_id, - dwellings.sub-address, + dwellings.sub_address, dwellings.id -- :name get-elector :? :1 @@ -469,7 +469,7 @@ ORDER BY lv_districts.name, -- :doc lists all existing dwelling records SELECT * FROM lv_dwellings ORDER BY lv_dwellings.address_id, - lv_dwellings.sub-address, + lv_dwellings.sub_address, lv_dwellings.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") @@ -481,7 +481,7 @@ FROM lv_dwellings, dwellings WHERE lv_dwellings.id = dwellings.id AND dwellings.address_id = :id ORDER BY lv_dwellings.address_id, - lv_dwellings.sub-address, + lv_dwellings.sub_address, lv_dwellings.id -- :name list-electors :? :* @@ -730,7 +730,13 @@ ORDER BY lv_visits.address_id, SELECT * FROM lv_addresses WHERE address LIKE '%params.address%' + OR postcode = params.postcode OR phone LIKE '%params.phone%' + OR district_id = params.district_id + OR latitude = params.latitude + OR longitude = params.longitude + OR locality = params.locality + OR id = params.id ORDER BY lv_addresses.address, lv_addresses.postcode, lv_addresses.id @@ -751,8 +757,13 @@ SELECT * FROM lv_canvassers WHERE username LIKE '%params.username%' OR fullname LIKE '%params.fullname%' + OR elector_id = params.elector_id + OR address_id = params.address_id OR phone LIKE '%params.phone%' OR email LIKE '%params.email%' + OR authority_id = params.authority_id + OR authorised = params.authorised + OR id = params.id ORDER BY lv_canvassers.username, lv_canvassers.fullname, lv_canvassers.email, @@ -765,6 +776,7 @@ ORDER BY lv_canvassers.username, SELECT * FROM lv_districts WHERE name LIKE '%params.name%' + OR id = params.id ORDER BY lv_districts.name, lv_districts.id --~ (if (:offset params) "OFFSET :offset ") @@ -774,9 +786,11 @@ ORDER BY lv_districts.name, -- :doc selects existing dwelling records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_dwellings WHERE -sub-address LIKE '%params.sub-address%' +address_id = params.address_id + OR sub_address LIKE '%params.sub-address%' + OR id = params.id ORDER BY lv_dwellings.address_id, - lv_dwellings.sub-address, + lv_dwellings.sub_address, lv_dwellings.id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") @@ -786,8 +800,11 @@ ORDER BY lv_dwellings.address_id, SELECT * FROM lv_electors WHERE name LIKE '%params.name%' + OR dwelling_id = params.dwelling_id OR phone LIKE '%params.phone%' OR email LIKE '%params.email%' + OR gender = params.gender + OR id = params.id ORDER BY lv_electors.name, lv_electors.phone, lv_electors.email, @@ -800,7 +817,12 @@ ORDER BY lv_electors.name, -- :doc selects existing followupaction records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_followupactions WHERE -notes LIKE '%params.notes%' +request_id = params.request_id + OR actor = params.actor + OR date = 'params.date' + OR notes LIKE '%params.notes%' + OR closed = params.closed + OR id = params.id ORDER BY lv_followupactions.date, lv_followupactions.notes, lv_followupactions.id @@ -819,6 +841,11 @@ id LIKE '%params.id%' -- :doc selects existing followuprequest records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_followuprequests WHERE +elector_id = params.elector_id + OR visit_id = params.visit_id + OR issue_id = params.issue_id + OR method_id = params.method_id + OR id = params.id ORDER BY lv_followuprequests.elector_id, lv_followuprequests.visit_id, lv_followuprequests.issue_id, @@ -838,6 +865,10 @@ id LIKE '%params.id%' -- :doc selects existing intention records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_intentions WHERE +visit_id = params.visit_id + OR elector_id = params.elector_id + OR option_id = params.option_id + OR Id = params.Id --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") @@ -846,6 +877,7 @@ WHERE SELECT * FROM lv_issues WHERE url LIKE '%params.url%' + OR current = params.current OR id LIKE '%params.id%' --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") @@ -863,6 +895,7 @@ id LIKE '%params.id%' SELECT * FROM lv_roles WHERE name LIKE '%params.name%' + OR id = params.id ORDER BY lv_roles.name, lv_roles.id --~ (if (:offset params) "OFFSET :offset ") @@ -873,6 +906,10 @@ ORDER BY lv_roles.name, SELECT * FROM lv_teams WHERE name LIKE '%params.name%' + OR district_id = params.district_id + OR latitude = params.latitude + OR longitude = params.longitude + OR id = params.id ORDER BY lv_teams.name, lv_teams.id --~ (if (:offset params) "OFFSET :offset ") @@ -882,6 +919,10 @@ ORDER BY lv_teams.name, -- :doc selects existing visit records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_visits WHERE +address_id = params.address_id + OR canvasser_id = params.canvasser_id + OR date = 'params.date' + OR id = params.id ORDER BY lv_visits.address_id, lv_visits.date, lv_visits.id @@ -923,7 +964,7 @@ WHERE districts.id = :id -- :doc updates an existing dwelling record UPDATE dwellings SET address_id = :address_id, - sub-address = :sub-address + sub_address = :sub-address WHERE dwellings.id = :id -- :name update-elector! :! :n diff --git a/resources/sql/youyesyet.postgres.sql b/resources/sql/youyesyet.postgres.sql new file mode 100644 index 0000000..6ca584c --- /dev/null +++ b/resources/sql/youyesyet.postgres.sql @@ -0,0 +1,902 @@ +------------------------------------------------------------------------ +-- Database definition for application +-- +-- youyesyet version 0.1.1 +-- +-- auto-generated by [Application Description Language framework] +-- +-- (https://github.com/simon-brooke/adl) at 20180619T185151.055Z +-- +-- +-- A web-app intended to be used by canvassers campaigning for a +-- 'Yes' vote in the second independence referendum. +-- +-- The web-app will be delivered to canvassers out knocking doors +-- primarily through an HTML5/React single-page app designed to work on a +-- mobile phone; it's possible that someone else may do an Android of +-- iPhone native app to address the same back end but at present I have +-- no plans for this. +-- +-- There must also be an administrative interface through which +-- privileged users can set the system up and authorise canvassers, and a +-- 'followup' interface through which issue-expert specialist canvassers +-- can address particular electors' queries. +-- +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +-- security group admin +------------------------------------------------------------------------ + +CREATE GROUP admin; + +------------------------------------------------------------------------ +-- security group analysts +------------------------------------------------------------------------ + +CREATE GROUP analysts; + +------------------------------------------------------------------------ +-- security group canvassers +------------------------------------------------------------------------ + +CREATE GROUP canvassers; + +------------------------------------------------------------------------ +-- security group issueeditors +------------------------------------------------------------------------ + +CREATE GROUP issueeditors; + +------------------------------------------------------------------------ +-- security group issueexperts +------------------------------------------------------------------------ + +CREATE GROUP issueexperts; + +------------------------------------------------------------------------ +-- security group public +------------------------------------------------------------------------ + +CREATE GROUP public; + +------------------------------------------------------------------------ +-- security group teamorganisers +------------------------------------------------------------------------ + +CREATE GROUP teamorganisers; + +------------------------------------------------------------------------ +-- primary table addresses for entity addresses +-- +-- Addresses of all buildings which contain dwellings. +------------------------------------------------------------------------ +CREATE TABLE addresses +( + id SERIAL NOT NULL PRIMARY KEY, + address VARCHAR(256) NOT NULL, + postcode VARCHAR(16) CONSTRAINT pattern_1 CHECK (postcode ~* '^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$'), + phone VARCHAR(16), + district_id INTEGER, + latitude DOUBLE PRECISION, + longitude DOUBLE PRECISION, + locality INTEGER +); +GRANT SELECT ON addresses TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON addresses TO admin ; +GRANT UPDATE ON addresses TO admin ; +GRANT DELETE ON addresses TO admin ; + +------------------------------------------------------------------------ +-- primary table authorities for entity authorities +-- +-- Authorities which may authenticate canvassers to the system. +------------------------------------------------------------------------ +CREATE TABLE authorities +( + id VARCHAR(32) NOT NULL PRIMARY KEY +); +GRANT SELECT ON authorities TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON authorities TO admin ; +GRANT UPDATE ON authorities TO admin ; +GRANT DELETE ON authorities TO admin ; + +------------------------------------------------------------------------ +-- primary table canvassers for entity canvassers +-- +-- Primary users of the system: those actually interviewing electors. +------------------------------------------------------------------------ +CREATE TABLE canvassers +( + id SERIAL NOT NULL PRIMARY KEY, + username VARCHAR(32) NOT NULL, + fullname VARCHAR(64) NOT NULL, + elector_id INTEGER, + address_id INTEGER NOT NULL, + phone VARCHAR(16), + email VARCHAR(128), + authority_id VARCHAR(32) NOT NULL, + authorised BOOLEAN +); +GRANT SELECT ON canvassers TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON canvassers TO admin, + canvassers, + teamorganisers ; +GRANT UPDATE ON canvassers TO admin, + canvassers, + teamorganisers ; +GRANT DELETE ON canvassers TO admin ; + +------------------------------------------------------------------------ +-- primary table districts for entity districts +-- +-- Electoral districts: TODO: Shape (polygon) information will need to be +-- added, for use in maps. +------------------------------------------------------------------------ +CREATE TABLE districts +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL +); +GRANT SELECT ON districts TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + public, + teamorganisers ; +GRANT INSERT ON districts TO admin ; +GRANT UPDATE ON districts TO admin ; +GRANT DELETE ON districts TO admin ; + +------------------------------------------------------------------------ +-- primary table dwellings for entity dwellings +-- +-- All dwellings within addresses in the system; a dwelling is a house, +-- flat or appartment in which electors live. +------------------------------------------------------------------------ +CREATE TABLE dwellings +( + id SERIAL NOT NULL PRIMARY KEY, + address_id INTEGER NOT NULL, + sub_address VARCHAR(32) +); +GRANT SELECT ON dwellings TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON dwellings TO admin ; +GRANT UPDATE ON dwellings TO admin ; +GRANT DELETE ON dwellings TO admin ; + +------------------------------------------------------------------------ +-- primary table electors for entity electors +-- +-- All electors known to the system; electors are people believed to be +-- entitled to vote in the current campaign. +------------------------------------------------------------------------ +CREATE TABLE electors +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL, + dwelling_id INTEGER NOT NULL, + phone VARCHAR(16), + email VARCHAR(128), + gender VARCHAR(32) DEFAULT 'Unknown' +); +GRANT SELECT ON electors TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON electors TO admin ; +GRANT UPDATE ON electors TO admin ; +GRANT DELETE ON electors TO admin ; + +------------------------------------------------------------------------ +-- primary table followupactions for entity followupactions +-- +-- Actions taken on followup requests. +------------------------------------------------------------------------ +CREATE TABLE followupactions +( + id SERIAL NOT NULL PRIMARY KEY, + request_id INTEGER NOT NULL, + actor INTEGER NOT NULL, + date TIMESTAMP DEFAULT 'now()' NOT NULL, + notes TEXT, + closed BOOLEAN DEFAULT false +); +GRANT SELECT ON followupactions TO admin, + analysts, + canvassers, + issueeditors, + issueexperts ; +GRANT INSERT ON followupactions TO admin, + issueexperts ; +GRANT UPDATE ON followupactions TO admin ; +GRANT DELETE ON followupactions TO admin ; + +------------------------------------------------------------------------ +-- primary table followupmethods for entity followupmethods +------------------------------------------------------------------------ +CREATE TABLE followupmethods +( + id VARCHAR(32) NOT NULL PRIMARY KEY +); +GRANT SELECT ON followupmethods TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON followupmethods TO admin ; +GRANT UPDATE ON followupmethods TO admin ; +GRANT DELETE ON followupmethods TO admin ; + +------------------------------------------------------------------------ +-- primary table followuprequests for entity followuprequests +-- +-- Requests for a followup with an issue expert +------------------------------------------------------------------------ +CREATE TABLE followuprequests +( + id SERIAL NOT NULL PRIMARY KEY, + elector_id INTEGER NOT NULL, + visit_id INTEGER NOT NULL, + issue_id VARCHAR(32) NOT NULL, + method_id VARCHAR(32) NOT NULL +); +GRANT SELECT ON followuprequests TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON followuprequests TO admin, + canvassers ; +GRANT UPDATE ON followuprequests TO admin ; +GRANT DELETE ON followuprequests TO admin ; + +------------------------------------------------------------------------ +-- primary table genders for entity genders +-- +-- All genders which may be assigned to electors. +------------------------------------------------------------------------ +CREATE TABLE genders +( + id VARCHAR(32) NOT NULL PRIMARY KEY +); +GRANT SELECT ON genders TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON genders TO admin ; +GRANT UPDATE ON genders TO admin ; +GRANT DELETE ON genders TO admin ; + +------------------------------------------------------------------------ +-- primary table intentions for entity intentions +-- +-- Link table. +------------------------------------------------------------------------ +CREATE TABLE intentions +( + Id SERIAL NOT NULL PRIMARY KEY, + visit_id INTEGER NOT NULL, + elector_id INTEGER NOT NULL, + option_id VARCHAR(32) NOT NULL +); +GRANT SELECT ON intentions TO admin, + analysts, + canvassers ; +GRANT INSERT ON intentions TO admin, + canvassers ; +GRANT UPDATE ON intentions TO admin ; +GRANT DELETE ON intentions TO admin ; + +------------------------------------------------------------------------ +-- primary table issues for entity issues +-- +-- Issues believed to be of interest to electors, about which they may +-- have questions. +------------------------------------------------------------------------ +CREATE TABLE issues +( + id VARCHAR(32) NOT NULL PRIMARY KEY, + url VARCHAR(256), + current BOOLEAN DEFAULT true +); +GRANT SELECT ON issues TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON issues TO admin, + issueeditors ; +GRANT UPDATE ON issues TO admin, + issueeditors ; +GRANT DELETE ON issues TO admin ; + +------------------------------------------------------------------------ +-- primary table options for entity options +-- +-- Options in the election or referendum being canvassed on +------------------------------------------------------------------------ +CREATE TABLE options +( + id VARCHAR(32) NOT NULL PRIMARY KEY +); +GRANT SELECT ON options TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON options TO admin ; +GRANT UPDATE ON options TO admin ; +GRANT DELETE ON options TO admin ; + +------------------------------------------------------------------------ +-- primary table roles for entity roles +-- +-- A role (essentially, the same as a group, but application layer rather +-- than database layer) of which a user may be a member. +------------------------------------------------------------------------ +CREATE TABLE roles +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL +); +GRANT SELECT ON roles TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON roles TO admin ; +GRANT UPDATE ON roles TO admin ; +GRANT DELETE ON roles TO admin ; + +------------------------------------------------------------------------ +-- primary table teams for entity teams +------------------------------------------------------------------------ +CREATE TABLE teams +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(64) NOT NULL, + district_id INTEGER NOT NULL, + latitude DOUBLE PRECISION, + longitude DOUBLE PRECISION +); +GRANT SELECT ON teams TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON teams TO admin, + teamorganisers ; +GRANT UPDATE ON teams TO admin, + teamorganisers ; +GRANT DELETE ON teams TO admin ; + +------------------------------------------------------------------------ +-- primary table visits for entity visits +-- +-- All visits made by canvassers to dwellings in which opinions were +-- recorded. +------------------------------------------------------------------------ +CREATE TABLE visits +( + id SERIAL NOT NULL PRIMARY KEY, + address_id INTEGER NOT NULL, + canvasser_id INTEGER NOT NULL, + date TIMESTAMP DEFAULT 'now()' NOT NULL +); +GRANT SELECT ON visits TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON visits TO admin, + canvassers, + teamorganisers ; +GRANT UPDATE ON visits TO admin ; +GRANT DELETE ON visits TO admin ; + +------------------------------------------------------------------------ +-- convenience view lv_addresses of entity addresses for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_addresses AS +SELECT addresses.address, + addresses.postcode, + addresses.phone, + districts.name AS district_id_expanded, + addresses.district_id, + addresses.latitude, + addresses.longitude, + addresses.locality, + addresses.id +FROM addresses, districts +WHERE addresses.district_id = districts.id +; +GRANT SELECT ON lv_addresses TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_authorities of entity authorities for lists, et +-- cetera +------------------------------------------------------------------------ +CREATE VIEW lv_authorities AS +SELECT authorities.id +FROM authorities +; +GRANT SELECT ON lv_authorities TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_canvassers of entity canvassers for lists, et +-- cetera +------------------------------------------------------------------------ +CREATE VIEW lv_canvassers AS +SELECT canvassers.username, + canvassers.fullname, + electors.name ||', '|| electors.phone ||', '|| electors.email ||', '|| genders.id AS elector_id_expanded, + canvassers.elector_id, + addresses.address ||', '|| addresses.postcode AS address_id_expanded, + canvassers.address_id, + canvassers.phone, + canvassers.email, + authorities.id AS authority_id_expanded, + canvassers.authority_id, + canvassers.authorised, + canvassers.id +FROM canvassers, authorities, addresses, genders, electors +WHERE canvassers.elector_id = electors.id + AND canvassers.address_id = addresses.id + AND canvassers.authority_id = authorities.id +; +GRANT SELECT ON lv_canvassers TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_districts of entity districts for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_districts AS +SELECT districts.name, + districts.id +FROM districts +; +GRANT SELECT ON lv_districts TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + public, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_dwellings of entity dwellings for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_dwellings AS +SELECT addresses.address ||', '|| addresses.postcode AS address_id_expanded, + dwellings.address_id, + dwellings.sub_address, + dwellings.id +FROM dwellings, addresses +WHERE dwellings.address_id = addresses.id +; +GRANT SELECT ON lv_dwellings TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_electors of entity electors for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_electors AS +SELECT electors.name, + addresses.address ||', '|| addresses.postcode ||', '|| dwellings.sub_address AS dwelling_id_expanded, + electors.dwelling_id, + electors.phone, + electors.email, + genders.id AS gender_expanded, + electors.gender, + electors.id +FROM dwellings, addresses, genders, electors +WHERE electors.dwelling_id = dwellings.id + AND electors.gender = genders.id +; +GRANT SELECT ON lv_electors TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_followupactions of entity followupactions for +-- lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_followupactions AS +SELECT electors.name ||', '|| electors.phone ||', '|| electors.email ||', '|| genders.id ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| visits.date ||', '|| issues.id AS request_id_expanded, + followupactions.request_id, + canvassers.username ||', '|| canvassers.fullname ||', '|| canvassers.email AS actor_expanded, + followupactions.actor, + followupactions.date, + followupactions.notes, + followupactions.closed, + followupactions.id +FROM followuprequests, visits, canvassers, addresses, followupactions, genders, issues, electors +WHERE followupactions.request_id = followuprequests.id + AND followupactions.actor = canvassers.id +; +GRANT SELECT ON lv_followupactions TO admin, + analysts, + canvassers, + issueeditors, + issueexperts ; + +------------------------------------------------------------------------ +-- convenience view lv_followupmethods of entity followupmethods for +-- lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_followupmethods AS +SELECT followupmethods.id +FROM followupmethods +; +GRANT SELECT ON lv_followupmethods TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_followuprequests of entity followuprequests for +-- lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_followuprequests AS +SELECT electors.name ||', '|| electors.phone ||', '|| electors.email ||', '|| genders.id AS elector_id_expanded, + followuprequests.elector_id, + addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS visit_id_expanded, + followuprequests.visit_id, + issues.id AS issue_id_expanded, + followuprequests.issue_id, + followupmethods.id AS method_id_expanded, + followuprequests.method_id, + followuprequests.id +FROM followuprequests, visits, addresses, genders, issues, electors, followupmethods +WHERE followuprequests.elector_id = electors.id + AND followuprequests.visit_id = visits.id + AND followuprequests.issue_id = issues.id + AND followuprequests.method_id = followupmethods.id +; +GRANT SELECT ON lv_followuprequests TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_genders of entity genders for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_genders AS +SELECT genders.id +FROM genders +; +GRANT SELECT ON lv_genders TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_intentions of entity intentions for lists, et +-- cetera +------------------------------------------------------------------------ +CREATE VIEW lv_intentions AS +SELECT addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS visit_id_expanded, + intentions.visit_id, + electors.name ||', '|| electors.phone ||', '|| electors.email ||', '|| genders.id AS elector_id_expanded, + intentions.elector_id, + options.id AS option_id_expanded, + intentions.option_id, + intentions.Id +FROM visits, intentions, addresses, genders, electors, options +WHERE intentions.visit_id = visits.id + AND intentions.elector_id = electors.id + AND intentions.option_id = options.id +; +GRANT SELECT ON lv_intentions TO admin, + analysts, + canvassers ; + +------------------------------------------------------------------------ +-- convenience view lv_issues of entity issues for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_issues AS +SELECT issues.url, + issues.current, + issues.id +FROM issues +; +GRANT SELECT ON lv_issues TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_options of entity options for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_options AS +SELECT options.id +FROM options +; +GRANT SELECT ON lv_options TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_roles of entity roles for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_roles AS +SELECT roles.name, + roles.id +FROM roles +; +GRANT SELECT ON lv_roles TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_teams of entity teams for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_teams AS +SELECT teams.name, + districts.name AS district_id_expanded, + teams.district_id, + teams.latitude, + teams.longitude, + teams.id +FROM teams, districts +WHERE teams.district_id = districts.id +; +GRANT SELECT ON lv_teams TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- convenience view lv_visits of entity visits for lists, et cetera +------------------------------------------------------------------------ +CREATE VIEW lv_visits AS +SELECT addresses.address ||', '|| addresses.postcode AS address_id_expanded, + visits.address_id, + canvassers.username ||', '|| canvassers.fullname ||', '|| canvassers.email AS canvasser_id_expanded, + visits.canvasser_id, + visits.date, + visits.id +FROM visits, canvassers, addresses +WHERE visits.address_id = addresses.id + AND visits.canvasser_id = canvassers.id +; +GRANT SELECT ON lv_visits TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; + +------------------------------------------------------------------------ +-- referential integrity links for primary tables +------------------------------------------------------------------------ + +ALTER TABLE addresses ADD CONSTRAINT ri_addresses_districts_district_id + FOREIGN KEY( district_id ) + REFERENCES districts(id) + ON DELETE NO ACTION ; + +ALTER TABLE canvassers ADD CONSTRAINT ri_canvassers_addresses_address_id + FOREIGN KEY( address_id ) + REFERENCES addresses(id) + ON DELETE NO ACTION ; + +ALTER TABLE canvassers ADD CONSTRAINT ri_canvassers_authorities_authority_id + FOREIGN KEY( authority_id ) + REFERENCES authorities(id) + ON DELETE NO ACTION ; + +ALTER TABLE canvassers ADD CONSTRAINT ri_canvassers_electors_elector_id + FOREIGN KEY( elector_id ) + REFERENCES electors(id) + ON DELETE NO ACTION ; + +ALTER TABLE dwellings ADD CONSTRAINT ri_dwellings_addresses_address_id + FOREIGN KEY( address_id ) + REFERENCES addresses(id) + ON DELETE NO ACTION ; + +ALTER TABLE electors ADD CONSTRAINT ri_electors_dwellings_dwelling_id + FOREIGN KEY( dwelling_id ) + REFERENCES dwellings(id) + ON DELETE NO ACTION ; + +ALTER TABLE electors ADD CONSTRAINT ri_electors_genders_gender + FOREIGN KEY( gender ) + REFERENCES genders(id) + ON DELETE NO ACTION ; + +ALTER TABLE followupactions ADD CONSTRAINT ri_followupactions_canvassers_actor + FOREIGN KEY( actor ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE followupactions ADD CONSTRAINT ri_followupactions_followuprequests_request_id + FOREIGN KEY( request_id ) + REFERENCES followuprequests(id) + ON DELETE NO ACTION ; + +ALTER TABLE followuprequests ADD CONSTRAINT ri_followuprequests_electors_elector_id + FOREIGN KEY( elector_id ) + REFERENCES electors(id) + ON DELETE NO ACTION ; + +ALTER TABLE followuprequests ADD CONSTRAINT ri_followuprequests_issues_issue_id + FOREIGN KEY( issue_id ) + REFERENCES issues(id) + ON DELETE NO ACTION ; + +ALTER TABLE followuprequests ADD CONSTRAINT ri_followuprequests_followupmethods_method_id + FOREIGN KEY( method_id ) + REFERENCES followupmethods(id) + ON DELETE NO ACTION ; + +ALTER TABLE followuprequests ADD CONSTRAINT ri_followuprequests_visits_visit_id + FOREIGN KEY( visit_id ) + REFERENCES visits(id) + ON DELETE NO ACTION ; + +ALTER TABLE intentions ADD CONSTRAINT ri_intentions_electors_elector_id + FOREIGN KEY( elector_id ) + REFERENCES electors(id) + ON DELETE NO ACTION ; + +ALTER TABLE intentions ADD CONSTRAINT ri_intentions_options_option_id + FOREIGN KEY( option_id ) + REFERENCES options(id) + ON DELETE NO ACTION ; + +ALTER TABLE intentions ADD CONSTRAINT ri_intentions_visits_visit_id + FOREIGN KEY( visit_id ) + REFERENCES visits(id) + ON DELETE NO ACTION ; + +ALTER TABLE teams ADD CONSTRAINT ri_teams_districts_district_id + FOREIGN KEY( district_id ) + REFERENCES districts(id) + ON DELETE NO ACTION ; + +ALTER TABLE visits ADD CONSTRAINT ri_visits_addresses_address_id + FOREIGN KEY( address_id ) + REFERENCES addresses(id) + ON DELETE NO ACTION ; + +ALTER TABLE visits ADD CONSTRAINT ri_visits_canvassers_canvasser_id + FOREIGN KEY( canvasser_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +------------------------------------------------------------------------ +-- link table joining canvassers with roles +------------------------------------------------------------------------ +CREATE TABLE ln_canvassers_roles +( + canvassers_id INTEGER, + roles_id INTEGER +); +GRANT SELECT ON ln_canvassers_roles TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_canvassers_roles TO admin, + canvassers, + teamorganisers ; +GRANT UPDATE ON ln_canvassers_roles TO admin, + canvassers, + teamorganisers ; +GRANT DELETE ON ln_canvassers_roles TO admin ; + +ALTER TABLE ln_canvassers_roles ADD CONSTRAINT ri_ln_canvassers_roles_canvassers_canvassers_id + FOREIGN KEY( canvassers_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_canvassers_roles ADD CONSTRAINT ri_ln_canvassers_roles_roles_roles_id + FOREIGN KEY( roles_id ) + REFERENCES roles(id) + ON DELETE NO ACTION ; + + + +------------------------------------------------------------------------ +-- link table joining teams with canvassers +------------------------------------------------------------------------ +CREATE TABLE ln_canvassers_teams +( + teams_id INTEGER, + canvassers_id INTEGER +); +GRANT SELECT ON ln_canvassers_teams TO admin, + analysts, + canvassers, + issueeditors, + issueexperts, + teamorganisers ; +GRANT INSERT ON ln_canvassers_teams TO admin, + teamorganisers ; +GRANT UPDATE ON ln_canvassers_teams TO admin, + teamorganisers ; +GRANT DELETE ON ln_canvassers_teams TO admin ; + +ALTER TABLE ln_canvassers_teams ADD CONSTRAINT ri_ln_canvassers_teams_canvassers_canvassers_id + FOREIGN KEY( canvassers_id ) + REFERENCES canvassers(id) + ON DELETE NO ACTION ; + +ALTER TABLE ln_canvassers_teams ADD CONSTRAINT ri_ln_canvassers_teams_teams_teams_id + FOREIGN KEY( teams_id ) + REFERENCES teams(id) + ON DELETE NO ACTION ; + diff --git a/resources/templates/auto/application-index.html b/resources/templates/auto/application-index.html index eecae38..b1c7666 100644 --- a/resources/templates/auto/application-index.html +++ b/resources/templates/auto/application-index.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html index b834a12..ac19037 100644 --- a/resources/templates/auto/form-addresses-Address.html +++ b/resources/templates/auto/form-addresses-Address.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -50,6 +53,10 @@ address {{record.address}} +{% else %} + +You are not permitted to view address of addresses + {% endifmemberof %} {% endifmemberof %}

@@ -64,6 +71,10 @@ postcode {{record.postcode}} +{% else %} + +You are not permitted to view postcode of addresses + {% endifmemberof %} {% endifmemberof %}

@@ -78,6 +89,10 @@ phone {{record.phone}} +{% else %} + +You are not permitted to view phone of addresses + {% endifmemberof %} {% endifmemberof %}

@@ -97,108 +112,9 @@ district_id {{record.district_id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.latitude}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.longitude}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.address}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.postcode}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.phone}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.district_id}} + +You are not permitted to view district_id of addresses {% endifmemberof %} {% endifmemberof %} @@ -214,6 +130,10 @@ latitude {{record.latitude}} +{% else %} + +You are not permitted to view latitude of addresses + {% endifmemberof %} {% endifmemberof %}

@@ -228,6 +148,10 @@ longitude {{record.longitude}} +{% else %} + +You are not permitted to view longitude of addresses + {% endifmemberof %} {% endifmemberof %}

@@ -235,13 +159,13 @@ longitude - +

- +

diff --git a/resources/templates/auto/form-authorities-Authority.html b/resources/templates/auto/form-authorities-Authority.html index 1f29004..ba66240 100644 --- a/resources/templates/auto/form-authorities-Authority.html +++ b/resources/templates/auto/form-authorities-Authority.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -35,33 +35,9 @@ id {{record.id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} + +You are not permitted to view id of authorities {% endifmemberof %} {% endifmemberof %} @@ -70,13 +46,13 @@ id - +

- +

diff --git a/resources/templates/auto/form-canvassers-Canvasser.html b/resources/templates/auto/form-canvassers-Canvasser.html index 32fe009..8ca174a 100644 --- a/resources/templates/auto/form-canvassers-Canvasser.html +++ b/resources/templates/auto/form-canvassers-Canvasser.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -50,6 +53,10 @@ username {{record.username}} +{% else %} + +You are not permitted to view username of canvassers + {% endifmemberof %} {% endifmemberof %}

@@ -64,6 +71,10 @@ fullname {{record.fullname}} +{% else %} + +You are not permitted to view fullname of canvassers + {% endifmemberof %} {% endifmemberof %}

@@ -83,146 +94,9 @@ elector_id {{record.elector_id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} -

- - -
{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.address_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.phone}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.email}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} -

- - -
-{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.authority_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.authorised}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.username}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.fullname}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} -

- - -
-{% else %} -{% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.elector_id}} + +You are not permitted to view elector_id of canvassers {% endifmemberof %} {% endifmemberof %} @@ -243,6 +117,10 @@ address_id {{record.address_id}} +{% else %} + +You are not permitted to view address_id of canvassers + {% endifmemberof %} {% endifmemberof %}

@@ -257,6 +135,10 @@ phone {{record.phone}} +{% else %} + +You are not permitted to view phone of canvassers + {% endifmemberof %} {% endifmemberof %}

@@ -271,6 +153,10 @@ email {{record.email}} +{% else %} + +You are not permitted to view email of canvassers + {% endifmemberof %} {% endifmemberof %}

@@ -290,6 +176,10 @@ authority_id {{record.authority_id}} +{% else %} + +You are not permitted to view authority_id of canvassers + {% endifmemberof %} {% endifmemberof %}

@@ -304,6 +194,10 @@ authorised {{record.authorised}} +{% else %} + +You are not permitted to view authorised of canvassers + {% endifmemberof %} {% endifmemberof %}

@@ -322,6 +216,10 @@ roles {{record.roles}} +{% else %} + +You are not permitted to view roles of canvassers + {% endifmemberof %} {% endifmemberof %}

@@ -329,13 +227,13 @@ roles - +

- +

diff --git a/resources/templates/auto/form-districts-District.html b/resources/templates/auto/form-districts-District.html index 1ba76eb..7ca8ef3 100644 --- a/resources/templates/auto/form-districts-District.html +++ b/resources/templates/auto/form-districts-District.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -50,33 +53,9 @@ name {{record.name}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin admin %} - {% else %} -{% ifmemberof public admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin admin %} - -{% else %} -{% ifmemberof public admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.name}} + +You are not permitted to view name of districts {% endifmemberof %} {% endifmemberof %} @@ -85,13 +64,13 @@ name - +

- +

diff --git a/resources/templates/auto/form-dwellings-Dwelling.html b/resources/templates/auto/form-dwellings-Dwelling.html index 1c716e9..4df6725 100644 --- a/resources/templates/auto/form-dwellings-Dwelling.html +++ b/resources/templates/auto/form-dwellings-Dwelling.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -55,52 +58,9 @@ address_id {{record.address_id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.sub-address}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.address_id}} + +You are not permitted to view address_id of dwellings {% endifmemberof %} {% endifmemberof %} @@ -116,6 +76,10 @@ sub-address {{record.sub-address}} +{% else %} + +You are not permitted to view sub-address of dwellings + {% endifmemberof %} {% endifmemberof %}

@@ -123,13 +87,13 @@ sub-address - +

- +

diff --git a/resources/templates/auto/form-electors-Elector.html b/resources/templates/auto/form-electors-Elector.html index 4b1a2a4..6ebac98 100644 --- a/resources/templates/auto/form-electors-Elector.html +++ b/resources/templates/auto/form-electors-Elector.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -50,6 +53,10 @@ name {{record.name}} +{% else %} + +You are not permitted to view name of electors + {% endifmemberof %} {% endifmemberof %}

@@ -69,6 +76,10 @@ dwelling_id {{record.dwelling_id}} +{% else %} + +You are not permitted to view dwelling_id of electors + {% endifmemberof %} {% endifmemberof %}

@@ -83,6 +94,10 @@ phone {{record.phone}} +{% else %} + +You are not permitted to view phone of electors + {% endifmemberof %} {% endifmemberof %}

@@ -97,6 +112,10 @@ email {{record.email}} +{% else %} + +You are not permitted to view email of electors + {% endifmemberof %} {% endifmemberof %}

@@ -115,98 +134,9 @@ gender {{record.gender}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.name}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.dwelling_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.phone}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.email}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.gender}} + +You are not permitted to view gender of electors {% endifmemberof %} {% endifmemberof %} @@ -215,13 +145,13 @@ gender - +

- +

diff --git a/resources/templates/auto/form-followupactions-Followupaction.html b/resources/templates/auto/form-followupactions-Followupaction.html index 4ab1afc..1e41b25 100644 --- a/resources/templates/auto/form-followupactions-Followupaction.html +++ b/resources/templates/auto/form-followupactions-Followupaction.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -55,99 +58,9 @@ request_id {{record.request_id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
{% else %} -{% ifmemberof issueexperts canvassers analysts issueeditors admin %} - -{{record.actor}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof issueexperts canvassers analysts issueeditors admin %} - -{{record.date}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof issueexperts canvassers analysts issueeditors admin %} - -{{record.notes}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof issueexperts canvassers analysts issueeditors admin %} - -{{record.closed}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof issueexperts canvassers analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
-{% else %} -{% ifmemberof issueexperts canvassers analysts issueeditors admin %} - -{{record.request_id}} + +You are not permitted to view request_id of followupactions {% endifmemberof %} {% endifmemberof %} @@ -168,6 +81,10 @@ actor {{record.actor}} +{% else %} + +You are not permitted to view actor of followupactions + {% endifmemberof %} {% endifmemberof %}

@@ -182,6 +99,10 @@ date {{record.date}} +{% else %} + +You are not permitted to view date of followupactions + {% endifmemberof %} {% endifmemberof %}

@@ -196,6 +117,10 @@ notes {{record.notes}} +{% else %} + +You are not permitted to view notes of followupactions + {% endifmemberof %} {% endifmemberof %}

@@ -210,6 +135,10 @@ closed {{record.closed}} +{% else %} + +You are not permitted to view closed of followupactions + {% endifmemberof %} {% endifmemberof %}

@@ -217,13 +146,13 @@ closed - +

- +

diff --git a/resources/templates/auto/form-followupmethods-Followupmethod.html b/resources/templates/auto/form-followupmethods-Followupmethod.html index dd0726b..7e2eb2e 100644 --- a/resources/templates/auto/form-followupmethods-Followupmethod.html +++ b/resources/templates/auto/form-followupmethods-Followupmethod.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -35,33 +35,9 @@ id {{record.id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} + +You are not permitted to view id of followupmethods {% endifmemberof %} {% endifmemberof %} @@ -70,13 +46,13 @@ id - +

- +

diff --git a/resources/templates/auto/form-followuprequests-Followuprequest.html b/resources/templates/auto/form-followuprequests-Followuprequest.html index d925dac..0edcbf9 100644 --- a/resources/templates/auto/form-followuprequests-Followuprequest.html +++ b/resources/templates/auto/form-followuprequests-Followuprequest.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -55,93 +58,9 @@ elector_id {{record.elector_id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.visit_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.issue_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.method_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.elector_id}} + +You are not permitted to view elector_id of followuprequests {% endifmemberof %} {% endifmemberof %} @@ -162,6 +81,10 @@ visit_id {{record.visit_id}} +{% else %} + +You are not permitted to view visit_id of followuprequests + {% endifmemberof %} {% endifmemberof %}

@@ -180,6 +103,10 @@ issue_id {{record.issue_id}} +{% else %} + +You are not permitted to view issue_id of followuprequests + {% endifmemberof %} {% endifmemberof %}

@@ -198,6 +125,10 @@ method_id {{record.method_id}} +{% else %} + +You are not permitted to view method_id of followuprequests + {% endifmemberof %} {% endifmemberof %}

@@ -205,13 +136,13 @@ method_id - +

- +

diff --git a/resources/templates/auto/form-genders-Gender.html b/resources/templates/auto/form-genders-Gender.html index 516b224..7113f50 100644 --- a/resources/templates/auto/form-genders-Gender.html +++ b/resources/templates/auto/form-genders-Gender.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -35,33 +35,9 @@ id {{record.id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} + +You are not permitted to view id of genders {% endifmemberof %} {% endifmemberof %} @@ -70,13 +46,13 @@ id - +

- +

diff --git a/resources/templates/auto/form-intentions-Intention.html b/resources/templates/auto/form-intentions-Intention.html index 3991bf2..084901d 100644 --- a/resources/templates/auto/form-intentions-Intention.html +++ b/resources/templates/auto/form-intentions-Intention.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -60,61 +63,9 @@ elector_id {{record.elector_id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- -
{% else %} -{% ifmemberof canvassers analysts admin %} - -{{record.option_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
-{% else %} -{% ifmemberof canvassers analysts admin %} - -{{record.visit_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} -

- - -
-{% else %} -{% ifmemberof canvassers analysts admin %} - -{{record.elector_id}} + +You are not permitted to view elector_id of intentions {% endifmemberof %} {% endifmemberof %} @@ -134,6 +85,10 @@ option_id {{record.option_id}} +{% else %} + +You are not permitted to view option_id of intentions + {% endifmemberof %} {% endifmemberof %}

@@ -141,13 +96,13 @@ option_id - +

- +

diff --git a/resources/templates/auto/form-issues-Issue.html b/resources/templates/auto/form-issues-Issue.html index e5268cc..682ea18 100644 --- a/resources/templates/auto/form-issues-Issue.html +++ b/resources/templates/auto/form-issues-Issue.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -35,19 +35,9 @@ id {{record.id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof issueeditors admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} + +You are not permitted to view id of issues {% endifmemberof %} {% endifmemberof %} @@ -63,47 +53,9 @@ url {{record.url}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof issueeditors admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.current}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof issueeditors admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof issueeditors admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.url}} + +You are not permitted to view url of issues {% endifmemberof %} {% endifmemberof %} @@ -119,6 +71,10 @@ current {{record.current}} +{% else %} + +You are not permitted to view current of issues + {% endifmemberof %} {% endifmemberof %}

@@ -126,13 +82,13 @@ current - +

- +

diff --git a/resources/templates/auto/form-options-Option.html b/resources/templates/auto/form-options-Option.html index b5c791c..90bc5ad 100644 --- a/resources/templates/auto/form-options-Option.html +++ b/resources/templates/auto/form-options-Option.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -35,33 +35,9 @@ id {{record.id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} + +You are not permitted to view id of options {% endifmemberof %} {% endifmemberof %} @@ -70,13 +46,13 @@ id - +

- +

diff --git a/resources/templates/auto/form-roles-Role.html b/resources/templates/auto/form-roles-Role.html index dad1065..2a8b4d8 100644 --- a/resources/templates/auto/form-roles-Role.html +++ b/resources/templates/auto/form-roles-Role.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -50,33 +53,9 @@ name {{record.name}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.name}} + +You are not permitted to view name of roles {% endifmemberof %} {% endifmemberof %} @@ -97,6 +76,10 @@ members {{record.members}} +{% else %} + +You are not permitted to view members of roles + {% endifmemberof %} {% endifmemberof %}

@@ -104,13 +87,13 @@ members - +

- +

diff --git a/resources/templates/auto/form-teams-Team.html b/resources/templates/auto/form-teams-Team.html index ff0da63..af9e574 100644 --- a/resources/templates/auto/form-teams-Team.html +++ b/resources/templates/auto/form-teams-Team.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -50,6 +53,10 @@ name {{record.name}} +{% else %} + +You are not permitted to view name of teams + {% endifmemberof %} {% endifmemberof %}

@@ -69,80 +76,9 @@ district_id {{record.district_id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof teamorganisers admin %} - {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.latitude}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.longitude}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof teamorganisers admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.name}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof teamorganisers admin %} -

- - -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.district_id}} + +You are not permitted to view district_id of teams {% endifmemberof %} {% endifmemberof %} @@ -158,6 +94,10 @@ latitude {{record.latitude}} +{% else %} + +You are not permitted to view latitude of teams + {% endifmemberof %} {% endifmemberof %}

@@ -177,6 +117,10 @@ members {{record.members}} +{% else %} + +You are not permitted to view members of teams + {% endifmemberof %} {% endifmemberof %}

@@ -196,6 +140,10 @@ organisers {{record.organisers}} +{% else %} + +You are not permitted to view organisers of teams + {% endifmemberof %} {% endifmemberof %}

@@ -210,6 +158,10 @@ longitude {{record.longitude}} +{% else %} + +You are not permitted to view longitude of teams + {% endifmemberof %} {% endifmemberof %}

@@ -217,13 +169,13 @@ longitude - +

- +

diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html index 0ac8647..1804624 100644 --- a/resources/templates/auto/form-visits-Visit.html +++ b/resources/templates/auto/form-visits-Visit.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -24,7 +24,6 @@ See [Application Description Language](https://github.com/simon-brooke/adl).-->
{% csrf-field %} -

@@ -55,71 +58,9 @@ address_id {{record.address_id}} -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin admin %} -

- - -
{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.canvasser_id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.date}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin admin %} - -{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.id}} - -{% endifmemberof %} -{% endifmemberof %} -

-

- -{% ifmemberof admin admin %} -

- - -
-{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} - -{{record.address_id}} + +You are not permitted to view address_id of visits {% endifmemberof %} {% endifmemberof %} @@ -140,6 +81,10 @@ canvasser_id {{record.canvasser_id}} +{% else %} + +You are not permitted to view canvasser_id of visits + {% endifmemberof %} {% endifmemberof %}

@@ -154,6 +99,10 @@ date {{record.date}} +{% else %} + +You are not permitted to view date of visits + {% endifmemberof %} {% endifmemberof %}

@@ -161,13 +110,13 @@ date - +

- +

diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html index b7cf928..2d6d337 100644 --- a/resources/templates/auto/list-addresses-Addresses.html +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-authorities-Authorities.html b/resources/templates/auto/list-authorities-Authorities.html index 05c6158..3271ca8 100644 --- a/resources/templates/auto/list-authorities-Authorities.html +++ b/resources/templates/auto/list-authorities-Authorities.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html index 1c18f20..01549e9 100644 --- a/resources/templates/auto/list-canvassers-Canvassers.html +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html index 834ad46..e25f1f2 100644 --- a/resources/templates/auto/list-districts-Districts.html +++ b/resources/templates/auto/list-districts-Districts.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html index 40943b9..aad96d1 100644 --- a/resources/templates/auto/list-dwellings-Dwellings.html +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html index e83a31a..0c4ebb3 100644 --- a/resources/templates/auto/list-electors-Electors.html +++ b/resources/templates/auto/list-electors-Electors.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html index af574bb..bba3be3 100644 --- a/resources/templates/auto/list-followupactions-Followupactions.html +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followupmethods-Followupmethods.html b/resources/templates/auto/list-followupmethods-Followupmethods.html index 7667db0..3949d0b 100644 --- a/resources/templates/auto/list-followupmethods-Followupmethods.html +++ b/resources/templates/auto/list-followupmethods-Followupmethods.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followuprequests-Followuprequests.html b/resources/templates/auto/list-followuprequests-Followuprequests.html index 79c4229..4889980 100644 --- a/resources/templates/auto/list-followuprequests-Followuprequests.html +++ b/resources/templates/auto/list-followuprequests-Followuprequests.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-genders-Genders.html b/resources/templates/auto/list-genders-Genders.html index 8e5ca41..ebc8cf6 100644 --- a/resources/templates/auto/list-genders-Genders.html +++ b/resources/templates/auto/list-genders-Genders.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-intentions-Intentions.html b/resources/templates/auto/list-intentions-Intentions.html index 37ef15e..fb8d0d6 100644 --- a/resources/templates/auto/list-intentions-Intentions.html +++ b/resources/templates/auto/list-intentions-Intentions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html index 658c994..ccab390 100644 --- a/resources/templates/auto/list-issues-Issues.html +++ b/resources/templates/auto/list-issues-Issues.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-options-Options.html b/resources/templates/auto/list-options-Options.html index 614a531..ed3dac1 100644 --- a/resources/templates/auto/list-options-Options.html +++ b/resources/templates/auto/list-options-Options.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html index 8578115..abe6b54 100644 --- a/resources/templates/auto/list-roles-Roles.html +++ b/resources/templates/auto/list-roles-Roles.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html index c7a2a3b..64a7f4a 100644 --- a/resources/templates/auto/list-teams-Teams.html +++ b/resources/templates/auto/list-teams-Teams.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html index 6b26d8a..9101021 100644 --- a/resources/templates/auto/list-visits-Visits.html +++ b/resources/templates/auto/list-visits-Visits.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/src/clj/youyesyet/db/core.clj b/src/clj/youyesyet/db/core.clj index 49bbe06..2158558 100644 --- a/src/clj/youyesyet/db/core.clj +++ b/src/clj/youyesyet/db/core.clj @@ -17,7 +17,6 @@ Timestamp PreparedStatement])) -;; (def ^:dynamic *db* {:name "java:comp/env/jdbc/EmployeeDB"}) (defstate ^:dynamic *db* :start (conman/connect! {:jdbc-url-env (env :database-url) :jdbc-url "jdbc:postgresql://127.0.0.1/youyesyet_dev?user=youyesyet&password=thisisnotsecure" diff --git a/src/clj/youyesyet/layout.clj b/src/clj/youyesyet/layout.clj index b38365d..88f3514 100644 --- a/src/clj/youyesyet/layout.clj +++ b/src/clj/youyesyet/layout.clj @@ -1,67 +1,55 @@ - (ns^{:doc "Render web pages using Selmer templating markup." - :author "Simon Brooke"} - youyesyet.layout - (:require [selmer.parser :as parser] - [selmer.filters :as filters] - [markdown.core :refer [md-to-html-string]] - [noir.session :as session] - [ring.util.http-response :refer [content-type ok]] - [ring.util.anti-forgery :refer [anti-forgery-field]] - [ring.middleware.anti-forgery :refer [*anti-forgery-token*]])) +(ns^{:doc "Render web pages using Selmer templating markup." + :author "Simon Brooke"} + youyesyet.layout + (:require [adl-support.tags :as tags] + [markdown.core :refer [md-to-html-string]] + [noir.session :as session] + [ring.util.http-response :refer [content-type ok]] + [ring.util.anti-forgery :refer [anti-forgery-field]] + [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] + [selmer.parser :as parser] + [selmer.filters :as filters] + )) - (declare ^:dynamic *app-context*) - (parser/set-resource-path! (clojure.java.io/resource "templates")) - (parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) - (filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)])) +(declare ^:dynamic *app-context*) +(parser/set-resource-path! (clojure.java.io/resource "templates")) +(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) +(filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)])) - (parser/add-tag! :ifmemberof - (fn [args context content] - (let [permitted (if args (some (:user-groups context) args) false)] - (if permitted - (get-in content [:ifreadable :content])))) - :else - (fn [args context content] - (let [permitted (if args (some (:user-groups context) args) false)] - (if (not permitted) - (get-in content [:else :content])))) - :ifmemberof) +(defn raw-get-user-roles [_] + #{"admin" "canvassers"}) + +(def get-user-roles (memoize raw-get-user-roles)) - (defn raw-get-user-roles [_] - #{"admin" "canvassers"}) - - (def get-user-roles (memoize raw-get-user-roles)) - - - (defn render - "renders the HTML template located relative to resources/templates" - [template & [params]] - (let [user (try session/get :user)] - (content-type - (ok +(defn render + "renders the HTML template located relative to resources/templates" + [template & [params]] + (let [user (try session/get :user)] + (content-type + (ok (parser/render-file - template - (assoc params - :page template - :csrf-token *anti-forgery-token* - :user user - :user-roles [get-user-roles user] - :version (System/getProperty "youyesyet.version")))) - "text/html; charset=utf-8"))) + template + (assoc params + :page template + :csrf-token *anti-forgery-token* + :user user + :user-roles (get-user-roles user) + :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 - :title - error title (optional) - :message - detailed error message (optional) - - returns a response map with the error page as the body - and the status specified by the status key" - [error-details] - {:status (:status error-details) - :headers {"Content-Type" "text/html; charset=utf-8"} - :body (parser/render-file "error.html" error-details)}) +(defn error-page + "error-details should be a map containing the following keys: + :status - error status + :title - error title (optional) + :message - detailed error message (optional) + returns a response map with the error page as the body + and the status specified by the status key" + [error-details] + {:status (:status error-details) + :headers {"Content-Type" "text/html; charset=utf-8"} + :body (parser/render-file "error.html" error-details)}) diff --git a/src/clj/youyesyet/routes/auto.clj b/src/clj/youyesyet/routes/auto.clj index ade6436..e881916 100644 --- a/src/clj/youyesyet/routes/auto.clj +++ b/src/clj/youyesyet/routes/auto.clj @@ -1,7 +1,8 @@ (ns youyesyet.routes.auto - "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180617T110557.025Z" + "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180619T185151.309Z" (:require + [adl-support.core :as support] [clojure.java.io :as io] [compojure.core :refer [defroutes GET POST]] [hugsql.core :as hugsql] @@ -13,119 +14,20 @@ [youyesyet.routes.manual :as m])) (defn - raw-resolve-template - [n] - (if - (.exists (io/as-file (str "resources/templates/" n))) - n - (str "auto/" n))) - -(def resolve-template (memoize raw-resolve-template)) - -(defn - index + admin [r] (l/render - (resolve-template "application-index.html") + (support/resolve-template "application-index.html") {:title "Administrative menu"})) -(defn - list-electors-Electors - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-electors-Electors.html") - {:title "Electors", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-elector db/*db* p) - (db/list-electors db/*db* {}))}))) - -(defn - form-electors-Elector - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-electors-Elector.html") - {:title "Elector", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-elector db/*db* p))}))) - -(defn - list-genders-Genders - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-genders-Genders.html") - {:title "Genders", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-gender db/*db* p) - (db/list-genders db/*db* {}))}))) - -(defn - form-genders-Gender - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-genders-Gender.html") - {:title "Gender", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-gender db/*db* p))}))) - -(defn - list-dwellings-Dwellings - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-dwellings-Dwellings.html") - {:title "Dwellings", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-dwelling db/*db* p) - (db/list-dwellings db/*db* {}))}))) - -(defn - form-dwellings-Dwelling - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-dwellings-Dwelling.html") - {:title "Dwelling", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-dwelling db/*db* p))}))) - (defn list-addresses-Addresses [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "list-addresses-Addresses.html") + (support/resolve-template "list-addresses-Addresses.html") {:title "Addresses", :params p, :records @@ -138,51 +40,24 @@ form-addresses-Address [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "form-addresses-Address.html") + (support/resolve-template "form-addresses-Address.html") {:title "Address", :params p, :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-address db/*db* p))}))) - -(defn - list-visits-Visits - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-visits-Visits.html") - {:title "Visits", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-visit db/*db* p) - (db/list-visits db/*db* {}))}))) - -(defn - form-visits-Visit - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-visits-Visit.html") - {:title "Visit", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-visit db/*db* p))}))) + (if (empty? (remove nil? (vals p))) [] (db/get-address db/*db* p)), + :districts (db/list-districts db/*db*)}))) (defn list-authorities-Authorities [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "list-authorities-Authorities.html") + (support/resolve-template "list-authorities-Authorities.html") {:title "Authorities", :params p, :records @@ -195,9 +70,10 @@ form-authorities-Authority [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "form-authorities-Authority.html") + (support/resolve-template "form-authorities-Authority.html") {:title "Authority", :params p, :record @@ -206,70 +82,14 @@ [] (db/get-authority db/*db* p))}))) -(defn - list-issues-Issues - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-issues-Issues.html") - {:title "Issues", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-issue db/*db* p) - (db/list-issues db/*db* {}))}))) - -(defn - form-issues-Issue - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-issues-Issue.html") - {:title "Issue", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-issue db/*db* p))}))) - -(defn - list-intentions-Intentions - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-intentions-Intentions.html") - {:title "Intentions", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-intention db/*db* p) - (db/list-intentions db/*db* {}))}))) - -(defn - form-intentions-Intention - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-intentions-Intention.html") - {:title "Intention", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-intention db/*db* p))}))) - (defn list-canvassers-Canvassers [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "list-canvassers-Canvassers.html") + (support/resolve-template "list-canvassers-Canvassers.html") {:title "Canvassers", :params p, :records @@ -282,108 +102,29 @@ form-canvassers-Canvasser [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "form-canvassers-Canvasser.html") + (support/resolve-template "form-canvassers-Canvasser.html") {:title "Canvasser", :params p, :record (if (empty? (remove nil? (vals p))) [] - (db/get-canvasser db/*db* p))}))) - -(defn - list-followuprequests-Followuprequests - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-followuprequests-Followuprequests.html") - {:title "Followuprequests", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-followuprequest db/*db* p) - (db/list-followuprequests db/*db* {}))}))) - -(defn - form-followuprequests-Followuprequest - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-followuprequests-Followuprequest.html") - {:title "Followuprequest", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-followuprequest db/*db* p))}))) - -(defn - list-roles-Roles - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-roles-Roles.html") - {:title "Roles", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-role db/*db* p) - (db/list-roles db/*db* {}))}))) - -(defn - form-roles-Role - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-roles-Role.html") - {:title "Role", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-role db/*db* p))}))) - -(defn - list-teams-Teams - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-teams-Teams.html") - {:title "Teams", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-team db/*db* p) - (db/list-teams db/*db* {}))}))) - -(defn - form-teams-Team - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-teams-Team.html") - {:title "Team", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-team db/*db* p))}))) + (db/get-canvasser db/*db* p)), + :electors (db/list-electors db/*db*), + :addresses (db/list-addresses db/*db*), + :authorities (db/list-authorities db/*db*)}))) (defn list-districts-Districts [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "list-districts-Districts.html") + (support/resolve-template "list-districts-Districts.html") {:title "Districts", :params p, :records @@ -396,9 +137,10 @@ form-districts-District [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "form-districts-District.html") + (support/resolve-template "form-districts-District.html") {:title "District", :params p, :record @@ -407,13 +149,79 @@ [] (db/get-district db/*db* p))}))) +(defn + list-dwellings-Dwellings + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-dwellings-Dwellings.html") + {:title "Dwellings", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-dwelling db/*db* p) + (db/list-dwellings db/*db* {}))}))) + +(defn + form-dwellings-Dwelling + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-dwellings-Dwelling.html") + {:title "Dwelling", + :params p, + :record + (if + (empty? (remove nil? (vals p))) + [] + (db/get-dwelling db/*db* p)), + :addresses (db/list-addresses db/*db*)}))) + +(defn + list-electors-Electors + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-electors-Electors.html") + {:title "Electors", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-elector db/*db* p) + (db/list-electors db/*db* {}))}))) + +(defn + form-electors-Elector + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-electors-Elector.html") + {:title "Elector", + :params p, + :record + (if (empty? (remove nil? (vals p))) [] (db/get-elector db/*db* p)), + :dwellings (db/list-dwellings db/*db*), + :genders (db/list-genders db/*db*)}))) + (defn list-followupactions-Followupactions [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "list-followupactions-Followupactions.html") + (support/resolve-template + "list-followupactions-Followupactions.html") {:title "Followupactions", :params p, :records @@ -426,54 +234,30 @@ form-followupactions-Followupaction [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "form-followupactions-Followupaction.html") + (support/resolve-template + "form-followupactions-Followupaction.html") {:title "Followupaction", :params p, :record (if (empty? (remove nil? (vals p))) [] - (db/get-followupaction db/*db* p))}))) - -(defn - list-options-Options - [r] - (let - [p (:params r)] - (l/render - (resolve-template "list-options-Options.html") - {:title "Options", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-option db/*db* p) - (db/list-options db/*db* {}))}))) - -(defn - form-options-Option - [r] - (let - [p (:params r)] - (l/render - (resolve-template "form-options-Option.html") - {:title "Option", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-option db/*db* p))}))) + (db/get-followupaction db/*db* p)), + :followuprequests (db/list-followuprequests db/*db*), + :canvassers (db/list-canvassers db/*db*)}))) (defn list-followupmethods-Followupmethods [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "list-followupmethods-Followupmethods.html") + (support/resolve-template + "list-followupmethods-Followupmethods.html") {:title "Followupmethods", :params p, :records @@ -486,9 +270,11 @@ form-followupmethods-Followupmethod [r] (let - [p (:params r)] + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] (l/render - (resolve-template "form-followupmethods-Followupmethod.html") + (support/resolve-template + "form-followupmethods-Followupmethod.html") {:title "Followupmethod", :params p, :record @@ -497,6 +283,262 @@ [] (db/get-followupmethod db/*db* p))}))) +(defn + list-followuprequests-Followuprequests + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template + "list-followuprequests-Followuprequests.html") + {:title "Followuprequests", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-followuprequest db/*db* p) + (db/list-followuprequests db/*db* {}))}))) + +(defn + form-followuprequests-Followuprequest + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template + "form-followuprequests-Followuprequest.html") + {:title "Followuprequest", + :params p, + :record + (if + (empty? (remove nil? (vals p))) + [] + (db/get-followuprequest db/*db* p)), + :electors (db/list-electors db/*db*), + :visits (db/list-visits db/*db*), + :issues (db/list-issues db/*db*), + :followupmethods (db/list-followupmethods db/*db*)}))) + +(defn + list-genders-Genders + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-genders-Genders.html") + {:title "Genders", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-gender db/*db* p) + (db/list-genders db/*db* {}))}))) + +(defn + form-genders-Gender + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-genders-Gender.html") + {:title "Gender", + :params p, + :record + (if + (empty? (remove nil? (vals p))) + [] + (db/get-gender db/*db* p))}))) + +(defn + list-intentions-Intentions + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-intentions-Intentions.html") + {:title "Intentions", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-intention db/*db* p) + (db/list-intentions db/*db* {}))}))) + +(defn + form-intentions-Intention + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-intentions-Intention.html") + {:title "Intention", + :params p, + :record + (if + (empty? (remove nil? (vals p))) + [] + (db/get-intention db/*db* p)), + :visits (db/list-visits db/*db*), + :electors (db/list-electors db/*db*), + :options (db/list-options db/*db*)}))) + +(defn + list-issues-Issues + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-issues-Issues.html") + {:title "Issues", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-issue db/*db* p) + (db/list-issues db/*db* {}))}))) + +(defn + form-issues-Issue + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-issues-Issue.html") + {:title "Issue", + :params p, + :record + (if (empty? (remove nil? (vals p))) [] (db/get-issue db/*db* p))}))) + +(defn + list-options-Options + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-options-Options.html") + {:title "Options", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-option db/*db* p) + (db/list-options db/*db* {}))}))) + +(defn + form-options-Option + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-options-Option.html") + {:title "Option", + :params p, + :record + (if + (empty? (remove nil? (vals p))) + [] + (db/get-option db/*db* p))}))) + +(defn + list-roles-Roles + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-roles-Roles.html") + {:title "Roles", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-role db/*db* p) + (db/list-roles db/*db* {}))}))) + +(defn + form-roles-Role + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-roles-Role.html") + {:title "Role", + :params p, + :record + (if (empty? (remove nil? (vals p))) [] (db/get-role db/*db* p))}))) + +(defn + list-teams-Teams + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-teams-Teams.html") + {:title "Teams", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-team db/*db* p) + (db/list-teams db/*db* {}))}))) + +(defn + form-teams-Team + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-teams-Team.html") + {:title "Team", + :params p, + :record + (if (empty? (remove nil? (vals p))) [] (db/get-team db/*db* p)), + :districts (db/list-districts db/*db*)}))) + +(defn + list-visits-Visits + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "list-visits-Visits.html") + {:title "Visits", + :params p, + :records + (if + (not (empty? (remove nil? (vals p)))) + (db/search-strings-visit db/*db* p) + (db/list-visits db/*db* {}))}))) + +(defn + form-visits-Visit + [r] + (let + [p + (merge (support/query-string-to-map (:query-string r)) (:params r))] + (l/render + (support/resolve-template "form-visits-Visit.html") + {:title "Visit", + :params p, + :record + (if (empty? (remove nil? (vals p))) [] (db/get-visit db/*db* p)), + :addresses (db/list-addresses db/*db*), + :canvassers (db/list-canvassers db/*db*)}))) + (defn raw-resolve-handler "Prefer the manually-written version of the handler with name `n`, if it exists, to the automatically generated one" diff --git a/src/clj/youyesyet/routes/auto_json.clj b/src/clj/youyesyet/routes/auto_json.clj index b95d993..42ea3a3 100644 --- a/src/clj/youyesyet/routes/auto_json.clj +++ b/src/clj/youyesyet/routes/auto_json.clj @@ -1,7 +1,8 @@ (ns youyesyet.routes.auto-json - "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180617T110553.546Z" + "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180619T185150.149Z" (:require + [adl-support.core :as support] [clojure.java.io :as io] [compojure.core :refer [defroutes GET POST]] [hugsql.core :as hugsql] From 88468461fdc56b2b87823cbf8d11fdcb715bf6aa Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Fri, 29 Jun 2018 11:11:50 +0100 Subject: [PATCH 21/51] Massive progress, following sorting the data import problem --- env/dev/clj/youyesyet/dev_middleware.clj | 15 +- env/dev/clj/youyesyet/env.clj | 6 +- resources/sql/queries.auto.sql | 156 +++++++++--------- resources/sql/youyesyet.postgres.sql | 2 +- .../templates/auto/application-index.html | 2 +- .../auto/form-addresses-Address.html | 14 +- .../auto/form-authorities-Authority.html | 4 +- .../auto/form-canvassers-Canvasser.html | 14 +- .../auto/form-districts-District.html | 6 +- .../auto/form-dwellings-Dwelling.html | 6 +- .../templates/auto/form-electors-Elector.html | 10 +- .../form-followupactions-Followupaction.html | 10 +- .../form-followupmethods-Followupmethod.html | 4 +- ...form-followuprequests-Followuprequest.html | 4 +- .../templates/auto/form-genders-Gender.html | 4 +- .../auto/form-intentions-Intention.html | 2 +- .../templates/auto/form-issues-Issue.html | 8 +- .../templates/auto/form-options-Option.html | 4 +- resources/templates/auto/form-roles-Role.html | 6 +- resources/templates/auto/form-teams-Team.html | 10 +- .../templates/auto/form-visits-Visit.html | 6 +- .../auto/list-addresses-Addresses.html | 2 +- .../auto/list-authorities-Authorities.html | 2 +- .../auto/list-canvassers-Canvassers.html | 2 +- .../auto/list-districts-Districts.html | 2 +- .../auto/list-dwellings-Dwellings.html | 2 +- .../auto/list-electors-Electors.html | 2 +- .../list-followupactions-Followupactions.html | 2 +- .../list-followupmethods-Followupmethods.html | 2 +- ...ist-followuprequests-Followuprequests.html | 2 +- .../templates/auto/list-genders-Genders.html | 2 +- .../auto/list-intentions-Intentions.html | 2 +- .../templates/auto/list-issues-Issues.html | 2 +- .../templates/auto/list-options-Options.html | 2 +- .../templates/auto/list-roles-Roles.html | 2 +- .../templates/auto/list-teams-Teams.html | 2 +- .../templates/auto/list-visits-Visits.html | 2 +- src/clj/youyesyet/handler.clj | 3 +- src/clj/youyesyet/routes/auto.clj | 100 ++++------- src/clj/youyesyet/routes/auto_json.clj | 2 +- 40 files changed, 201 insertions(+), 229 deletions(-) diff --git a/env/dev/clj/youyesyet/dev_middleware.clj b/env/dev/clj/youyesyet/dev_middleware.clj index d64d5d5..e3881c8 100644 --- a/env/dev/clj/youyesyet/dev_middleware.clj +++ b/env/dev/clj/youyesyet/dev_middleware.clj @@ -1,10 +1,13 @@ (ns youyesyet.dev-middleware - (:require [ring.middleware.reload :refer [wrap-reload]] + (:require +;; [ring.middleware.reload :refer [wrap-reload]] [selmer.middleware :refer [wrap-error-page]] - [prone.middleware :refer [wrap-exceptions]])) + [prone.middleware :refer [wrap-exceptions]] + )) (defn wrap-dev [handler] - (-> handler - wrap-reload - wrap-error-page - wrap-exceptions)) + (-> handler +;; wrap-reload + wrap-error-page + wrap-exceptions + )) diff --git a/env/dev/clj/youyesyet/env.clj b/env/dev/clj/youyesyet/env.clj index 87bfa55..ef35fd0 100644 --- a/env/dev/clj/youyesyet/env.clj +++ b/env/dev/clj/youyesyet/env.clj @@ -1,7 +1,8 @@ (ns youyesyet.env (:require [selmer.parser :as parser] [clojure.tools.logging :as log] - [youyesyet.dev-middleware :refer [wrap-dev]])) + [youyesyet.dev-middleware :refer [wrap-dev]] + )) (def defaults {:init @@ -11,4 +12,5 @@ :stop (fn [] (log/info "\n-=[youyesyet has shut down successfully]=-")) - :middleware wrap-dev}) + :middleware wrap-dev + }) diff --git a/resources/sql/queries.auto.sql b/resources/sql/queries.auto.sql index 9de72ba..0f482c4 100644 --- a/resources/sql/queries.auto.sql +++ b/resources/sql/queries.auto.sql @@ -1,7 +1,7 @@ ------------------------------------------------------------------------ -- File queries.sql -- --- autogenerated by adl.to-hugsql-queries at 2018-06-19T18:51:50.013Z +-- autogenerated by adl.to-hugsql-queries at 2018-06-29T10:10:58.177Z -- -- See [Application Description -- Language](https://github.com/simon-brooke/adl). @@ -728,15 +728,15 @@ ORDER BY lv_visits.address_id, -- :name search-strings-address :? :1 -- :doc selects existing address records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_addresses -WHERE -address LIKE '%params.address%' - OR postcode = params.postcode - OR phone LIKE '%params.phone%' - OR district_id = params.district_id - OR latitude = params.latitude - OR longitude = params.longitude - OR locality = params.locality - OR id = params.id +WHERE false + --~ (if (:address params) "OR address LIKE '%:address%'") + --~ (if (:postcode params) "OR postcode LIKE '%:postcode%'") + --~ (if (:phone params) "OR phone LIKE '%:phone%'") + --~ (if (:district_id params) "OR district_id = :district_id") + --~ (if (:latitude params) "OR latitude = :latitude") + --~ (if (:longitude params) "OR longitude = :longitude") + --~ (if (:locality params) "OR locality = :locality") + --~ (if (:id params) "OR id = :id") ORDER BY lv_addresses.address, lv_addresses.postcode, lv_addresses.id @@ -746,24 +746,24 @@ ORDER BY lv_addresses.address, -- :name search-strings-authority :? :1 -- :doc selects existing authority records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_authorities -WHERE -id LIKE '%params.id%' +WHERE false + --~ (if (:id params) "OR id LIKE '%: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 the parameter of the same name by substring match SELECT * FROM lv_canvassers -WHERE -username LIKE '%params.username%' - OR fullname LIKE '%params.fullname%' - OR elector_id = params.elector_id - OR address_id = params.address_id - OR phone LIKE '%params.phone%' - OR email LIKE '%params.email%' - OR authority_id = params.authority_id - OR authorised = params.authorised - OR id = params.id +WHERE false + --~ (if (:username params) "OR username LIKE '%:username%'") + --~ (if (:fullname params) "OR fullname LIKE '%:fullname%'") + --~ (if (:elector_id params) "OR elector_id = :elector_id") + --~ (if (:address_id params) "OR address_id = :address_id") + --~ (if (:phone params) "OR phone LIKE '%:phone%'") + --~ (if (:email params) "OR email LIKE '%:email%'") + --~ (if (:authority_id params) "OR authority_id = :authority_id") + --~ (if (:authorised params) "OR authorised = :authorised") + --~ (if (:id params) "OR id = :id") ORDER BY lv_canvassers.username, lv_canvassers.fullname, lv_canvassers.email, @@ -774,9 +774,9 @@ ORDER BY lv_canvassers.username, -- :name search-strings-district :? :1 -- :doc selects existing district records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_districts -WHERE -name LIKE '%params.name%' - OR id = params.id +WHERE false + --~ (if (:name params) "OR name LIKE '%:name%'") + --~ (if (:id params) "OR id = :id") ORDER BY lv_districts.name, lv_districts.id --~ (if (:offset params) "OFFSET :offset ") @@ -785,10 +785,10 @@ ORDER BY lv_districts.name, -- :name search-strings-dwelling :? :1 -- :doc selects existing dwelling records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_dwellings -WHERE -address_id = params.address_id - OR sub_address LIKE '%params.sub-address%' - OR id = params.id +WHERE false + --~ (if (:address_id params) "OR address_id = :address_id") + --~ (if (:sub-address params) "OR sub_address LIKE '%:sub-address%'") + --~ (if (:id params) "OR id = :id") ORDER BY lv_dwellings.address_id, lv_dwellings.sub_address, lv_dwellings.id @@ -798,13 +798,13 @@ ORDER BY lv_dwellings.address_id, -- :name search-strings-elector :? :1 -- :doc selects existing elector records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_electors -WHERE -name LIKE '%params.name%' - OR dwelling_id = params.dwelling_id - OR phone LIKE '%params.phone%' - OR email LIKE '%params.email%' - OR gender = params.gender - OR id = params.id +WHERE false + --~ (if (:name params) "OR name LIKE '%:name%'") + --~ (if (:dwelling_id params) "OR dwelling_id = :dwelling_id") + --~ (if (:phone params) "OR phone LIKE '%:phone%'") + --~ (if (:email params) "OR email LIKE '%:email%'") + --~ (if (:gender params) "OR gender = :gender") + --~ (if (:id params) "OR id = :id") ORDER BY lv_electors.name, lv_electors.phone, lv_electors.email, @@ -816,13 +816,13 @@ ORDER BY lv_electors.name, -- :name search-strings-followupaction :? :1 -- :doc selects existing followupaction records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_followupactions -WHERE -request_id = params.request_id - OR actor = params.actor - OR date = 'params.date' - OR notes LIKE '%params.notes%' - OR closed = params.closed - OR id = params.id +WHERE false + --~ (if (:request_id params) "OR request_id = :request_id") + --~ (if (:actor params) "OR actor = :actor") + --~ (if (:date params) "OR date = ':date'") + --~ (if (:notes params) "OR notes LIKE '%:notes%'") + --~ (if (:closed params) "OR closed = :closed") + --~ (if (:id params) "OR id = :id") ORDER BY lv_followupactions.date, lv_followupactions.notes, lv_followupactions.id @@ -832,20 +832,20 @@ ORDER BY lv_followupactions.date, -- :name search-strings-followupmethod :? :1 -- :doc selects existing followupmethod records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_followupmethods -WHERE -id LIKE '%params.id%' +WHERE false + --~ (if (:id params) "OR id LIKE '%:id%'") --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name search-strings-followuprequest :? :1 -- :doc selects existing followuprequest records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_followuprequests -WHERE -elector_id = params.elector_id - OR visit_id = params.visit_id - OR issue_id = params.issue_id - OR method_id = params.method_id - OR id = params.id +WHERE false + --~ (if (:elector_id params) "OR elector_id = :elector_id") + --~ (if (:visit_id params) "OR visit_id = :visit_id") + --~ (if (:issue_id params) "OR issue_id = :issue_id") + --~ (if (:method_id params) "OR method_id = :method_id") + --~ (if (:id params) "OR id = :id") ORDER BY lv_followuprequests.elector_id, lv_followuprequests.visit_id, lv_followuprequests.issue_id, @@ -856,46 +856,46 @@ ORDER BY lv_followuprequests.elector_id, -- :name search-strings-gender :? :1 -- :doc selects existing gender records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_genders -WHERE -id LIKE '%params.id%' +WHERE false + --~ (if (:id params) "OR id LIKE '%:id%'") --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name search-strings-intention :? :1 -- :doc selects existing intention records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_intentions -WHERE -visit_id = params.visit_id - OR elector_id = params.elector_id - OR option_id = params.option_id - OR Id = params.Id +WHERE false + --~ (if (:visit_id params) "OR visit_id = :visit_id") + --~ (if (:elector_id params) "OR elector_id = :elector_id") + --~ (if (:option_id params) "OR option_id = :option_id") + --~ (if (:Id params) "OR Id = :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 the parameter of the same name by substring match SELECT * FROM lv_issues -WHERE -url LIKE '%params.url%' - OR current = params.current - OR id LIKE '%params.id%' +WHERE false + --~ (if (:url params) "OR url LIKE '%:url%'") + --~ (if (:current params) "OR current = :current") + --~ (if (:id params) "OR id LIKE '%:id%'") --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") -- :name search-strings-option :? :1 -- :doc selects existing option records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_options -WHERE -id LIKE '%params.id%' +WHERE false + --~ (if (:id params) "OR id LIKE '%:id%'") --~ (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 the parameter of the same name by substring match SELECT * FROM lv_roles -WHERE -name LIKE '%params.name%' - OR id = params.id +WHERE false + --~ (if (:name params) "OR name LIKE '%:name%'") + --~ (if (:id params) "OR id = :id") ORDER BY lv_roles.name, lv_roles.id --~ (if (:offset params) "OFFSET :offset ") @@ -904,12 +904,12 @@ ORDER BY lv_roles.name, -- :name search-strings-team :? :1 -- :doc selects existing team records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_teams -WHERE -name LIKE '%params.name%' - OR district_id = params.district_id - OR latitude = params.latitude - OR longitude = params.longitude - OR id = params.id +WHERE false + --~ (if (:name params) "OR name LIKE '%:name%'") + --~ (if (:district_id params) "OR district_id = :district_id") + --~ (if (:latitude params) "OR latitude = :latitude") + --~ (if (:longitude params) "OR longitude = :longitude") + --~ (if (:id params) "OR id = :id") ORDER BY lv_teams.name, lv_teams.id --~ (if (:offset params) "OFFSET :offset ") @@ -918,11 +918,11 @@ ORDER BY lv_teams.name, -- :name search-strings-visit :? :1 -- :doc selects existing visit records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_visits -WHERE -address_id = params.address_id - OR canvasser_id = params.canvasser_id - OR date = 'params.date' - OR id = params.id +WHERE false + --~ (if (:address_id params) "OR address_id = :address_id") + --~ (if (:canvasser_id params) "OR canvasser_id = :canvasser_id") + --~ (if (:date params) "OR date = ':date'") + --~ (if (:id params) "OR id = :id") ORDER BY lv_visits.address_id, lv_visits.date, lv_visits.id diff --git a/resources/sql/youyesyet.postgres.sql b/resources/sql/youyesyet.postgres.sql index 6ca584c..1bc4b7d 100644 --- a/resources/sql/youyesyet.postgres.sql +++ b/resources/sql/youyesyet.postgres.sql @@ -5,7 +5,7 @@ -- -- auto-generated by [Application Description Language framework] -- --- (https://github.com/simon-brooke/adl) at 20180619T185151.055Z +-- (https://github.com/simon-brooke/adl) at 20180629T101059.126Z -- -- -- A web-app intended to be used by canvassers campaigning for a diff --git a/resources/templates/auto/application-index.html b/resources/templates/auto/application-index.html index b1c7666..1edf694 100644 --- a/resources/templates/auto/application-index.html +++ b/resources/templates/auto/application-index.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html index ac19037..a6d8011 100644 --- a/resources/templates/auto/form-addresses-Address.html +++ b/resources/templates/auto/form-addresses-Address.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -47,7 +47,7 @@ You are not permitted to view id of addresses address {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -65,7 +65,7 @@ You are not permitted to view address of addresses postcode {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -83,7 +83,7 @@ You are not permitted to view postcode of addresses phone {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -124,7 +124,7 @@ You are not permitted to view district_id of addresses latitude {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -142,7 +142,7 @@ You are not permitted to view latitude of addresses longitude {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-authorities-Authority.html b/resources/templates/auto/form-authorities-Authority.html index ba66240..6b4756b 100644 --- a/resources/templates/auto/form-authorities-Authority.html +++ b/resources/templates/auto/form-authorities-Authority.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-canvassers-Canvasser.html b/resources/templates/auto/form-canvassers-Canvasser.html index 8ca174a..baa9afa 100644 --- a/resources/templates/auto/form-canvassers-Canvasser.html +++ b/resources/templates/auto/form-canvassers-Canvasser.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -47,7 +47,7 @@ You are not permitted to view id of canvassers username {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -65,7 +65,7 @@ You are not permitted to view username of canvassers fullname {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -129,7 +129,7 @@ You are not permitted to view address_id of canvassers phone {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -147,7 +147,7 @@ You are not permitted to view phone of canvassers email {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -188,7 +188,7 @@ You are not permitted to view authority_id of canvassers authorised {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers admin canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-districts-District.html b/resources/templates/auto/form-districts-District.html index 7ca8ef3..75a5ca2 100644 --- a/resources/templates/auto/form-districts-District.html +++ b/resources/templates/auto/form-districts-District.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin admin %} - + {% else %} {% ifmemberof public admin canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -47,7 +47,7 @@ You are not permitted to view id of districts name {% ifmemberof admin admin %} - + {% else %} {% ifmemberof public admin canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-dwellings-Dwelling.html b/resources/templates/auto/form-dwellings-Dwelling.html index 4df6725..9fce23f 100644 --- a/resources/templates/auto/form-dwellings-Dwelling.html +++ b/resources/templates/auto/form-dwellings-Dwelling.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -70,7 +70,7 @@ You are not permitted to view address_id of dwellings sub-address {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-electors-Elector.html b/resources/templates/auto/form-electors-Elector.html index 6ebac98..63c2281 100644 --- a/resources/templates/auto/form-electors-Elector.html +++ b/resources/templates/auto/form-electors-Elector.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -47,7 +47,7 @@ You are not permitted to view id of electors name {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -88,7 +88,7 @@ You are not permitted to view dwelling_id of electors phone {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -106,7 +106,7 @@ You are not permitted to view phone of electors email {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-followupactions-Followupaction.html b/resources/templates/auto/form-followupactions-Followupaction.html index 1e41b25..5e2c3a2 100644 --- a/resources/templates/auto/form-followupactions-Followupaction.html +++ b/resources/templates/auto/form-followupactions-Followupaction.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof issueexperts canvassers analysts issueeditors admin %} @@ -93,7 +93,7 @@ You are not permitted to view actor of followupactions date {% ifmemberof admin %} - + {% else %} {% ifmemberof issueexperts canvassers analysts issueeditors admin %} @@ -111,7 +111,7 @@ You are not permitted to view date of followupactions notes {% ifmemberof admin %} - + {% else %} {% ifmemberof issueexperts canvassers analysts issueeditors admin %} @@ -129,7 +129,7 @@ You are not permitted to view notes of followupactions closed {% ifmemberof admin %} - + {% else %} {% ifmemberof issueexperts canvassers analysts issueeditors admin %} diff --git a/resources/templates/auto/form-followupmethods-Followupmethod.html b/resources/templates/auto/form-followupmethods-Followupmethod.html index 7e2eb2e..990d86a 100644 --- a/resources/templates/auto/form-followupmethods-Followupmethod.html +++ b/resources/templates/auto/form-followupmethods-Followupmethod.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-followuprequests-Followuprequest.html b/resources/templates/auto/form-followuprequests-Followuprequest.html index 0edcbf9..e728eaa 100644 --- a/resources/templates/auto/form-followuprequests-Followuprequest.html +++ b/resources/templates/auto/form-followuprequests-Followuprequest.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-genders-Gender.html b/resources/templates/auto/form-genders-Gender.html index 7113f50..75f2d8c 100644 --- a/resources/templates/auto/form-genders-Gender.html +++ b/resources/templates/auto/form-genders-Gender.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-intentions-Intention.html b/resources/templates/auto/form-intentions-Intention.html index 084901d..5092d37 100644 --- a/resources/templates/auto/form-intentions-Intention.html +++ b/resources/templates/auto/form-intentions-Intention.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-issues-Issue.html b/resources/templates/auto/form-issues-Issue.html index 682ea18..2b84f42 100644 --- a/resources/templates/auto/form-issues-Issue.html +++ b/resources/templates/auto/form-issues-Issue.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof issueeditors admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -47,7 +47,7 @@ You are not permitted to view id of issues url {% ifmemberof issueeditors admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -65,7 +65,7 @@ You are not permitted to view url of issues current {% ifmemberof issueeditors admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-options-Option.html b/resources/templates/auto/form-options-Option.html index 90bc5ad..f59aa32 100644 --- a/resources/templates/auto/form-options-Option.html +++ b/resources/templates/auto/form-options-Option.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-roles-Role.html b/resources/templates/auto/form-roles-Role.html index 2a8b4d8..5f71430 100644 --- a/resources/templates/auto/form-roles-Role.html +++ b/resources/templates/auto/form-roles-Role.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -47,7 +47,7 @@ You are not permitted to view id of roles name {% ifmemberof admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-teams-Team.html b/resources/templates/auto/form-teams-Team.html index af9e574..0a0d09d 100644 --- a/resources/templates/auto/form-teams-Team.html +++ b/resources/templates/auto/form-teams-Team.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -47,7 +47,7 @@ You are not permitted to view id of teams name {% ifmemberof teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -88,7 +88,7 @@ You are not permitted to view district_id of teams latitude {% ifmemberof teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -152,7 +152,7 @@ You are not permitted to view organisers of teams longitude {% ifmemberof teamorganisers admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html index 1804624..e07cca1 100644 --- a/resources/templates/auto/form-visits-Visit.html +++ b/resources/templates/auto/form-visits-Visit.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -29,7 +29,7 @@ See [Application Description Language](https://github.com/simon-brooke/adl).--> id {% ifmemberof admin admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} @@ -93,7 +93,7 @@ You are not permitted to view canvasser_id of visits date {% ifmemberof admin admin %} - + {% else %} {% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html index 2d6d337..829be5b 100644 --- a/resources/templates/auto/list-addresses-Addresses.html +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-authorities-Authorities.html b/resources/templates/auto/list-authorities-Authorities.html index 3271ca8..d20fe9d 100644 --- a/resources/templates/auto/list-authorities-Authorities.html +++ b/resources/templates/auto/list-authorities-Authorities.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html index 01549e9..c9719e0 100644 --- a/resources/templates/auto/list-canvassers-Canvassers.html +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html index e25f1f2..982e2d4 100644 --- a/resources/templates/auto/list-districts-Districts.html +++ b/resources/templates/auto/list-districts-Districts.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html index aad96d1..4d633ca 100644 --- a/resources/templates/auto/list-dwellings-Dwellings.html +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html index 0c4ebb3..383fce7 100644 --- a/resources/templates/auto/list-electors-Electors.html +++ b/resources/templates/auto/list-electors-Electors.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html index bba3be3..26697ec 100644 --- a/resources/templates/auto/list-followupactions-Followupactions.html +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followupmethods-Followupmethods.html b/resources/templates/auto/list-followupmethods-Followupmethods.html index 3949d0b..74c7b37 100644 --- a/resources/templates/auto/list-followupmethods-Followupmethods.html +++ b/resources/templates/auto/list-followupmethods-Followupmethods.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followuprequests-Followuprequests.html b/resources/templates/auto/list-followuprequests-Followuprequests.html index 4889980..55a4b48 100644 --- a/resources/templates/auto/list-followuprequests-Followuprequests.html +++ b/resources/templates/auto/list-followuprequests-Followuprequests.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-genders-Genders.html b/resources/templates/auto/list-genders-Genders.html index ebc8cf6..8ce6cc1 100644 --- a/resources/templates/auto/list-genders-Genders.html +++ b/resources/templates/auto/list-genders-Genders.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-intentions-Intentions.html b/resources/templates/auto/list-intentions-Intentions.html index fb8d0d6..c90c0c9 100644 --- a/resources/templates/auto/list-intentions-Intentions.html +++ b/resources/templates/auto/list-intentions-Intentions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html index ccab390..622cbae 100644 --- a/resources/templates/auto/list-issues-Issues.html +++ b/resources/templates/auto/list-issues-Issues.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-options-Options.html b/resources/templates/auto/list-options-Options.html index ed3dac1..bb735be 100644 --- a/resources/templates/auto/list-options-Options.html +++ b/resources/templates/auto/list-options-Options.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html index abe6b54..76cdc21 100644 --- a/resources/templates/auto/list-roles-Roles.html +++ b/resources/templates/auto/list-roles-Roles.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html index 64a7f4a..613cff7 100644 --- a/resources/templates/auto/list-teams-Teams.html +++ b/resources/templates/auto/list-teams-Teams.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html index 9101021..f37ba72 100644 --- a/resources/templates/auto/list-visits-Visits.html +++ b/resources/templates/auto/list-visits-Visits.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index 909dea4..8361e69 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -81,5 +81,4 @@ :title "page not found"}))))) -(def app #'app-routes) -;; (middleware/wrap-base #'app-routes)) +(def app (middleware/wrap-base #'app-routes)) diff --git a/src/clj/youyesyet/routes/auto.clj b/src/clj/youyesyet/routes/auto.clj index e881916..50f81f8 100644 --- a/src/clj/youyesyet/routes/auto.clj +++ b/src/clj/youyesyet/routes/auto.clj @@ -1,6 +1,6 @@ (ns youyesyet.routes.auto - "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180619T185151.309Z" + "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180629T101059.328Z" (:require [adl-support.core :as support] [clojure.java.io :as io] @@ -14,7 +14,7 @@ [youyesyet.routes.manual :as m])) (defn - admin + index [r] (l/render (support/resolve-template "application-index.html") @@ -24,8 +24,7 @@ list-addresses-Addresses [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-addresses-Addresses.html") {:title "Addresses", @@ -40,8 +39,7 @@ form-addresses-Address [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-addresses-Address.html") {:title "Address", @@ -54,8 +52,7 @@ list-authorities-Authorities [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-authorities-Authorities.html") {:title "Authorities", @@ -70,8 +67,7 @@ form-authorities-Authority [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-authorities-Authority.html") {:title "Authority", @@ -86,8 +82,7 @@ list-canvassers-Canvassers [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-canvassers-Canvassers.html") {:title "Canvassers", @@ -102,8 +97,7 @@ form-canvassers-Canvasser [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-canvassers-Canvasser.html") {:title "Canvasser", @@ -121,8 +115,7 @@ list-districts-Districts [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-districts-Districts.html") {:title "Districts", @@ -137,8 +130,7 @@ form-districts-District [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-districts-District.html") {:title "District", @@ -153,8 +145,7 @@ list-dwellings-Dwellings [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-dwellings-Dwellings.html") {:title "Dwellings", @@ -169,8 +160,7 @@ form-dwellings-Dwelling [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-dwellings-Dwelling.html") {:title "Dwelling", @@ -186,8 +176,7 @@ list-electors-Electors [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-electors-Electors.html") {:title "Electors", @@ -202,8 +191,7 @@ form-electors-Elector [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-electors-Elector.html") {:title "Elector", @@ -217,8 +205,7 @@ list-followupactions-Followupactions [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-followupactions-Followupactions.html") @@ -234,8 +221,7 @@ form-followupactions-Followupaction [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-followupactions-Followupaction.html") @@ -253,8 +239,7 @@ list-followupmethods-Followupmethods [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-followupmethods-Followupmethods.html") @@ -270,8 +255,7 @@ form-followupmethods-Followupmethod [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-followupmethods-Followupmethod.html") @@ -287,8 +271,7 @@ list-followuprequests-Followuprequests [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-followuprequests-Followuprequests.html") @@ -304,8 +287,7 @@ form-followuprequests-Followuprequest [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-followuprequests-Followuprequest.html") @@ -325,8 +307,7 @@ list-genders-Genders [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-genders-Genders.html") {:title "Genders", @@ -341,8 +322,7 @@ form-genders-Gender [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-genders-Gender.html") {:title "Gender", @@ -357,8 +337,7 @@ list-intentions-Intentions [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-intentions-Intentions.html") {:title "Intentions", @@ -373,8 +352,7 @@ form-intentions-Intention [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-intentions-Intention.html") {:title "Intention", @@ -392,8 +370,7 @@ list-issues-Issues [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-issues-Issues.html") {:title "Issues", @@ -408,8 +385,7 @@ form-issues-Issue [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-issues-Issue.html") {:title "Issue", @@ -421,8 +397,7 @@ list-options-Options [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-options-Options.html") {:title "Options", @@ -437,8 +412,7 @@ form-options-Option [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-options-Option.html") {:title "Option", @@ -453,8 +427,7 @@ list-roles-Roles [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-roles-Roles.html") {:title "Roles", @@ -469,8 +442,7 @@ form-roles-Role [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-roles-Role.html") {:title "Role", @@ -482,8 +454,7 @@ list-teams-Teams [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-teams-Teams.html") {:title "Teams", @@ -498,8 +469,7 @@ form-teams-Team [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-teams-Team.html") {:title "Team", @@ -512,8 +482,7 @@ list-visits-Visits [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "list-visits-Visits.html") {:title "Visits", @@ -528,8 +497,7 @@ form-visits-Visit [r] (let - [p - (merge (support/query-string-to-map (:query-string r)) (:params r))] + [p (support/massage-params (:params r))] (l/render (support/resolve-template "form-visits-Visit.html") {:title "Visit", diff --git a/src/clj/youyesyet/routes/auto_json.clj b/src/clj/youyesyet/routes/auto_json.clj index 42ea3a3..81a300d 100644 --- a/src/clj/youyesyet/routes/auto_json.clj +++ b/src/clj/youyesyet/routes/auto_json.clj @@ -1,6 +1,6 @@ (ns youyesyet.routes.auto-json - "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180619T185150.149Z" + "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180629T101058.294Z" (:require [adl-support.core :as support] [clojure.java.io :as io] From 4e296537c4ff0046397a210503620b4c47f5e923 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Fri, 29 Jun 2018 17:14:55 +0100 Subject: [PATCH 22/51] Progress on #2, #5; oauth isn't working but not far off Using the Noir session doesn't seem to be the right thing to do. I should be using the Ring session, but I'm having a hard time getting my head round it. @yogthos is too bloody clever! --- env/dev/clj/youyesyet/dev_middleware.clj | 2 +- resources/public/img/authorities/GitHub.png | Bin 0 -> 13424 bytes resources/public/img/authorities/GitHub.xcf | Bin 0 -> 79906 bytes resources/public/img/authorities/Twitter.jpg | Bin 0 -> 26692 bytes resources/public/img/authorities/Twitter.png | Bin 0 -> 6757 bytes resources/public/img/authorities/Twitter.xcf | Bin 0 -> 21415 bytes resources/sql/queries.auto.sql | 42 ++++++-- resources/sql/youyesyet.postgres.sql | 35 ++++-- .../templates/auto/application-index.html | 11 +- .../auto/form-addresses-Address.html | 2 +- .../auto/form-authorities-Authority.html | 92 +++++++++++++++- .../auto/form-canvassers-Canvasser.html | 2 +- .../auto/form-districts-District.html | 2 +- .../auto/form-dwellings-Dwelling.html | 2 +- .../templates/auto/form-electors-Elector.html | 2 +- .../form-followupactions-Followupaction.html | 2 +- .../form-followupmethods-Followupmethod.html | 2 +- ...form-followuprequests-Followuprequest.html | 2 +- .../templates/auto/form-genders-Gender.html | 2 +- .../auto/form-intentions-Intention.html | 20 +++- .../templates/auto/form-issues-Issue.html | 2 +- .../templates/auto/form-options-Option.html | 2 +- resources/templates/auto/form-roles-Role.html | 2 +- resources/templates/auto/form-teams-Team.html | 30 +++--- .../templates/auto/form-visits-Visit.html | 2 +- .../auto/list-addresses-Addresses.html | 2 +- .../auto/list-authorities-Authorities.html | 29 ++++- .../auto/list-canvassers-Canvassers.html | 2 +- .../auto/list-districts-Districts.html | 2 +- .../auto/list-dwellings-Dwellings.html | 6 +- .../auto/list-electors-Electors.html | 2 +- .../list-followupactions-Followupactions.html | 2 +- .../list-followupmethods-Followupmethods.html | 2 +- ...ist-followuprequests-Followuprequests.html | 2 +- .../templates/auto/list-genders-Genders.html | 2 +- .../auto/list-intentions-Intentions.html | 11 +- .../templates/auto/list-issues-Issues.html | 2 +- .../templates/auto/list-options-Options.html | 2 +- .../templates/auto/list-roles-Roles.html | 2 +- .../templates/auto/list-teams-Teams.html | 2 +- .../templates/auto/list-visits-Visits.html | 2 +- resources/templates/base-authenticated.html | 10 +- resources/templates/base.html | 12 +-- resources/templates/login.html | 24 ++++- src/clj/youyesyet/handler.clj | 3 +- src/clj/youyesyet/oauth.clj | 100 +++++++++++++++--- src/clj/youyesyet/routes/auto.clj | 2 +- src/clj/youyesyet/routes/auto_json.clj | 14 ++- src/clj/youyesyet/routes/home.clj | 7 +- src/clj/youyesyet/routes/oauth.clj | 6 +- youyesyet.adl.xml | 21 +++- youyesyet.canonical.adl.xml | 50 ++++++++- 52 files changed, 465 insertions(+), 114 deletions(-) create mode 100644 resources/public/img/authorities/GitHub.png create mode 100644 resources/public/img/authorities/GitHub.xcf create mode 100644 resources/public/img/authorities/Twitter.jpg create mode 100644 resources/public/img/authorities/Twitter.png create mode 100644 resources/public/img/authorities/Twitter.xcf diff --git a/env/dev/clj/youyesyet/dev_middleware.clj b/env/dev/clj/youyesyet/dev_middleware.clj index e3881c8..23fd9db 100644 --- a/env/dev/clj/youyesyet/dev_middleware.clj +++ b/env/dev/clj/youyesyet/dev_middleware.clj @@ -1,6 +1,6 @@ (ns youyesyet.dev-middleware (:require -;; [ring.middleware.reload :refer [wrap-reload]] +;; [ring.middleware.reload :refer [wrap-reload]] ;; this fails with a self referential dependency, which I haven't tracked down. [selmer.middleware :refer [wrap-error-page]] [prone.middleware :refer [wrap-exceptions]] )) diff --git a/resources/public/img/authorities/GitHub.png b/resources/public/img/authorities/GitHub.png new file mode 100644 index 0000000000000000000000000000000000000000..f69f65769e3ab6dd54a1b8fdccf09d3f7b5c8238 GIT binary patch literal 13424 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

TyGN!7A8z7QN(` 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 K(i6V&#Qy`Rj>?+= literal 0 HcmV?d00001 diff --git a/resources/public/img/authorities/GitHub.xcf b/resources/public/img/authorities/GitHub.xcf new file mode 100644 index 0000000000000000000000000000000000000000..936861d48da6bc5f77b715d7bad7a1ce433680f3 GIT binary patch literal 79906 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 T#`t~i{;7gq(9gGc{^8eMw<76~zn* zX)q~E8nVlb-B5%OevjVwy{_N;{+2(U>$&dx+|Ri`_i~-{Ip=v`WoarXARzz(fdtLX zOl&|P9^%fkix<$$D2^WnUObmio0@F20xCM8E{UxUpCNa1bNmCfo?$zlyn-dLz)i>#hr_^mPWYD$X}Q@TwV1)@Yhk< z1;a_^HuK{Lczl=(+8{5a@f%$fY$ct=9?;B6>~7-@#cNaU>zJp3mX7AY?5H!MKHZKy z1|PS6R6p=U(gyCJ`9IUqqlw721 zC1sW{jJ<<^20~a#@F_t%oWx=N_Ip1B7!18jYnL(*(A}1WkqEJzS@la!F|CRk`l?Lk zEz&fW_>;d3!rTXUhapS}(ls^S>;lEX%MIy@`Yh=`7Shb|j_Kl~I)~eCI~*q`k4SeP zCl{epa@U`QBTT(XlUO1zv~XvJ`w0LT>6%@%ADywINSN~%);GF6iE|a*>LZPbfOI9n zcfm|diPC6cDZmyvW8sa>DWq^Tlwc+HKK5_~1H9}(jWB+U&cF64 z>KGQV>cLMn?fB+7BEbM_i8&HQpq==O04A(YxcrWrV1+@UpSnFrn6U6W3opIkjpGG8 zZ9h9FzS3q?c@LeJr*D<6sjYyNNrQQ{)!gG2j zyrBE8uT+9eU{Xwtvpyk1s~&^TImhF|iOJ=R4^w1Ih>1>V<{$@JUw%j-x?I zmH6n;{r?GtOJ>DGV_4t(;8!@UicF8pr5w_Gs?uwIGCP zi5W1#*slo17I4MC^4Y@TI?IFJU%T>b=HK=9vBtUZ?8o}(nira`zCpI^H zUiJC)TW^1V8`z%cc`wlZ!5cThzb6S1Bg3lNDjnKx`SfF9q1-c9ZWg<(%8+rXES)jX z)@?mjc1XCk(azs8Gw5<(!LyomT_y_3Py2C^-6fIi=Gh1E<&;uHxfmDb;N&L;E3%$J?@uIE1vZHSC^WVIq>wbI2ThSf$+QljT zA!h4a-wdykli5~2AscD&ud9y`T^L^aRrSHM+#`i(D?Bxru&V64QN8T9oWj8p>^>z2`!v$vPZokyf>XdGf2LCchI=ek#t{cQcQnxTOw&HO*2)nnT> z-P4Z^pB!=3HQ}9Faq)Uuoa~m6Jh2N!%sszSGp>=-HGbGHgR>Z5IN^K*7(k+ilg>KW zDq9nzM!X;7jYY4y9_}wloGiKgn7(>zYrVQ+ql(h)#7C|Top!g3Rk`59ZB>g&nDVI3 z+gth9CgJvv&ce@d#(2GepZ#>Z3+loP_il-yIsacBb4G7(n3xjAAJb7d*I$-X6H}QP z54DAc?k4XN>7DSn`=mBhE=DqHC`0a-57BQ3x0zX^eq)k(l_l}>)7g<2!;|mZ36;5P zaplD9aP-XZ1}XkYg(U01MttJ4bX%84$jLnU$T1GhNI8ET(c7_MQtPDp=_ElU9Wqes zq)7kVu?v8egny;yepx+Dp>iGh+;d&pDR5H)VWyx< zvp;?eiPC&#KL69E?UFUjvK-8~v%LCRfqtYuS@p%Cg^@QQmqgc#xh+~1^WzzAQHvL( z-R(~L&F%EySy$dvyg(pcBqnpFCa2%s6(27dY}?=0yV;w-Lx&v)Rhu^!|KNv-JD#^d;p z;EIGOm?KYKGR)Q0vf%g=N?kycJf|TkhB>G}1o*%Z`+wovl*n2^(AA%Cdg3kklsFL%OuQha9o5i*`ipiM4wwazjE^>NkGyAXlXC|v zIJj-x9)zgI`06}-tfv+>wIhpM5IlGHSMY17<@Y95U$R8ixrf2dyo+ix1+f zT<)6sx!AH$Ue_nzi#mHpEx|#O`m)@RDcH?ED+0+j5q|W6%A{|CNbpbK`>L4zAYr9u zt%Kk&Lh1yd1Cg>-p%=}VtnS}h&@WvB@x`Ev_+ku*;l1G*SsfDX;~%o^R#!L{W9BW8 zIMiFZ;wpn7;pg6Zr9}9GWZ4)@Cd4dZ0>n$g$2lCUapWuoYQ;Fl;ds+K9=pef$k754 zThw-CSdMvjcYJGRafh-Z|DK++Z~~>yqFj(y;03xkd~v(Vxbf8`j4JdSoagkxvqm;UIqwV2+NhT0q^aS(J3 z=KL(hcfrW=sida^RGt`kT@XPJ`U>CPqSkHeViP2Xc@a=jHbE3*5yjaFTm(gL@;MEb ze!-CqZkU)bM1W?-m$>P-H;y$oY}&hf-PH;AB!WB+;lN=W(AC7O`y+dw?R)Cq%dt|Y zwg%sfXx4#fPtvUy zLIIkQ3zhw}2%F8HU zA^XdMP8dNH>)5PPWPMcPWzNj#;rWi~kh@n|T1REw?+TuI@8$a{vi<3B-#A1NP}DZrSxZGV**%o?Au3QH2T zuN_t28p7VoW8~sa>7&(Lw;kbS=k>tngSg%OQ>u ze3$1+?ZcRb=tg;RmJc07R(a+bPw^4S*bn&-$+-_B;_os8gdW&H#t1j7u4zfcyKG&= za4?+s^q(Cap>^>1tvl@FdQN+*WiLjw)*I(J-gj+FmJVhN96 z&knwPNCAPE z-l?e1K|U-otXq{C!0V6%4yXNJ7Q5Jie^S0mMh^Pt`qmqinwU)f zzD>Pr;X6(&_SUB+4W4M@x56t8t+TZ0%rZWkdsfc!ORpK^@w9ObRY`z1I7ZFurE=VX zq)r(v?|N|gUYUR(Q^_nJ#S~|Po_<0%l_8!Y!E9xBn8yQ7ib8&ZnPmWv2NJ@hFN5;` zp07wpOd9N6zJia^Jed`TD?gq%ZMjh;l>k;WV|(OZwx{OPcv|e&c(Ex=3$l+(bYQ7E zv$0bkRt|%7DIxEVb{yzh76oI^3igGrD$ob=+@fUpBjhn^DxVsaG)_#?6?y}eRIXX! zElJrCrzYXK=Oe%QFIxTXE^l*h9~Drnd;2xh;G$;M(f9T|R`B&1ZFbor{|RHFlxMe!?Q}c@EUL^ib9+8;0{+D-0-+(-VO=X%UXX8+J za;EXc$?VQ)v}apIpFk-J^pC{8S-%$D+&{dz>~`AxHfijgMSF7+u(1zX64)}c*JSi# zn<_s3-1Pw1GH!ZR)_I;m!j>K>^PRc z*IH=GP2pp4l&a*hd_@Z`AKT+Ec(xUs(MXd>sie4_ea6EmU=vtX5;g^5A7*#6Jtk=B z76c6{|Fw#mEvB*+JWvvM^Z8$*f%&i6GkoyDzvQUyK9WiaW8Pv*AwP}!uI`4F9V3R) z6imJIX`Nb`8cz>--BE(wltlL3P=b{i5z|$_XAgEoMR|p?2i;rK4B`Y6Wgx*_9D<)@0OnIHhdb?Ay z2Azv_t?(1s&T6bgfylvpvYcNU!qh9D_I0z=U@Cr8nBE3lmB^vZhyqcAJMOX&rZ+`m zI$wQ1NRCXVU9LS|YI1oyW9&nI{3@yX^BZe{iBZn-9#{D$nI2viXEEkuSwV_Tf99*D zoClSZ6a|Xzb>N@@>k{d;Ar^dxdzwthLUJ>(IG5fHtLatQ9wzDL#NqB?dbcoR_l z396+JH6IfB!+2vBgMOL6(NxrnfaskeVnpCw``{d!XJ(1(^Y2Qq;+NV=3e0gE|6w6h z?-Ik7s58XgH-SbuUJ0oQuM+lWl>0tz*K2=C?PA_4Ioe{sBqQnPyc_oVb000!CTJnj zgJSrmu=Ivb57IKB%^m+~wOpl(>0MH)l4Y~~-8J)MZ3@Gmk!w9TX17k5o=r=dUhEyH zO>_W@CsJJZircvvoaDf6sJ^sD>gu$t@bZ~cosa}~r z$DoZyWfDG|tgrT#9$pfSdPql)N;LEk1Hr89-k~oxDOncjem5ub`Upm%P ztScQ85W~@%oEjS!>PomCwA!2ZTE)T&&a{8Y^cB?Z4K3>0aA2gz;Vjlp0~z-XvF&c0 zXsJ!?p1vIz_^7+1c_q%X=(86OcKWD(_-?dB>w}_)ZfEzHXN{|t6^Lq-M5p}^7)xC#)eS}_ zf>!uZgyIfHm;cxOzk&ZZT)|q_I&!_w{U9Qy#An|ZW1c;z;O+l5ifA988P_=sb_$pg zE+yk%#e*NC%`I~wCQ%Z962K>gyQTA?4oD!7>+3)bzW=GgKXMIZvk)?K5HAYo@~<)M zWq{<)*yH7QLU66KS`F;%QtGYR|G^4Tl}3Jv8sAms|ubExXOg=O4unb8c>Ig z?htrjHw0?r#V^4(s`hTjB=O?C1y1sd~Q-AZEBPUNw z*B>X_4kGs~97y7DFlc~`mH^glkCl_p=VnKz-npD6YD#f*&4@3L4sGc6$Q(?zUpqsN zG*N8)mnH3+ebFH+8K(Ii(lC~22Ms(*ExZrz8R|7yWhk2w-II1M0?Q+Kq2!})BL=a= zt56~Z(#whUGI-9O%Zxmb_|uF?n&lz>VVEQi$(dRyzoe%`cUD+^yuN$r(peoL}m zIjum!Y}e@rjchS|_cf5=xo4plxBcH-=7RUOHut;V4#ec-x}aoVcDFSx?fh#|hFIlq jc|BbCN&e6&v@N8!?v@y#ipm3i3W3Z|TAEN#T#WcHyA{f= literal 0 HcmV?d00001 diff --git a/resources/public/img/authorities/Twitter.xcf b/resources/public/img/authorities/Twitter.xcf new file mode 100644 index 0000000000000000000000000000000000000000..cad1df447f9b0551716c3f51270f6bf281bfdead GIT binary patch literal 21415 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 KG4c3fO!B|tjfLF+ literal 0 HcmV?d00001 diff --git a/resources/sql/queries.auto.sql b/resources/sql/queries.auto.sql index 0f482c4..3e66e95 100644 --- a/resources/sql/queries.auto.sql +++ b/resources/sql/queries.auto.sql @@ -1,7 +1,7 @@ ------------------------------------------------------------------------ -- File queries.sql -- --- autogenerated by adl.to-hugsql-queries at 2018-06-29T10:10:58.177Z +-- autogenerated by adl.to-hugsql-queries at 2018-06-29T14:15:37.324Z -- -- See [Application Description -- Language](https://github.com/simon-brooke/adl). @@ -27,8 +27,18 @@ returning id -- :name create-authority! :! :n -- :doc creates a new authority record -INSERT INTO authorities (id) -VALUES (:id) +INSERT INTO authorities (request_token_uri, + access_token_uri, + authorize_uri, + consumer_key, + consumer_secret, + id) +VALUES (:request_token_uri, + :access_token_uri, + :authorize_uri, + :consumer_key, + :consumer_secret, + :id) returning id -- :name create-canvasser! :! :n @@ -121,10 +131,12 @@ returning id -- :doc creates a new intention record INSERT INTO intentions (visit_id, elector_id, - option_id) + option_id, + locality) VALUES (:visit_id, :elector_id, - :option_id) + :option_id, + :locality) returning Id -- :name create-issue! :! :n @@ -747,6 +759,11 @@ ORDER BY lv_addresses.address, -- :doc selects existing authority records having any string field matching the parameter of the same name by substring match SELECT * FROM lv_authorities WHERE false + --~ (if (:request-token-uri params) "OR request_token_uri LIKE '%:request-token-uri%'") + --~ (if (:access-token-uri params) "OR access_token_uri LIKE '%:access-token-uri%'") + --~ (if (:authorize-uri params) "OR authorize_uri LIKE '%:authorize-uri%'") + --~ (if (:consumer-key params) "OR consumer_key LIKE '%:consumer-key%'") + --~ (if (:consumer-secret params) "OR consumer_secret LIKE '%:consumer-secret%'") --~ (if (:id params) "OR id LIKE '%:id%'") --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") @@ -868,6 +885,7 @@ WHERE false --~ (if (:visit_id params) "OR visit_id = :visit_id") --~ (if (:elector_id params) "OR elector_id = :elector_id") --~ (if (:option_id params) "OR option_id = :option_id") + --~ (if (:locality params) "OR locality = :locality") --~ (if (:Id params) "OR Id = :Id") --~ (if (:offset params) "OFFSET :offset ") --~ (if (:limit params) "LIMIT :limit" "LIMIT 100") @@ -941,6 +959,17 @@ SET address = :address, locality = :locality WHERE addresses.id = :id +-- :name update-authority! :! :n +-- :doc updates an existing authority record +UPDATE authorities +SET request_token_uri = :request-token-uri, + access_token_uri = :access-token-uri, + authorize_uri = :authorize-uri, + consumer_key = :consumer-key, + consumer_secret = :consumer-secret, + id = :id +WHERE authorities.id = :id + -- :name update-canvasser! :! :n -- :doc updates an existing canvasser record UPDATE canvassers @@ -1001,7 +1030,8 @@ WHERE followuprequests.id = :id UPDATE intentions SET visit_id = :visit_id, elector_id = :elector_id, - option_id = :option_id + option_id = :option_id, + locality = :locality WHERE intentions.Id = :Id -- :name update-issue! :! :n diff --git a/resources/sql/youyesyet.postgres.sql b/resources/sql/youyesyet.postgres.sql index 1bc4b7d..2dbb0c6 100644 --- a/resources/sql/youyesyet.postgres.sql +++ b/resources/sql/youyesyet.postgres.sql @@ -5,7 +5,7 @@ -- -- auto-generated by [Application Description Language framework] -- --- (https://github.com/simon-brooke/adl) at 20180629T101059.126Z +-- (https://github.com/simon-brooke/adl) at 20180629T141538.194Z -- -- -- A web-app intended to be used by canvassers campaigning for a @@ -99,7 +99,12 @@ GRANT DELETE ON addresses TO admin ; ------------------------------------------------------------------------ CREATE TABLE authorities ( - id VARCHAR(32) NOT NULL PRIMARY KEY + id VARCHAR(32) NOT NULL PRIMARY KEY, + request_token_uri VARCHAR(256) NOT NULL, + access_token_uri VARCHAR(256) NOT NULL, + authorize_uri VARCHAR(256) NOT NULL, + consumer_key VARCHAR(32) DEFAULT 'youyesyet' NOT NULL, + consumer_secret VARCHAR(256) NOT NULL ); GRANT SELECT ON authorities TO admin, analysts, @@ -167,8 +172,15 @@ GRANT DELETE ON districts TO admin ; ------------------------------------------------------------------------ -- primary table dwellings for entity dwellings -- --- All dwellings within addresses in the system; a dwelling is a house, --- flat or appartment in which electors live. +-- All dwellings within addresses in the system; a dwelling is a +-- house, flat or appartment in which electors live. Every address +-- should have +-- at least one dwelling; essentially, an address maps onto a +-- street door and +-- dwellings map onto what's behind that door. So a tenement or a +-- block of flats +-- would be one address with many dwellings. +-- ------------------------------------------------------------------------ CREATE TABLE dwellings ( @@ -298,14 +310,15 @@ GRANT DELETE ON genders TO admin ; ------------------------------------------------------------------------ -- primary table intentions for entity intentions -- --- Link table. +-- Intentions of electors to vote for options elicited in visits. ------------------------------------------------------------------------ CREATE TABLE intentions ( Id SERIAL NOT NULL PRIMARY KEY, visit_id INTEGER NOT NULL, - elector_id INTEGER NOT NULL, - option_id VARCHAR(32) NOT NULL + elector_id INTEGER, + option_id VARCHAR(32) NOT NULL, + locality INTEGER NOT NULL ); GRANT SELECT ON intentions TO admin, analysts, @@ -455,7 +468,12 @@ GRANT SELECT ON lv_addresses TO admin, -- cetera ------------------------------------------------------------------------ CREATE VIEW lv_authorities AS -SELECT authorities.id +SELECT authorities.request_token_uri, + authorities.access_token_uri, + authorities.authorize_uri, + authorities.consumer_key, + authorities.consumer_secret, + authorities.id FROM authorities ; GRANT SELECT ON lv_authorities TO admin, @@ -641,6 +659,7 @@ SELECT addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS vis intentions.elector_id, options.id AS option_id_expanded, intentions.option_id, + intentions.locality, intentions.Id FROM visits, intentions, addresses, genders, electors, options WHERE intentions.visit_id = visits.id diff --git a/resources/templates/auto/application-index.html b/resources/templates/auto/application-index.html index 1edf694..517fd53 100644 --- a/resources/templates/auto/application-index.html +++ b/resources/templates/auto/application-index.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -49,7 +49,12 @@ Dwelling

-All dwellings within addresses in the system; a dwelling is a house, flat or appartment in which electors live. +All dwellings within addresses in the system; a dwelling is a + house, flat or appartment in which electors live. Every address should have + at least one dwelling; essentially, an address maps onto a street door and + dwellings map onto what's behind that door. So a tenement or a block of flats + would be one address with many dwellings. +

@@ -99,7 +104,7 @@ Intention

-Link table. +Intentions of electors to vote for options elicited in visits.

diff --git a/resources/templates/auto/form-addresses-Address.html b/resources/templates/auto/form-addresses-Address.html index a6d8011..e3dc979 100644 --- a/resources/templates/auto/form-addresses-Address.html +++ b/resources/templates/auto/form-addresses-Address.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/form-authorities-Authority.html b/resources/templates/auto/form-authorities-Authority.html index 6b4756b..8e6a888 100644 --- a/resources/templates/auto/form-authorities-Authority.html +++ b/resources/templates/auto/form-authorities-Authority.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -42,6 +42,96 @@ You are not permitted to view id of authorities {% endifmemberof %} {% endifmemberof %}

+

+ +{% ifmemberof admin %} + +{% else %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} + +{{record.request-token-uri}} + +{% else %} + +You are not permitted to view request-token-uri of authorities + +{% endifmemberof %} +{% endifmemberof %} +

+

+ +{% ifmemberof admin %} + +{% else %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} + +{{record.access-token-uri}} + +{% else %} + +You are not permitted to view access-token-uri of authorities + +{% endifmemberof %} +{% endifmemberof %} +

+

+ +{% ifmemberof admin %} + +{% else %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} + +{{record.authorize-uri}} + +{% else %} + +You are not permitted to view authorize-uri of authorities + +{% endifmemberof %} +{% endifmemberof %} +

+

+ +{% ifmemberof admin %} + +{% else %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} + +{{record.consumer-key}} + +{% else %} + +You are not permitted to view consumer-key of authorities + +{% endifmemberof %} +{% endifmemberof %} +

+

+ +{% ifmemberof admin %} + +{% else %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} + +{{record.consumer-secret}} + +{% else %} + +You are not permitted to view consumer-secret of authorities + +{% endifmemberof %} +{% endifmemberof %} +

+

+ +{% ifmemberof admin %} + +{% else %} +{% ifmemberof canvassers analysts admin %} + +{{record.locality}} + +{% else %} + +You are not permitted to view locality of intentions + +{% endifmemberof %} +{% endifmemberof %} +

{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.district_id}} @@ -87,10 +87,10 @@ You are not permitted to view district_id of teams -{% ifmemberof teamorganisers admin %} +{% ifmemberof teamorganisers admin teamorganisers admin %} {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.latitude}} @@ -105,7 +105,7 @@ You are not permitted to view latitude of teams -{% ifmemberof teamorganisers admin %} +{% ifmemberof teamorganisers admin teamorganisers admin %}
{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.members}} @@ -128,7 +128,7 @@ You are not permitted to view members of teams -{% ifmemberof teamorganisers admin %} +{% ifmemberof teamorganisers admin teamorganisers admin %}
{% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.organisers}} @@ -151,10 +151,10 @@ You are not permitted to view organisers of teams -{% ifmemberof teamorganisers admin %} +{% ifmemberof teamorganisers admin teamorganisers admin %} {% else %} -{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %} +{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin canvassers teamorganisers issueexperts analysts issueeditors admin %} {{record.longitude}} diff --git a/resources/templates/auto/form-visits-Visit.html b/resources/templates/auto/form-visits-Visit.html index e07cca1..65e51ea 100644 --- a/resources/templates/auto/form-visits-Visit.html +++ b/resources/templates/auto/form-visits-Visit.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-addresses-Addresses.html b/resources/templates/auto/list-addresses-Addresses.html index 829be5b..96da5ee 100644 --- a/resources/templates/auto/list-addresses-Addresses.html +++ b/resources/templates/auto/list-addresses-Addresses.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-authorities-Authorities.html b/resources/templates/auto/list-authorities-Authorities.html index d20fe9d..35d09f1 100644 --- a/resources/templates/auto/list-authorities-Authorities.html +++ b/resources/templates/auto/list-authorities-Authorities.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -33,12 +33,30 @@ Add a new Authority id + +request-token-uri + + +access-token-uri + + +authorize-uri + + + + + + + + + + @@ -50,6 +68,15 @@ id {{ record.id }} +{{ record.request_token_uri }} + + +{{ record.access_token_uri }} + + +{{ record.authorize_uri }} + +
View diff --git a/resources/templates/auto/list-canvassers-Canvassers.html b/resources/templates/auto/list-canvassers-Canvassers.html index c9719e0..c37423a 100644 --- a/resources/templates/auto/list-canvassers-Canvassers.html +++ b/resources/templates/auto/list-canvassers-Canvassers.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-districts-Districts.html b/resources/templates/auto/list-districts-Districts.html index 982e2d4..1ae5e72 100644 --- a/resources/templates/auto/list-districts-Districts.html +++ b/resources/templates/auto/list-districts-Districts.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-dwellings-Dwellings.html b/resources/templates/auto/list-dwellings-Dwellings.html index 4d633ca..22a42e9 100644 --- a/resources/templates/auto/list-dwellings-Dwellings.html +++ b/resources/templates/auto/list-dwellings-Dwellings.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -48,7 +48,7 @@ sub-address - + @@ -67,7 +67,7 @@ sub-address -{{ record.sub-address }} +{{ record.sub_address }} diff --git a/resources/templates/auto/list-electors-Electors.html b/resources/templates/auto/list-electors-Electors.html index 383fce7..103b700 100644 --- a/resources/templates/auto/list-electors-Electors.html +++ b/resources/templates/auto/list-electors-Electors.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followupactions-Followupactions.html b/resources/templates/auto/list-followupactions-Followupactions.html index 26697ec..a4c3dca 100644 --- a/resources/templates/auto/list-followupactions-Followupactions.html +++ b/resources/templates/auto/list-followupactions-Followupactions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followupmethods-Followupmethods.html b/resources/templates/auto/list-followupmethods-Followupmethods.html index 74c7b37..457d6f5 100644 --- a/resources/templates/auto/list-followupmethods-Followupmethods.html +++ b/resources/templates/auto/list-followupmethods-Followupmethods.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-followuprequests-Followuprequests.html b/resources/templates/auto/list-followuprequests-Followuprequests.html index 55a4b48..c81c924 100644 --- a/resources/templates/auto/list-followuprequests-Followuprequests.html +++ b/resources/templates/auto/list-followuprequests-Followuprequests.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-genders-Genders.html b/resources/templates/auto/list-genders-Genders.html index 8ce6cc1..64da2fe 100644 --- a/resources/templates/auto/list-genders-Genders.html +++ b/resources/templates/auto/list-genders-Genders.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-intentions-Intentions.html b/resources/templates/auto/list-intentions-Intentions.html index c90c0c9..8fc6118 100644 --- a/resources/templates/auto/list-intentions-Intentions.html +++ b/resources/templates/auto/list-intentions-Intentions.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} @@ -39,6 +39,9 @@ elector_id option_id + +locality + @@ -51,6 +54,9 @@ option_id + + + @@ -74,6 +80,9 @@ option_id +{{ record.locality }} + + View diff --git a/resources/templates/auto/list-issues-Issues.html b/resources/templates/auto/list-issues-Issues.html index 622cbae..1c08c80 100644 --- a/resources/templates/auto/list-issues-Issues.html +++ b/resources/templates/auto/list-issues-Issues.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-options-Options.html b/resources/templates/auto/list-options-Options.html index bb735be..fd49da7 100644 --- a/resources/templates/auto/list-options-Options.html +++ b/resources/templates/auto/list-options-Options.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-roles-Roles.html b/resources/templates/auto/list-roles-Roles.html index 76cdc21..af780fe 100644 --- a/resources/templates/auto/list-roles-Roles.html +++ b/resources/templates/auto/list-roles-Roles.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-teams-Teams.html b/resources/templates/auto/list-teams-Teams.html index 613cff7..089ef34 100644 --- a/resources/templates/auto/list-teams-Teams.html +++ b/resources/templates/auto/list-teams-Teams.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/auto/list-visits-Visits.html b/resources/templates/auto/list-visits-Visits.html index f37ba72..93fe01b 100644 --- a/resources/templates/auto/list-visits-Visits.html +++ b/resources/templates/auto/list-visits-Visits.html @@ -1,5 +1,5 @@ {% extends "base.html" %} - {% block head %} diff --git a/resources/templates/base-authenticated.html b/resources/templates/base-authenticated.html index 22af08a..4fd1834 100644 --- a/resources/templates/base-authenticated.html +++ b/resources/templates/base-authenticated.html @@ -16,12 +16,12 @@ - -

- {{title}} -

+ +

{{title}}

{% endblock %}
@@ -39,15 +42,18 @@
{% block content %} + {% endblock %}

{% block foot %} +
@@ -66,11 +72,18 @@
{% endblock %} {% endblock %} + {% block extra-tail %} + + + {% endblock %} diff --git a/resources/templates/call-me-accepted.html b/resources/templates/call-me-accepted.html index a720998..9bcc836 100644 --- a/resources/templates/call-me-accepted.html +++ b/resources/templates/call-me-accepted.html @@ -1,41 +1,15 @@ -{% extends "base-unauthenticated.html" %} +{% extends "base.html" %} {% block big-links %} - {% endblock %} {% block content %} +

+ We'll be in touch! +

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

-
-

- - -

-

- - -

-

- - -

-

- - -

-

- - -

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

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

+ {% csrf-field %}

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

diff --git a/resources/templates/canvasser.html b/resources/templates/canvasser.html index b223313..ceb6c6c 100644 --- a/resources/templates/canvasser.html +++ b/resources/templates/canvasser.html @@ -1,4 +1,4 @@ -{% extends "base-authenticated.html" %} +{% extends "base.html" %} {% block title %} {% endblock %} {% block content %} diff --git a/resources/templates/home.html b/resources/templates/home.html index ea4c068..59a851f 100644 --- a/resources/templates/home.html +++ b/resources/templates/home.html @@ -1,4 +1,4 @@ -{% extends "base-unauthenticated.html" %} +{% extends "base.html" %} {% block big-links %} {% endblock %} +{% block content %} +

+ Alpha test code +

+

+ Although addresses in the database mostly are real, all personal data in the database + is randomly generated and does not represent real people. +

+{% endblock %} + diff --git a/resources/templates/login.html b/resources/templates/login.html index fab8b01..0d305b6 100644 --- a/resources/templates/login.html +++ b/resources/templates/login.html @@ -1,9 +1,6 @@ -{% extends "base-unauthenticated.html" %} -{% block content %} - - {% for authority in authorities %} +{% extends "base.html" %} +{% block big-links %} + {% for authority in authorities %} {% endfor %} +{% endblock %} + +{% block content %} {% csrf-field %}

diff --git a/resources/templates/notyet.html b/resources/templates/notyet.html index 47b9fec..9574c55 100644 --- a/resources/templates/notyet.html +++ b/resources/templates/notyet.html @@ -1,4 +1,4 @@ -{% extends "base-unauthenticated.html" %} +{% extends "base.html" %} {% block big-links %}
{% block content %} @@ -81,13 +81,11 @@ {% endblock %} diff --git a/src/clj/youyesyet/db/core.clj b/src/clj/youyesyet/db/core.clj index 2158558..ccbb495 100644 --- a/src/clj/youyesyet/db/core.clj +++ b/src/clj/youyesyet/db/core.clj @@ -1,5 +1,4 @@ -(ns ^{:doc "Database access functions." - :author "Simon Brooke"} +(ns ^{:doc "Database access functions, mostly from Luminus template."} youyesyet.db.core (:require [cheshire.core :refer [generate-string parse-string]] diff --git a/src/clj/youyesyet/layout.clj b/src/clj/youyesyet/layout.clj index 060082a..2033ecf 100644 --- a/src/clj/youyesyet/layout.clj +++ b/src/clj/youyesyet/layout.clj @@ -50,7 +50,10 @@ user (do (log/debug (str "seeking roles for user " user)) - (set (map #(lower-case (:name %)) (db/list-roles-by-canvasser db/*db* user)))))) + (let [roles + (set (map #(lower-case (:name %)) (db/list-roles-by-canvasser db/*db* user)))] + (log/debug (str "found roles " roles " for user " user)) + roles)))) ;; role assignments change only rarely. @@ -58,22 +61,26 @@ (defn render - "renders the HTML template located relative to resources/templates" + "renders the HTML `template` located relative to resources/templates in + the context of this session and with these parameters." [template session & [params]] (let [user (:user session)] - (content-type - (ok + (log/debug (str "layout/render: template: '" template "'; user: '" user "'.")) + (assoc + (content-type + (ok (parser/render-file - template - (assoc params - :page template - :csrf-token *anti-forgery-token* - :user user - :user-roles (get-user-roles user) - :site-title (:site-title env) - :site-logo (:site-logo env) - :version (System/getProperty "youyesyet.version")))) - "text/html; charset=utf-8"))) + template + (assoc params + :page template + :csrf-token *anti-forgery-token* + :version (System/getProperty "youyesyet.version")))) + "text/html; charset=utf-8") + :user user + :user-roles (get-user-roles user) + :site-title (:site-title env) + :site-logo (:site-logo env) + :session session))) (defn error-page diff --git a/src/clj/youyesyet/middleware.clj b/src/clj/youyesyet/middleware.clj index 96414ac..fe581a2 100644 --- a/src/clj/youyesyet/middleware.clj +++ b/src/clj/youyesyet/middleware.clj @@ -1,16 +1,17 @@ (ns ^{:doc "Plumbing, mainly boilerplate from Luminus."} youyesyet.middleware - (:require [youyesyet.env :refer [defaults]] - [clojure.tools.logging :as log] - [youyesyet.layout :refer [*app-context* error-page]] + (:require [clojure.tools.logging :as log] [ring.middleware.anti-forgery :refer [wrap-anti-forgery]] - [ring.middleware.webjars :refer [wrap-webjars]] + [ring.middleware.defaults :refer [site-defaults wrap-defaults]] [ring.middleware.format :refer [wrap-restful-format]] - [youyesyet.config :refer [env]] + [ring.middleware.webjars :refer [wrap-webjars]] [ring-ttl-session.core :refer [ttl-memory-store]] - [ring.middleware.defaults :refer [site-defaults wrap-defaults]]) + [youyesyet.env :refer [defaults]] + [youyesyet.config :refer [env]] + [youyesyet.layout :refer [*app-context* error-page]]) (:import [javax.servlet ServletContext])) + (defn wrap-context [handler] (fn [request] (binding [*app-context* @@ -26,6 +27,7 @@ (:app-context env))] (handler request)))) + (defn wrap-internal-error [handler] (fn [req] (try @@ -36,6 +38,7 @@ :title "Something very bad has happened!" :message "We've dispatched a team of highly trained gnomes to take care of the problem."}))))) + (defn wrap-csrf [handler] (wrap-anti-forgery handler @@ -44,6 +47,7 @@ {:status 403 :title "Invalid anti-forgery token"})})) + (defn wrap-formats [handler] (let [wrapped (wrap-restful-format handler @@ -53,6 +57,7 @@ ;; since they're not compatible with this middleware ((if (:websocket? request) handler wrapped) request)))) + (defn wrap-base [handler] (-> ((:middleware defaults) handler) wrap-webjars diff --git a/src/clj/youyesyet/routes/auto.clj b/src/clj/youyesyet/routes/auto.clj index 4e02a3a..63860d0 100644 --- a/src/clj/youyesyet/routes/auto.clj +++ b/src/clj/youyesyet/routes/auto.clj @@ -1,9 +1,11 @@ (ns youyesyet.routes.auto - "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180630T113600.562Z" + "User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180701T221533.599Z" (:require [adl-support.core :as support] [clojure.java.io :as io] + [clojure.set :refer [subset?]] + [clojure.tools.logging :as log] [compojure.core :refer [defroutes GET POST]] [hugsql.core :as hugsql] [noir.response :as nresponse] @@ -25,523 +27,1023 @@ list-addresses-Addresses [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some + #{"district_id" "postcode" "longitude" "id" "latitude" "address" + "locality" "phone"} + (keys p)) + (support/do-or-log-error + (db/search-strings-address db/*db* p) + :message + "Error while searching address records" + :error-return + {:warnings ["Error while searching address records"]}) + (support/do-or-log-error + (db/list-addresses db/*db* {}) + :message + "Error while fetching address records" + :error-return + {:warnings ["Error while fetching address records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-addresses-Addresses.html") - (:session r) - {:title "Addresses", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-address db/*db* p) - (db/list-addresses db/*db* {}))}))) + (:session 'r) + (merge {:title "Addresses", :params p} c)))) (defn form-addresses-Address [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-address db/*db* p)) + :message + "Error while fetching address record" + :error-return + {:warnings ["Error while fetching address record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :districts + (support/do-or-log-error + (db/list-districts db/*db*) + :message + "Error while fetching district record")})] (l/render (support/resolve-template "form-addresses-Address.html") - (:session r) - {:title "Address", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-address db/*db* p)), - :districts (db/list-districts db/*db*)}))) + (:session 'r) + (merge {:title "Address", :params p} c)))) (defn list-authorities-Authorities [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some + #{"authorize-uri" "request-token-uri" "access-token-uri" + "consumer-key" "id" "consumer-secret"} + (keys p)) + (support/do-or-log-error + (db/search-strings-authority db/*db* p) + :message + "Error while searching authority records" + :error-return + {:warnings ["Error while searching authority records"]}) + (support/do-or-log-error + (db/list-authorities db/*db* {}) + :message + "Error while fetching authority records" + :error-return + {:warnings ["Error while fetching authority records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-authorities-Authorities.html") - (:session r) - {:title "Authorities", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-authority db/*db* p) - (db/list-authorities db/*db* {}))}))) + (:session 'r) + (merge {:title "Authorities", :params p} c)))) (defn form-authorities-Authority [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-authority db/*db* p)) + :message + "Error while fetching authority record" + :error-return + {:warnings ["Error while fetching authority record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil)})] (l/render (support/resolve-template "form-authorities-Authority.html") - (:session r) - {:title "Authority", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-authority db/*db* p))}))) + (:session 'r) + (merge {:title "Authority", :params p} c)))) (defn list-canvassers-Canvassers [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some + #{"username" "address_id" "elector_id" "id" "email" + "authority_id" "authorised" "phone" "fullname" "roles"} + (keys p)) + (support/do-or-log-error + (db/search-strings-canvasser db/*db* p) + :message + "Error while searching canvasser records" + :error-return + {:warnings ["Error while searching canvasser records"]}) + (support/do-or-log-error + (db/list-canvassers db/*db* {}) + :message + "Error while fetching canvasser records" + :error-return + {:warnings ["Error while fetching canvasser records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-canvassers-Canvassers.html") - (:session r) - {:title "Canvassers", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-canvasser db/*db* p) - (db/list-canvassers db/*db* {}))}))) + (:session 'r) + (merge {:title "Canvassers", :params p} c)))) (defn form-canvassers-Canvasser [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-canvasser db/*db* p)) + :message + "Error while fetching canvasser record" + :error-return + {:warnings ["Error while fetching canvasser record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :electors + (support/do-or-log-error + (db/list-electors db/*db*) + :message + "Error while fetching elector record"), + :addresses + (support/do-or-log-error + (db/list-addresses db/*db*) + :message + "Error while fetching address record"), + :authorities + (support/do-or-log-error + (db/list-authorities db/*db*) + :message + "Error while fetching authority record"), + :roles + (support/do-or-log-error + (db/list-roles db/*db*) + :message + "Error while fetching role record")})] (l/render (support/resolve-template "form-canvassers-Canvasser.html") - (:session r) - {:title "Canvasser", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-canvasser db/*db* p)), - :electors (db/list-electors db/*db*), - :addresses (db/list-addresses db/*db*), - :authorities (db/list-authorities db/*db*), - :roles (db/list-roles db/*db*)}))) + (:session 'r) + (merge {:title "Canvasser", :params p} c)))) (defn list-districts-Districts [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some #{"id" "name"} (keys p)) + (support/do-or-log-error + (db/search-strings-district db/*db* p) + :message + "Error while searching district records" + :error-return + {:warnings ["Error while searching district records"]}) + (support/do-or-log-error + (db/list-districts db/*db* {}) + :message + "Error while fetching district records" + :error-return + {:warnings ["Error while fetching district records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-districts-Districts.html") - (:session r) - {:title "Districts", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-district db/*db* p) - (db/list-districts db/*db* {}))}))) + (:session 'r) + (merge {:title "Districts", :params p} c)))) (defn form-districts-District [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-district db/*db* p)) + :message + "Error while fetching district record" + :error-return + {:warnings ["Error while fetching district record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil)})] (l/render (support/resolve-template "form-districts-District.html") - (:session r) - {:title "District", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-district db/*db* p))}))) + (:session 'r) + (merge {:title "District", :params p} c)))) (defn list-dwellings-Dwellings [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some #{"sub-address" "address_id" "id"} (keys p)) + (support/do-or-log-error + (db/search-strings-dwelling db/*db* p) + :message + "Error while searching dwelling records" + :error-return + {:warnings ["Error while searching dwelling records"]}) + (support/do-or-log-error + (db/list-dwellings db/*db* {}) + :message + "Error while fetching dwelling records" + :error-return + {:warnings ["Error while fetching dwelling records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-dwellings-Dwellings.html") - (:session r) - {:title "Dwellings", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-dwelling db/*db* p) - (db/list-dwellings db/*db* {}))}))) + (:session 'r) + (merge {:title "Dwellings", :params p} c)))) (defn form-dwellings-Dwelling [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-dwelling db/*db* p)) + :message + "Error while fetching dwelling record" + :error-return + {:warnings ["Error while fetching dwelling record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :addresses + (support/do-or-log-error + (db/list-addresses db/*db*) + :message + "Error while fetching address record")})] (l/render (support/resolve-template "form-dwellings-Dwelling.html") - (:session r) - {:title "Dwelling", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-dwelling db/*db* p)), - :addresses (db/list-addresses db/*db*)}))) + (:session 'r) + (merge {:title "Dwelling", :params p} c)))) (defn list-electors-Electors [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some + #{"id" "gender" "email" "name" "dwelling_id" "phone"} + (keys p)) + (support/do-or-log-error + (db/search-strings-elector db/*db* p) + :message + "Error while searching elector records" + :error-return + {:warnings ["Error while searching elector records"]}) + (support/do-or-log-error + (db/list-electors db/*db* {}) + :message + "Error while fetching elector records" + :error-return + {:warnings ["Error while fetching elector records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-electors-Electors.html") - (:session r) - {:title "Electors", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-elector db/*db* p) - (db/list-electors db/*db* {}))}))) + (:session 'r) + (merge {:title "Electors", :params p} c)))) (defn form-electors-Elector [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-elector db/*db* p)) + :message + "Error while fetching elector record" + :error-return + {:warnings ["Error while fetching elector record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :dwellings + (support/do-or-log-error + (db/list-dwellings db/*db*) + :message + "Error while fetching dwelling record"), + :genders + (support/do-or-log-error + (db/list-genders db/*db*) + :message + "Error while fetching gender record")})] (l/render (support/resolve-template "form-electors-Elector.html") - (:session r) - {:title "Elector", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-elector db/*db* p)), - :dwellings (db/list-dwellings db/*db*), - :genders (db/list-genders db/*db*)}))) + (:session 'r) + (merge {:title "Elector", :params p} c)))) (defn list-followupactions-Followupactions [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some + #{"request_id" "id" "closed" "date" "notes" "actor"} + (keys p)) + (support/do-or-log-error + (db/search-strings-followupaction db/*db* p) + :message + "Error while searching followupaction records" + :error-return + {:warnings ["Error while searching followupaction records"]}) + (support/do-or-log-error + (db/list-followupactions db/*db* {}) + :message + "Error while fetching followupaction records" + :error-return + {:warnings ["Error while fetching followupaction records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-followupactions-Followupactions.html") - (:session r) - {:title "Followupactions", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-followupaction db/*db* p) - (db/list-followupactions db/*db* {}))}))) + (:session 'r) + (merge {:title "Followupactions", :params p} c)))) (defn form-followupactions-Followupaction [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if + (subset? #{"id"} #{p keys}) + (db/get-followupaction db/*db* p)) + :message + "Error while fetching followupaction record" + :error-return + {:warnings ["Error while fetching followupaction record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :followuprequests + (support/do-or-log-error + (db/list-followuprequests db/*db*) + :message + "Error while fetching followuprequest record"), + :canvassers + (support/do-or-log-error + (db/list-canvassers db/*db*) + :message + "Error while fetching canvasser record")})] (l/render (support/resolve-template "form-followupactions-Followupaction.html") - (:session r) - {:title "Followupaction", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-followupaction db/*db* p)), - :followuprequests (db/list-followuprequests db/*db*), - :canvassers (db/list-canvassers db/*db*)}))) + (:session 'r) + (merge {:title "Followupaction", :params p} c)))) (defn list-followupmethods-Followupmethods [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some #{"id"} (keys p)) + (support/do-or-log-error + (db/search-strings-followupmethod db/*db* p) + :message + "Error while searching followupmethod records" + :error-return + {:warnings ["Error while searching followupmethod records"]}) + (support/do-or-log-error + (db/list-followupmethods db/*db* {}) + :message + "Error while fetching followupmethod records" + :error-return + {:warnings ["Error while fetching followupmethod records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-followupmethods-Followupmethods.html") - (:session r) - {:title "Followupmethods", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-followupmethod db/*db* p) - (db/list-followupmethods db/*db* {}))}))) + (:session 'r) + (merge {:title "Followupmethods", :params p} c)))) (defn form-followupmethods-Followupmethod [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if + (subset? #{"id"} #{p keys}) + (db/get-followupmethod db/*db* p)) + :message + "Error while fetching followupmethod record" + :error-return + {:warnings ["Error while fetching followupmethod record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil)})] (l/render (support/resolve-template "form-followupmethods-Followupmethod.html") - (:session r) - {:title "Followupmethod", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-followupmethod db/*db* p))}))) + (:session 'r) + (merge {:title "Followupmethod", :params p} c)))) (defn list-followuprequests-Followuprequests [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some + #{"elector_id" "id" "issue_id" "visit_id" "method_id"} + (keys p)) + (support/do-or-log-error + (db/search-strings-followuprequest db/*db* p) + :message + "Error while searching followuprequest records" + :error-return + {:warnings ["Error while searching followuprequest records"]}) + (support/do-or-log-error + (db/list-followuprequests db/*db* {}) + :message + "Error while fetching followuprequest records" + :error-return + {:warnings ["Error while fetching followuprequest records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-followuprequests-Followuprequests.html") - (:session r) - {:title "Followuprequests", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-followuprequest db/*db* p) - (db/list-followuprequests db/*db* {}))}))) + (:session 'r) + (merge {:title "Followuprequests", :params p} c)))) (defn form-followuprequests-Followuprequest [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if + (subset? #{"id"} #{p keys}) + (db/get-followuprequest db/*db* p)) + :message + "Error while fetching followuprequest record" + :error-return + {:warnings ["Error while fetching followuprequest record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :electors + (support/do-or-log-error + (db/list-electors db/*db*) + :message + "Error while fetching elector record"), + :visits + (support/do-or-log-error + (db/list-visits db/*db*) + :message + "Error while fetching visit record"), + :issues + (support/do-or-log-error + (db/list-issues db/*db*) + :message + "Error while fetching issue record"), + :followupmethods + (support/do-or-log-error + (db/list-followupmethods db/*db*) + :message + "Error while fetching followupmethod record")})] (l/render (support/resolve-template "form-followuprequests-Followuprequest.html") - (:session r) - {:title "Followuprequest", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-followuprequest db/*db* p)), - :electors (db/list-electors db/*db*), - :visits (db/list-visits db/*db*), - :issues (db/list-issues db/*db*), - :followupmethods (db/list-followupmethods db/*db*)}))) + (:session 'r) + (merge {:title "Followuprequest", :params p} c)))) (defn list-genders-Genders [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some #{"id"} (keys p)) + (support/do-or-log-error + (db/search-strings-gender db/*db* p) + :message + "Error while searching gender records" + :error-return + {:warnings ["Error while searching gender records"]}) + (support/do-or-log-error + (db/list-genders db/*db* {}) + :message + "Error while fetching gender records" + :error-return + {:warnings ["Error while fetching gender records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-genders-Genders.html") - (:session r) - {:title "Genders", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-gender db/*db* p) - (db/list-genders db/*db* {}))}))) + (:session 'r) + (merge {:title "Genders", :params p} c)))) (defn form-genders-Gender [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-gender db/*db* p)) + :message + "Error while fetching gender record" + :error-return + {:warnings ["Error while fetching gender record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil)})] (l/render (support/resolve-template "form-genders-Gender.html") - (:session r) - {:title "Gender", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-gender db/*db* p))}))) + (:session 'r) + (merge {:title "Gender", :params p} c)))) (defn list-intentions-Intentions [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"Id"})) + c + (let + [records + (if + (some + #{"elector_id" "option_id" "locality" "visit_id" "Id"} + (keys p)) + (support/do-or-log-error + (db/search-strings-intention db/*db* p) + :message + "Error while searching intention records" + :error-return + {:warnings ["Error while searching intention records"]}) + (support/do-or-log-error + (db/list-intentions db/*db* {}) + :message + "Error while fetching intention records" + :error-return + {:warnings ["Error while fetching intention records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-intentions-Intentions.html") - (:session r) - {:title "Intentions", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-intention db/*db* p) - (db/list-intentions db/*db* {}))}))) + (:session 'r) + (merge {:title "Intentions", :params p} c)))) (defn form-intentions-Intention [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"Id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"Id"} #{p keys}) (db/get-intention db/*db* p)) + :message + "Error while fetching intention record" + :error-return + {:warnings ["Error while fetching intention record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :visits + (support/do-or-log-error + (db/list-visits db/*db*) + :message + "Error while fetching visit record"), + :electors + (support/do-or-log-error + (db/list-electors db/*db*) + :message + "Error while fetching elector record"), + :options + (support/do-or-log-error + (db/list-options db/*db*) + :message + "Error while fetching option record")})] (l/render (support/resolve-template "form-intentions-Intention.html") - (:session r) - {:title "Intention", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-intention db/*db* p)), - :visits (db/list-visits db/*db*), - :electors (db/list-electors db/*db*), - :options (db/list-options db/*db*)}))) + (:session 'r) + (merge {:title "Intention", :params p} c)))) (defn list-issues-Issues [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some #{"url" "id" "current"} (keys p)) + (support/do-or-log-error + (db/search-strings-issue db/*db* p) + :message + "Error while searching issue records" + :error-return + {:warnings ["Error while searching issue records"]}) + (support/do-or-log-error + (db/list-issues db/*db* {}) + :message + "Error while fetching issue records" + :error-return + {:warnings ["Error while fetching issue records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-issues-Issues.html") - (:session r) - {:title "Issues", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-issue db/*db* p) - (db/list-issues db/*db* {}))}))) + (:session 'r) + (merge {:title "Issues", :params p} c)))) (defn form-issues-Issue [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-issue db/*db* p)) + :message + "Error while fetching issue record" + :error-return + {:warnings ["Error while fetching issue record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil)})] (l/render (support/resolve-template "form-issues-Issue.html") - (:session r) - {:title "Issue", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-issue db/*db* p))}))) + (:session 'r) + (merge {:title "Issue", :params p} c)))) (defn list-options-Options [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some #{"id"} (keys p)) + (support/do-or-log-error + (db/search-strings-option db/*db* p) + :message + "Error while searching option records" + :error-return + {:warnings ["Error while searching option records"]}) + (support/do-or-log-error + (db/list-options db/*db* {}) + :message + "Error while fetching option records" + :error-return + {:warnings ["Error while fetching option records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-options-Options.html") - (:session r) - {:title "Options", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-option db/*db* p) - (db/list-options db/*db* {}))}))) + (:session 'r) + (merge {:title "Options", :params p} c)))) (defn form-options-Option [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-option db/*db* p)) + :message + "Error while fetching option record" + :error-return + {:warnings ["Error while fetching option record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil)})] (l/render (support/resolve-template "form-options-Option.html") - (:session r) - {:title "Option", - :params p, - :record - (if - (empty? (remove nil? (vals p))) - [] - (db/get-option db/*db* p))}))) + (:session 'r) + (merge {:title "Option", :params p} c)))) (defn list-roles-Roles [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some #{"members" "id" "name"} (keys p)) + (support/do-or-log-error + (db/search-strings-role db/*db* p) + :message + "Error while searching role records" + :error-return + {:warnings ["Error while searching role records"]}) + (support/do-or-log-error + (db/list-roles db/*db* {}) + :message + "Error while fetching role records" + :error-return + {:warnings ["Error while fetching role records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-roles-Roles.html") - (:session r) - {:title "Roles", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-role db/*db* p) - (db/list-roles db/*db* {}))}))) + (:session 'r) + (merge {:title "Roles", :params p} c)))) (defn form-roles-Role [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-role db/*db* p)) + :message + "Error while fetching role record" + :error-return + {:warnings ["Error while fetching role record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :canvassers + (support/do-or-log-error + (db/list-canvassers db/*db*) + :message + "Error while fetching canvasser record")})] (l/render (support/resolve-template "form-roles-Role.html") - (:session r) - {:title "Role", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-role db/*db* p)), - :canvassers (db/list-canvassers db/*db*)}))) + (:session 'r) + (merge {:title "Role", :params p} c)))) (defn list-teams-Teams [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some + #{"organisers" "district_id" "members" "longitude" "id" + "latitude" "name"} + (keys p)) + (support/do-or-log-error + (db/search-strings-team db/*db* p) + :message + "Error while searching team records" + :error-return + {:warnings ["Error while searching team records"]}) + (support/do-or-log-error + (db/list-teams db/*db* {}) + :message + "Error while fetching team records" + :error-return + {:warnings ["Error while fetching team records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-teams-Teams.html") - (:session r) - {:title "Teams", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-team db/*db* p) - (db/list-teams db/*db* {}))}))) + (:session 'r) + (merge {:title "Teams", :params p} c)))) (defn form-teams-Team [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-team db/*db* p)) + :message + "Error while fetching team record" + :error-return + {:warnings ["Error while fetching team record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :districts + (support/do-or-log-error + (db/list-districts db/*db*) + :message + "Error while fetching district record"), + :canvassers + (support/do-or-log-error + (db/list-canvassers db/*db*) + :message + "Error while fetching canvasser record")})] (l/render (support/resolve-template "form-teams-Team.html") - (:session r) - {:title "Team", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-team db/*db* p)), - :districts (db/list-districts db/*db*), - :canvassers (db/list-canvassers db/*db*)}))) + (:session 'r) + (merge {:title "Team", :params p} c)))) (defn list-visits-Visits [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [records + (if + (some #{"address_id" "id" "canvasser_id" "date"} (keys p)) + (support/do-or-log-error + (db/search-strings-visit db/*db* p) + :message + "Error while searching visit records" + :error-return + {:warnings ["Error while searching visit records"]}) + (support/do-or-log-error + (db/list-visits db/*db* {}) + :message + "Error while fetching visit records" + :error-return + {:warnings ["Error while fetching visit records"]}))] + (if (:warnings records) records {:records records}))] (l/render (support/resolve-template "list-visits-Visits.html") - (:session r) - {:title "Visits", - :params p, - :records - (if - (not (empty? (remove nil? (vals p)))) - (db/search-strings-visit db/*db* p) - (db/list-visits db/*db* {}))}))) + (:session 'r) + (merge {:title "Visits", :params p} c)))) (defn form-visits-Visit [r] (let - [p (support/massage-params (:params r))] + [p + (merge + {:offset 0, :limit 25} + (support/massage-params (:params r) (:form-params r) #{"id"})) + c + (let + [record + (support/do-or-log-error + (if (subset? #{"id"} #{p keys}) (db/get-visit db/*db* p)) + :message + "Error while fetching visit record" + :error-return + {:warnings ["Error while fetching visit record"]})] + {:warnings (:warnings record), + :record (assoc record :warnings nil), + :addresses + (support/do-or-log-error + (db/list-addresses db/*db*) + :message + "Error while fetching address record"), + :canvassers + (support/do-or-log-error + (db/list-canvassers db/*db*) + :message + "Error while fetching canvasser record")})] (l/render (support/resolve-template "form-visits-Visit.html") - (:session r) - {:title "Visit", - :params p, - :record - (if (empty? (remove nil? (vals p))) [] (db/get-visit db/*db* p)), - :addresses (db/list-addresses db/*db*), - :canvassers (db/list-canvassers db/*db*)}))) + (:session 'r) + (merge {:title "Visit", :params p} c)))) (defn raw-resolve-handler diff --git a/src/clj/youyesyet/routes/auto_json.clj b/src/clj/youyesyet/routes/auto_json.clj index cccc570..9c7234b 100644 --- a/src/clj/youyesyet/routes/auto_json.clj +++ b/src/clj/youyesyet/routes/auto_json.clj @@ -1,10 +1,11 @@ (ns youyesyet.routes.auto-json - "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180630T113559.533Z" + "JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180701T221528.540Z" (:require [adl-support.core :as support] - [clojure.java.io :as io] [clojure.core.memoize :as memo] + [clojure.java.io :as io] + [clojure.tools.logging :as log] [compojure.core :refer [defroutes GET POST]] [hugsql.core :as hugsql] [noir.response :as nresponse] @@ -16,228 +17,629 @@ create-address! "Auto-generated method to insert one record to the `addresses` table. Expects the following key(s) to be present in `params`: `(:address :postcode :phone :district_id :latitude :longitude :locality)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-address! params))) + (do + (log/debug + (str + "Calling query '" + "create-address!" + "' with params " + (map + (fn + [p] + (if + (#{"postcode" "address"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/create-address! 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`: `(:request-token-uri :access-token-uri :authorize-uri :consumer-key :consumer-secret :id)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-authority! params))) + (do + (log/debug + (str + "Calling query '" + "create-authority!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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`: `(: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))) + (do + (log/debug + (str + "Calling query '" + "create-canvasser!" + "' with params " + (map + (fn + [p] + (if + (#{"username" "address_id" "email" "phone" "fullname"} + (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (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`: `(:name)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-district! params))) + (do + (log/debug + (str + "Calling query '" + "create-district!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/create-district! params))) (defn create-dwelling! "Auto-generated method to insert one record to the `dwellings` table. Expects the following key(s) to be present in `params`: `(:address_id :sub-address)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-dwelling! params))) + (do + (log/debug + (str + "Calling query '" + "create-dwelling!" + "' with params " + (map + (fn + [p] + (if + (#{"sub-address" "address_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/create-dwelling! 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`: `(:name :dwelling_id :phone :email :gender)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-elector! params))) + (do + (log/debug + (str + "Calling query '" + "create-elector!" + "' with params " + (map + (fn + [p] + (if + (#{"gender" "email" "name" "phone"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (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`: `(:request_id :actor :date :notes :closed)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-followupaction! params))) + (do + (log/debug + (str + "Calling query '" + "create-followupaction!" + "' with params " + (map + (fn + [p] + (if (#{"date" "notes"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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))) + (do + (log/debug + (str + "Calling query '" + "create-followupmethod!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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`: `(: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))) + (do + (log/debug + (str + "Calling query '" + "create-followuprequest!" + "' with params " + (map + (fn + [p] + (if + (#{"elector_id" "issue_id" "visit_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/create-followuprequest! params))) (defn create-gender! "Auto-generated method to insert one record to the `genders` 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-gender! params))) + (do + (log/debug + (str + "Calling query '" + "create-gender!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/create-gender! 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 :locality)`. Returns a map containing the keys `#{\"Id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-intention! params))) + (do + (log/debug + (str + "Calling query '" + "create-intention!" + "' with params " + (map + (fn [p] (if (#{} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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`: `(:url :current :id)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-issue! params))) + (do + (log/debug + (str + "Calling query '" + "create-issue!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/create-issue! 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))) + (do + (log/debug + (str + "Calling query '" + "create-option!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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`: `(:name)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-role! params))) + (do + (log/debug + (str + "Calling query '" + "create-role!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/create-role! 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`: `(:name :district_id :latitude :longitude)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-team! params))) + (do + (log/debug + (str + "Calling query '" + "create-team!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/create-team! 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`: `(:address_id :canvasser_id :date)`. Returns a map containing the keys `#{\"id\"}` identifying the record created." [{:keys [params]}] - (do (db/create-visit! params))) + (do + (log/debug + (str + "Calling query '" + "create-visit!" + "' with params " + (map + (fn + [p] + (if + (#{"address_id" "date"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/create-visit! params))) (defn 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-address! params)) + (do + (log/debug + (str + "Calling query '" + "delete-address!" + "' with params " + (map + (fn + [p] + (if + (#{"postcode" "address"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/delete-address! 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)) + (do + (log/debug + (str + "Calling query '" + "delete-authority!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "delete-canvasser!" + "' with params " + (map + (fn + [p] + (if + (#{"username" "address_id" "email" "phone" "fullname"} + (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "delete-district!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/delete-district! params)) (response/found "/")) (defn delete-dwelling! "Auto-generated method to delete one record from the `dwellings` table. Expects the following key(s) to be present in `params`: `#{\"id\"}`." [{:keys [params]}] - (do (db/delete-dwelling! params)) + (do + (log/debug + (str + "Calling query '" + "delete-dwelling!" + "' with params " + (map + (fn + [p] + (if + (#{"sub-address" "address_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/delete-dwelling! 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)) + (do + (log/debug + (str + "Calling query '" + "delete-elector!" + "' with params " + (map + (fn + [p] + (if + (#{"gender" "email" "name" "phone"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "delete-followupaction!" + "' with params " + (map + (fn + [p] + (if (#{"date" "notes"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "delete-followupmethod!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "delete-followuprequest!" + "' with params " + (map + (fn + [p] + (if + (#{"elector_id" "issue_id" "visit_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/delete-followuprequest! params)) (response/found "/")) (defn delete-gender! "Auto-generated method to delete one record from the `genders` table. Expects the following key(s) to be present in `params`: `#{\"id\"}`." [{:keys [params]}] - (do (db/delete-gender! params)) + (do + (log/debug + (str + "Calling query '" + "delete-gender!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/delete-gender! params)) (response/found "/")) (defn delete-intention! "Auto-generated method to delete one record from the `intentions` table. Expects the following key(s) to be present in `params`: `#{\"Id\"}`." [{:keys [params]}] - (do (db/delete-intention! params)) + (do + (log/debug + (str + "Calling query '" + "delete-intention!" + "' with params " + (map + (fn [p] (if (#{} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/delete-intention! 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)) + (do + (log/debug + (str + "Calling query '" + "delete-issue!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "delete-option!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/delete-option! params)) (response/found "/")) (defn delete-role! "Auto-generated method to delete one record from the `roles` table. Expects the following key(s) to be present in `params`: `#{\"id\"}`." [{:keys [params]}] - (do (db/delete-role! params)) + (do + (log/debug + (str + "Calling query '" + "delete-role!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/delete-role! params)) (response/found "/")) (defn delete-team! "Auto-generated method to delete one record from the `teams` table. Expects the following key(s) to be present in `params`: `#{\"id\"}`." [{:keys [params]}] - (do (db/delete-team! params)) + (do + (log/debug + (str + "Calling query '" + "delete-team!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/delete-team! 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)) + (do + (log/debug + (str + "Calling query '" + "delete-visit!" + "' with params " + (map + (fn + [p] + (if + (#{"address_id" "date"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/delete-visit! params)) (response/found "/")) (def get-address (memo/ttl - (fn [{:keys [params]}] (do (db/get-address params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-address" + "' with params " + (map + (fn + [p] + (if + (#{"postcode" "address"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/get-address params)) + nil) :ttl/threshold 1000000000)) (def get-authority (memo/ttl - (fn [{:keys [params]}] (do (db/get-authority params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-authority" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-authority params)) + nil) :ttl/threshold 10000000000)) (def get-canvasser (memo/ttl - (fn [{:keys [params]}] (do (db/get-canvasser params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-canvasser" + "' with params " + (map + (fn + [p] + (if + (#{"username" "address_id" "email" "phone" "fullname"} + (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/get-canvasser params)) + nil) :ttl/threshold 10000000)) @@ -246,7 +648,22 @@ (memo/ttl (fn [{:keys [params]}] - (do (db/get-canvasser-by-username params)) + (do + (log/debug + (str + "Calling query '" + "get-canvasser-by-username" + "' with params " + (map + (fn + [p] + (if + (#{"username" "address_id" "email" "phone" "fullname"} + (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/get-canvasser-by-username params)) nil) :ttl/threshold 10000000)) @@ -254,21 +671,67 @@ (def get-district (memo/ttl - (fn [{:keys [params]}] (do (db/get-district params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-district" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-district params)) + nil) :ttl/threshold 10000000000)) (def get-dwelling (memo/ttl - (fn [{:keys [params]}] (do (db/get-dwelling params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-dwelling" + "' with params " + (map + (fn + [p] + (if + (#{"sub-address" "address_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/get-dwelling params)) + nil) :ttl/threshold 1000000000)) (def get-elector (memo/ttl - (fn [{:keys [params]}] (do (db/get-elector params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-elector" + "' with params " + (map + (fn + [p] + (if + (#{"gender" "email" "name" "phone"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/get-elector params)) + nil) :ttl/threshold 100000000)) @@ -276,81 +739,240 @@ 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: `clojure.lang.LazySeq@6b32af0e`." [{:keys [params]}] - (do (db/get-followupaction params))) + (do + (log/debug + (str + "Calling query '" + "get-followupaction" + "' with params " + (map + (fn + [p] + (if (#{"date" "notes"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-followupaction params))) (def get-followupmethod (memo/ttl - (fn [{:keys [params]}] (do (db/get-followupmethod params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-followupmethod" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-followupmethod params)) + nil) :ttl/threshold 10000000000)) (def get-followuprequest (memo/ttl - (fn [{:keys [params]}] (do (db/get-followuprequest params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-followuprequest" + "' with params " + (map + (fn + [p] + (if + (#{"elector_id" "issue_id" "visit_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/get-followuprequest params)) + nil) :ttl/threshold 100000)) (def get-gender (memo/ttl - (fn [{:keys [params]}] (do (db/get-gender params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-gender" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-gender params)) + nil) :ttl/threshold 1000000000)) -(defn +(def get-intention - "Auto-generated method to select one record from the `intentions` table. Expects the following key(s) to be present in `params`: `#{\"Id\"}`. Returns a map containing the following keys: `clojure.lang.LazySeq@27b20cd9`." - [{:keys [params]}] - (do (db/get-intention params))) + (memo/ttl + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-intention" + "' with params " + (map + (fn [p] (if (#{} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-intention params)) + nil) + :ttl/threshold + 100000)) (def get-issue (memo/ttl - (fn [{:keys [params]}] (do (db/get-issue params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-issue" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-issue params)) + nil) :ttl/threshold 1000000)) (def get-option (memo/ttl - (fn [{:keys [params]}] (do (db/get-option params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-option" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-option params)) + nil) :ttl/threshold 10000000000)) (def get-role (memo/ttl - (fn [{:keys [params]}] (do (db/get-role params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-role" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-role params)) + nil) :ttl/threshold 10000000000)) (def get-role-by-name (memo/ttl - (fn [{:keys [params]}] (do (db/get-role-by-name params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-role-by-name" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-role-by-name params)) + nil) :ttl/threshold 10000000000)) (def get-team (memo/ttl - (fn [{:keys [params]}] (do (db/get-team params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-team" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/get-team params)) + nil) :ttl/threshold 10000000)) (def get-visit (memo/ttl - (fn [{:keys [params]}] (do (db/get-visit params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "get-visit" + "' with params " + (map + (fn + [p] + (if + (#{"address_id" "date"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/get-visit params)) + nil) :ttl/threshold 100000)) (def list-addresses (memo/ttl - (fn [{:keys [params]}] (do (db/list-addresses params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-addresses" + "' with params " + (map + (fn + [p] + (if + (#{"postcode" "address"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/list-addresses params)) + nil) :ttl/threshold 1000000000)) @@ -362,14 +984,44 @@ (def list-authorities (memo/ttl - (fn [{:keys [params]}] (do (db/list-authorities params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-authorities" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-authorities params)) + nil) :ttl/threshold 10000000000)) (def list-canvassers (memo/ttl - (fn [{:keys [params]}] (do (db/list-canvassers params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-canvassers" + "' with params " + (map + (fn + [p] + (if + (#{"username" "address_id" "email" "phone" "fullname"} + (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/list-canvassers params)) + nil) :ttl/threshold 10000000)) @@ -396,14 +1048,43 @@ (def list-districts (memo/ttl - (fn [{:keys [params]}] (do (db/list-districts params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-districts" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-districts params)) + nil) :ttl/threshold 10000000000)) (def list-dwellings (memo/ttl - (fn [{:keys [params]}] (do (db/list-dwellings params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-dwellings" + "' with params " + (map + (fn + [p] + (if + (#{"sub-address" "address_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/list-dwellings params)) + nil) :ttl/threshold 1000000000)) @@ -415,7 +1096,24 @@ (def list-electors (memo/ttl - (fn [{:keys [params]}] (do (db/list-electors params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-electors" + "' with params " + (map + (fn + [p] + (if + (#{"gender" "email" "name" "phone"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/list-electors params)) + nil) :ttl/threshold 100000000)) @@ -433,7 +1131,18 @@ 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: `(:request_id :actor :date :notes :closed :id)`." [{:keys [params]}] - (do (db/list-followupactions params))) + (do + (log/debug + (str + "Calling query '" + "list-followupactions" + "' with params " + (map + (fn + [p] + (if (#{"date" "notes"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-followupactions params))) (defn list-followupactions-by-canvasser @@ -448,14 +1157,43 @@ (def list-followupmethods (memo/ttl - (fn [{:keys [params]}] (do (db/list-followupmethods params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-followupmethods" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-followupmethods params)) + nil) :ttl/threshold 10000000000)) (def list-followuprequests (memo/ttl - (fn [{:keys [params]}] (do (db/list-followuprequests params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-followuprequests" + "' with params " + (map + (fn + [p] + (if + (#{"elector_id" "issue_id" "visit_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/list-followuprequests params)) + nil) :ttl/threshold 100000)) @@ -482,15 +1220,40 @@ (def list-genders (memo/ttl - (fn [{:keys [params]}] (do (db/list-genders params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-genders" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-genders params)) + nil) :ttl/threshold 1000000000)) -(defn +(def list-intentions - "Auto-generated method to select all records from the `intentions` 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: `(:visit_id :elector_id :option_id :locality :Id)`." - [{:keys [params]}] - (do (db/list-intentions params))) + (memo/ttl + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-intentions" + "' with params " + (map + (fn [p] (if (#{} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-intentions params)) + nil) + :ttl/threshold + 100000)) (defn list-intentions-by-elector @@ -510,21 +1273,57 @@ (def list-issues (memo/ttl - (fn [{:keys [params]}] (do (db/list-issues params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-issues" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-issues params)) + nil) :ttl/threshold 1000000)) (def list-options (memo/ttl - (fn [{:keys [params]}] (do (db/list-options params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-options" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-options params)) + nil) :ttl/threshold 10000000000)) (def list-roles (memo/ttl - (fn [{:keys [params]}] (do (db/list-roles params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-roles" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-roles params)) + nil) :ttl/threshold 10000000000)) @@ -536,7 +1335,19 @@ (def list-teams (memo/ttl - (fn [{:keys [params]}] (do (db/list-teams params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-teams" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/list-teams params)) + nil) :ttl/threshold 10000000)) @@ -553,7 +1364,24 @@ (def list-visits (memo/ttl - (fn [{:keys [params]}] (do (db/list-visits params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "list-visits" + "' with params " + (map + (fn + [p] + (if + (#{"address_id" "date"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/list-visits params)) + nil) :ttl/threshold 100000)) @@ -570,42 +1398,135 @@ (def search-strings-address (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-address params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-address" + "' with params " + (map + (fn + [p] + (if + (#{"postcode" "address"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/search-strings-address params)) + nil) :ttl/threshold 1000000000)) (def search-strings-authority (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-authority params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-authority" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-authority params)) + nil) :ttl/threshold 10000000000)) (def search-strings-canvasser (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-canvasser params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-canvasser" + "' with params " + (map + (fn + [p] + (if + (#{"username" "address_id" "email" "phone" "fullname"} + (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/search-strings-canvasser params)) + nil) :ttl/threshold 10000000)) (def search-strings-district (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-district params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-district" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-district params)) + nil) :ttl/threshold 10000000000)) (def search-strings-dwelling (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-dwelling params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-dwelling" + "' with params " + (map + (fn + [p] + (if + (#{"sub-address" "address_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/search-strings-dwelling params)) + nil) :ttl/threshold 1000000000)) (def search-strings-elector (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-elector params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-elector" + "' with params " + (map + (fn + [p] + (if + (#{"gender" "email" "name" "phone"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/search-strings-elector params)) + nil) :ttl/threshold 100000000)) @@ -613,14 +1534,34 @@ search-strings-followupaction "Auto-generated method to select all records from the `followupactions` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(:request_id :actor :date :notes :closed :id)`." [{:keys [params]}] - (do (db/search-strings-followupaction params))) + (do + (log/debug + (str + "Calling query '" + "search-strings-followupaction" + "' with params " + (map + (fn + [p] + (if (#{"date" "notes"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-followupaction params))) (def search-strings-followupmethod (memo/ttl (fn [{:keys [params]}] - (do (db/search-strings-followupmethod params)) + (do + (log/debug + (str + "Calling query '" + "search-strings-followupmethod" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-followupmethod params)) nil) :ttl/threshold 10000000000)) @@ -630,7 +1571,21 @@ (memo/ttl (fn [{:keys [params]}] - (do (db/search-strings-followuprequest params)) + (do + (log/debug + (str + "Calling query '" + "search-strings-followuprequest" + "' with params " + (map + (fn + [p] + (if + (#{"elector_id" "issue_id" "visit_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/search-strings-followuprequest params)) nil) :ttl/threshold 100000)) @@ -638,48 +1593,138 @@ (def search-strings-gender (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-gender params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-gender" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-gender params)) + nil) :ttl/threshold 1000000000)) -(defn +(def search-strings-intention - "Auto-generated method to select all records from the `intentions` table with any text field matching the value of the key `:pattern` which should be in the request. 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: `(:visit_id :elector_id :option_id :locality :Id)`." - [{:keys [params]}] - (do (db/search-strings-intention params))) + (memo/ttl + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-intention" + "' with params " + (map + (fn [p] (if (#{} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-intention params)) + nil) + :ttl/threshold + 100000)) (def search-strings-issue (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-issue params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-issue" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-issue params)) + nil) :ttl/threshold 1000000)) (def search-strings-option (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-option params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-option" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-option params)) + nil) :ttl/threshold 10000000000)) (def search-strings-role (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-role params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-role" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-role params)) + nil) :ttl/threshold 10000000000)) (def search-strings-team (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-team params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-team" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/search-strings-team params)) + nil) :ttl/threshold 10000000)) (def search-strings-visit (memo/ttl - (fn [{:keys [params]}] (do (db/search-strings-visit params)) nil) + (fn + [{:keys [params]}] + (do + (log/debug + (str + "Calling query '" + "search-strings-visit" + "' with params " + (map + (fn + [p] + (if + (#{"address_id" "date"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/search-strings-visit params)) + nil) :ttl/threshold 100000)) @@ -687,91 +1732,241 @@ 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 :locality :longitude :phone :postcode)`." [{:keys [params]}] - (do (db/update-address! params)) + (do + (log/debug + (str + "Calling query '" + "update-address!" + "' with params " + (map + (fn + [p] + (if + (#{"postcode" "address"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/update-address! params)) (response/found "/")) (defn update-authority! "Auto-generated method to update one record in the `authorities` table. Expects the following key(s) to be present in `params`: `(:access-token-uri :authorize-uri :consumer-key :consumer-secret :id :request-token-uri)`." [{:keys [params]}] - (do (db/update-authority! params)) + (do + (log/debug + (str + "Calling query '" + "update-authority!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/update-authority! 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)) + (do + (log/debug + (str + "Calling query '" + "update-canvasser!" + "' with params " + (map + (fn + [p] + (if + (#{"username" "address_id" "email" "phone" "fullname"} + (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "update-district!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/update-district! params)) (response/found "/")) (defn update-dwelling! "Auto-generated method to update one record in the `dwellings` table. Expects the following key(s) to be present in `params`: `(:address_id :id :sub-address)`." [{:keys [params]}] - (do (db/update-dwelling! params)) + (do + (log/debug + (str + "Calling query '" + "update-dwelling!" + "' with params " + (map + (fn + [p] + (if + (#{"sub-address" "address_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/update-dwelling! 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`: `(:dwelling_id :email :gender :id :name :phone)`." [{:keys [params]}] - (do (db/update-elector! params)) + (do + (log/debug + (str + "Calling query '" + "update-elector!" + "' with params " + (map + (fn + [p] + (if + (#{"gender" "email" "name" "phone"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "update-followupaction!" + "' with params " + (map + (fn + [p] + (if (#{"date" "notes"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (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)) + (do + (log/debug + (str + "Calling query '" + "update-followuprequest!" + "' with params " + (map + (fn + [p] + (if + (#{"elector_id" "issue_id" "visit_id"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/update-followuprequest! params)) (response/found "/")) (defn update-intention! "Auto-generated method to update one record in the `intentions` table. Expects the following key(s) to be present in `params`: `(:Id :elector_id :locality :option_id :visit_id)`." [{:keys [params]}] - (do (db/update-intention! params)) + (do + (log/debug + (str + "Calling query '" + "update-intention!" + "' with params " + (map + (fn [p] (if (#{} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/update-intention! 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`: `(:current :id :url)`." [{:keys [params]}] - (do (db/update-issue! params)) + (do + (log/debug + (str + "Calling query '" + "update-issue!" + "' with params " + (map + (fn [p] (if (#{"id"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/update-issue! params)) (response/found "/")) (defn update-role! "Auto-generated method to update one record in the `roles` table. Expects the following key(s) to be present in `params`: `(:id :name)`." [{:keys [params]}] - (do (db/update-role! params)) + (do + (log/debug + (str + "Calling query '" + "update-role!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/update-role! params)) (response/found "/")) (defn update-team! "Auto-generated method to update one record in the `teams` table. Expects the following key(s) to be present in `params`: `(:district_id :id :latitude :longitude :name)`." [{:keys [params]}] - (do (db/update-team! params)) + (do + (log/debug + (str + "Calling query '" + "update-team!" + "' with params " + (map + (fn [p] (if (#{"name"} (str (name p))) (params p) "[ELIDED]")) + (keys params)))) + (db/update-team! 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)) + (do + (log/debug + (str + "Calling query '" + "update-visit!" + "' with params " + (map + (fn + [p] + (if + (#{"address_id" "date"} (str (name p))) + (params p) + "[ELIDED]")) + (keys params)))) + (db/update-visit! params)) (response/found "/")) (defroutes diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index 86b2729..bcd418b 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -1,17 +1,16 @@ (ns ^{:doc "Routes/pages available to unauthenticated users." :author "Simon Brooke"} youyesyet.routes.home (:require [clojure.java.io :as io] + [clojure.string :as s] [clojure.tools.logging :as log] [clojure.walk :refer [keywordize-keys]] - [noir.response :as nresponse] [noir.util.route :as route] - [ring.util.http-response :refer [content-type ok]] + [ring.util.http-response :as response] [youyesyet.config :refer [env]] [youyesyet.db.core :as db-core] [youyesyet.layout :as layout] [youyesyet.oauth :as oauth] [compojure.core :refer [defroutes GET POST]] - [ring.util.http-response :as response] )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -63,13 +62,13 @@ user (:user session) roles (if user (db-core/list-roles-by-canvasser db-core/*db* {:id (:id user)}))] (cond - roles (layout/render "roles.html" - (:session request) - {: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))))) + roles (layout/render "roles.html" + (:session request) + {: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))))) (defn home-page [] @@ -86,42 +85,47 @@ password (:password params) redirect-to (or (:redirect-to params) "roles")] (cond - (:authority params) - (let [auth (oauth/authority! (:authority params))] - (if auth - (do - (log/info "Attempting to authorise with authority " (:authority params)) - (oauth/fetch-request-token - (assoc request :session (assoc session :authority auth)) - auth)) - (throw (Exception. (str "No such authority: " (:authority params)))))) - ;; 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 user :roles (layout/get-user-roles user))) - username + (:authority params) + (let [auth (oauth/authority! (:authority params))] + (if auth + (do + (log/info "Attempting to authorise with authority " (:authority params)) + (oauth/fetch-request-token + (assoc request :session (assoc session :authority auth)) + auth)) + (throw (Exception. (str "No such authority: " (:authority params)))))) + ;; 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)) + (let + [roles (layout/get-user-roles user)] + (log/info (str "Logged in user '" username "' with roles " roles)) + (assoc + (response/found redirect-to) + :session + (assoc session :user user :roles roles))) + ;; if we've got a username but either no user object or else + ;; the password doesn't match + username (layout/render - "login.html" - session - {:title (str "User " username " is unknown") :redirect-to redirect-to}) - true + "login.html" + session + {:title (str "User " username " is unknown") + :redirect-to redirect-to + :warnings ["Your user name was not recognised or your password did not match"]}) + ;; if we've no username, just invite the user to log in + true (layout/render - "login.html" - session - {:title "Please log in" - :redirect-to redirect-to - :authorities (db-core/list-authorities db-core/*db*)})))) + "login.html" + session + {:title "Please log in" + :redirect-to redirect-to + :authorities (db-core/list-authorities db-core/*db*)})))) (defroutes home-routes (GET "/" [] (home-page)) -;; (GET "/js/:file" [file] -;; (-> (io/input-stream (str "resources/public/js/" file)) -;; ok -;; (content-type "text/javascript;charset=UTF-8"))) (GET "/home" [] (home-page)) (GET "/about" [] (about-page)) (GET "/roles" request (route/restricted (roles-page request))) diff --git a/youyesyet.adl.xml b/youyesyet.adl.xml index d542d6f..ac62804 100644 --- a/youyesyet.adl.xml +++ b/youyesyet.adl.xml @@ -12,37 +12,6 @@ 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. - - - - - - - - - {{site-title}}: {{title}} - - - - - - - See diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml index 1034995..60c7984 100644 --- a/youyesyet.canonical.adl.xml +++ b/youyesyet.canonical.adl.xml @@ -21,36 +21,6 @@ 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. - - - - - - - - - {{site-title}}: {{title}} - - - - - - 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, @@ -102,7 +72,7 @@ - + @@ -113,8 +83,8 @@ - -
+ +
@@ -332,7 +302,7 @@ - + Intentions of electors to vote for options elicited in visits. From 16af0405375e99b4083296f04a9932fab4b553af Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 2 Jul 2018 16:22:38 +0100 Subject: [PATCH 28/51] Tactical commit while I try to understand why the app doesn't rebuild --- env/dev/clj/youyesyet/core.clj | 9 +- package-lock.json | 11 + project.clj | 35 +- .../20161014170335-basic-setup.down.sql | 50 -- .../20161014170335-basic-setup.up.sql | 669 ------------------ .../20170315190500-roles-and-teams.down.sql | 13 - .../20170315190500-roles-and-teams.up.sql | 39 - ...170401115900-basic-reference-data.down.sql | 17 - ...20170401115900-basic-reference-data.up.sql | 58 -- .../20170415102900-core-views.down.sql | 11 - .../20170415102900-core-views.up.sql | 59 -- .../20170721084900-dwellings.down.sql | 69 -- .../20170721084900-dwellings.up.sql | 87 --- ...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 | 18 - .../20180317175047-test-data.down.sql | 3 - .../20180317175047-test-data.up.sql | 41 -- .../20180408124500-reference-data.down.sql | 1 - .../20180408124500-reference-data.up.sql | 2 - .../20180526162051-dwellings.down.sql | 8 - .../20180526162051-dwellings.up.sql | 11 - .../20180611204244-bootstrap.down.sql | 0 .../20180611204244-bootstrap.up.sql | 15 - resources/templates/app.html | 11 +- resources/templates/base.html | 9 +- resources/templates/canvasser.html | 60 -- src/clj/youyesyet/db/schema.clj | 484 ------------- src/clj/youyesyet/handler.clj | 3 +- src/clj/youyesyet/layout.clj | 25 +- src/clj/youyesyet/routes/authenticated.clj | 76 -- src/clj/youyesyet/routes/home.clj | 11 +- .../youyesyet/canvasser_app/handlers.cljs | 14 + src/cljs/youyesyet/canvasser_app/state.cljs | 4 +- .../youyesyet/canvasser_app/ui_utils.cljs | 21 +- .../canvasser_app/views/building.cljs | 2 +- .../canvasser_app/views/elector.cljs | 104 +++ .../canvasser_app/views/electors.cljs | 58 +- .../youyesyet/canvasser_app/views/gdpr.cljs | 60 ++ .../youyesyet/canvasser_app/views/issue.cljs | 2 +- .../youyesyet/canvasser_app/views/issues.cljs | 2 +- .../youyesyet/canvasser_app/views/map.cljs | 5 +- 45 files changed, 282 insertions(+), 1973 deletions(-) create mode 100644 package-lock.json delete mode 100644 resources/migrations/20161014170335-basic-setup.down.sql delete mode 100644 resources/migrations/20161014170335-basic-setup.up.sql delete mode 100644 resources/migrations/20170315190500-roles-and-teams.down.sql delete mode 100644 resources/migrations/20170315190500-roles-and-teams.up.sql delete mode 100644 resources/migrations/20170401115900-basic-reference-data.down.sql delete mode 100644 resources/migrations/20170401115900-basic-reference-data.up.sql delete mode 100644 resources/migrations/20170415102900-core-views.down.sql delete mode 100644 resources/migrations/20170415102900-core-views.up.sql delete mode 100644 resources/migrations/20170721084900-dwellings.down.sql delete mode 100644 resources/migrations/20170721084900-dwellings.up.sql delete mode 100644 resources/migrations/20180316110100-intentions-and-options.down.sql delete mode 100644 resources/migrations/20180316110100-intentions-and-options.up.sql delete mode 100644 resources/migrations/20180317170000-gender.down.sql delete mode 100644 resources/migrations/20180317170000-gender.up.sql delete mode 100644 resources/migrations/20180317170907-reference-data.down.sql delete mode 100644 resources/migrations/20180317170907-reference-data.up.sql delete mode 100644 resources/migrations/20180317175047-test-data.down.sql delete mode 100644 resources/migrations/20180317175047-test-data.up.sql delete mode 100644 resources/migrations/20180408124500-reference-data.down.sql delete mode 100644 resources/migrations/20180408124500-reference-data.up.sql delete mode 100644 resources/migrations/20180526162051-dwellings.down.sql delete mode 100644 resources/migrations/20180526162051-dwellings.up.sql delete mode 100644 resources/migrations/20180611204244-bootstrap.down.sql delete mode 100644 resources/migrations/20180611204244-bootstrap.up.sql delete mode 100644 resources/templates/canvasser.html delete mode 100644 src/clj/youyesyet/db/schema.clj delete mode 100644 src/clj/youyesyet/routes/authenticated.clj create mode 100644 src/cljs/youyesyet/canvasser_app/views/elector.cljs create mode 100644 src/cljs/youyesyet/canvasser_app/views/gdpr.cljs diff --git a/env/dev/clj/youyesyet/core.clj b/env/dev/clj/youyesyet/core.clj index c64c2e7..c9f3446 100644 --- a/env/dev/clj/youyesyet/core.clj +++ b/env/dev/clj/youyesyet/core.clj @@ -1,4 +1,5 @@ -(ns youyesyet.core +(ns ^{:doc "Devalopment launcher, entirely boilerplate from Luminus."} + youyesyet.core (:require [youyesyet.handler :as handler] [luminus.repl-server :as repl] [luminus.http-server :as http] @@ -60,8 +61,8 @@ :else (start-app args))) -(mount/stop) - -(mount/start) +;; (mount/start) +;; (mount/stop) + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..17b97dd --- /dev/null +++ b/package-lock.json @@ -0,0 +1,11 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "bower": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/bower/-/bower-1.8.4.tgz", + "integrity": "sha1-54dqB23rgTf30GUl3F6MZtuC8oo=" + } + } +} diff --git a/project.clj b/project.clj index 303e26e..2e6879b 100644 --- a/project.clj +++ b/project.clj @@ -51,15 +51,16 @@ :main ^:skip-aot youyesyet.core :migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")} - :plugins [[lein-cprop "1.0.1"] - [migratus-lein "0.4.2"] - [org.clojars.punkisdead/lein-cucumber "1.0.5"] + :plugins [[lein-bower "0.5.1"] [lein-cljsbuild "1.1.4"] [lein-codox "0.10.3"] - [lein-uberwar "0.2.0"] - [lein-bower "0.5.1"] + [lein-cprop "1.0.1"] [lein-less "1.7.5"] - [lein-codox "0.10.3"]] + [lein-npm "0.6.2"] + [lein-uberwar "0.2.0"] + [migratus-lein "0.4.2"] + [org.clojars.punkisdead/lein-cucumber "1.0.5"] + ] :bower-dependencies [[leaflet "0.7.3"] [jquery "3.3.1"] @@ -73,6 +74,13 @@ :languages [:clojure :clojurescript] :source-paths ["src/clj" "src/cljc" "src/cljs"]} + :npm {:dependencies [[datatables.net "1.10.19"] + [datatables.net-dt "1.10.19"] + [jquery "3.3.1"] + [leaflet "1.3.1"] + [signature_pad "2.3.2"]] + :root "resources/public/js/lib"} + :uberwar {:handler youyesyet.handler/app :init youyesyet.handler/init @@ -80,7 +88,8 @@ :name "youyesyet.war"} :clean-targets ^{:protect false} - [:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]] + [:target-path [:cljsbuild :builds :app :compiler :output-dir] + [:cljsbuild :builds :app :compiler :output-to]] :figwheel {:http-server-root "public" @@ -93,7 +102,8 @@ {:uberjar {:omit-source true :prep-tasks ["compile" ["cljsbuild" "once" "min"]] :cljsbuild - {:builds + {:prep-tasks [["npm" "install"]] + :builds {:min {:source-paths ["src/cljc" "src/cljs" "env/prod/cljs"] :compiler @@ -129,7 +139,8 @@ [lein-figwheel "0.5.9"] [org.clojure/clojurescript "1.9.495"]] :cljsbuild - {:builds + {:prep-tasks [["npm" "install"]] + :builds {:app {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"] :compiler @@ -140,9 +151,6 @@ :source-map true :optimizations :none :pretty-print true}}}} - - - :doo {:build "test"} :source-paths ["env/dev/clj"] :resource-paths ["env/dev/resources"] @@ -151,7 +159,8 @@ (pjstadig.humane-test-output/activate!)]} :project/test {:resource-paths ["env/test/resources"] :cljsbuild - {:builds + {:prep-tasks [["npm" "install"]] + :builds {:test {:source-paths ["src/cljc" "src/cljs" "test/cljs"] :compiler diff --git a/resources/migrations/20161014170335-basic-setup.down.sql b/resources/migrations/20161014170335-basic-setup.down.sql deleted file mode 100644 index 1005cee..0000000 --- a/resources/migrations/20161014170335-basic-setup.down.sql +++ /dev/null @@ -1,50 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20161014170335-basic-setup.down.sql: database schema for youyesyet. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2016 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- - --- intended to reverse out the database changes made in --- 20161014170335-basic-setup.up.sql - -drop table addresses cascade; ---;; -drop table authorities cascade; ---;; -drop table canvassers cascade; ---;; -drop table districts cascade; ---;; -drop table electors cascade; ---;; -drop table followupactions cascade; ---;; -drop table followupmethods cascade; ---;; -drop table followuprequests cascade; ---;; -drop table issueexpertise cascade; ---;; -drop table issues cascade; ---;; -drop table options cascade; ---;; -drop table visits cascade; ---;; diff --git a/resources/migrations/20161014170335-basic-setup.up.sql b/resources/migrations/20161014170335-basic-setup.up.sql deleted file mode 100644 index 4d74c06..0000000 --- a/resources/migrations/20161014170335-basic-setup.up.sql +++ /dev/null @@ -1,669 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20161014170335-basic-setup.up.sql: database schema for youyesyet. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2016 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -SET statement_timeout = 0; ---;; -SET lock_timeout = 0; ---;; -SET client_encoding = 'UTF8'; ---;; -SET standard_conforming_strings = on; ---;; -SET check_function_bodies = false; ---;; -SET client_min_messages = warning; ---;; --- --- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: --- - --- CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; ---;; - --- --- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: --- - --- COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; ---;; - -SET search_path = public, pg_catalog; ---;; -SET default_tablespace = ''; ---;; -SET default_with_oids = false; ---;; --- --- Name: addresses; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS addresses ( - id integer NOT NULL, - address character varying(256) NOT NULL, - postcode character varying(16), - phone character varying(16), - district_id integer, - latitude real, - longitude real -); ---;; - -ALTER TABLE public.addresses OWNER TO youyesyet; ---;; --- --- Name: addresses_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE addresses_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - -ALTER TABLE public.addresses_id_seq OWNER TO youyesyet; ---;; --- --- Name: addresses_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE addresses_id_seq OWNED BY addresses.id; ---;; - --- --- Name: authorities; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS authorities ( - id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.authorities OWNER TO youyesyet; ---;; --- --- Name: canvassers; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS canvassers ( - id serial, - username character varying(32) NOT NULL, - fullname character varying(64) NOT NULL, - elector_id integer, - address_id integer NOT NULL, - phone character varying(16), - email character varying(128), - authority_id character varying(32) NOT NULL, - introduced_by int references canvassers(id), - authorised boolean -); ---;; - -ALTER TABLE public.canvassers OWNER TO youyesyet; ---;; --- --- Name: districts; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS districts ( - id integer NOT NULL, - name character varying(64) NOT NULL -); ---;; - -ALTER TABLE public.districts OWNER TO youyesyet; ---;; --- --- Name: electors; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS electors ( - id integer NOT NULL, - name character varying(64) NOT NULL, - address_id integer NOT NULL, - phone character varying(16), - email character varying(128) -); ---;; - -ALTER TABLE public.electors OWNER TO youyesyet; ---;; --- --- Name: followupactions; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS followupactions ( - id integer NOT NULL, - request_id integer NOT NULL, - actor integer NOT NULL, - date timestamp with time zone DEFAULT now() NOT NULL, - notes text, - closed boolean -); ---;; - -ALTER TABLE public.followupactions OWNER TO youyesyet; - --- --- Name: followupactions_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE followupactions_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - -ALTER TABLE public.followupactions_id_seq OWNER TO youyesyet; ---;; --- --- Name: followupactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE followupactions_id_seq OWNED BY followupactions.id; ---;; - --- --- Name: followupmethods; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS followupmethods ( - id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.followupmethods OWNER TO youyesyet; ---;; --- --- Name: followuprequests; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -insert into followupmethods values ('Telephone'); ---;; -insert into followupmethods values ('eMail'); ---;; -insert into followupmethods values ('Post'); ---;; - - -CREATE TABLE IF NOT EXISTS followuprequests ( - id integer NOT NULL, - elector_id integer NOT NULL, - visit_id integer NOT NULL, - issue_id character varying(32) NOT NULL, - method_id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.followuprequests OWNER TO youyesyet; ---;; - --- --- Name: followuprequests_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE followuprequests_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - - -ALTER TABLE public.followuprequests_id_seq OWNER TO youyesyet; ---;; - --- --- Name: followuprequests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE followuprequests_id_seq OWNED BY followuprequests.id; ---;; - - --- --- Name: issueexpertise; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS issueexpertise ( - canvasser_id integer NOT NULL, - issue_id character varying(32) NOT NULL, - method_id character varying(32) NOT NULL -); ---;; - - -ALTER TABLE public.issueexpertise OWNER TO youyesyet; ---;; - --- --- Name: issues; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS issues ( - id character varying(32) NOT NULL, - url character varying(256) -); ---;; - - -ALTER TABLE public.issues OWNER TO youyesyet; ---;; - --- --- Name: options; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS options ( - id character varying(32) NOT NULL -); ---;; - - -ALTER TABLE public.options OWNER TO youyesyet; ---;; - --- --- Name: schema_migrations; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS schema_migrations ( - id bigint NOT NULL -); ---;; - - -ALTER TABLE public.schema_migrations OWNER TO youyesyet; ---;; - --- --- Name: visits; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS visits ( - id integer NOT NULL, - address_id integer NOT NULL, - canvasser_id integer NOT NULL, - date timestamp with time zone DEFAULT now() NOT NULL -); ---;; - - -ALTER TABLE public.visits OWNER TO youyesyet; ---;; - --- --- Name: visits_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE visits_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - - -ALTER TABLE public.visits_id_seq OWNER TO youyesyet; ---;; - --- --- Name: visits_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE visits_id_seq OWNED BY visits.id; ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY addresses ALTER COLUMN id SET DEFAULT nextval('addresses_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions ALTER COLUMN id SET DEFAULT nextval('followupactions_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests ALTER COLUMN id SET DEFAULT nextval('followuprequests_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits ALTER COLUMN id SET DEFAULT nextval('visits_id_seq'::regclass); ---;; - - --- --- Name: addresses_address_key; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_address_key UNIQUE (address); ---;; - - --- --- Name: addresses_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_pkey PRIMARY KEY (id); ---;; - - --- --- Name: authorities_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY authorities - ADD CONSTRAINT authorities_pkey PRIMARY KEY (id); ---;; - - --- --- Name: canvassers_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_pkey PRIMARY KEY (id); ---;; - - --- --- Name: districts_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY districts - ADD CONSTRAINT districts_pkey PRIMARY KEY (id); ---;; - - --- --- Name: electors_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY electors - ADD CONSTRAINT electors_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followupactions_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followupmethods_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followupmethods - ADD CONSTRAINT followupmethods_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followuprequests_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_pkey PRIMARY KEY (id); ---;; - - --- --- Name: issues_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY issues - ADD CONSTRAINT issues_pkey PRIMARY KEY (id); ---;; - - --- --- Name: options_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY options - ADD CONSTRAINT options_pkey PRIMARY KEY (id); ---;; - - --- --- Name: schema_migrations_id_key; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - ---ALTER TABLE ONLY schema_migrations --- ADD CONSTRAINT schema_migrations_id_key UNIQUE (id); ---;; - - --- --- Name: visits_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_pkey PRIMARY KEY (id); ---;; - - --- --- Name: addresses_district_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_district_id_fkey FOREIGN KEY (district_id) REFERENCES districts(id); ---;; - - --- --- Name: canvassers_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: canvassers_authority_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_authority_id_fkey FOREIGN KEY (authority_id) REFERENCES authorities(id); ---;; - - --- --- Name: canvassers_elector_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_elector_id_fkey FOREIGN KEY (elector_id) REFERENCES electors(id); ---;; - -create unique index canvassers_username_ix on canvassers (username); -create unique index canvassers_email_ix on canvassers(email); - --- --- Name: electors_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY electors - ADD CONSTRAINT electors_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: followupactions_actor_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_actor_fkey FOREIGN KEY (actor) REFERENCES canvassers(id); ---;; - - --- --- Name: followupactions_request_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_request_id_fkey FOREIGN KEY (request_id) REFERENCES followuprequests(id); ---;; - - --- --- Name: followuprequests_elector_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_elector_id_fkey FOREIGN KEY (elector_id) REFERENCES electors(id); ---;; - - --- --- Name: followuprequests_issue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id); ---;; - - --- --- Name: followuprequests_method_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_method_id_fkey FOREIGN KEY (method_id) REFERENCES followupmethods(id); ---;; - - --- --- Name: followuprequests_visit_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_visit_id_fkey FOREIGN KEY (visit_id) REFERENCES visits(id); ---;; - - --- --- Name: issueexpertise_canvasser_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_canvasser_id_fkey FOREIGN KEY (canvasser_id) REFERENCES canvassers(id); ---;; - - --- --- Name: issueexpertise_issue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id); ---;; - - --- --- Name: issueexpertise_method_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_method_id_fkey FOREIGN KEY (method_id) REFERENCES followupmethods(id); ---;; - - --- --- Name: visits_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: visits_canvasser_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_canvasser_id_fkey FOREIGN KEY (canvasser_id) REFERENCES canvassers(id); ---;; - - --- --- Name: public; Type: ACL; Schema: -; Owner: postgres --- - -REVOKE ALL ON SCHEMA public FROM PUBLIC; ---;; - -REVOKE ALL ON SCHEMA public FROM postgres; ---;; - -GRANT ALL ON SCHEMA public TO postgres; ---;; - -GRANT ALL ON SCHEMA public TO PUBLIC; ---;; - - --- --- PostgreSQL database dump complete --- - diff --git a/resources/migrations/20170315190500-roles-and-teams.down.sql b/resources/migrations/20170315190500-roles-and-teams.down.sql deleted file mode 100644 index a8bdb6a..0000000 --- a/resources/migrations/20170315190500-roles-and-teams.down.sql +++ /dev/null @@ -1,13 +0,0 @@ -drop table teammemberships; - -drop table teamorganiserships; - -drop index ix_teams_name; - -drop table teams; - -drop table rolememberships; - -drop index ix_roles_name; - -drop table roles; diff --git a/resources/migrations/20170315190500-roles-and-teams.up.sql b/resources/migrations/20170315190500-roles-and-teams.up.sql deleted file mode 100644 index 7ff7ffc..0000000 --- a/resources/migrations/20170315190500-roles-and-teams.up.sql +++ /dev/null @@ -1,39 +0,0 @@ -create table if not exists roles ( - id serial primary key, - name varchar(64) not null -); - -create unique index ix_roles_name on roles(name); - -create table if not exists rolememberships ( - role_id integer not null references roles(id), - canvasser_id integer not null references canvassers(id) -); - -create table if not exists teams ( - id serial primary key, - name varchar(64) not null, - district_id integer not null references districts(id), - latitude real, - longitude real -); - -create unique index ix_teams_name on teams(name); - -create table if not exists teammemberships ( - team_id integer not null references teams(id), - canvasser_id integer not null references canvassers(id) -); - -create table if not exists teamorganiserships ( - team_id integer not null references teams(id), - canvasser_id integer not null references canvassers(id) -); - -alter table roles owner to youyesyet; - -alter table rolememberships owner to youyesyet; - -alter table teams owner to youyesyet; - -alter table teammemberships owner to youyesyet; diff --git a/resources/migrations/20170401115900-basic-reference-data.down.sql b/resources/migrations/20170401115900-basic-reference-data.down.sql deleted file mode 100644 index a0b3aa1..0000000 --- a/resources/migrations/20170401115900-basic-reference-data.down.sql +++ /dev/null @@ -1,17 +0,0 @@ --- this is just a teardown of everything set up in the corresponding .up.sql file - -delete from roles where name = 'Expert'; -delete from roles where name = 'Administrator'; -delete from roles where name = 'Recruiter'; -delete from roles where name = 'Organiser'; -delete from roles where name = 'Editor'; - -alter table issues drop column content; -alter table issues drop column current; - -delete from issues where id = 'Currency'; -delete from issues where id = 'Monarchy'; -delete from issues where id = 'Defence'; - -delete from options where id = 'Yes'; -delete from options where id = 'No'; diff --git a/resources/migrations/20170401115900-basic-reference-data.up.sql b/resources/migrations/20170401115900-basic-reference-data.up.sql deleted file mode 100644 index 26d3bf1..0000000 --- a/resources/migrations/20170401115900-basic-reference-data.up.sql +++ /dev/null @@ -1,58 +0,0 @@ - --- We don't explicitly instantiate the 'Canvasser' role since every user is --- deemed to be a canvasser. - - --- an 'Expert' is someone with expertise in one or more issues, who is --- trusted to discuss those issues in detail with electors. -insert into roles (name) values ('Expert'); - - --- an 'Administrator' is someone entitled to broadly alter reference data --- throughout the system. -insert into roles (name) values ('Administrator'); - - --- a 'Recruiter' is someone entitled to invite other people to become users --- ('Canvassers'). A Recruiter is entitled to lock the account of anyone they --- have recruited, recursively. -insert into roles (name) values ('Recruiter'); - - --- an 'Organiser' is someone who organises one or more local teams. An Organiser --- is entitled to exclude any Canvasser from any team they organise. -insert into roles (name) values ('Organiser'); - - --- an 'Editor' is someone entitled to add and edit issues. -insert into roles (name) values ('Editor'); - --- issue text is local; there may still in addition be a further link to more --- information, but the basic issue text should be part of the issue record. --- The text should fit on a phone screen without scrolling, so is reasonably --- short. -alter table issues add column content varchar(1024); - --- an issue may be current or not current; when not current it is not deleted --- from the system but kept because it may become current again later. Only --- current issues are shown in the app. Typically not fewer than three and not --- more than about seven issues should be current at any time. -alter table issues add column current boolean default false; - -insert into issues (id, content, current) values ('Currency', - 'Scotland could keep the Pound, or use the Euro. But we could also set up a new currency of our own.', - true); - -insert into issues (id, content, current) values ('Monarchy', - 'Scotland could keep the Queen. This is an issue to be decided after independence.', - true); - -insert into issues (id, content, current) values ('Defence', - 'Scotland will not have nuclear weapons, and will probably not choose to engage in far-off wars. But we could remain members of NATO.', - true); - - -insert into options (id) values ('Yes'); - -insert into options (id) values ('No'); - diff --git a/resources/migrations/20170415102900-core-views.down.sql b/resources/migrations/20170415102900-core-views.down.sql deleted file mode 100644 index c576947..0000000 --- a/resources/migrations/20170415102900-core-views.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -drop view if exists roles_by_canvasser; - -drop view if exists teams_by_canvasser; - -drop view if exists canvassers_by_team; - -drop view if exists canvassers_by_introducer; - -drop view if exists teams_by_organiser; - -drop view if exists organisers_by_team; diff --git a/resources/migrations/20170415102900-core-views.up.sql b/resources/migrations/20170415102900-core-views.up.sql deleted file mode 100644 index 53346a6..0000000 --- a/resources/migrations/20170415102900-core-views.up.sql +++ /dev/null @@ -1,59 +0,0 @@ - -create view roles_by_canvasser as - select canvassers.id as canvasser, roles.name - from roles, rolememberships, canvassers - where roles.id = rolememberships.role_id - and canvassers.id = rolememberships.canvasser_id - and canvassers.authorised = true; - -create view teams_by_canvasser as - select canvassers.id as canvasser, teams.id, teams.name, teams.latitude, teams.longitude - from teams, teammemberships, canvassers - where teams.id = teammemberships.team_id - and canvassers.id = teammemberships.canvasser_id; - -create view canvassers_by_team as - select teams.id as team, - canvassers.id, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone - from teams, teammemberships, canvassers - where teams.id = teammemberships.team_id - and canvassers.id = teammemberships.canvasser_id - and canvassers.authorised = true; - -create view canvassers_by_introducer as - select introducers.id as introducer, - canvassers.id as canvasser, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone, - canvassers.authorised - from canvassers, canvassers as introducers - where introducers.id = canvassers.introduced_by; - -create view teams_by_organiser as - select canvassers.id as organiser, - teams.id, - teams.name, - teams.latitude, - teams.longitude - from teams, teamorganiserships, canvassers - where teams.id = teamorganiserships.team_id - and canvassers.id = teamorganiserships.canvasser_id - and canvassers.authorised = true; - -create view organisers_by_team as - select teams.id as team, - canvassers.id, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone - from teams, teamorganiserships, canvassers - where teams.id = teamorganiserships.team_id - and canvassers.id = teamorganiserships.canvasser_id - and canvassers.authorised = true; diff --git a/resources/migrations/20170721084900-dwellings.down.sql b/resources/migrations/20170721084900-dwellings.down.sql deleted file mode 100644 index 674fb1b..0000000 --- a/resources/migrations/20170721084900-dwellings.down.sql +++ /dev/null @@ -1,69 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20170721084900.up.sql: add dwellings table, to deal with flatted addresses. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2017 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -alter table canvassers add column address_id integer references addresses(id); ---;; -alter table electors add column address_id integer references addresses(id); ---;; -alter table visits add column address_id integer references addresses(id); ---;; - -update canvassers set address_id = - (select address_id from dwellings where id = canvassers.dwelling_id); ---;; - -update electors set address_id = - (select address_id from dwellings where id = electors.dwelling_id); ---;; - -update visits set address_id = - (select address_id from dwellings where id = visits.dwelling_id); ---;; - -alter table canvassers alter column address_id set not null; ---;; -alter table electors alter column address_id set not null; ---;; -alter table visits alter column address_id set not null; ---;; - -alter table canvassers drop column dwelling_id; ---;; -alter table electors drop column dwelling_id; ---;; -alter table visits drop column dwelling_id; ---;; - -drop table if exists dwellings; ---;; diff --git a/resources/migrations/20170721084900-dwellings.up.sql b/resources/migrations/20170721084900-dwellings.up.sql deleted file mode 100644 index 74e6a27..0000000 --- a/resources/migrations/20170721084900-dwellings.up.sql +++ /dev/null @@ -1,87 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20170721084900.up.sql: add dwellings table, to deal with flatted addresses. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2017 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -CREATE TABLE IF NOT EXISTS dwellings ( - id serial NOT NULL primary key, - address_id integer NOT NULL references addresses(id), - sub_address varchar(16) -); ---;; - -ALTER TABLE public.dwellings OWNER TO youyesyet; ---;; - -INSERT INTO dwellings (address_id, sub_address) - SELECT DISTINCT id, '' FROM addresses; ---;; - -alter table canvassers add column dwelling_id integer references dwellings(id); ---;; -alter table electors add column dwelling_id integer references dwellings(id); ---;; -alter table visits add column dwelling_id integer references dwellings(id); ---;; - -update canvassers set dwelling_id = - (select id from dwellings where address_id = canvassers.address_id); ---;; - -update electors set dwelling_id = - (select id from dwellings where address_id = electors.address_id); ---;; - -update visits set dwelling_id = - (select id from dwellings where address_id = visits.address_id); ---;; - -alter table canvassers alter column dwelling_id set not null; ---;; -alter table electors alter column dwelling_id set not null; ---;; -alter table visits alter column dwelling_id set not null; ---;; - -alter table canvassers drop constraint canvassers_address_id_fkey; ---;; -alter table electors drop constraint electors_address_id_fkey; ---;; -alter table visits drop constraint visits_address_id_fkey; ---;; - -alter table canvassers drop column address_id; ---;; -alter table electors drop column address_id; ---;; -alter table visits drop column address_id; ---;; diff --git a/resources/migrations/20180316110100-intentions-and-options.down.sql b/resources/migrations/20180316110100-intentions-and-options.down.sql deleted file mode 100644 index 84c98b8..0000000 --- a/resources/migrations/20180316110100-intentions-and-options.down.sql +++ /dev/null @@ -1,24 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20180316110100intentions-and-options.down.sql: remove intentions and options ----- ----- 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 ----- --------------------------------------------------------------------------------- - -drop table intentions; diff --git a/resources/migrations/20180316110100-intentions-and-options.up.sql b/resources/migrations/20180316110100-intentions-and-options.up.sql deleted file mode 100644 index 0736740..0000000 --- a/resources/migrations/20180316110100-intentions-and-options.up.sql +++ /dev/null @@ -1,30 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20180316110100intentions-and-options.up.sql: add intentions and options ----- ----- 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 ----- --------------------------------------------------------------------------------- - -CREATE TABLE IF NOT EXISTS intentions ( - visit_id int not null references visits(id) on delete no action, - elector_id int not null references electors(id) on delete no action, - option_id varchar(32) not null references options(id) on delete no action - ); - -ALTER TABLE intentions owner to youyesyet; diff --git a/resources/migrations/20180317170000-gender.down.sql b/resources/migrations/20180317170000-gender.down.sql deleted file mode 100644 index 5eac53a..0000000 --- a/resources/migrations/20180317170000-gender.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -alter table electors drop column gender; - -drop table genders; diff --git a/resources/migrations/20180317170000-gender.up.sql b/resources/migrations/20180317170000-gender.up.sql deleted file mode 100644 index 9512ea2..0000000 --- a/resources/migrations/20180317170000-gender.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -create table genders ( - id varchar(32) not null primary key -); - --- genders is reference data -insert into genders values ('Female'); -insert into genders values ('Male'); -insert into genders values ('Non-binary'); -insert into genders values ('Unknown'); - -alter table electors add column gender varchar(32) references genders(id) default 'Unknown'; diff --git a/resources/migrations/20180317170907-reference-data.down.sql b/resources/migrations/20180317170907-reference-data.down.sql deleted file mode 100644 index ed67940..0000000 --- a/resources/migrations/20180317170907-reference-data.down.sql +++ /dev/null @@ -1,10 +0,0 @@ -delete from options where id = 'Yes'; - -delete from options where id = 'No'; - -delete from issues where id = 'Currency'; - -delete from issues where id = 'Monarchy'; - -delete from issues where id = 'Defence'; - diff --git a/resources/migrations/20180317170907-reference-data.up.sql b/resources/migrations/20180317170907-reference-data.up.sql deleted file mode 100644 index ed1dfa4..0000000 --- a/resources/migrations/20180317170907-reference-data.up.sql +++ /dev/null @@ -1,18 +0,0 @@ -insert into options values ('Yes'); - -insert into options values ('No'); - - -insert into issues (id, url) values ('Currency', 'https://www.yyy.scot/wiki/issues/Currency'); - -insert into issues (id, url) values ('Monarchy', 'https://www.yyy.scot/wiki/issues/Monarchy'); - -insert into issues (id, url) values ('Defence', 'https://www.yyy.scot/wiki/issues/Defence'); - -insert into genders (id ) values ('Female'); - -insert into genders (id ) values ('Male'); - -insert into genders (id ) values ('Non-binary'); - -insert into genders (id ) values ('Unknown'); diff --git a/resources/migrations/20180317175047-test-data.down.sql b/resources/migrations/20180317175047-test-data.down.sql deleted file mode 100644 index 4a5583c..0000000 --- a/resources/migrations/20180317175047-test-data.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -delete from addresses where id < = 4; - -delete from electors where id <= 10; diff --git a/resources/migrations/20180317175047-test-data.up.sql b/resources/migrations/20180317175047-test-data.up.sql deleted file mode 100644 index 0cc4b20..0000000 --- a/resources/migrations/20180317175047-test-data.up.sql +++ /dev/null @@ -1,41 +0,0 @@ -insert into addresses (id, address, postcode, latitude, longitude) -values (1, '13 Imaginary Terrace, IM1 3TE', 'IM1 3TE', 55.8253043, -4.2569057); - -insert into addresses (id, address, postcode, latitude, longitude) -values (2, '15 Imaginary Terrace, IM1 3TE', 'IM1 3TE', 55.8252354, -4.2572778); - -insert into addresses (id, address, postcode, latitude, longitude) -values (3, '17 Imaginary Terrace, IM1 3TE', 'IM1 3TE', 55.825166, -4.257026); - -insert into addresses (id, address, postcode, latitude, longitude) -values (4, '19 Imaginary Terrace, IM1 3TE', 'IM1 3TE', 55.8250695, -4.2570239); - -insert into electors (id, name, address_id, gender) -values (1, 'Alan Anderson', 1, 'Male'); - -insert into electors (id, name, address_id, gender) -values (2, 'Ann Anderson', 1, 'Female'); - -insert into electors (id, name, address_id, gender) -values (3, 'Alex Anderson', 1, 'Non-binary'); - -insert into electors (id, name, address_id) -values (4, 'Andy Anderson', 1); - -insert into electors (id, name, address_id, gender) -values (5, 'Beryl Brown', 2, 'Female'); - -insert into electors (id, name, address_id, gender) -values (6, 'Betty Black', 2, 'Female'); - -insert into electors (id, name, address_id, gender) -values (7, 'Catriona Crathie', 3, 'Female'); - -insert into electors (id, name, address_id, gender) -values (8, 'Colin Caruthers', 3, 'Male'); - -insert into electors (id, name, address_id, gender) -values (9, 'Calum Crathie', 3, 'Unknown'); - -insert into electors (id, name, address_id, gender) -values (10, 'David Dewar', 4, 'Male'); diff --git a/resources/migrations/20180408124500-reference-data.down.sql b/resources/migrations/20180408124500-reference-data.down.sql deleted file mode 100644 index 68bada9..0000000 --- a/resources/migrations/20180408124500-reference-data.down.sql +++ /dev/null @@ -1 +0,0 @@ -alter table issues drop column current; diff --git a/resources/migrations/20180408124500-reference-data.up.sql b/resources/migrations/20180408124500-reference-data.up.sql deleted file mode 100644 index aaae234..0000000 --- a/resources/migrations/20180408124500-reference-data.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -alter table issues add column current boolean default true; - diff --git a/resources/migrations/20180526162051-dwellings.down.sql b/resources/migrations/20180526162051-dwellings.down.sql deleted file mode 100644 index ab91769..0000000 --- a/resources/migrations/20180526162051-dwellings.down.sql +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 41e1a6e..0000000 --- a/resources/migrations/20180526162051-dwellings.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -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/migrations/20180611204244-bootstrap.down.sql b/resources/migrations/20180611204244-bootstrap.down.sql deleted file mode 100644 index e69de29..0000000 diff --git a/resources/migrations/20180611204244-bootstrap.up.sql b/resources/migrations/20180611204244-bootstrap.up.sql deleted file mode 100644 index d0377ad..0000000 --- a/resources/migrations/20180611204244-bootstrap.up.sql +++ /dev/null @@ -1,15 +0,0 @@ --- enough data to get the system working and real logins - -insert into addresses (address, postcode, latitude, longitude) -values ('West Croft, Standingstone, Auchencairn', 'DG7 1RF', 54.822389, -3.920265); - -insert into dwellings (id, address_id, sub_address) -values (5, 5, ''); - -insert into electors (name, dwelling_id, gender) -values ('Simon Brooke', 1, 'Male'); - -insert into authorities (id) values ('GitHub'); - -insert into canvassers (username, fullname, elector_id, address_id, authority_id, authorised) -values ('simon_brooke', 'Simon Brooke', 2, 2, 'GitHub', true); diff --git a/resources/templates/app.html b/resources/templates/app.html index 7b181ca..4b14abe 100644 --- a/resources/templates/app.html +++ b/resources/templates/app.html @@ -23,13 +23,22 @@

{% endblock %} +{% block extra-script %} + var user = { + "username": "{{user.username}}", + "fullname": "{{user.fullname}}", + "id": {{user.id|default:-1}}, + "authorised": {{user.authorised|default:false}} + }; +{% endblock %} {% block extra-tail %} -{% script "js/lib/leaflet/dist/leaflet.js" %} +{% script "js/lib/node_modules/signature_pad/dist/signature_pad.min.js" %} +{% script "js/lib/node_modules/leaflet/dist/leaflet.js" %} {% script "/js/app.js" %} {% endblock %} diff --git a/resources/templates/base.html b/resources/templates/base.html index 93c779c..317fc4f 100644 --- a/resources/templates/base.html +++ b/resources/templates/base.html @@ -10,7 +10,6 @@ {{site-title}}: {{title}} - {% script "js/lib/jquery/dist/jquery.min.js" %} {% endblock %} {% block extra-head %} @@ -76,10 +75,6 @@ {% endblock %} {% endblock %} - {% block extra-tail %} - - - {% endblock %} + {% block extra-tail %} + + + {% endblock %} diff --git a/resources/templates/canvasser.html b/resources/templates/canvasser.html deleted file mode 100644 index ceb6c6c..0000000 --- a/resources/templates/canvasser.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "base.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/db/schema.clj b/src/clj/youyesyet/db/schema.clj deleted file mode 100644 index 7571251..0000000 --- a/src/clj/youyesyet/db/schema.clj +++ /dev/null @@ -1,484 +0,0 @@ -(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])) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; -;;;; youyesyet.db.schema: database schema for youyesyet. -;;;; -;;;; This program is free software; you can redistribute it and/or -;;;; modify it under the terms of the GNU General Public License -;;;; as published by the Free Software Foundation; either version 2 -;;;; of the License, or (at your option) any later version. -;;;; -;;;; This program is distributed in the hope that it will be useful, -;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;;;; GNU General Public License for more details. -;;;; -;;;; You should have received a copy of the GNU General Public License -;;;; along with this program; if not, write to the Free Software -;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -;;;; USA. -;;;; -;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign -;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;; Note that this is the (old) Korma way of doing things; -;;; it may not play well with migrations, nor with the HugSQL way of doing things recommended -;;; in Web Development with Clojure, Second Ed. So this may be temporary 'get us started' code, -;;; which later gets thrown away. The 'create-x-table!' functions in this file may be -;;; redundant, and if they are the namespace probably needs to be renamed to 'entities'. -;;; See also resources/migrations/20161014170335-basic-setup.up.sql - -(defn create-districts-table! - "Create a table to hold the electoral districts in which electors are registered. - Note that, as this app is being developed for the independence referendum in which - polling is across the whole of Scotland, this part of the design isn't fully thought - through; if later adapted to general or local elections, some breakdown or hierarchy - of polling districts into constituencies will be required." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :districts - ;; it may be necessary to have a serial abstract primary key but I suspect - ;; polling districts already have numbers assigned by the Electoral Commission and - ;; it would be sensible to use those. TODO: check. - [:id "integer not null primary key"] - [:name "varchar(64) not null"] - ;; TODO: it would make sense to hold polygon data for polling districts so we can reflect - ;; them on the map, but I haven't thought through how to do that yet. - ))) - - -(kc/defentity district - (kc/pk :id) - (kc/table :districts) - (kc/database yyydb/*db*) - (kc/entity-fields :id :name)) - - -(defn create-addresses-table! - "Create a table to hold the addresses at which electors are registered." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :addresses - [:id "serial not null primary key"] - ;; we do NOT want to hold multiple address records for the same household. When we receive - ;; the electoral roll data the addresses are likely to be text fields inlined in the elector - ;; record; in digesting the roll data we need to split these out and resolve them against existing - ;; addresses in the table, creating a new address record only if there's no match. - [:address "varchar(256) not null unique"] - [:postcode "varchar(16)"] - [:phone "varchar(16)"] - ;; the electoral district within which this address exists - [:district_id "integer references districts(id)"] - [:latitude :real] - [:longitude :real]))) - - -(kc/defentity address - (kc/pk :id) - (kc/table :addresses) - (kc/database yyydb/*db*) - (kc/entity-fields :id :address :postcode :phone :latitude :longitude) - (kc/has-one district)) - - -(defn create-authorities-table! - "Create a table to hold the oauth authorities against which we with authenticate canvassers." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :authorities - [:id "varchar(32) not null primary key"] - ;; more stuff here when I understand more - ))) - - -(kc/defentity authority - (kc/pk :id) - (kc/table :authorities) - (kc/database yyydb/*db*) - (kc/entity-fields :id)) - - -(defn create-electors-table! - "Create a table to hold electors data." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :electors - ;; id should be the roll number on the electoral roll, I think, but only if this is unique - ;; across Scotland. Otherwise we need a separate id field. TODO: check. - [:id "integer primary key"] - [:name "varchar(64) not null"] - [:address_id "integer not null references addresses(id)" ] - [:phone "varchar(16)"] - ;; we'll probably only capture email data on electors if they request a followup - ;; on a particular issue by email. - [:email "varchar(128)"]))) - - -(kc/defentity elector - (kc/pk :id) - (kc/table :electors) - (kc/database yyydb/*db*) - (kc/entity-fields :id :name :phone :email) - (kc/has-one address)) - - -;;; Lifecycle of the canvasser record goes like this, I think: -;;; A canvasser record is created when an existing canvasser issues an invitation to a friend. -;;; The invitation takes the form of an automatically generated email with a magic token in it. -;;; At this point the record has only an email address, the introduced_by and the magic token, -;;; which is itself probably a hash of the email address. Therefore, having the username as the -;;; primary key won't work. -;;; -;;; The invited person clicks on the link in the email and completes the sign-up form, adding -;;; their full name, and their phone number. If the username they have chosen is unique, they -;;; are then sent a second email with a new magic token, possibly a hash of email address + -;;; full name. When they click on the link in this second email, their 'authorised' flag is -;;; set to 'true'. -;;; -;;; Administrators can also create canvasser records directly.aw -;;; TODO: Do we actually need a username at all? Wouldn't the email address do? - -(defn create-canvassers-table! - "Create a table to hold data on canvassers (including authentication data)." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :canvassers - ;; id is the username the canvasser logs in as. - [:id "serial primary key"] - [:username "varchar(32) unique"] - [:fullname "varchar(64) not null"] - ;; most canvassers will be electors, we should link them: - [:elector_id "integer references electors(id) on delete no action"] - ;; but some canvassers may not be electors, so we need contact details separately: - [:address_id "integer not null references addresses(id)" ] - [:phone "varchar(16)"] - [:email "varchar(128)"] - ;; with which authority do we authenticate this canvasser? I do not want to hold even - ;; encrypted passwords locally - [:authority_id "varchar(32) not null references authorities(id) on delete no action"] - [:introduced_by "integer references canvassers(id)"] - [:is_admin :boolean] - ;; true if the canvasser is authorised to use the app; else false. This allows us to - ;; block canvassers we suspect of misbehaving. - [:authorised :boolean]))) - - -(kc/defentity canvasser - (kc/pk :id) - (kc/table :canvassers) - (kc/database yyydb/*db*) - (kc/entity-fields :id :fullname :phone :email :is_admin :authorised) - (kc/has-one elector) - (kc/has-one address) -;; (kc/has-one canvasser {:fk :introduced_by}) - (kc/has-one authority)) - - -(defn create-visits-table! - "Create a table to record visits by canvassers to addresses (including virtual visits by telephone)." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :visits - [:id "serial not null primary key"] - [:address_id "integer not null references addresses(id)"] - [:canvasser_id "integer not null references canvassers(id)"] - [:date "timestamp with time zone not null default now()"]))) - - -(kc/defentity visit - (kc/pk :id) - (kc/table :visits) - (kc/database yyydb/*db*) - (kc/entity-fields :id :date) - (kc/has-one address) - (kc/has-one canvasser)) - - -(defn create-options-table! - "Create a table to record options in the vote. This app is being created for the Independence - referendum, which will have just two options, 'Yes' and 'No', but it might later be adapted - for more general political canvassing." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :options - ;; id is also the text of the option; e.g. 'Yes', 'No'. - [:id "varchar(32) not null primary key"] - ;; To do elections you probably need party and candidate and stuff here, but - ;; for the referendum it's unnecessary. - ))) - - -(kc/defentity option - (kc/pk :id) - (kc/table :options) - (kc/database yyydb/*db*) - (kc/entity-fields :id)) - - -(defn create-option-district-table! - "Create a table to link options to the districts in which they are relevant. This is extremely - simple for the referendum: both options are relevant to all districts. This table is essentially - 'for later expansion'." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :optionsdistricts - [:option_id "varchar(32) not null references options(option)"] - [:district_id "integer not null references districts(id)"]))) - - -;; I think we don't need an entity for optionsdistricts, because it's just a link table. - - -(defn create-intention-table! - "Create a table to record the intention of an elector as solicited by a canvasser during a visit. - TODO: decide whether to insert a record in this table for 'don't knows'." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :intentions - [:id "serial primary key"] - ;; the elector who gave this intention - [:elector_id "integer not null references electors(id)"] - ;; the option the elector said they were planning to vote for - [:option_id "varchar(32) not null references options(option)"] - [:visit_id "integer not null references visits(id)"]))) - - -(kc/defentity intention - (kc/pk :id) - (kc/table :intentions) - (kc/database yyydb/*db*) - (kc/entity-fields :id) - (kc/has-one elector) - (kc/has-one option) - (kc/has-one visit)) - - -(defn create-issues-table! - "A table for issues we predict electors may raise on the doorstep, for which we may be - able to provide extra information or arrange for issue-specialists to phone and talk - to the elector." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :issues - ;; short name of this issue, e.g. 'currency', 'defence', 'pensions' - [:id "varchar(32) not null primary key"] - ;; URL of some brief material the canvasser can use on the doorstap - [:url "varchar(256)"]))) - - -(kc/defentity issue - (kc/pk :id) - (kc/table :issues) - (kc/database yyydb/*db*) - (kc/entity-fields :id :url)) - - -(defn create-followup-methods-table! - "Create a table to hold reference data on followup methods." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :followupmethods - [;; the method, e.g. 'telephone', 'email', 'post' - :id "varchar(32) not null primary key"]))) - - -(kc/defentity followup-method - (kc/pk :id) - (kc/table :followupmethods) - (kc/database yyydb/*db*) - (kc/entity-fields :id)) - - -(defn create-issue-expertise-table! - "A table to record which canvassers have expertise in which issues, so that followup - requests can be directed to the right canvassers." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :issueexpertise - ;; the expert canvasser - [:canvasser_id "integer not null references canvassers(id)"] - ;; the issue they have expertise in - [:issue_id "varchar(32) not null references issues(id)"] - ;; the method by which this expert can respond to electors on this issue - [:method_id "varchar(32) not null references followupmethods(id)"]))) - - -(kc/defentity issue-expertise - (kc/table :issueexpertise) - (kc/database yyydb/*db*) - (kc/entity-fields :id) - (kc/has-one canvasser) - (kc/has-one issue) - (kc/has-one followup-method)) - - -(defn create-followup-requests-table! - "Create a table to record requests for followup contacts on particular issues." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :followuprequests - [:id "serial primary key"] - [:elector_id "integer not null references electors(id)"] - [:visit_id "integer not null references visits(id)"] - [:issue_id "varchar(32) not null references issues(id)"] - ;; We probably need a followupmethod (telephone, email, postal) and, for telephone, - ;; convenient times but I haven't thought through how to represent this or how - ;; the user interface will work. - [:method_id "varchar(32) not null references followupmethods(id)"]))) - - -(kc/defentity followup-request - (kc/table :followuprequests) - (kc/database yyydb/*db*) - (kc/entity-fields :id) - (kc/has-one elector) - (kc/has-one visit) - (kc/has-one issue) - (kc/has-one followup-method)) - - -(defn create-followup-actions-table! - "Create a table to record actions on followup requests. Record in this table are almost - certainly created through a desktop-style interface rather than through te app, so it's - reasonable that there should be narrative fields." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :followupactions - [:id "serial primary key"] - [:request_id "integer not null references followuprequests(id)"] - [:actor "integer not null references canvassers(id)"] - [:date "timestamp with time zone not null default now()"] - [:notes "text"] - ;; true if this action closes the request - [:closed :boolean]))) - - -(kc/defentity followup-action - (kc/table :followupactions) - (kc/database yyydb/*db*) - (kc/entity-fields :id :notes :date :closed) - (kc/has-one followup-request) - (kc/has-one canvasser {:fk :actor})) - - - -(defn create-role-table! - "Create a table to record roles. I'm not even yet certain that this is strictly necessary, - but it allows us to record the fact that different users (canvassers) have different roles - in the system." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :roles - [:id "serial primary key"] - [:name "varchar(64) not null"]))) - - -(defn create-role-membership-table! - "Create a link table to record membership of roles." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :rolememberships - [:role_id "integer not null references role(id)"] - [:canvasser_id "integer not null references canvasser(id)"]))) - - -(kc/defentity role - (kc/table :roles) - (kc/database yyydb/*db*) - (kc/entity-fields :id :name) - (kc/many-to-many canvasser :rolememberships)) - - -(defn create-team-table! - "Create a table to record teams." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :teams - [:id "serial primary key"] - [:name "varchar(64) not null"] - ;; the electoral district within which this address exists - [:district_id "integer references districts(id)"] - ;; nominal home location of this team - [:latitude :real] - [:longitude :real]))) - - -(defn create-team-membership-table! - "Create a link table to record membership of team." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :teammemberships - [:team_id "integer not null references team(id)"] - [:canvasser_id "integer not null references canvasser(id)"]))) - - -(kc/defentity team - (kc/table :teams) - (kc/database yyydb/*db*) - (kc/entity-fields :id :name :latitude :longitude) - (kc/has-one district) - (kc/many-to-many canvasser :teammemberships)) - - -(defn init-db! [] - "Initialised the whole database." - (create-districts-table!) - (create-addresses-table!) - (create-authorities-table!) - (create-electors-table!) - (create-canvassers-table!) - (create-visits-table!) - (create-options-table!) - (create-issues-table!) - (create-followup-methods-table!) - (create-issue-expertise-table!) - (create-followup-requests-table!) - (create-followup-actions-table!) - (create-role-table!) - (create-role-membership-table!) - (create-team-table!) - (create-team-membership-table!) - ) diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index b131f44..c9c8119 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -9,7 +9,6 @@ [youyesyet.config :refer [env]] [youyesyet.layout :refer [error-page]] [youyesyet.middleware :as middleware] - [youyesyet.routes.authenticated :refer [authenticated-routes]] [youyesyet.routes.home :refer [home-routes]] [youyesyet.routes.oauth :refer [oauth-routes]] [youyesyet.routes.auto-json :refer [auto-rest-routes]] @@ -73,7 +72,7 @@ (wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-formats)) 'oauth-routes - #'authenticated-routes + (route/resources "/") (route/not-found (:body (error-page {:status 404 diff --git a/src/clj/youyesyet/layout.clj b/src/clj/youyesyet/layout.clj index 2033ecf..129f495 100644 --- a/src/clj/youyesyet/layout.clj +++ b/src/clj/youyesyet/layout.clj @@ -66,21 +66,18 @@ [template session & [params]] (let [user (:user session)] (log/debug (str "layout/render: template: '" template "'; user: '" user "'.")) - (assoc - (content-type - (ok + (content-type + (ok (parser/render-file - template - (assoc params - :page template - :csrf-token *anti-forgery-token* - :version (System/getProperty "youyesyet.version")))) - "text/html; charset=utf-8") - :user user - :user-roles (get-user-roles user) - :site-title (:site-title env) - :site-logo (:site-logo env) - :session session))) + template + (assoc params + :page template + :csrf-token *anti-forgery-token* + :user user + :user-roles (get-user-roles user) + :site-title (:site-title env) + :version (System/getProperty "youyesyet.version")))) + "text/html; charset=utf-8"))) (defn error-page diff --git a/src/clj/youyesyet/routes/authenticated.clj b/src/clj/youyesyet/routes/authenticated.clj deleted file mode 100644 index cc6b9ea..0000000 --- a/src/clj/youyesyet/routes/authenticated.clj +++ /dev/null @@ -1,76 +0,0 @@ -(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]] - [noir.response :as nresponse] - [noir.util.route :as route] - [ring.util.http-response :as response] - [youyesyet.layout :as layout] - [youyesyet.db.core :as db])) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; -;;;; 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 code adapted from http://www.luminusweb.net/docs#accessing_the_database - -(defn post? - "Return true if the argument is a ring request which is a post request" - [request] - 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) - (if (post? request) - (db/update-canvasser! params) - (db/create-canvasser! params)) - (db/get-canvasser (:id params))) - ))] - (layout/render - "canvasser.html" - (:session request) - {: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" - [request] - (layout/render "routing.html" (:session request))) - -(defroutes authenticated-routes - (GET "/edit-canvasser" request (canvasser-page request)) - (POST "/edit-canvasser" request (canvasser-page request)) - (GET "/routing" [request] (routing-page request))) diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index bcd418b..deaf047 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -36,12 +36,14 @@ ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn app-page [] - (layout/render "app.html" {})) +(defn app-page [request] + (layout/render "app.html" {:title "Canvasser app" + :user (:user (:session request))})) (defn about-page [] - (layout/render "about.html" {} {:title (str "About " (:site-title env))})) + (layout/render "about.html" {} {:title + (str "About " (:site-title env))})) (defn call-me-page [request] @@ -129,8 +131,7 @@ (GET "/home" [] (home-page)) (GET "/about" [] (about-page)) (GET "/roles" request (route/restricted (roles-page request))) - (GET "/canvassers" [] (route/restricted (app-page))) - (GET "/app" [] (route/restricted (app-page))) + (GET "/canvassers" [request] (route/restricted (app-page request))) (GET "/call-me" [] (call-me-page nil)) (POST "/call-me" request (call-me-page request)) (GET "/auth" request (login-page request)) diff --git a/src/cljs/youyesyet/canvasser_app/handlers.cljs b/src/cljs/youyesyet/canvasser_app/handlers.cljs index a010699..2b7596a 100644 --- a/src/cljs/youyesyet/canvasser_app/handlers.cljs +++ b/src/cljs/youyesyet/canvasser_app/handlers.cljs @@ -224,3 +224,17 @@ (if (integer? zoom) (assoc db :zoom zoom) db))) + + +(defn get-current-location [] + "Get the current location from the device." + (try + (if (.-geolocation js/navigator) + (.getCurrentPosition + (.-geolocation js/navigator) + (fn [position] + (dispatch [:set-latitude (.-latitude (.-coords position))]) + (dispatch [:set-longitude (.-longitude (.-coords position))]))) + (js/console.log "Geolocation not available")) + (catch js/Object any + (js/console.log "Exception while trying to access location: " + any)))) diff --git a/src/cljs/youyesyet/canvasser_app/state.cljs b/src/cljs/youyesyet/canvasser_app/state.cljs index 241752e..4a770c8 100644 --- a/src/cljs/youyesyet/canvasser_app/state.cljs +++ b/src/cljs/youyesyet/canvasser_app/state.cljs @@ -27,9 +27,6 @@ ;;; This is the constructor for the atom in which the state of the user interface is held. ;;; The atom gets updated by 'events' registered in handler.cljs, q.v. -;;; -;;; not wonderfully happy with 'db' as a name for this namespace; will probably change to -;;; 'client-state'. (def default-db {;;; the currently selected address, if any. @@ -87,3 +84,4 @@ :latitude 55.82 :longitude -4.25 :zoom 12}) + diff --git a/src/cljs/youyesyet/canvasser_app/ui_utils.cljs b/src/cljs/youyesyet/canvasser_app/ui_utils.cljs index 068bad6..63e1846 100644 --- a/src/cljs/youyesyet/canvasser_app/ui_utils.cljs +++ b/src/cljs/youyesyet/canvasser_app/ui_utils.cljs @@ -28,15 +28,22 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn back-link [] - [:div.back-link-container {:id "back-link-container"} - [:a {:href "javascript:history.back()" :id "back-link"} "Back"]]) +(defn back-link + "Generate a back link to the preceding page, or, if `target` is specified, + to a particular page." + ([] + (back-link "javascript:history.back()")) + ([target] + [:div.back-link-container {:id "back-link-container"} + [:a {:href target :id "back-link"} "Back"]])) - -(defn big-link [text target] +(defn big-link + [text & {:keys [target intention]}] [:div.big-link-container {:key target} - [:a.big-link {:href target} text]]) - + [:a.big-link (merge + (if target {:href target}{}) + (if intention {:on-click intention})) + text]]) (defn nav-link [uri title page collapsed?] (let [selected-page (rf/subscribe [:page])] diff --git a/src/cljs/youyesyet/canvasser_app/views/building.cljs b/src/cljs/youyesyet/canvasser_app/views/building.cljs index 54a1ed2..47d9d8f 100644 --- a/src/cljs/youyesyet/canvasser_app/views/building.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/building.cljs @@ -48,7 +48,7 @@ [dwelling] (ui/big-link (:sub-address dwelling) - (str "#/electors/" (:id dwelling))) ) + :target (str "#/electors/" (:id dwelling))) ) (sort #(< (:sub-address %1) (:sub-address %2)) (:dwellings address)))]]])) diff --git a/src/cljs/youyesyet/canvasser_app/views/elector.cljs b/src/cljs/youyesyet/canvasser_app/views/elector.cljs new file mode 100644 index 0000000..0f94f1f --- /dev/null +++ b/src/cljs/youyesyet/canvasser_app/views/elector.cljs @@ -0,0 +1,104 @@ +(ns ^{:doc "Canvasser app single elector panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.electors + (:require [reagent.core :refer [atom]] + [re-frame.core :refer [reg-sub subscribe dispatch]] + [youyesyet.canvasser-app.ui-utils :as ui])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.canvasser-app.views.elector: elector view for youyesyet. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defn gender-row + "Generate a row containing a cell showing the gender of this `elector`." + [elector] + (let [gender (:gender elector) + image (if gender (name gender) "unknown")] + [:tr + [:td {:key (:id elector)} + [:img {:src (str "img/gender/" image ".png") :alt image}]]])) + + +(defn name-row + "Generate a row containing a cell showing the name of this `elector`." + [elector] + [:tr + [:td {:key (:id elector)} + (:name elector)]]) + + +(defn option-row + "Generate a row showing this `option` for this elector." + [elector option] + (let [optid (:id option) + optname (name optid)] + [:tr {:key (str "options-" optname)} + (let [selected (= optid (:intention elector)) + image (if selected (str "img/option/" optname "-selected.png") + (str "img/option/" optname "-unselected.png"))] + [:td {:key (str "option-" optid "-" (:id elector))} + [:img + {:src image + :alt optname + :on-click #(dispatch + [:send-intention {:elector-id (:id elector) + :intention optid}])}]])])) + +(defn issue-row + "Generate a row containing an issue cell for a particular elector" + [elector] + [:tr + [:td {:key (:id elector)} + [:a {:href (str "#/issues/" (:id elector))} + [:img {:src "img/issues.png" :alt "Issues"}]]]]) + + +(defn panel + "Generate the elector panel." + [] + (let [address @(subscribe [:address]) + dwelling @(subscribe [:dwelling]) + elector @(subscribe [:elector]) + electors [elector] + options @(subscribe [:options]) + sub-address (:sub-address dwelling)] + (if address + [:div + [:h1 (if sub-address + (str sub-address ", " (:address address)) + (:address address))] + [:div.container {:id "main-container"} + [:table + [:tbody + ;; genders row + (gender-row elector) + ;; names row + (name-row elector) + ;; options rows + (map + #(option-row elector %) + options) + ;; issues row + (issues-row elector)]] + (ui/back-link)]] + (ui/error-panel "No address selected")))) diff --git a/src/cljs/youyesyet/canvasser_app/views/electors.cljs b/src/cljs/youyesyet/canvasser_app/views/electors.cljs index 482d21a..8f0f310 100644 --- a/src/cljs/youyesyet/canvasser_app/views/electors.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/electors.cljs @@ -40,15 +40,17 @@ ;;; Each column contains ;;; 1. a stick figure identifying gender (for recognition); ;;; 2. the elector's name; -;;; 3. one icon for each option on the ballot; -;;; 4. an 'issues' icon. ;;; The mechanics of how this panel is laid out don't matter. (defn gender-cell [elector] (let [gender (:gender elector) image (if gender (name gender) "unknown")] - [:td {:key (:id elector)} [:img {:src (str "img/gender/" image ".png") :alt image}]])) + [:td {:key (str "gender-" (:id elector))} + [:img {:src (str "img/gender/" image ".png") :alt image + :on-click #(dispatch + [:set-elector-and-page {:elector-id (:id elector) + :page "gdpr"}])}]])) (defn genders-row @@ -60,7 +62,12 @@ (defn name-cell [elector] - [:td {:key (str "name-" (:id elector))} (:name elector)]) + [:td {:key (str "name-" (:id elector)) + :on-click #(dispatch + [:set-elector-and-page {:elector-id (:id elector) + :page "gdpr"}])} + (:name elector)]) + (defn names-row [electors] @@ -69,41 +76,6 @@ #(name-cell %) electors)]) -(defn options-row - [electors option] - (let [optid (:id option) - optname (name optid)] - [:tr {:key (str "options-" optname)} - (map - (fn [elector] (let [selected (= optid (:intention elector)) - image (if selected (str "img/option/" optname "-selected.png") - (str "img/option/" optname "-unselected.png"))] - [:td {:key (str "option-" optid "-" (:id elector))} - [:img - {:src image - :alt optname - :on-click #(dispatch - [:send-intention {:elector-id (:id elector) - :intention optid}])}]])) - ;; TODO: impose an ordering on electors - by name or by id - electors)])) - - -(defn issue-cell - "Create an issue cell for a particular elector" - [elector] - [:td {:key (:id elector)} - [:a {:href (str "#/issues/" (:id elector))} - [:img {:src "img/issues.png" :alt "Issues"}]]]) - - -(defn issues-row - [electors] - [:tr - (map - #(issue-cell %) - electors)]) - (defn panel "Generate the electors panel." [] @@ -123,13 +95,7 @@ ;; genders row (genders-row electors) ;; names row - (names-row electors) - ;; options rows - (map - #(options-row electors %) - options) - ;; issues row - (issues-row electors)]] + (names-row electors)]] (ui/back-link)]] (ui/error-panel "No address selected")))) diff --git a/src/cljs/youyesyet/canvasser_app/views/gdpr.cljs b/src/cljs/youyesyet/canvasser_app/views/gdpr.cljs new file mode 100644 index 0000000..b28369c --- /dev/null +++ b/src/cljs/youyesyet/canvasser_app/views/gdpr.cljs @@ -0,0 +1,60 @@ +(ns ^{:doc "Canvasser app electors in household panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.gdpr + (:require [reagent.core :refer [atom]] + [re-frame.core :refer [reg-sub subscribe dispatch]] + [youyesyet.canvasser-app.ui-utils :as ui])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.canvasser-app.views.gdpr: consent form. +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; OK, the idea here is a GDPR consent form to be signed by the elector + + +(defn gdpr-panel-render + [] + (let [elector @(subscribe [:elector])] + [:div + [:h1 "GDPR Consent"] + [:div.container {:id "main-container"} + (ui/back-link "#electors") + [:table + [:tbody + [:tr + [:th "I," (:name elector)]] + [:tr + [:td + [:p "Consent to have data about my voting intention stored by " + [:b "Project Hope"] + " for use in the current referendum campaign, after which + it will be anonymised or deleted."] + [:p [:i "If you do not consent, we will store your voting intention + only against your electoral district, and not link it to you"]]]] + [:tr + [:td {:id "signature-pad"} + [:canvas]]]]]] + (ui/big-link "I consent" :target "#elector") ;; TODO: need to save the signature + (ui/big-link "I DO NOT consent" :target "#elector")])) + + + diff --git a/src/cljs/youyesyet/canvasser_app/views/issue.cljs b/src/cljs/youyesyet/canvasser_app/views/issue.cljs index 34afe10..46b16b2 100644 --- a/src/cljs/youyesyet/canvasser_app/views/issue.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issue.cljs @@ -47,5 +47,5 @@ [:div {:id "issue-text" :dangerouslySetInnerHTML {:__html (md->html (issues issue))}}]] - (ui/big-link "Request call" "#/followup") + (ui/big-link "Request call" :target "#/followup") (ui/back-link)]])) diff --git a/src/cljs/youyesyet/canvasser_app/views/issues.cljs b/src/cljs/youyesyet/canvasser_app/views/issues.cljs index 8b2ca47..9253a75 100644 --- a/src/cljs/youyesyet/canvasser_app/views/issues.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issues.cljs @@ -46,5 +46,5 @@ [:div.container {:id "main-container"} (ui/back-link) [:div {:id "issue-list"} - (map (fn [k] (ui/big-link k (str "#/issue/" k))) (keys issues))]]] + (map (fn [k] (ui/big-link k :target (str "#/issue/" k))) (keys issues))]]] (ui/error-panel "No issues loaded")))) diff --git a/src/cljs/youyesyet/canvasser_app/views/map.cljs b/src/cljs/youyesyet/canvasser_app/views/map.cljs index 257e404..c0163b2 100644 --- a/src/cljs/youyesyet/canvasser_app/views/map.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/map.cljs @@ -2,7 +2,8 @@ :author "Simon Brooke"} youyesyet.canvasser-app.views.map (:require [re-frame.core :refer [reg-sub subscribe dispatch]] - [reagent.core :as reagent])) + [reagent.core :as reagent] + [youyesyet.canvasser-app.handlers :refer [get-current-location]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; @@ -115,6 +116,7 @@ (defn map-did-mount-mapbox "Did-mount function loading map tile data from MapBox (proprietary)." [] + (get-current-location) (let [view (.setView (.map js/L "map" (clj->js {:zoomControl "false"})) #js [55.82 -4.25] 40)] ;; NEED TO REPLACE FIXME with your mapID! (.addTo (.tileLayer js/L "http://{s}.tiles.mapbox.com/v3/FIXME/{z}/{x}/{y}.png" @@ -126,6 +128,7 @@ (defn map-did-mount-osm "Did-mount function loading map tile data from Open Street Map." [] + (get-current-location) (let [view (.setView (.map js/L "map" (clj->js {:zoomControl false})) #js [@(subscribe [:latitude]) @(subscribe [:longitude])] From 7aac88891287886128f2e7b883a3587a7d7fcff3 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 2 Jul 2018 16:22:38 +0100 Subject: [PATCH 29/51] Tactical commit while I try to understand why the app doesn't rebuild --- env/dev/clj/youyesyet/core.clj | 9 +- package-lock.json | 11 + project.clj | 35 +- .../20161014170335-basic-setup.down.sql | 50 -- .../20161014170335-basic-setup.up.sql | 669 ------------------ .../20170315190500-roles-and-teams.down.sql | 13 - .../20170315190500-roles-and-teams.up.sql | 39 - ...170401115900-basic-reference-data.down.sql | 17 - ...20170401115900-basic-reference-data.up.sql | 58 -- .../20170415102900-core-views.down.sql | 11 - .../20170415102900-core-views.up.sql | 59 -- .../20170721084900-dwellings.down.sql | 69 -- .../20170721084900-dwellings.up.sql | 87 --- ...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 | 18 - .../20180317175047-test-data.down.sql | 3 - .../20180317175047-test-data.up.sql | 41 -- .../20180408124500-reference-data.down.sql | 1 - .../20180408124500-reference-data.up.sql | 2 - .../20180526162051-dwellings.down.sql | 8 - .../20180526162051-dwellings.up.sql | 11 - .../20180611204244-bootstrap.down.sql | 0 .../20180611204244-bootstrap.up.sql | 15 - resources/templates/app.html | 13 +- resources/templates/base.html | 9 +- resources/templates/canvasser.html | 60 -- src/clj/youyesyet/db/schema.clj | 484 ------------- src/clj/youyesyet/handler.clj | 3 +- src/clj/youyesyet/layout.clj | 25 +- src/clj/youyesyet/routes/authenticated.clj | 76 -- src/clj/youyesyet/routes/home.clj | 11 +- .../youyesyet/canvasser_app/handlers.cljs | 14 + src/cljs/youyesyet/canvasser_app/state.cljs | 4 +- .../youyesyet/canvasser_app/ui_utils.cljs | 21 +- .../canvasser_app/views/building.cljs | 2 +- .../canvasser_app/views/elector.cljs | 104 +++ .../canvasser_app/views/electors.cljs | 58 +- .../youyesyet/canvasser_app/views/gdpr.cljs | 60 ++ .../youyesyet/canvasser_app/views/issue.cljs | 2 +- .../youyesyet/canvasser_app/views/issues.cljs | 2 +- .../youyesyet/canvasser_app/views/map.cljs | 5 +- 45 files changed, 283 insertions(+), 1974 deletions(-) create mode 100644 package-lock.json delete mode 100644 resources/migrations/20161014170335-basic-setup.down.sql delete mode 100644 resources/migrations/20161014170335-basic-setup.up.sql delete mode 100644 resources/migrations/20170315190500-roles-and-teams.down.sql delete mode 100644 resources/migrations/20170315190500-roles-and-teams.up.sql delete mode 100644 resources/migrations/20170401115900-basic-reference-data.down.sql delete mode 100644 resources/migrations/20170401115900-basic-reference-data.up.sql delete mode 100644 resources/migrations/20170415102900-core-views.down.sql delete mode 100644 resources/migrations/20170415102900-core-views.up.sql delete mode 100644 resources/migrations/20170721084900-dwellings.down.sql delete mode 100644 resources/migrations/20170721084900-dwellings.up.sql delete mode 100644 resources/migrations/20180316110100-intentions-and-options.down.sql delete mode 100644 resources/migrations/20180316110100-intentions-and-options.up.sql delete mode 100644 resources/migrations/20180317170000-gender.down.sql delete mode 100644 resources/migrations/20180317170000-gender.up.sql delete mode 100644 resources/migrations/20180317170907-reference-data.down.sql delete mode 100644 resources/migrations/20180317170907-reference-data.up.sql delete mode 100644 resources/migrations/20180317175047-test-data.down.sql delete mode 100644 resources/migrations/20180317175047-test-data.up.sql delete mode 100644 resources/migrations/20180408124500-reference-data.down.sql delete mode 100644 resources/migrations/20180408124500-reference-data.up.sql delete mode 100644 resources/migrations/20180526162051-dwellings.down.sql delete mode 100644 resources/migrations/20180526162051-dwellings.up.sql delete mode 100644 resources/migrations/20180611204244-bootstrap.down.sql delete mode 100644 resources/migrations/20180611204244-bootstrap.up.sql delete mode 100644 resources/templates/canvasser.html delete mode 100644 src/clj/youyesyet/db/schema.clj delete mode 100644 src/clj/youyesyet/routes/authenticated.clj create mode 100644 src/cljs/youyesyet/canvasser_app/views/elector.cljs create mode 100644 src/cljs/youyesyet/canvasser_app/views/gdpr.cljs diff --git a/env/dev/clj/youyesyet/core.clj b/env/dev/clj/youyesyet/core.clj index c64c2e7..c9f3446 100644 --- a/env/dev/clj/youyesyet/core.clj +++ b/env/dev/clj/youyesyet/core.clj @@ -1,4 +1,5 @@ -(ns youyesyet.core +(ns ^{:doc "Devalopment launcher, entirely boilerplate from Luminus."} + youyesyet.core (:require [youyesyet.handler :as handler] [luminus.repl-server :as repl] [luminus.http-server :as http] @@ -60,8 +61,8 @@ :else (start-app args))) -(mount/stop) - -(mount/start) +;; (mount/start) +;; (mount/stop) + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..17b97dd --- /dev/null +++ b/package-lock.json @@ -0,0 +1,11 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "bower": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/bower/-/bower-1.8.4.tgz", + "integrity": "sha1-54dqB23rgTf30GUl3F6MZtuC8oo=" + } + } +} diff --git a/project.clj b/project.clj index 303e26e..2e6879b 100644 --- a/project.clj +++ b/project.clj @@ -51,15 +51,16 @@ :main ^:skip-aot youyesyet.core :migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")} - :plugins [[lein-cprop "1.0.1"] - [migratus-lein "0.4.2"] - [org.clojars.punkisdead/lein-cucumber "1.0.5"] + :plugins [[lein-bower "0.5.1"] [lein-cljsbuild "1.1.4"] [lein-codox "0.10.3"] - [lein-uberwar "0.2.0"] - [lein-bower "0.5.1"] + [lein-cprop "1.0.1"] [lein-less "1.7.5"] - [lein-codox "0.10.3"]] + [lein-npm "0.6.2"] + [lein-uberwar "0.2.0"] + [migratus-lein "0.4.2"] + [org.clojars.punkisdead/lein-cucumber "1.0.5"] + ] :bower-dependencies [[leaflet "0.7.3"] [jquery "3.3.1"] @@ -73,6 +74,13 @@ :languages [:clojure :clojurescript] :source-paths ["src/clj" "src/cljc" "src/cljs"]} + :npm {:dependencies [[datatables.net "1.10.19"] + [datatables.net-dt "1.10.19"] + [jquery "3.3.1"] + [leaflet "1.3.1"] + [signature_pad "2.3.2"]] + :root "resources/public/js/lib"} + :uberwar {:handler youyesyet.handler/app :init youyesyet.handler/init @@ -80,7 +88,8 @@ :name "youyesyet.war"} :clean-targets ^{:protect false} - [:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]] + [:target-path [:cljsbuild :builds :app :compiler :output-dir] + [:cljsbuild :builds :app :compiler :output-to]] :figwheel {:http-server-root "public" @@ -93,7 +102,8 @@ {:uberjar {:omit-source true :prep-tasks ["compile" ["cljsbuild" "once" "min"]] :cljsbuild - {:builds + {:prep-tasks [["npm" "install"]] + :builds {:min {:source-paths ["src/cljc" "src/cljs" "env/prod/cljs"] :compiler @@ -129,7 +139,8 @@ [lein-figwheel "0.5.9"] [org.clojure/clojurescript "1.9.495"]] :cljsbuild - {:builds + {:prep-tasks [["npm" "install"]] + :builds {:app {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"] :compiler @@ -140,9 +151,6 @@ :source-map true :optimizations :none :pretty-print true}}}} - - - :doo {:build "test"} :source-paths ["env/dev/clj"] :resource-paths ["env/dev/resources"] @@ -151,7 +159,8 @@ (pjstadig.humane-test-output/activate!)]} :project/test {:resource-paths ["env/test/resources"] :cljsbuild - {:builds + {:prep-tasks [["npm" "install"]] + :builds {:test {:source-paths ["src/cljc" "src/cljs" "test/cljs"] :compiler diff --git a/resources/migrations/20161014170335-basic-setup.down.sql b/resources/migrations/20161014170335-basic-setup.down.sql deleted file mode 100644 index 1005cee..0000000 --- a/resources/migrations/20161014170335-basic-setup.down.sql +++ /dev/null @@ -1,50 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20161014170335-basic-setup.down.sql: database schema for youyesyet. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2016 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- - --- intended to reverse out the database changes made in --- 20161014170335-basic-setup.up.sql - -drop table addresses cascade; ---;; -drop table authorities cascade; ---;; -drop table canvassers cascade; ---;; -drop table districts cascade; ---;; -drop table electors cascade; ---;; -drop table followupactions cascade; ---;; -drop table followupmethods cascade; ---;; -drop table followuprequests cascade; ---;; -drop table issueexpertise cascade; ---;; -drop table issues cascade; ---;; -drop table options cascade; ---;; -drop table visits cascade; ---;; diff --git a/resources/migrations/20161014170335-basic-setup.up.sql b/resources/migrations/20161014170335-basic-setup.up.sql deleted file mode 100644 index 4d74c06..0000000 --- a/resources/migrations/20161014170335-basic-setup.up.sql +++ /dev/null @@ -1,669 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20161014170335-basic-setup.up.sql: database schema for youyesyet. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2016 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -SET statement_timeout = 0; ---;; -SET lock_timeout = 0; ---;; -SET client_encoding = 'UTF8'; ---;; -SET standard_conforming_strings = on; ---;; -SET check_function_bodies = false; ---;; -SET client_min_messages = warning; ---;; --- --- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: --- - --- CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; ---;; - --- --- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: --- - --- COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; ---;; - -SET search_path = public, pg_catalog; ---;; -SET default_tablespace = ''; ---;; -SET default_with_oids = false; ---;; --- --- Name: addresses; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS addresses ( - id integer NOT NULL, - address character varying(256) NOT NULL, - postcode character varying(16), - phone character varying(16), - district_id integer, - latitude real, - longitude real -); ---;; - -ALTER TABLE public.addresses OWNER TO youyesyet; ---;; --- --- Name: addresses_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE addresses_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - -ALTER TABLE public.addresses_id_seq OWNER TO youyesyet; ---;; --- --- Name: addresses_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE addresses_id_seq OWNED BY addresses.id; ---;; - --- --- Name: authorities; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS authorities ( - id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.authorities OWNER TO youyesyet; ---;; --- --- Name: canvassers; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS canvassers ( - id serial, - username character varying(32) NOT NULL, - fullname character varying(64) NOT NULL, - elector_id integer, - address_id integer NOT NULL, - phone character varying(16), - email character varying(128), - authority_id character varying(32) NOT NULL, - introduced_by int references canvassers(id), - authorised boolean -); ---;; - -ALTER TABLE public.canvassers OWNER TO youyesyet; ---;; --- --- Name: districts; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS districts ( - id integer NOT NULL, - name character varying(64) NOT NULL -); ---;; - -ALTER TABLE public.districts OWNER TO youyesyet; ---;; --- --- Name: electors; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS electors ( - id integer NOT NULL, - name character varying(64) NOT NULL, - address_id integer NOT NULL, - phone character varying(16), - email character varying(128) -); ---;; - -ALTER TABLE public.electors OWNER TO youyesyet; ---;; --- --- Name: followupactions; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS followupactions ( - id integer NOT NULL, - request_id integer NOT NULL, - actor integer NOT NULL, - date timestamp with time zone DEFAULT now() NOT NULL, - notes text, - closed boolean -); ---;; - -ALTER TABLE public.followupactions OWNER TO youyesyet; - --- --- Name: followupactions_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE followupactions_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - -ALTER TABLE public.followupactions_id_seq OWNER TO youyesyet; ---;; --- --- Name: followupactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE followupactions_id_seq OWNED BY followupactions.id; ---;; - --- --- Name: followupmethods; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS followupmethods ( - id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.followupmethods OWNER TO youyesyet; ---;; --- --- Name: followuprequests; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -insert into followupmethods values ('Telephone'); ---;; -insert into followupmethods values ('eMail'); ---;; -insert into followupmethods values ('Post'); ---;; - - -CREATE TABLE IF NOT EXISTS followuprequests ( - id integer NOT NULL, - elector_id integer NOT NULL, - visit_id integer NOT NULL, - issue_id character varying(32) NOT NULL, - method_id character varying(32) NOT NULL -); ---;; - -ALTER TABLE public.followuprequests OWNER TO youyesyet; ---;; - --- --- Name: followuprequests_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE followuprequests_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - - -ALTER TABLE public.followuprequests_id_seq OWNER TO youyesyet; ---;; - --- --- Name: followuprequests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE followuprequests_id_seq OWNED BY followuprequests.id; ---;; - - --- --- Name: issueexpertise; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS issueexpertise ( - canvasser_id integer NOT NULL, - issue_id character varying(32) NOT NULL, - method_id character varying(32) NOT NULL -); ---;; - - -ALTER TABLE public.issueexpertise OWNER TO youyesyet; ---;; - --- --- Name: issues; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS issues ( - id character varying(32) NOT NULL, - url character varying(256) -); ---;; - - -ALTER TABLE public.issues OWNER TO youyesyet; ---;; - --- --- Name: options; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS options ( - id character varying(32) NOT NULL -); ---;; - - -ALTER TABLE public.options OWNER TO youyesyet; ---;; - --- --- Name: schema_migrations; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS schema_migrations ( - id bigint NOT NULL -); ---;; - - -ALTER TABLE public.schema_migrations OWNER TO youyesyet; ---;; - --- --- Name: visits; Type: TABLE; Schema: public; Owner: youyesyet; Tablespace: --- - -CREATE TABLE IF NOT EXISTS visits ( - id integer NOT NULL, - address_id integer NOT NULL, - canvasser_id integer NOT NULL, - date timestamp with time zone DEFAULT now() NOT NULL -); ---;; - - -ALTER TABLE public.visits OWNER TO youyesyet; ---;; - --- --- Name: visits_id_seq; Type: SEQUENCE; Schema: public; Owner: youyesyet --- - -CREATE SEQUENCE visits_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; ---;; - - -ALTER TABLE public.visits_id_seq OWNER TO youyesyet; ---;; - --- --- Name: visits_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: youyesyet --- - -ALTER SEQUENCE visits_id_seq OWNED BY visits.id; ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY addresses ALTER COLUMN id SET DEFAULT nextval('addresses_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions ALTER COLUMN id SET DEFAULT nextval('followupactions_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests ALTER COLUMN id SET DEFAULT nextval('followuprequests_id_seq'::regclass); ---;; - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits ALTER COLUMN id SET DEFAULT nextval('visits_id_seq'::regclass); ---;; - - --- --- Name: addresses_address_key; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_address_key UNIQUE (address); ---;; - - --- --- Name: addresses_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_pkey PRIMARY KEY (id); ---;; - - --- --- Name: authorities_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY authorities - ADD CONSTRAINT authorities_pkey PRIMARY KEY (id); ---;; - - --- --- Name: canvassers_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_pkey PRIMARY KEY (id); ---;; - - --- --- Name: districts_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY districts - ADD CONSTRAINT districts_pkey PRIMARY KEY (id); ---;; - - --- --- Name: electors_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY electors - ADD CONSTRAINT electors_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followupactions_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followupmethods_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followupmethods - ADD CONSTRAINT followupmethods_pkey PRIMARY KEY (id); ---;; - - --- --- Name: followuprequests_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_pkey PRIMARY KEY (id); ---;; - - --- --- Name: issues_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY issues - ADD CONSTRAINT issues_pkey PRIMARY KEY (id); ---;; - - --- --- Name: options_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY options - ADD CONSTRAINT options_pkey PRIMARY KEY (id); ---;; - - --- --- Name: schema_migrations_id_key; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - ---ALTER TABLE ONLY schema_migrations --- ADD CONSTRAINT schema_migrations_id_key UNIQUE (id); ---;; - - --- --- Name: visits_pkey; Type: CONSTRAINT; Schema: public; Owner: youyesyet; Tablespace: --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_pkey PRIMARY KEY (id); ---;; - - --- --- Name: addresses_district_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY addresses - ADD CONSTRAINT addresses_district_id_fkey FOREIGN KEY (district_id) REFERENCES districts(id); ---;; - - --- --- Name: canvassers_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: canvassers_authority_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_authority_id_fkey FOREIGN KEY (authority_id) REFERENCES authorities(id); ---;; - - --- --- Name: canvassers_elector_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY canvassers - ADD CONSTRAINT canvassers_elector_id_fkey FOREIGN KEY (elector_id) REFERENCES electors(id); ---;; - -create unique index canvassers_username_ix on canvassers (username); -create unique index canvassers_email_ix on canvassers(email); - --- --- Name: electors_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY electors - ADD CONSTRAINT electors_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: followupactions_actor_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_actor_fkey FOREIGN KEY (actor) REFERENCES canvassers(id); ---;; - - --- --- Name: followupactions_request_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followupactions - ADD CONSTRAINT followupactions_request_id_fkey FOREIGN KEY (request_id) REFERENCES followuprequests(id); ---;; - - --- --- Name: followuprequests_elector_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_elector_id_fkey FOREIGN KEY (elector_id) REFERENCES electors(id); ---;; - - --- --- Name: followuprequests_issue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id); ---;; - - --- --- Name: followuprequests_method_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_method_id_fkey FOREIGN KEY (method_id) REFERENCES followupmethods(id); ---;; - - --- --- Name: followuprequests_visit_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY followuprequests - ADD CONSTRAINT followuprequests_visit_id_fkey FOREIGN KEY (visit_id) REFERENCES visits(id); ---;; - - --- --- Name: issueexpertise_canvasser_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_canvasser_id_fkey FOREIGN KEY (canvasser_id) REFERENCES canvassers(id); ---;; - - --- --- Name: issueexpertise_issue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id); ---;; - - --- --- Name: issueexpertise_method_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY issueexpertise - ADD CONSTRAINT issueexpertise_method_id_fkey FOREIGN KEY (method_id) REFERENCES followupmethods(id); ---;; - - --- --- Name: visits_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses(id); ---;; - - --- --- Name: visits_canvasser_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet --- - -ALTER TABLE ONLY visits - ADD CONSTRAINT visits_canvasser_id_fkey FOREIGN KEY (canvasser_id) REFERENCES canvassers(id); ---;; - - --- --- Name: public; Type: ACL; Schema: -; Owner: postgres --- - -REVOKE ALL ON SCHEMA public FROM PUBLIC; ---;; - -REVOKE ALL ON SCHEMA public FROM postgres; ---;; - -GRANT ALL ON SCHEMA public TO postgres; ---;; - -GRANT ALL ON SCHEMA public TO PUBLIC; ---;; - - --- --- PostgreSQL database dump complete --- - diff --git a/resources/migrations/20170315190500-roles-and-teams.down.sql b/resources/migrations/20170315190500-roles-and-teams.down.sql deleted file mode 100644 index a8bdb6a..0000000 --- a/resources/migrations/20170315190500-roles-and-teams.down.sql +++ /dev/null @@ -1,13 +0,0 @@ -drop table teammemberships; - -drop table teamorganiserships; - -drop index ix_teams_name; - -drop table teams; - -drop table rolememberships; - -drop index ix_roles_name; - -drop table roles; diff --git a/resources/migrations/20170315190500-roles-and-teams.up.sql b/resources/migrations/20170315190500-roles-and-teams.up.sql deleted file mode 100644 index 7ff7ffc..0000000 --- a/resources/migrations/20170315190500-roles-and-teams.up.sql +++ /dev/null @@ -1,39 +0,0 @@ -create table if not exists roles ( - id serial primary key, - name varchar(64) not null -); - -create unique index ix_roles_name on roles(name); - -create table if not exists rolememberships ( - role_id integer not null references roles(id), - canvasser_id integer not null references canvassers(id) -); - -create table if not exists teams ( - id serial primary key, - name varchar(64) not null, - district_id integer not null references districts(id), - latitude real, - longitude real -); - -create unique index ix_teams_name on teams(name); - -create table if not exists teammemberships ( - team_id integer not null references teams(id), - canvasser_id integer not null references canvassers(id) -); - -create table if not exists teamorganiserships ( - team_id integer not null references teams(id), - canvasser_id integer not null references canvassers(id) -); - -alter table roles owner to youyesyet; - -alter table rolememberships owner to youyesyet; - -alter table teams owner to youyesyet; - -alter table teammemberships owner to youyesyet; diff --git a/resources/migrations/20170401115900-basic-reference-data.down.sql b/resources/migrations/20170401115900-basic-reference-data.down.sql deleted file mode 100644 index a0b3aa1..0000000 --- a/resources/migrations/20170401115900-basic-reference-data.down.sql +++ /dev/null @@ -1,17 +0,0 @@ --- this is just a teardown of everything set up in the corresponding .up.sql file - -delete from roles where name = 'Expert'; -delete from roles where name = 'Administrator'; -delete from roles where name = 'Recruiter'; -delete from roles where name = 'Organiser'; -delete from roles where name = 'Editor'; - -alter table issues drop column content; -alter table issues drop column current; - -delete from issues where id = 'Currency'; -delete from issues where id = 'Monarchy'; -delete from issues where id = 'Defence'; - -delete from options where id = 'Yes'; -delete from options where id = 'No'; diff --git a/resources/migrations/20170401115900-basic-reference-data.up.sql b/resources/migrations/20170401115900-basic-reference-data.up.sql deleted file mode 100644 index 26d3bf1..0000000 --- a/resources/migrations/20170401115900-basic-reference-data.up.sql +++ /dev/null @@ -1,58 +0,0 @@ - --- We don't explicitly instantiate the 'Canvasser' role since every user is --- deemed to be a canvasser. - - --- an 'Expert' is someone with expertise in one or more issues, who is --- trusted to discuss those issues in detail with electors. -insert into roles (name) values ('Expert'); - - --- an 'Administrator' is someone entitled to broadly alter reference data --- throughout the system. -insert into roles (name) values ('Administrator'); - - --- a 'Recruiter' is someone entitled to invite other people to become users --- ('Canvassers'). A Recruiter is entitled to lock the account of anyone they --- have recruited, recursively. -insert into roles (name) values ('Recruiter'); - - --- an 'Organiser' is someone who organises one or more local teams. An Organiser --- is entitled to exclude any Canvasser from any team they organise. -insert into roles (name) values ('Organiser'); - - --- an 'Editor' is someone entitled to add and edit issues. -insert into roles (name) values ('Editor'); - --- issue text is local; there may still in addition be a further link to more --- information, but the basic issue text should be part of the issue record. --- The text should fit on a phone screen without scrolling, so is reasonably --- short. -alter table issues add column content varchar(1024); - --- an issue may be current or not current; when not current it is not deleted --- from the system but kept because it may become current again later. Only --- current issues are shown in the app. Typically not fewer than three and not --- more than about seven issues should be current at any time. -alter table issues add column current boolean default false; - -insert into issues (id, content, current) values ('Currency', - 'Scotland could keep the Pound, or use the Euro. But we could also set up a new currency of our own.', - true); - -insert into issues (id, content, current) values ('Monarchy', - 'Scotland could keep the Queen. This is an issue to be decided after independence.', - true); - -insert into issues (id, content, current) values ('Defence', - 'Scotland will not have nuclear weapons, and will probably not choose to engage in far-off wars. But we could remain members of NATO.', - true); - - -insert into options (id) values ('Yes'); - -insert into options (id) values ('No'); - diff --git a/resources/migrations/20170415102900-core-views.down.sql b/resources/migrations/20170415102900-core-views.down.sql deleted file mode 100644 index c576947..0000000 --- a/resources/migrations/20170415102900-core-views.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -drop view if exists roles_by_canvasser; - -drop view if exists teams_by_canvasser; - -drop view if exists canvassers_by_team; - -drop view if exists canvassers_by_introducer; - -drop view if exists teams_by_organiser; - -drop view if exists organisers_by_team; diff --git a/resources/migrations/20170415102900-core-views.up.sql b/resources/migrations/20170415102900-core-views.up.sql deleted file mode 100644 index 53346a6..0000000 --- a/resources/migrations/20170415102900-core-views.up.sql +++ /dev/null @@ -1,59 +0,0 @@ - -create view roles_by_canvasser as - select canvassers.id as canvasser, roles.name - from roles, rolememberships, canvassers - where roles.id = rolememberships.role_id - and canvassers.id = rolememberships.canvasser_id - and canvassers.authorised = true; - -create view teams_by_canvasser as - select canvassers.id as canvasser, teams.id, teams.name, teams.latitude, teams.longitude - from teams, teammemberships, canvassers - where teams.id = teammemberships.team_id - and canvassers.id = teammemberships.canvasser_id; - -create view canvassers_by_team as - select teams.id as team, - canvassers.id, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone - from teams, teammemberships, canvassers - where teams.id = teammemberships.team_id - and canvassers.id = teammemberships.canvasser_id - and canvassers.authorised = true; - -create view canvassers_by_introducer as - select introducers.id as introducer, - canvassers.id as canvasser, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone, - canvassers.authorised - from canvassers, canvassers as introducers - where introducers.id = canvassers.introduced_by; - -create view teams_by_organiser as - select canvassers.id as organiser, - teams.id, - teams.name, - teams.latitude, - teams.longitude - from teams, teamorganiserships, canvassers - where teams.id = teamorganiserships.team_id - and canvassers.id = teamorganiserships.canvasser_id - and canvassers.authorised = true; - -create view organisers_by_team as - select teams.id as team, - canvassers.id, - canvassers.username, - canvassers.fullname, - canvassers.email, - canvassers.phone - from teams, teamorganiserships, canvassers - where teams.id = teamorganiserships.team_id - and canvassers.id = teamorganiserships.canvasser_id - and canvassers.authorised = true; diff --git a/resources/migrations/20170721084900-dwellings.down.sql b/resources/migrations/20170721084900-dwellings.down.sql deleted file mode 100644 index 674fb1b..0000000 --- a/resources/migrations/20170721084900-dwellings.down.sql +++ /dev/null @@ -1,69 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20170721084900.up.sql: add dwellings table, to deal with flatted addresses. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2017 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -alter table canvassers add column address_id integer references addresses(id); ---;; -alter table electors add column address_id integer references addresses(id); ---;; -alter table visits add column address_id integer references addresses(id); ---;; - -update canvassers set address_id = - (select address_id from dwellings where id = canvassers.dwelling_id); ---;; - -update electors set address_id = - (select address_id from dwellings where id = electors.dwelling_id); ---;; - -update visits set address_id = - (select address_id from dwellings where id = visits.dwelling_id); ---;; - -alter table canvassers alter column address_id set not null; ---;; -alter table electors alter column address_id set not null; ---;; -alter table visits alter column address_id set not null; ---;; - -alter table canvassers drop column dwelling_id; ---;; -alter table electors drop column dwelling_id; ---;; -alter table visits drop column dwelling_id; ---;; - -drop table if exists dwellings; ---;; diff --git a/resources/migrations/20170721084900-dwellings.up.sql b/resources/migrations/20170721084900-dwellings.up.sql deleted file mode 100644 index 74e6a27..0000000 --- a/resources/migrations/20170721084900-dwellings.up.sql +++ /dev/null @@ -1,87 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20170721084900.up.sql: add dwellings table, to deal with flatted addresses. ----- ----- This program is free software; you can redistribute it and/or ----- modify it under the terms of the GNU General Public License ----- as published by the Free Software Foundation; either version 2 ----- of the License, or (at your option) any later version. ----- ----- This program is distributed in the hope that it will be useful, ----- but WITHOUT ANY WARRANTY; without even the implied warranty of ----- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ----- GNU General Public License for more details. ----- ----- You should have received a copy of the GNU General Public License ----- along with this program; if not, write to the Free Software ----- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ----- USA. ----- ----- Copyright (C) 2017 Simon Brooke for Radical Independence Campaign ----- --------------------------------------------------------------------------------- ----- ----- NOTE ----- This file is essentially a Postgres schema dump of a database schema which was ----- created with the function initdb! in the file src/clj/youyesyet/db/schema.clj. ----- This file has then been mildly massaged to work with Migratus. ----- Either this file or src/clj/youyesyet/db/schema.clj is redundant; schema.clj ----- represents the older, Korma, way of doing things but does not readily allow ----- for migrations; this file represents the newer Migratus/HugSQL way. I'm not ----- certain which of these paths I'm going to go down. ----- --------------------------------------------------------------------------------- - -CREATE TABLE IF NOT EXISTS dwellings ( - id serial NOT NULL primary key, - address_id integer NOT NULL references addresses(id), - sub_address varchar(16) -); ---;; - -ALTER TABLE public.dwellings OWNER TO youyesyet; ---;; - -INSERT INTO dwellings (address_id, sub_address) - SELECT DISTINCT id, '' FROM addresses; ---;; - -alter table canvassers add column dwelling_id integer references dwellings(id); ---;; -alter table electors add column dwelling_id integer references dwellings(id); ---;; -alter table visits add column dwelling_id integer references dwellings(id); ---;; - -update canvassers set dwelling_id = - (select id from dwellings where address_id = canvassers.address_id); ---;; - -update electors set dwelling_id = - (select id from dwellings where address_id = electors.address_id); ---;; - -update visits set dwelling_id = - (select id from dwellings where address_id = visits.address_id); ---;; - -alter table canvassers alter column dwelling_id set not null; ---;; -alter table electors alter column dwelling_id set not null; ---;; -alter table visits alter column dwelling_id set not null; ---;; - -alter table canvassers drop constraint canvassers_address_id_fkey; ---;; -alter table electors drop constraint electors_address_id_fkey; ---;; -alter table visits drop constraint visits_address_id_fkey; ---;; - -alter table canvassers drop column address_id; ---;; -alter table electors drop column address_id; ---;; -alter table visits drop column address_id; ---;; diff --git a/resources/migrations/20180316110100-intentions-and-options.down.sql b/resources/migrations/20180316110100-intentions-and-options.down.sql deleted file mode 100644 index 84c98b8..0000000 --- a/resources/migrations/20180316110100-intentions-and-options.down.sql +++ /dev/null @@ -1,24 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20180316110100intentions-and-options.down.sql: remove intentions and options ----- ----- 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 ----- --------------------------------------------------------------------------------- - -drop table intentions; diff --git a/resources/migrations/20180316110100-intentions-and-options.up.sql b/resources/migrations/20180316110100-intentions-and-options.up.sql deleted file mode 100644 index 0736740..0000000 --- a/resources/migrations/20180316110100-intentions-and-options.up.sql +++ /dev/null @@ -1,30 +0,0 @@ --------------------------------------------------------------------------------- ----- ----- 20180316110100intentions-and-options.up.sql: add intentions and options ----- ----- 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 ----- --------------------------------------------------------------------------------- - -CREATE TABLE IF NOT EXISTS intentions ( - visit_id int not null references visits(id) on delete no action, - elector_id int not null references electors(id) on delete no action, - option_id varchar(32) not null references options(id) on delete no action - ); - -ALTER TABLE intentions owner to youyesyet; diff --git a/resources/migrations/20180317170000-gender.down.sql b/resources/migrations/20180317170000-gender.down.sql deleted file mode 100644 index 5eac53a..0000000 --- a/resources/migrations/20180317170000-gender.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -alter table electors drop column gender; - -drop table genders; diff --git a/resources/migrations/20180317170000-gender.up.sql b/resources/migrations/20180317170000-gender.up.sql deleted file mode 100644 index 9512ea2..0000000 --- a/resources/migrations/20180317170000-gender.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -create table genders ( - id varchar(32) not null primary key -); - --- genders is reference data -insert into genders values ('Female'); -insert into genders values ('Male'); -insert into genders values ('Non-binary'); -insert into genders values ('Unknown'); - -alter table electors add column gender varchar(32) references genders(id) default 'Unknown'; diff --git a/resources/migrations/20180317170907-reference-data.down.sql b/resources/migrations/20180317170907-reference-data.down.sql deleted file mode 100644 index ed67940..0000000 --- a/resources/migrations/20180317170907-reference-data.down.sql +++ /dev/null @@ -1,10 +0,0 @@ -delete from options where id = 'Yes'; - -delete from options where id = 'No'; - -delete from issues where id = 'Currency'; - -delete from issues where id = 'Monarchy'; - -delete from issues where id = 'Defence'; - diff --git a/resources/migrations/20180317170907-reference-data.up.sql b/resources/migrations/20180317170907-reference-data.up.sql deleted file mode 100644 index ed1dfa4..0000000 --- a/resources/migrations/20180317170907-reference-data.up.sql +++ /dev/null @@ -1,18 +0,0 @@ -insert into options values ('Yes'); - -insert into options values ('No'); - - -insert into issues (id, url) values ('Currency', 'https://www.yyy.scot/wiki/issues/Currency'); - -insert into issues (id, url) values ('Monarchy', 'https://www.yyy.scot/wiki/issues/Monarchy'); - -insert into issues (id, url) values ('Defence', 'https://www.yyy.scot/wiki/issues/Defence'); - -insert into genders (id ) values ('Female'); - -insert into genders (id ) values ('Male'); - -insert into genders (id ) values ('Non-binary'); - -insert into genders (id ) values ('Unknown'); diff --git a/resources/migrations/20180317175047-test-data.down.sql b/resources/migrations/20180317175047-test-data.down.sql deleted file mode 100644 index 4a5583c..0000000 --- a/resources/migrations/20180317175047-test-data.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -delete from addresses where id < = 4; - -delete from electors where id <= 10; diff --git a/resources/migrations/20180317175047-test-data.up.sql b/resources/migrations/20180317175047-test-data.up.sql deleted file mode 100644 index 0cc4b20..0000000 --- a/resources/migrations/20180317175047-test-data.up.sql +++ /dev/null @@ -1,41 +0,0 @@ -insert into addresses (id, address, postcode, latitude, longitude) -values (1, '13 Imaginary Terrace, IM1 3TE', 'IM1 3TE', 55.8253043, -4.2569057); - -insert into addresses (id, address, postcode, latitude, longitude) -values (2, '15 Imaginary Terrace, IM1 3TE', 'IM1 3TE', 55.8252354, -4.2572778); - -insert into addresses (id, address, postcode, latitude, longitude) -values (3, '17 Imaginary Terrace, IM1 3TE', 'IM1 3TE', 55.825166, -4.257026); - -insert into addresses (id, address, postcode, latitude, longitude) -values (4, '19 Imaginary Terrace, IM1 3TE', 'IM1 3TE', 55.8250695, -4.2570239); - -insert into electors (id, name, address_id, gender) -values (1, 'Alan Anderson', 1, 'Male'); - -insert into electors (id, name, address_id, gender) -values (2, 'Ann Anderson', 1, 'Female'); - -insert into electors (id, name, address_id, gender) -values (3, 'Alex Anderson', 1, 'Non-binary'); - -insert into electors (id, name, address_id) -values (4, 'Andy Anderson', 1); - -insert into electors (id, name, address_id, gender) -values (5, 'Beryl Brown', 2, 'Female'); - -insert into electors (id, name, address_id, gender) -values (6, 'Betty Black', 2, 'Female'); - -insert into electors (id, name, address_id, gender) -values (7, 'Catriona Crathie', 3, 'Female'); - -insert into electors (id, name, address_id, gender) -values (8, 'Colin Caruthers', 3, 'Male'); - -insert into electors (id, name, address_id, gender) -values (9, 'Calum Crathie', 3, 'Unknown'); - -insert into electors (id, name, address_id, gender) -values (10, 'David Dewar', 4, 'Male'); diff --git a/resources/migrations/20180408124500-reference-data.down.sql b/resources/migrations/20180408124500-reference-data.down.sql deleted file mode 100644 index 68bada9..0000000 --- a/resources/migrations/20180408124500-reference-data.down.sql +++ /dev/null @@ -1 +0,0 @@ -alter table issues drop column current; diff --git a/resources/migrations/20180408124500-reference-data.up.sql b/resources/migrations/20180408124500-reference-data.up.sql deleted file mode 100644 index aaae234..0000000 --- a/resources/migrations/20180408124500-reference-data.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -alter table issues add column current boolean default true; - diff --git a/resources/migrations/20180526162051-dwellings.down.sql b/resources/migrations/20180526162051-dwellings.down.sql deleted file mode 100644 index ab91769..0000000 --- a/resources/migrations/20180526162051-dwellings.down.sql +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 41e1a6e..0000000 --- a/resources/migrations/20180526162051-dwellings.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -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/migrations/20180611204244-bootstrap.down.sql b/resources/migrations/20180611204244-bootstrap.down.sql deleted file mode 100644 index e69de29..0000000 diff --git a/resources/migrations/20180611204244-bootstrap.up.sql b/resources/migrations/20180611204244-bootstrap.up.sql deleted file mode 100644 index d0377ad..0000000 --- a/resources/migrations/20180611204244-bootstrap.up.sql +++ /dev/null @@ -1,15 +0,0 @@ --- enough data to get the system working and real logins - -insert into addresses (address, postcode, latitude, longitude) -values ('West Croft, Standingstone, Auchencairn', 'DG7 1RF', 54.822389, -3.920265); - -insert into dwellings (id, address_id, sub_address) -values (5, 5, ''); - -insert into electors (name, dwelling_id, gender) -values ('Simon Brooke', 1, 'Male'); - -insert into authorities (id) values ('GitHub'); - -insert into canvassers (username, fullname, elector_id, address_id, authority_id, authorised) -values ('simon_brooke', 'Simon Brooke', 2, 2, 'GitHub', true); diff --git a/resources/templates/app.html b/resources/templates/app.html index 7b181ca..3227f33 100644 --- a/resources/templates/app.html +++ b/resources/templates/app.html @@ -23,13 +23,22 @@

{% endblock %} +{% block extra-script %} + var user = { + "username": "{{user.username}}", + "fullname": "{{user.fullname}}", + "id": {{user.id|default:-1}}, + "authorised": {{user.authorised|default:false}} + }; +{% endblock %} {% block extra-tail %} - -{% script "js/lib/leaflet/dist/leaflet.js" %} + +{% script "js/lib/node_modules/signature_pad/dist/signature_pad.min.js" %} +{% script "js/lib/node_modules/leaflet/dist/leaflet.js" %} {% script "/js/app.js" %} {% endblock %} diff --git a/resources/templates/base.html b/resources/templates/base.html index 93c779c..317fc4f 100644 --- a/resources/templates/base.html +++ b/resources/templates/base.html @@ -10,7 +10,6 @@ {{site-title}}: {{title}} - {% script "js/lib/jquery/dist/jquery.min.js" %} {% endblock %} {% block extra-head %} @@ -76,10 +75,6 @@ {% endblock %} {% endblock %} - {% block extra-tail %} - - - {% endblock %} + {% block extra-tail %} + + + {% endblock %} diff --git a/resources/templates/canvasser.html b/resources/templates/canvasser.html deleted file mode 100644 index ceb6c6c..0000000 --- a/resources/templates/canvasser.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "base.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/db/schema.clj b/src/clj/youyesyet/db/schema.clj deleted file mode 100644 index 7571251..0000000 --- a/src/clj/youyesyet/db/schema.clj +++ /dev/null @@ -1,484 +0,0 @@ -(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])) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; -;;;; youyesyet.db.schema: database schema for youyesyet. -;;;; -;;;; This program is free software; you can redistribute it and/or -;;;; modify it under the terms of the GNU General Public License -;;;; as published by the Free Software Foundation; either version 2 -;;;; of the License, or (at your option) any later version. -;;;; -;;;; This program is distributed in the hope that it will be useful, -;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;;;; GNU General Public License for more details. -;;;; -;;;; You should have received a copy of the GNU General Public License -;;;; along with this program; if not, write to the Free Software -;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -;;;; USA. -;;;; -;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign -;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;; Note that this is the (old) Korma way of doing things; -;;; it may not play well with migrations, nor with the HugSQL way of doing things recommended -;;; in Web Development with Clojure, Second Ed. So this may be temporary 'get us started' code, -;;; which later gets thrown away. The 'create-x-table!' functions in this file may be -;;; redundant, and if they are the namespace probably needs to be renamed to 'entities'. -;;; See also resources/migrations/20161014170335-basic-setup.up.sql - -(defn create-districts-table! - "Create a table to hold the electoral districts in which electors are registered. - Note that, as this app is being developed for the independence referendum in which - polling is across the whole of Scotland, this part of the design isn't fully thought - through; if later adapted to general or local elections, some breakdown or hierarchy - of polling districts into constituencies will be required." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :districts - ;; it may be necessary to have a serial abstract primary key but I suspect - ;; polling districts already have numbers assigned by the Electoral Commission and - ;; it would be sensible to use those. TODO: check. - [:id "integer not null primary key"] - [:name "varchar(64) not null"] - ;; TODO: it would make sense to hold polygon data for polling districts so we can reflect - ;; them on the map, but I haven't thought through how to do that yet. - ))) - - -(kc/defentity district - (kc/pk :id) - (kc/table :districts) - (kc/database yyydb/*db*) - (kc/entity-fields :id :name)) - - -(defn create-addresses-table! - "Create a table to hold the addresses at which electors are registered." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :addresses - [:id "serial not null primary key"] - ;; we do NOT want to hold multiple address records for the same household. When we receive - ;; the electoral roll data the addresses are likely to be text fields inlined in the elector - ;; record; in digesting the roll data we need to split these out and resolve them against existing - ;; addresses in the table, creating a new address record only if there's no match. - [:address "varchar(256) not null unique"] - [:postcode "varchar(16)"] - [:phone "varchar(16)"] - ;; the electoral district within which this address exists - [:district_id "integer references districts(id)"] - [:latitude :real] - [:longitude :real]))) - - -(kc/defentity address - (kc/pk :id) - (kc/table :addresses) - (kc/database yyydb/*db*) - (kc/entity-fields :id :address :postcode :phone :latitude :longitude) - (kc/has-one district)) - - -(defn create-authorities-table! - "Create a table to hold the oauth authorities against which we with authenticate canvassers." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :authorities - [:id "varchar(32) not null primary key"] - ;; more stuff here when I understand more - ))) - - -(kc/defentity authority - (kc/pk :id) - (kc/table :authorities) - (kc/database yyydb/*db*) - (kc/entity-fields :id)) - - -(defn create-electors-table! - "Create a table to hold electors data." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :electors - ;; id should be the roll number on the electoral roll, I think, but only if this is unique - ;; across Scotland. Otherwise we need a separate id field. TODO: check. - [:id "integer primary key"] - [:name "varchar(64) not null"] - [:address_id "integer not null references addresses(id)" ] - [:phone "varchar(16)"] - ;; we'll probably only capture email data on electors if they request a followup - ;; on a particular issue by email. - [:email "varchar(128)"]))) - - -(kc/defentity elector - (kc/pk :id) - (kc/table :electors) - (kc/database yyydb/*db*) - (kc/entity-fields :id :name :phone :email) - (kc/has-one address)) - - -;;; Lifecycle of the canvasser record goes like this, I think: -;;; A canvasser record is created when an existing canvasser issues an invitation to a friend. -;;; The invitation takes the form of an automatically generated email with a magic token in it. -;;; At this point the record has only an email address, the introduced_by and the magic token, -;;; which is itself probably a hash of the email address. Therefore, having the username as the -;;; primary key won't work. -;;; -;;; The invited person clicks on the link in the email and completes the sign-up form, adding -;;; their full name, and their phone number. If the username they have chosen is unique, they -;;; are then sent a second email with a new magic token, possibly a hash of email address + -;;; full name. When they click on the link in this second email, their 'authorised' flag is -;;; set to 'true'. -;;; -;;; Administrators can also create canvasser records directly.aw -;;; TODO: Do we actually need a username at all? Wouldn't the email address do? - -(defn create-canvassers-table! - "Create a table to hold data on canvassers (including authentication data)." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :canvassers - ;; id is the username the canvasser logs in as. - [:id "serial primary key"] - [:username "varchar(32) unique"] - [:fullname "varchar(64) not null"] - ;; most canvassers will be electors, we should link them: - [:elector_id "integer references electors(id) on delete no action"] - ;; but some canvassers may not be electors, so we need contact details separately: - [:address_id "integer not null references addresses(id)" ] - [:phone "varchar(16)"] - [:email "varchar(128)"] - ;; with which authority do we authenticate this canvasser? I do not want to hold even - ;; encrypted passwords locally - [:authority_id "varchar(32) not null references authorities(id) on delete no action"] - [:introduced_by "integer references canvassers(id)"] - [:is_admin :boolean] - ;; true if the canvasser is authorised to use the app; else false. This allows us to - ;; block canvassers we suspect of misbehaving. - [:authorised :boolean]))) - - -(kc/defentity canvasser - (kc/pk :id) - (kc/table :canvassers) - (kc/database yyydb/*db*) - (kc/entity-fields :id :fullname :phone :email :is_admin :authorised) - (kc/has-one elector) - (kc/has-one address) -;; (kc/has-one canvasser {:fk :introduced_by}) - (kc/has-one authority)) - - -(defn create-visits-table! - "Create a table to record visits by canvassers to addresses (including virtual visits by telephone)." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :visits - [:id "serial not null primary key"] - [:address_id "integer not null references addresses(id)"] - [:canvasser_id "integer not null references canvassers(id)"] - [:date "timestamp with time zone not null default now()"]))) - - -(kc/defentity visit - (kc/pk :id) - (kc/table :visits) - (kc/database yyydb/*db*) - (kc/entity-fields :id :date) - (kc/has-one address) - (kc/has-one canvasser)) - - -(defn create-options-table! - "Create a table to record options in the vote. This app is being created for the Independence - referendum, which will have just two options, 'Yes' and 'No', but it might later be adapted - for more general political canvassing." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :options - ;; id is also the text of the option; e.g. 'Yes', 'No'. - [:id "varchar(32) not null primary key"] - ;; To do elections you probably need party and candidate and stuff here, but - ;; for the referendum it's unnecessary. - ))) - - -(kc/defentity option - (kc/pk :id) - (kc/table :options) - (kc/database yyydb/*db*) - (kc/entity-fields :id)) - - -(defn create-option-district-table! - "Create a table to link options to the districts in which they are relevant. This is extremely - simple for the referendum: both options are relevant to all districts. This table is essentially - 'for later expansion'." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :optionsdistricts - [:option_id "varchar(32) not null references options(option)"] - [:district_id "integer not null references districts(id)"]))) - - -;; I think we don't need an entity for optionsdistricts, because it's just a link table. - - -(defn create-intention-table! - "Create a table to record the intention of an elector as solicited by a canvasser during a visit. - TODO: decide whether to insert a record in this table for 'don't knows'." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :intentions - [:id "serial primary key"] - ;; the elector who gave this intention - [:elector_id "integer not null references electors(id)"] - ;; the option the elector said they were planning to vote for - [:option_id "varchar(32) not null references options(option)"] - [:visit_id "integer not null references visits(id)"]))) - - -(kc/defentity intention - (kc/pk :id) - (kc/table :intentions) - (kc/database yyydb/*db*) - (kc/entity-fields :id) - (kc/has-one elector) - (kc/has-one option) - (kc/has-one visit)) - - -(defn create-issues-table! - "A table for issues we predict electors may raise on the doorstep, for which we may be - able to provide extra information or arrange for issue-specialists to phone and talk - to the elector." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :issues - ;; short name of this issue, e.g. 'currency', 'defence', 'pensions' - [:id "varchar(32) not null primary key"] - ;; URL of some brief material the canvasser can use on the doorstap - [:url "varchar(256)"]))) - - -(kc/defentity issue - (kc/pk :id) - (kc/table :issues) - (kc/database yyydb/*db*) - (kc/entity-fields :id :url)) - - -(defn create-followup-methods-table! - "Create a table to hold reference data on followup methods." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :followupmethods - [;; the method, e.g. 'telephone', 'email', 'post' - :id "varchar(32) not null primary key"]))) - - -(kc/defentity followup-method - (kc/pk :id) - (kc/table :followupmethods) - (kc/database yyydb/*db*) - (kc/entity-fields :id)) - - -(defn create-issue-expertise-table! - "A table to record which canvassers have expertise in which issues, so that followup - requests can be directed to the right canvassers." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :issueexpertise - ;; the expert canvasser - [:canvasser_id "integer not null references canvassers(id)"] - ;; the issue they have expertise in - [:issue_id "varchar(32) not null references issues(id)"] - ;; the method by which this expert can respond to electors on this issue - [:method_id "varchar(32) not null references followupmethods(id)"]))) - - -(kc/defentity issue-expertise - (kc/table :issueexpertise) - (kc/database yyydb/*db*) - (kc/entity-fields :id) - (kc/has-one canvasser) - (kc/has-one issue) - (kc/has-one followup-method)) - - -(defn create-followup-requests-table! - "Create a table to record requests for followup contacts on particular issues." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :followuprequests - [:id "serial primary key"] - [:elector_id "integer not null references electors(id)"] - [:visit_id "integer not null references visits(id)"] - [:issue_id "varchar(32) not null references issues(id)"] - ;; We probably need a followupmethod (telephone, email, postal) and, for telephone, - ;; convenient times but I haven't thought through how to represent this or how - ;; the user interface will work. - [:method_id "varchar(32) not null references followupmethods(id)"]))) - - -(kc/defentity followup-request - (kc/table :followuprequests) - (kc/database yyydb/*db*) - (kc/entity-fields :id) - (kc/has-one elector) - (kc/has-one visit) - (kc/has-one issue) - (kc/has-one followup-method)) - - -(defn create-followup-actions-table! - "Create a table to record actions on followup requests. Record in this table are almost - certainly created through a desktop-style interface rather than through te app, so it's - reasonable that there should be narrative fields." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :followupactions - [:id "serial primary key"] - [:request_id "integer not null references followuprequests(id)"] - [:actor "integer not null references canvassers(id)"] - [:date "timestamp with time zone not null default now()"] - [:notes "text"] - ;; true if this action closes the request - [:closed :boolean]))) - - -(kc/defentity followup-action - (kc/table :followupactions) - (kc/database yyydb/*db*) - (kc/entity-fields :id :notes :date :closed) - (kc/has-one followup-request) - (kc/has-one canvasser {:fk :actor})) - - - -(defn create-role-table! - "Create a table to record roles. I'm not even yet certain that this is strictly necessary, - but it allows us to record the fact that different users (canvassers) have different roles - in the system." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :roles - [:id "serial primary key"] - [:name "varchar(64) not null"]))) - - -(defn create-role-membership-table! - "Create a link table to record membership of roles." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :rolememberships - [:role_id "integer not null references role(id)"] - [:canvasser_id "integer not null references canvasser(id)"]))) - - -(kc/defentity role - (kc/table :roles) - (kc/database yyydb/*db*) - (kc/entity-fields :id :name) - (kc/many-to-many canvasser :rolememberships)) - - -(defn create-team-table! - "Create a table to record teams." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :teams - [:id "serial primary key"] - [:name "varchar(64) not null"] - ;; the electoral district within which this address exists - [:district_id "integer references districts(id)"] - ;; nominal home location of this team - [:latitude :real] - [:longitude :real]))) - - -(defn create-team-membership-table! - "Create a link table to record membership of team." - [] - (sql/db-do-commands - yyydb/*db* - (sql/create-table-ddl - :teammemberships - [:team_id "integer not null references team(id)"] - [:canvasser_id "integer not null references canvasser(id)"]))) - - -(kc/defentity team - (kc/table :teams) - (kc/database yyydb/*db*) - (kc/entity-fields :id :name :latitude :longitude) - (kc/has-one district) - (kc/many-to-many canvasser :teammemberships)) - - -(defn init-db! [] - "Initialised the whole database." - (create-districts-table!) - (create-addresses-table!) - (create-authorities-table!) - (create-electors-table!) - (create-canvassers-table!) - (create-visits-table!) - (create-options-table!) - (create-issues-table!) - (create-followup-methods-table!) - (create-issue-expertise-table!) - (create-followup-requests-table!) - (create-followup-actions-table!) - (create-role-table!) - (create-role-membership-table!) - (create-team-table!) - (create-team-membership-table!) - ) diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index b131f44..c9c8119 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -9,7 +9,6 @@ [youyesyet.config :refer [env]] [youyesyet.layout :refer [error-page]] [youyesyet.middleware :as middleware] - [youyesyet.routes.authenticated :refer [authenticated-routes]] [youyesyet.routes.home :refer [home-routes]] [youyesyet.routes.oauth :refer [oauth-routes]] [youyesyet.routes.auto-json :refer [auto-rest-routes]] @@ -73,7 +72,7 @@ (wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-formats)) 'oauth-routes - #'authenticated-routes + (route/resources "/") (route/not-found (:body (error-page {:status 404 diff --git a/src/clj/youyesyet/layout.clj b/src/clj/youyesyet/layout.clj index 2033ecf..129f495 100644 --- a/src/clj/youyesyet/layout.clj +++ b/src/clj/youyesyet/layout.clj @@ -66,21 +66,18 @@ [template session & [params]] (let [user (:user session)] (log/debug (str "layout/render: template: '" template "'; user: '" user "'.")) - (assoc - (content-type - (ok + (content-type + (ok (parser/render-file - template - (assoc params - :page template - :csrf-token *anti-forgery-token* - :version (System/getProperty "youyesyet.version")))) - "text/html; charset=utf-8") - :user user - :user-roles (get-user-roles user) - :site-title (:site-title env) - :site-logo (:site-logo env) - :session session))) + template + (assoc params + :page template + :csrf-token *anti-forgery-token* + :user user + :user-roles (get-user-roles user) + :site-title (:site-title env) + :version (System/getProperty "youyesyet.version")))) + "text/html; charset=utf-8"))) (defn error-page diff --git a/src/clj/youyesyet/routes/authenticated.clj b/src/clj/youyesyet/routes/authenticated.clj deleted file mode 100644 index cc6b9ea..0000000 --- a/src/clj/youyesyet/routes/authenticated.clj +++ /dev/null @@ -1,76 +0,0 @@ -(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]] - [noir.response :as nresponse] - [noir.util.route :as route] - [ring.util.http-response :as response] - [youyesyet.layout :as layout] - [youyesyet.db.core :as db])) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; -;;;; 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 code adapted from http://www.luminusweb.net/docs#accessing_the_database - -(defn post? - "Return true if the argument is a ring request which is a post request" - [request] - 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) - (if (post? request) - (db/update-canvasser! params) - (db/create-canvasser! params)) - (db/get-canvasser (:id params))) - ))] - (layout/render - "canvasser.html" - (:session request) - {: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" - [request] - (layout/render "routing.html" (:session request))) - -(defroutes authenticated-routes - (GET "/edit-canvasser" request (canvasser-page request)) - (POST "/edit-canvasser" request (canvasser-page request)) - (GET "/routing" [request] (routing-page request))) diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index bcd418b..deaf047 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -36,12 +36,14 @@ ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn app-page [] - (layout/render "app.html" {})) +(defn app-page [request] + (layout/render "app.html" {:title "Canvasser app" + :user (:user (:session request))})) (defn about-page [] - (layout/render "about.html" {} {:title (str "About " (:site-title env))})) + (layout/render "about.html" {} {:title + (str "About " (:site-title env))})) (defn call-me-page [request] @@ -129,8 +131,7 @@ (GET "/home" [] (home-page)) (GET "/about" [] (about-page)) (GET "/roles" request (route/restricted (roles-page request))) - (GET "/canvassers" [] (route/restricted (app-page))) - (GET "/app" [] (route/restricted (app-page))) + (GET "/canvassers" [request] (route/restricted (app-page request))) (GET "/call-me" [] (call-me-page nil)) (POST "/call-me" request (call-me-page request)) (GET "/auth" request (login-page request)) diff --git a/src/cljs/youyesyet/canvasser_app/handlers.cljs b/src/cljs/youyesyet/canvasser_app/handlers.cljs index a010699..2b7596a 100644 --- a/src/cljs/youyesyet/canvasser_app/handlers.cljs +++ b/src/cljs/youyesyet/canvasser_app/handlers.cljs @@ -224,3 +224,17 @@ (if (integer? zoom) (assoc db :zoom zoom) db))) + + +(defn get-current-location [] + "Get the current location from the device." + (try + (if (.-geolocation js/navigator) + (.getCurrentPosition + (.-geolocation js/navigator) + (fn [position] + (dispatch [:set-latitude (.-latitude (.-coords position))]) + (dispatch [:set-longitude (.-longitude (.-coords position))]))) + (js/console.log "Geolocation not available")) + (catch js/Object any + (js/console.log "Exception while trying to access location: " + any)))) diff --git a/src/cljs/youyesyet/canvasser_app/state.cljs b/src/cljs/youyesyet/canvasser_app/state.cljs index 241752e..4a770c8 100644 --- a/src/cljs/youyesyet/canvasser_app/state.cljs +++ b/src/cljs/youyesyet/canvasser_app/state.cljs @@ -27,9 +27,6 @@ ;;; This is the constructor for the atom in which the state of the user interface is held. ;;; The atom gets updated by 'events' registered in handler.cljs, q.v. -;;; -;;; not wonderfully happy with 'db' as a name for this namespace; will probably change to -;;; 'client-state'. (def default-db {;;; the currently selected address, if any. @@ -87,3 +84,4 @@ :latitude 55.82 :longitude -4.25 :zoom 12}) + diff --git a/src/cljs/youyesyet/canvasser_app/ui_utils.cljs b/src/cljs/youyesyet/canvasser_app/ui_utils.cljs index 068bad6..63e1846 100644 --- a/src/cljs/youyesyet/canvasser_app/ui_utils.cljs +++ b/src/cljs/youyesyet/canvasser_app/ui_utils.cljs @@ -28,15 +28,22 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn back-link [] - [:div.back-link-container {:id "back-link-container"} - [:a {:href "javascript:history.back()" :id "back-link"} "Back"]]) +(defn back-link + "Generate a back link to the preceding page, or, if `target` is specified, + to a particular page." + ([] + (back-link "javascript:history.back()")) + ([target] + [:div.back-link-container {:id "back-link-container"} + [:a {:href target :id "back-link"} "Back"]])) - -(defn big-link [text target] +(defn big-link + [text & {:keys [target intention]}] [:div.big-link-container {:key target} - [:a.big-link {:href target} text]]) - + [:a.big-link (merge + (if target {:href target}{}) + (if intention {:on-click intention})) + text]]) (defn nav-link [uri title page collapsed?] (let [selected-page (rf/subscribe [:page])] diff --git a/src/cljs/youyesyet/canvasser_app/views/building.cljs b/src/cljs/youyesyet/canvasser_app/views/building.cljs index 54a1ed2..47d9d8f 100644 --- a/src/cljs/youyesyet/canvasser_app/views/building.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/building.cljs @@ -48,7 +48,7 @@ [dwelling] (ui/big-link (:sub-address dwelling) - (str "#/electors/" (:id dwelling))) ) + :target (str "#/electors/" (:id dwelling))) ) (sort #(< (:sub-address %1) (:sub-address %2)) (:dwellings address)))]]])) diff --git a/src/cljs/youyesyet/canvasser_app/views/elector.cljs b/src/cljs/youyesyet/canvasser_app/views/elector.cljs new file mode 100644 index 0000000..0f94f1f --- /dev/null +++ b/src/cljs/youyesyet/canvasser_app/views/elector.cljs @@ -0,0 +1,104 @@ +(ns ^{:doc "Canvasser app single elector panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.electors + (:require [reagent.core :refer [atom]] + [re-frame.core :refer [reg-sub subscribe dispatch]] + [youyesyet.canvasser-app.ui-utils :as ui])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.canvasser-app.views.elector: elector view for youyesyet. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defn gender-row + "Generate a row containing a cell showing the gender of this `elector`." + [elector] + (let [gender (:gender elector) + image (if gender (name gender) "unknown")] + [:tr + [:td {:key (:id elector)} + [:img {:src (str "img/gender/" image ".png") :alt image}]]])) + + +(defn name-row + "Generate a row containing a cell showing the name of this `elector`." + [elector] + [:tr + [:td {:key (:id elector)} + (:name elector)]]) + + +(defn option-row + "Generate a row showing this `option` for this elector." + [elector option] + (let [optid (:id option) + optname (name optid)] + [:tr {:key (str "options-" optname)} + (let [selected (= optid (:intention elector)) + image (if selected (str "img/option/" optname "-selected.png") + (str "img/option/" optname "-unselected.png"))] + [:td {:key (str "option-" optid "-" (:id elector))} + [:img + {:src image + :alt optname + :on-click #(dispatch + [:send-intention {:elector-id (:id elector) + :intention optid}])}]])])) + +(defn issue-row + "Generate a row containing an issue cell for a particular elector" + [elector] + [:tr + [:td {:key (:id elector)} + [:a {:href (str "#/issues/" (:id elector))} + [:img {:src "img/issues.png" :alt "Issues"}]]]]) + + +(defn panel + "Generate the elector panel." + [] + (let [address @(subscribe [:address]) + dwelling @(subscribe [:dwelling]) + elector @(subscribe [:elector]) + electors [elector] + options @(subscribe [:options]) + sub-address (:sub-address dwelling)] + (if address + [:div + [:h1 (if sub-address + (str sub-address ", " (:address address)) + (:address address))] + [:div.container {:id "main-container"} + [:table + [:tbody + ;; genders row + (gender-row elector) + ;; names row + (name-row elector) + ;; options rows + (map + #(option-row elector %) + options) + ;; issues row + (issues-row elector)]] + (ui/back-link)]] + (ui/error-panel "No address selected")))) diff --git a/src/cljs/youyesyet/canvasser_app/views/electors.cljs b/src/cljs/youyesyet/canvasser_app/views/electors.cljs index 482d21a..8f0f310 100644 --- a/src/cljs/youyesyet/canvasser_app/views/electors.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/electors.cljs @@ -40,15 +40,17 @@ ;;; Each column contains ;;; 1. a stick figure identifying gender (for recognition); ;;; 2. the elector's name; -;;; 3. one icon for each option on the ballot; -;;; 4. an 'issues' icon. ;;; The mechanics of how this panel is laid out don't matter. (defn gender-cell [elector] (let [gender (:gender elector) image (if gender (name gender) "unknown")] - [:td {:key (:id elector)} [:img {:src (str "img/gender/" image ".png") :alt image}]])) + [:td {:key (str "gender-" (:id elector))} + [:img {:src (str "img/gender/" image ".png") :alt image + :on-click #(dispatch + [:set-elector-and-page {:elector-id (:id elector) + :page "gdpr"}])}]])) (defn genders-row @@ -60,7 +62,12 @@ (defn name-cell [elector] - [:td {:key (str "name-" (:id elector))} (:name elector)]) + [:td {:key (str "name-" (:id elector)) + :on-click #(dispatch + [:set-elector-and-page {:elector-id (:id elector) + :page "gdpr"}])} + (:name elector)]) + (defn names-row [electors] @@ -69,41 +76,6 @@ #(name-cell %) electors)]) -(defn options-row - [electors option] - (let [optid (:id option) - optname (name optid)] - [:tr {:key (str "options-" optname)} - (map - (fn [elector] (let [selected (= optid (:intention elector)) - image (if selected (str "img/option/" optname "-selected.png") - (str "img/option/" optname "-unselected.png"))] - [:td {:key (str "option-" optid "-" (:id elector))} - [:img - {:src image - :alt optname - :on-click #(dispatch - [:send-intention {:elector-id (:id elector) - :intention optid}])}]])) - ;; TODO: impose an ordering on electors - by name or by id - electors)])) - - -(defn issue-cell - "Create an issue cell for a particular elector" - [elector] - [:td {:key (:id elector)} - [:a {:href (str "#/issues/" (:id elector))} - [:img {:src "img/issues.png" :alt "Issues"}]]]) - - -(defn issues-row - [electors] - [:tr - (map - #(issue-cell %) - electors)]) - (defn panel "Generate the electors panel." [] @@ -123,13 +95,7 @@ ;; genders row (genders-row electors) ;; names row - (names-row electors) - ;; options rows - (map - #(options-row electors %) - options) - ;; issues row - (issues-row electors)]] + (names-row electors)]] (ui/back-link)]] (ui/error-panel "No address selected")))) diff --git a/src/cljs/youyesyet/canvasser_app/views/gdpr.cljs b/src/cljs/youyesyet/canvasser_app/views/gdpr.cljs new file mode 100644 index 0000000..b28369c --- /dev/null +++ b/src/cljs/youyesyet/canvasser_app/views/gdpr.cljs @@ -0,0 +1,60 @@ +(ns ^{:doc "Canvasser app electors in household panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.gdpr + (:require [reagent.core :refer [atom]] + [re-frame.core :refer [reg-sub subscribe dispatch]] + [youyesyet.canvasser-app.ui-utils :as ui])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.canvasser-app.views.gdpr: consent form. +;;;; +;;;; 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 +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; OK, the idea here is a GDPR consent form to be signed by the elector + + +(defn gdpr-panel-render + [] + (let [elector @(subscribe [:elector])] + [:div + [:h1 "GDPR Consent"] + [:div.container {:id "main-container"} + (ui/back-link "#electors") + [:table + [:tbody + [:tr + [:th "I," (:name elector)]] + [:tr + [:td + [:p "Consent to have data about my voting intention stored by " + [:b "Project Hope"] + " for use in the current referendum campaign, after which + it will be anonymised or deleted."] + [:p [:i "If you do not consent, we will store your voting intention + only against your electoral district, and not link it to you"]]]] + [:tr + [:td {:id "signature-pad"} + [:canvas]]]]]] + (ui/big-link "I consent" :target "#elector") ;; TODO: need to save the signature + (ui/big-link "I DO NOT consent" :target "#elector")])) + + + diff --git a/src/cljs/youyesyet/canvasser_app/views/issue.cljs b/src/cljs/youyesyet/canvasser_app/views/issue.cljs index 34afe10..46b16b2 100644 --- a/src/cljs/youyesyet/canvasser_app/views/issue.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issue.cljs @@ -47,5 +47,5 @@ [:div {:id "issue-text" :dangerouslySetInnerHTML {:__html (md->html (issues issue))}}]] - (ui/big-link "Request call" "#/followup") + (ui/big-link "Request call" :target "#/followup") (ui/back-link)]])) diff --git a/src/cljs/youyesyet/canvasser_app/views/issues.cljs b/src/cljs/youyesyet/canvasser_app/views/issues.cljs index 8b2ca47..9253a75 100644 --- a/src/cljs/youyesyet/canvasser_app/views/issues.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issues.cljs @@ -46,5 +46,5 @@ [:div.container {:id "main-container"} (ui/back-link) [:div {:id "issue-list"} - (map (fn [k] (ui/big-link k (str "#/issue/" k))) (keys issues))]]] + (map (fn [k] (ui/big-link k :target (str "#/issue/" k))) (keys issues))]]] (ui/error-panel "No issues loaded")))) diff --git a/src/cljs/youyesyet/canvasser_app/views/map.cljs b/src/cljs/youyesyet/canvasser_app/views/map.cljs index 257e404..c0163b2 100644 --- a/src/cljs/youyesyet/canvasser_app/views/map.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/map.cljs @@ -2,7 +2,8 @@ :author "Simon Brooke"} youyesyet.canvasser-app.views.map (:require [re-frame.core :refer [reg-sub subscribe dispatch]] - [reagent.core :as reagent])) + [reagent.core :as reagent] + [youyesyet.canvasser-app.handlers :refer [get-current-location]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; @@ -115,6 +116,7 @@ (defn map-did-mount-mapbox "Did-mount function loading map tile data from MapBox (proprietary)." [] + (get-current-location) (let [view (.setView (.map js/L "map" (clj->js {:zoomControl "false"})) #js [55.82 -4.25] 40)] ;; NEED TO REPLACE FIXME with your mapID! (.addTo (.tileLayer js/L "http://{s}.tiles.mapbox.com/v3/FIXME/{z}/{x}/{y}.png" @@ -126,6 +128,7 @@ (defn map-did-mount-osm "Did-mount function loading map tile data from Open Street Map." [] + (get-current-location) (let [view (.setView (.map js/L "map" (clj->js {:zoomControl false})) #js [@(subscribe [:latitude]) @(subscribe [:longitude])] From e19e01d9be5e72c4e4707bac04e7427435c7f6b6 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 2 Jul 2018 17:29:01 +0100 Subject: [PATCH 30/51] Trying to track down why we're seeing a version of the electors panel which should no longer be there. Failing. --- project.clj | 8 +++----- resources/templates/app.html | 2 +- src/cljs/youyesyet/canvasser_app/core.cljs | 6 ++++++ .../youyesyet/canvasser_app/views/building.cljs | 2 +- .../youyesyet/canvasser_app/views/elector.cljs | 4 ++-- src/cljs/youyesyet/canvasser_app/views/map.cljs | 14 ++++++++------ 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/project.clj b/project.clj index 2e6879b..702368e 100644 --- a/project.clj +++ b/project.clj @@ -77,7 +77,7 @@ :npm {:dependencies [[datatables.net "1.10.19"] [datatables.net-dt "1.10.19"] [jquery "3.3.1"] - [leaflet "1.3.1"] + [leaflet "0.7.3"] ;; old version works, new ["1.3.1"] doesn't [signature_pad "2.3.2"]] :root "resources/public/js/lib"} @@ -139,8 +139,7 @@ [lein-figwheel "0.5.9"] [org.clojure/clojurescript "1.9.495"]] :cljsbuild - {:prep-tasks [["npm" "install"]] - :builds + {:builds {:app {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"] :compiler @@ -159,8 +158,7 @@ (pjstadig.humane-test-output/activate!)]} :project/test {:resource-paths ["env/test/resources"] :cljsbuild - {:prep-tasks [["npm" "install"]] - :builds + {:builds {:test {:source-paths ["src/cljc" "src/cljs" "test/cljs"] :compiler diff --git a/resources/templates/app.html b/resources/templates/app.html index 4b14abe..3227f33 100644 --- a/resources/templates/app.html +++ b/resources/templates/app.html @@ -36,7 +36,7 @@ - + {% script "js/lib/node_modules/signature_pad/dist/signature_pad.min.js" %} {% script "js/lib/node_modules/leaflet/dist/leaflet.js" %} {% script "/js/app.js" %} diff --git a/src/cljs/youyesyet/canvasser_app/core.cljs b/src/cljs/youyesyet/canvasser_app/core.cljs index e492a35..f750122 100644 --- a/src/cljs/youyesyet/canvasser_app/core.cljs +++ b/src/cljs/youyesyet/canvasser_app/core.cljs @@ -15,8 +15,10 @@ [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.elector :as elector] [youyesyet.canvasser-app.views.electors :as electors] [youyesyet.canvasser-app.views.followup :as followup] + [youyesyet.canvasser-app.views.gdpr :as gdpr] [youyesyet.canvasser-app.views.issue :as issue] [youyesyet.canvasser-app.views.issues :as issues] [youyesyet.canvasser-app.views.map :as maps]) @@ -57,6 +59,9 @@ (defn electors-page [] (electors/panel)) +(defn elector-page [] + (elector/panel)) + (defn followup-page [] (followup/panel)) @@ -72,6 +77,7 @@ (def pages {:about #'about-page :building #'building-page + :elector #'elector-page :electors #'electors-page :followup #'followup-page :issues #'issues-page diff --git a/src/cljs/youyesyet/canvasser_app/views/building.cljs b/src/cljs/youyesyet/canvasser_app/views/building.cljs index 47d9d8f..75ecc24 100644 --- a/src/cljs/youyesyet/canvasser_app/views/building.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/building.cljs @@ -48,7 +48,7 @@ [dwelling] (ui/big-link (:sub-address dwelling) - :target (str "#/electors/" (:id dwelling))) ) + :target (str "#electors/" (:id dwelling))) ) (sort #(< (:sub-address %1) (:sub-address %2)) (:dwellings address)))]]])) diff --git a/src/cljs/youyesyet/canvasser_app/views/elector.cljs b/src/cljs/youyesyet/canvasser_app/views/elector.cljs index 0f94f1f..cdc3e0b 100644 --- a/src/cljs/youyesyet/canvasser_app/views/elector.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/elector.cljs @@ -1,6 +1,6 @@ (ns ^{:doc "Canvasser app single elector panel." :author "Simon Brooke"} - youyesyet.canvasser-app.views.electors + youyesyet.canvasser-app.views.elector (:require [reagent.core :refer [atom]] [re-frame.core :refer [reg-sub subscribe dispatch]] [youyesyet.canvasser-app.ui-utils :as ui])) @@ -99,6 +99,6 @@ #(option-row elector %) options) ;; issues row - (issues-row elector)]] + (issue-row elector)]] (ui/back-link)]] (ui/error-panel "No address selected")))) diff --git a/src/cljs/youyesyet/canvasser_app/views/map.cljs b/src/cljs/youyesyet/canvasser_app/views/map.cljs index c0163b2..e7a6c24 100644 --- a/src/cljs/youyesyet/canvasser_app/views/map.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/map.cljs @@ -97,19 +97,21 @@ lng (:longitude address) pin (.icon js/L (clj->js - {:iconUrl (str "img/map-pins/" (pin-image address) ".png") - :shadowUrl "img/map-pins/shadow_pin.png" + {:iconAnchor [16 41] :iconSize [32 42] + :iconUrl (str "img/map-pins/" (pin-image address) ".png") + :riseOnHover true + :shadowAnchor [16 23] :shadowSize [57 24] - :iconAnchor [16 41] - :shadowAnchor [16 23]})) + :shadowUrl "img/map-pins/shadow_pin.png"})) marker (.marker js/L (.latLng js/L lat lng) (clj->js {:icon pin :title (:address address)})) ] - (.on marker "click" (fn [_] (map-pin-click-handler (str (:id address))))) - (.addTo marker view))) + + (.on (.addTo marker view) "click" (fn [_] (map-pin-click-handler (str (:id address))))) + )) ;; My gods mapbox is user-hostile! From 13f454afb57d2d589c753b13556826863f4c3ba2 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 2 Jul 2018 17:42:15 +0100 Subject: [PATCH 31/51] Updated README database section; reformat of electors Electors page is STILL rendering an obsolete version which is nowhere in the code, and I cannot see how it possibly can. --- README.md | 16 ++++++++++++---- .../youyesyet/canvasser_app/views/electors.cljs | 13 ++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index dd054c8..f21afe9 100644 --- a/README.md +++ b/README.md @@ -59,16 +59,24 @@ Do get the database initialised, run createdb youyesyet_dev -followed by +I'm no longer using Migratus as I'm using [Application Description Language]() +to generate the majority of the application, and, as changes are made to the application +description, new database schemas are generated. The database initialisation script will +be found at `resources/sql/youyesyet.postgres.sql`. Reference data initialisation scripts +will in due course be stored in the same directory. - lein migratus migrate - -**NOTE THAT** in the namespace *youyesyet.db.schema*, there are a series of functions *create-xxx-table!*. These are a snare and a delusion; they are how I originally bootstrapped the database, but are no longer used (and should probably be deleted). The database is now constructed using [migratus](https://github.com/yogthos/migratus). +Once we have a more or less finished application it may be worth going back to +[Migratus](https://github.com/yogthos/migratus); I might have a go at generating migrations from +diffs between successive versions of the application description. ## Running in a dev environment To run in a dev environment, checkout the *develop* branch +To download and install Javascript delendencies, run + + lein npm install + To start a development web server for the application, run: lein run diff --git a/src/cljs/youyesyet/canvasser_app/views/electors.cljs b/src/cljs/youyesyet/canvasser_app/views/electors.cljs index 8f0f310..eb1f5ec 100644 --- a/src/cljs/youyesyet/canvasser_app/views/electors.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/electors.cljs @@ -62,11 +62,12 @@ (defn name-cell [elector] - [:td {:key (str "name-" (:id elector)) - :on-click #(dispatch - [:set-elector-and-page {:elector-id (:id elector) - :page "gdpr"}])} - (:name elector)]) + [:td {:key (str "name-" (:id elector)) + :on-click #(dispatch + [:set-elector-and-page + {:elector-id (:id elector) + :page "gdpr"}])} + (:name elector)]) (defn names-row @@ -92,9 +93,7 @@ [:div.container {:id "main-container"} [:table [:tbody - ;; genders row (genders-row electors) - ;; names row (names-row electors)]] (ui/back-link)]] (ui/error-panel "No address selected")))) From c7b64845970a0ee3ea4cb5db53c26fd9644ef5bf Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 2 Jul 2018 18:17:46 +0100 Subject: [PATCH 32/51] Got rid of obsolete stuff Still trying to work out where the obsolete version of the app is hiding! --- dummies/mapview.png | Bin 3962517 -> 0 bytes dummies/mapview.svg | 4874 ----------------- dummies/mapview.xcf | Bin 365501 -> 0 bytes dummies/mapview_800.png | Bin 681083 -> 0 bytes dummies/mapview_800.xcf | Bin 1316227 -> 0 bytes dummies/occupants.png | Bin 367547 -> 0 bytes dummies/occupants.svg | 454 -- dummies/occupants.xcf | Bin 1046838 -> 0 bytes dummies/occupants_800.png | Bin 93285 -> 0 bytes dummies/occupants_800.xcf | Bin 201210 -> 0 bytes dummies/ujack.png | Bin 5937 -> 0 bytes dummies/ujack.xcf | Bin 58268 -> 0 bytes dummies/unknown.png | Bin 2863 -> 0 bytes dummies/unknown.xcf | Bin 43990 -> 0 bytes followup.cljs | 72 - src/cljs/youyesyet/canvasser_app/core.cljs | 4 + .../youyesyet/canvasser_app/handlers.cljs | 1 + .../youyesyet/canvasser_app/views/gdpr.cljs | 15 +- 18 files changed, 17 insertions(+), 5403 deletions(-) delete mode 100644 dummies/mapview.png delete mode 100644 dummies/mapview.svg delete mode 100644 dummies/mapview.xcf delete mode 100644 dummies/mapview_800.png delete mode 100644 dummies/mapview_800.xcf delete mode 100644 dummies/occupants.png delete mode 100644 dummies/occupants.svg delete mode 100644 dummies/occupants.xcf delete mode 100644 dummies/occupants_800.png delete mode 100644 dummies/occupants_800.xcf delete mode 100644 dummies/ujack.png delete mode 100644 dummies/ujack.xcf delete mode 100644 dummies/unknown.png delete mode 100644 dummies/unknown.xcf delete mode 100644 followup.cljs diff --git a/dummies/mapview.png b/dummies/mapview.png deleted file mode 100644 index 1fe15e1b2dec91ae3c0383b0cb71efc3cd85bce3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3962517 zcmaI7WmH_j(k?vM5FmJPC%C(7a8H2X5`qVJ9h~6q?iw_>ySux)4z2@y(sDr8OM@0H)9XzEG|O!p`rLc#aYpjw&`LjxGlF#sC)=7bbIS3kO33 zTVp00d(*UY0ek>}6d)rms_L44+O`I^S<)oAbDMu1hwP{0p%^X@M(hm>&V#Wuqz-K_aiMKxN#RLE%e0QG)s8B+w`zOQo zFR2o~@BfzeKQo&Iup6kG!oZ6-fDppw5p~$OADH}s&gTuLQy6g30eIk6!p`__H~){8 z0!$x#-q1SVU^k%vy51+?G{`-o&l^&#`b+J9R^VtdDjbqr|^U5e?{f`IjAyvQxod3cK7ib7P;FZ|t{;JR8{(LJg3qqmi^=NFl zawm?aeQ|)={(4)vv*2~Ma6!;8R*(NXZK$>!Fw0wn@htaD ztouif4Ob2S;s3?s|DSm)Sm)3WBv25>_J?UX=B*;H4K2d(+vbdJ2>0=)72be!YVTlEwP-O@sKm0l^%SDUVP zS>8?NcaY^xO!B-qaItJrxrL;E>Ojcth$>vS5UPq9xj4}!~Z?w?O6YX{$~AU{+%-`fLC?EGp3mBKXbl* z{C}9U0TZGP@WShqf^>&5w+DAzyS$>TKaRTy@s_=BWqH@jKAcN`;RpYRQB$FR#DCZ$ zsqc8hv*^3}M-&)?|IT_FoJOQg66gmxudU~fx97_m;pZCHyCu^sLNz4pR*6OzFtHG1 z3w1~Tb;LX5`5yedM*@A}6^=FXpZ!DxXYuvj;Qv>qd-8VzH*ugK0s5|Q&l^!$&rMl9 z>=OdlyH#3Mn@R{S;PpP#mr2wueMp#h7tDxo=wC!@f?Mt?}`4du5egdS{ilGlpmb8r0af)=|v7I=))T%>&>C{aX-!LaRdIs9FXN^ zpZp)m+Wwryt^ebq{@32gs^z9mUI)iY`A|AR*I|HFcD0q;;`;clq(t0)NucDyvaajoCgHwbFJ z8}I?x-R5zB(YEp2_HMwRc2`6I;dkf&hnQlP|G*{2{O@46#$LR8Ey}mc8R3_ihfa8* z3)m^ms?9VQ=Jp42J<#oUNEZpjSO3W?JYo24A+G1we*~g3DRsOwk*JwZSWjoov;MR-R(%nlPz1zXhLAOiR+o~yq-J2k981f~xE&Xsa+0&zC~ZuueE))@5kk@Prq zM6%t}+pgJpPdqW@?R^{XlU-wa>@^Nci-J=>U84A7QHi>kuyVk7q3xmb$91A=YEBM< zDc)FDF_zV&QwE#odnx6%j~&mfde4RXXR{IAP|ubYd2_vWvI8})hLm~Ay~Py|C)VE~ z;QkRuw^6TT9VG1b3LB$sy()PJ^0M(ZV~p|%x*xm4mi3Y!b4E$Z7pyXH099EfL@v-* zEWRkuw(cFNTJPmId>nPz!$`V)sNGQV&Ud6jMxhY^3gp9^e9D`ovryNG@*EL(p%DaS zC_4B58Yuglx7RS&M8e?-vMxQBz0K_Mvd~W%)pc3zKs|lNS~vmy*s${^Kk0MasRH5X zFcH+B`HhVIKC|~9aV7)VBUyGvoD5ji*jsx%KtMgY+s-_+Q7cjI*Y&@+Ee`fv(N}J^ z108RiWh$EWf{5DP-}EZ&#<^zDoAiP#^|zdH7VY|On|Jp2f!s+1I@Z?xk7LNzTr*e; zn134deb4M`(Tn(ecgK_CtuDQ&w5hv215Xa(^S+IpDYq&ZvqtH)F?jb5x9Y)1MYepG zzLNn~EW#^Fs&g~&S2#!P+IOHlo4#sq+2zKJbZQ4$JTS{=TD0kHB+E9!f&*Qu3zRyA z2BF$yIxB9ND*}%j&-w4(Bl7*4YvYrb-fK#K#*6=OyR7)sJllpZSqj!c$cCuK51g>|Yxd zIAN*ll_loc-5C-S?^pGVVtFJ;8x0yl7%{6YcshY^UD{x{?CzPQTNQG`T*oY<)d+Hm>dE+B-p9xC?DqItaTbkDZ; zxSZz=zIL#|OIkRTKMjf|` z+n1)ruitDkoF=^5)C8R~vMO1tPn!N5mT|zFe zgD~<8t6q8f#=Tz36+U9G_ACHPJBT_ezyFc+0)I(~Q-Nwldr>^SavstkTZJ0$w@m0y zVe@F^8t1m5+;l7R=ABCBRATQ7RKI>DiB;!KVhc?ujXO4vz|`Nww(^?p|4C0{$$QwP z>7;+sM${?%+#!4o_IlO#zO;F~jq-m0;wi8tIF5RB^zY0(T9sb*oAU$(X+G+J$W(CA6zB?q9(>04iX#i84N zw&2s}uV}1>Oz-XcS+#WS{oUH{A24Q0$!Z=-hrFDCbim-n`{zdQw_k3F&#q^63m3!m zl{>c!`aKqvZISAFZpf7#uY45?^w!qa7pFDc?+zEpJs}ik`W`CA{#`owzYjO}_m}Km zPuB(q#IWm^WUr zb!ZajmmMD<90hIA3Oo0C342RTiQtBe7zJ>3Z}~<1oQSDuUUoAwbXG#fD|e2!VeX+p?VSNs=Y#Mu07#U?LEt7a%h## zv0R+fqrZ6-kI3VT}%+CR2V z_E)>yTvDZS`~XLDeV~}0RINg)qRmdrJwsgL^FeNdS(Xy!z&b^U=Al&3F~Bt^!WACAl{)3?VuJFVF0*2O zKKx~o4{fxmi20l}pBBr(>0$~jeE6gu7hQN}VDYx!O!zl8JqL&x(`vEz-9ig%jIj#6JSA3ZY zp$YOtiZkax(=e-0Y&mlZNeyc^Lo{N|B2z%6`y5C!h41ou1glahJRq)UUBS9{w;Xjj z>@ElA*-T0$iDZWIm%Adq>YOF&b=Gc&SeGcj{6Z?tjqgB=Y@70+UaWw7CEg;@;!msWM}a#1CHRIK5O zoqyChDsETP4Yat6%p(waTd)!fE4b`FdDTjzJ%)2*8am zf%hQNjo%ra#AldGIrG?OBHZVnh#)0rKVKNX(&VR^=Jyx1<&8`lh;<-oaci3Ihc}Kp zpGSAeO%sq3t?fcrWFhmh#yPtzHoGzDwF_^)h$gzN2>LrX zyV_xXD*BnnV!B=5rG00@m^>5JrKI zl)y;cacOFq%QDbsJ54cybB$r;VjyiK?u*3a_o})srDg8oWDK8I>ie8qwtq0g-i@WF{^X*56u8%F_#uUA}-14x?f|5=T;fbXtYRl)=(7NI29SX zRqHKFACXhA4t}qr+V6S6@B!PWg9JJCBL!&}RoL)3EW^C8vwRDrZv%!A;rE$z)%NOW2Op(75MUN>YM zwbv8zXGP^cCHwRF0E#q$6L$bJ$+W%(zQ`yut5&cgNzu-##u-5trIyB!bbFY5M-)Z$M&Fj&Bf@!NTcXpGsBSc(v4-nG zu|RB;`a9N)%v`9^c<-gzxpNd@G_+X{Gm&*nwp*lsnr3tSCvivNCB3kN{5o`YY%Jy} z0rTK=>4tP^?j=5m(i*|F z@cz|BJ5JwXEW;D7DgqK2`7m`YBUZ+zwK1tZu>TkdJRCd>e=sK4`Fx1D#C2T+^}UY@ zTh1m_(GXhf+2K`<5tL(weE1xkKmhw$P;fh&@Xy!5!o^8OlYt94BNzs6Vz0Yms5J<7 zzY?q>L2Xz#p}!@J=^9x}YLro<+%ayQPGqRaQ&r9)L9Npdl7z4oIuaebUX5pqs^74#Xr^GWwr|D!|7DbQOg& z-&>Y^i1;(Rzxyii-wDMZwSzy`FS(qOvqpYip0ZjMU$d}D>XEjSnZCh z8u#LL5g;o_atYW6OjrL2_XH8R6|6tuJ|FGF-0;oNzGQ7-I$qxg@w0eVfo!()Y5H+z zzT<0wCkwyg35{M51zBN3)kZ*un2?_~he8M-ELE@BGd==;%GOyS}&QT+57*&J`#k`%!VoQB> zvWObb&74grqINf0m~3my^qH%|e?_M!R>(5D6S9-|2k}v#e8l}j<2R*Yq(r$~a4V>o zjAljnaQ?*Y1Z>Kf(pmH#GUo-*`W#NL#3Cy4$Bc$#A6%PgNQoqIG$PMFIQ}r`$NObG zR2d?t>VqE!3DBQ=M>|u#Q~F zg0Yn76;_tEgVF$T6i0$Us&w#u)6U^vomeFQ%;kuNFBh$S4kYp~y!66Bywoa(Jj!j* zm>1KZl)@F92C(un@lm@S%kh%SR&Ilf5(pCZQU8vz=icx?HIl|C@gv2$yR5?)@lZrh zFL*jj>mne6!*G@2*6&0Ex(M)f_nOrWeFQO63W!o8aKVmdBz)NxS2ZUYmKCWEIBhJ& z=3jdKMG8E{zXkjr3RniPE9*B}@~zTG?_? z{ApHT5o2yFw|D=b1D-^$n~bG1P?s9jD|#els*|x0#jSh&l;=I^n0GP&nOdmV`^{k2t-U~{>0A}*BG9c_hdg=VJo(i)4P2{>?vG!vy89wgv`be3{F zq8j}cTGbC{M(P;8vYBgCNTE`b;DmCAeGYh{*B7qA7ghe1bDhBFf{=@9#bv6-#0Kg7 z`NZLgO+skbCQ8+hL8Wr$*Y##nnX;lo4<|FnD>^`tjoYt4B+wt}m}}Rl%N3oe=6fPT zQj*{+X$Lo|yA##_l1;q$hwlsqk4xEqsHy4~Dnm!YMC#Wtq9Oa#ySxvQZB`(c}>(ttUfEx~E$iT{jM&P_&J?|2Ch}P2z1cXZv#DL%G=IjVMYLnW#Jw^NL7P zo3C+f@Wk`&aYppJM*&|yWc0S|&QdZ6;f03qX5KVbNoC|C{UMvrB;28hsqph8Srimo zh6PGLZx)Zq>sMK>FW6Q>aGK`>ME5Qm&C5IoN~~>^T&yx$C{!>ehGj35H)bLy_K`hZ z2F}LG!ZZ6ahn(DG<1o2pZ~T|utcRuwUKClMrah{2LSE8SG+)ruDB`b542}MLY)5Xj z>crh|@UQdRqZcqacN=a8%u=20!Ffm{W+;g@EoF1e#M!&rFCtr^laLJa=NK0o_m5?i zdz}*AF{C$qUN77oHPf>5`zSFX3_DEKu%m}+B11BA!pZ)_tD7Z}c%({hXK4BGRQh-5 z5^;~-FOSWIF%(pL%yZTU_b*@XD3ia$RXQQf@R6O_Wl6_+s=pmO$~@sN9~H&QI*mu7US*{z`hPTl=`uCnM!lf^mcy@ z_Pid0cWFyj0v)UTOBlXq_pF27lZI^%4+FxH-ZxRN=m3%L9UU$px0MTb4c=Ibs2&RI zMIOJsKct^P?q$V05((I1#5v3OeR5EJas5^PKm85zmt+Z1yH&FK|xanRMhd+XBU^BkpT~Z}ZJ2H}(wC0rzTFNi!T1 zZ0i^GIjapq%9V^q=#c$;NjB?Q`lBQ_6kAA)5?+Da{$7lif`B>%fUv&wB@x4g)hqZ! zt>O2_SoSKUy`H;^Z>qatY~2a)I0<*q*}{M>c)*LVtmN2c<`9>HJ!kVLGUMK`Jga*X zDQ*WXHXPtRcb+hSU>4nwFCVj%$q`#a+TEqd-_7}(=d8=HAA(BD=4$SBziyUb{vOfH ziUgoZ*Nc3$B)Uf^w|VunI7QH z40;RN$+VXe3b5plCAHw_ucmH9Z37PC1H4gA_OHolIH2>*noc3ZfFlFEXa+(Ibi>aR0mIR}#4lR_Q+I3?q3FiX@di-r+ToET9iJhQ~| zm}R1s(~LntVW1tAK#Fz(Y;lwQEn;fo7xGJ1WV&D9w@qB-Ge2m;k(M6=3df)>W5Weo zEM3!-KP?0yJ}>~Y7?a$4y`=0{4u$`aV6*<+TxuCwctp|Wq6?-Hc_2;S4&MoQGBe87 zi8{C*reM5vo3_r*tzM2_DeMMQRlOp%=M@Gqe{rJbUmixi`iK-71zmJ&G|g@Ye$w*$ z4fx4YopLRjt2b<7oE(=kx*ttU%#+U*)ivyDwu$B}dNc@o)jWGs)cdE4ea_&IScxz9 zujhm_mBEx}*t;*7_>)o>XgVD&9dG+1argqtk98LQYH5L;BIpz9VW8%z@9-XyRO&`7ZI#^fC zC^X|XX)h&_+R)J}I-AA~qsbb$+{Dn&4{%|!Un}KJC-Niq_iIKHIVfJh-v{#HPM*;` zOF#33#G?M>%U%SR&D=Ug(&Lm{^X40niVeVBCAWNx;Q-D^QRSrYCETc8g)Qe-)UH+F zpfvYOXio5$>+t4*_IBtx(Dlp`v%T_IT%5U;lv5}uJm=|VFm1hKy@+jM#*#No#9R?) zc0ZjgtV3eC6$YZd7n5*icI zHELcZUEembcxL!<2pZ4{}h!lG%sOc>V!h7;mLRT#$`%J^EH<~)vQSTE1* zWG_i!RDx~dEKxDnVd{UXGM@USFO8KJQ0xo|`2_Og7jM^EtCvtthwvz8-AwVAa_$c2goS? zRh|&mNT)rVSO3rK$N8@I+U!z)y2q~48S3nNdhmd4roVV92oZwYG#9Z3 z2pBR(VYsqz$E)W#h;iVf^bTX1QmILv&L&Jb(MDxpeDEdI$%|0r)C;jv`NX?;eDGS) zxVi65i{dHd6-QTc8+S;Tyv#H|(9np5FAEIzl_J5#Az{g2C46(X@lXfwt75f9m7}rPS&S;Cz$xv#4lUG~LJ#~Jy^N#M}& z6Jmc3L_IkUd;1F5@+8#(wZ2?5ZP5OrxS%6b{a$qNqQ_W8ROYgAI(IYIr|?~eI~V!_ ztX^&Dz>l3=j=|*3uFn3fU-(c~rGrwQ-M zEL|}s{bN`x+~KN>F;#P-(6*14Gms+_uX+78Vfhg5?Emt{M z!!W_TI_ml6y9Zs2Ukw+*^kI{DtfWOj4kV`$6zdSnkD-s(GBw2#kHPX4K|Njav8^%u z0n&UvxGm&2RA##U&c@{tRspB&jdAno>H~KSEA=^?8>M|KKO`NfP=B!F68v1Uw&rBJ zIS8r9MA{CJ;!&jt$XV&x-+M#}5fN{+lH{zO+c+wYA)+KWzMs9R;Bv^7=pV-NwPK&0 za6mWv_+}wMmP?Nb zEBHu`pU=jvyJ~3BuYF{(e^Je>j;h*U&g?-Oen7E~m$TZebJ>;GgBeunDvkbt!*5fs z)w;yMzkMd}%|cw;j?%oWro`%V7AT#ms@juA_GwKjZdEx(OI$7BkZMzpAsr*)$0NlJ zhd0T#-J8lUB^`yeQ}DAc!EeO)NOl=N6li|e=ZL^w_ z^5qeHg)-GeeI>8!B@Uc{B_K6Q9wkor(j0c`3+`qgfD1fWot2G`#i4H9cURfIjySG} z(bu>zCL-Tcc1s!&G_M{A3AaN}(_DKo7oqzV)WuZlEhH?64LbwOnB(7Qobzshi)?cx zI1o(#jKriORmFVVq5Jxi97(?Y^h3rW=WSb<0Z1Aed2jqqSp}~@Y|K$6oK%4|B(>c6 zz)N;az>h;qu}xcWAyy*B^XpPV5fy!&=7lI?R4I$>wa0g=U+ukXSl=f?%+mdDlLT>Z zOC*U4>N}s=W6cZUJN)6PlJ|K zZ!~u#QfT40s$WvlP%T^eJj^5_QYs`dvm++P!#iqoVrDRD8!xA_i0)Rr8b-&AU=Uzr zGG&RpjE*lgk>n@R(atW4245^z-7z{+TK|45QmF6n_c5b|lQf?{$w}J0OL-u5Oz7_o zAsfN>)W;{`$m9CJFpuwKhv6|8J`yeN(0o)>{ZhC6_PmO7vHt@9txx~ukM}KyY5jX3 zpkHnCz!APP6#lLMGN%s*xLIGBQ4)R`%`nZZ1{YfAjNlJoveiEjRbK{c_Jen_YJb=7 zff>)O77w3%MpFuq>v=lusjHo8mbp_J#@c)M+vi4|;&pgR@+QpfGGR*V&1Vftk|o>jAeQ#)m(KQb&|0;= z%MFNRC3^>wuC*>!=CjIOgPNwO3bS9#~p24wj3H5W+XJhA2(X^7P1 za=eh>v~6RYvf85*C{6_HlL_O{;;KC@bbAvfypj=f z4a%IdV!hpKxt)9_8H~bBJj_qchfBQ|-Pjq9=;*xzUet%<-W5}b$E0oz(Ir=NMy`ARb-F_^@Ia2CZ5r`&EF8pBV#TxUxVxA!#JzY2a=-xLr1t=Lz}+T5@zazlJ3c> zYaJ+r7E@?7IPkGURwdSo%W+PhNPHaSQ{*Lh#_O|>*W>BEaa7eVP>! z12R`X5apyAf+dJ`P`OutCK`CvOIFKRAn@Xf^#%=zJ+5LpJbxyQKWK+i$ee6mq=uJO zPC~g-n|IWEl|=6ET_7^U=>c?0@q`dq(#S znHs&`1{LLd$5$jQ%v7@A{}Crix35}cFB&AW$|2!hAM`c$TBGKtz|!H1clAS8u_*EZ zCL(uCV(vQlxGuk4bEQ7kY+GqWB>?6?RnA-f#8z8)$VenAJ<}$0MM{Xl?lvCNY2$x) zgSZ;P!eFTyoc@>_A zH!jO-Q)LBs60tNqn!Oy9N8SHyd-b}^J86bIe$)bGDsJ3FzLJ9gSMbA4`g|52HsX%D zE__W}!YzX?DDfP*b~Q4|!#3Sl=B$df{xW(A5m^u)d?>%)nrWV3W2_WxP)Xr8CpV5Z zinKfa;oqScOIKw>JI}@rrXlM#2+{BvBS{AR*w2~<7J`OurO{jR0yLR z6uGhUSYvAcwTTb$h>;$7`{_O^sNF0LRX)m)=~uRBucW(%f=y>+fWCk>Fd4lBeSqGc zT-V&^c~ls$QwYYA%@uPIH}*yI`9;s7{&UF*j|?nirT;HI=`VgRj$l-_mF}j)KZz>_ zH9{^V6p?!R0$J7fxMdrgh93cD;e)kh92_JM@y-lcr+#R55~+;0SeD9e`QO5icqfQ! z?*+6bh6X>J_!tyPJ&u1K$1E^yoJk+I+FcO$Sj6xSY)d_io0hyg2p=a}v?G)s*H9cp z%}W;aL5z@yrNZ2N9gz4I7f ztZc?%6nW(rR z6sy8jETo5fQaixZoC#3cy8Mejy%?CogcpjBE9n1MN;c%{?5Cb*ZG1<71G9V!+PoKiiFWMx|DzLu-c8Obca`lnAgExU&|N8HEy6@E} zS+H{(RyYw5z+R3Rf!^UTiE$L~nilq>5oYt2EWYZbo%|V57NfwI@y{F48*PZabScji zC+(g;^k*w{!}xPLPLM zo&B}k^MkZ1&xNr-o{C%XxYpNa)~COm+o0e?X5*wS>bNC9Mw%0C5B8}m!6M2sN3LCv7A8@xi zKDD!T_>Z;u_$ACnjHyX*J*kC1As(KftjA_159HSeWei>)H@bh`Nq0rI&6K4uBrHHw zesbTZAKUhr#n-Di@`CCfuqT|I%lvIg?4F95Z7Sp^T?V{YM3&d`H?yAE71|QWK7E>x zD?Td9SDiCUE7jmry*TKRwUCv+$j5AH|-i7!VdX0viv?uz=Ay$CeZ^r6^`L&$g0?+C&L4K zt1!0$i9K^gb!i4Z=dy?8Cz(&J3;RFgOG;LmU%yUmqMF|iMLfn{6lfzhE1WCBUG5U6 z;qZ^QW0nFfgfro$EZmtUNn%o4`bK2n-~Ec_GL)&hmcw= zl_D|f`XH*A{*1}H0n@~kmp9|}{PcF;T2aRM0J#WiOLIg1iS*2RcEI!Fy;CX0gcYLj z$O|iTmJ)?wCf$r)y&~sslW@#sTAjpS<}-vZwzaeHdChd~R|A!sb?P0&jo#asS)G11 z#H&pX@Dqafgqff_&3|KZzoINJ_cFFNo%ag&?Iprqz`E-W@@j`SNoD&Jou0=TXY1?J z$>RNs4ChV@#=qjVsVJ{c9gk@p2msgHxdq#U_oC$48Kor=o&7im(iHfxYnOh8nf&zr zCi7PXnD&o-IN3bDMz~)~9_! z#k>TU;}Sh_Q@yfk==pt48jVOZ(+o>Yfb0&oc2}veRxs*aet`R z_>_IOIwZVrBXy$Do8LpCFTWXeus!0o9AC^SFYS0x>|anotx(sgL_M9jtwyD4;JI6` z(uGaS;O;2Q%X~>S{GDENjh5-)=5?O6uq6?jl12#D|A~U=O(RJ2ji|=fcW_BuRS4~p z5Ydef@4@v825n=_41PGp`AIZ8;fp8~01$(cV8SjpYF;mR|5KuISb9{1C2@_w?x6$I zl>d^O!;f8{NfwN@*6LHp=Nv;OB|CDx<1WTzDNW7e(K&)5k&}s1o1e!HkIdjfiltK$ zO4UyqJ9fpj=W-|sFP^@QO5B+~5rR_o2`|>!)|)Tl3#@h-DI1#Em@DJ5ADYhMv7>&* z?3Vwq$*Ma{{t9%9$;mlILM9z8KJs2KySsm z342$m?nic?sX6kafm}H@%5LykG3kY)p|syqov?;=d#a50^wfuqm1k}->v*F}dy6ox z8Hm$hT0>~|P%tBZI}X_mnnu>%x&QiN%BRSa^~Inf?+UH?{Hc`~{(IExshbFiV%V<3 zHNWPbxKhT7$E@ywRet@H@7|xQ&QFfM|xl%)Bcq@jdV$U)n@ip^QlH)e&9QH ztlqD)6q3(JtS|Tix}~|x2b+J+ zwZIm6cxA6oHF>fLugp99jJVx}1(_OSF&^Poq^)dcIb7VU+~uP6a#ytZ zbc5>(Dm3lCCv(&AQBQoYNzP&De)Z_c5Umwk`@QJICQzahFqMXto}n;IYdh+KBFK4i zjJJfD=_+?m0yECA7~cQ6ttijDd2dFFjcCa|`izU%QCEk$aH2p6v7|;@XXmD0W`kIs z&N^pkNaFT*kVCYKjLWXAu!0Zn?xzk;+}p=!#aeHyEjQVmeI1S@r+L1GHBI3I3i2*F zRuwkYTLsLGp~U$6YvmBZgiJfZJWXs8IYJiM)MfGB!o?nSIwVUEInqxs&wv`6*RS>R zlxkjC9l3D|(MRrVaVN4ia+kt~XL(AQ^$8$*Au2wZt}0i=UG9%a`%-bHwFo32EhlyL zoft5>f|7T%VX4613a*TA5MSBD2Zk`B35*Jf!qr_P1o5IJcnSjLKM`MIrC2ZN*r=s% zt7mceYPxb{blOi0r6ER1V&389*iEZZB zz78H0qb_ZqdHnzMhXms2bs{mh`na@TXZ&lFX+Q92|4-nkw|f3PcY9muO{D)gtbVb_ z?9Kc7dKmTkLH}twNqK|WRqo<4%3A=K-~oPkTG;5kw(-bxmX{UO0NTr#UU4mcul#)W zEsoE1#5q)S5LM;V%Ygf3S-PPR-A=<`)>oZfe%0RdPZ2KE{B~zN@LPPEGA@xpg&k&U zyEgqkSBv9|>U7t~yT$qxHwN-LakDW3(G2Z@^_`h`e|d$vLc(2G9{HVbyn+!1zgjjg z-Ssy)U;|dBQ=B=ld@ZGty1lF1_uIEV$7vm(1VNOTtZz=Hkn=8$DPI=xU+kA8+jl4q zgzQOom8v($gNID6cfoD%Roz4$VCPi_b^SS9$T`UJ^IFTnh7QxHEWhqzRE=G)V)fys zCLBK9YVOl?LyQ@W(*9k-u$-o0Zz`KK=r}GASS(j9z*#)CAj! z8KPMc)O5C~)A3ryuTvf_ybam_oANZlFV&KKd`zy;+T~>;*P&213AxL7g3 zcS43nlK8hzA>wF@jlT`iN@Ee(FP7-wC`%p!w*UT)8ovkA93bwkOG(l6*2@$KQ;)3* za0W_g(Au)Or3aG*;!^(=i#+?p^I>MNUbC3q{iL2)!Y*zLS6h5fT>CGD{$xzj zWG=g=`KFt*@-t2Kr)(5b%&^Kh?dGO~%sd6*7$CoKzo%5BixeAed(hl%r6+xp_kk`T zV((~1&d8xK)o~#unzwA&`ndvalRL>p#ayy1`9_H=y=GsUmRHTGi}a^W?6jq)Fe)`Q zBGpN)-uF>H<)HB)q?hdo@9p*Sbk+AbWhhzEWSZqAK}(TGf}MSwQpp)Cz>;X@-(A1N zPA%FJS>E&Aiqb7H&AKO>6P4V7MI8v2rz`GXf{s)`Swd3Rk)0Ao_XlHpuBF7-3J1Th z_SLgv3N+53b5fQg^yzcR1WdBFSP|G3^=*t_JrWLs3%=tADYidmULWrR-jl35tWR|t z>k7K_TchWC>hu$W)>6?VAWt~^b4s=%crxwPep}OA4gu>5Z=jd;#|9QT$TKzfe$#lm z(8yvI+4%wbXy7s1ehtG~p67P$;(@G?$9hKH%+Ayq z&rty4JiM+$uJOw`hD%s#rk%eyxqWd7i$EN;+lVV^8dDbz)<(#Njmm;7PVo<3klDOC z3-gkifp+VmGsbZ+$#b!dOMFVJINm5+4a*~xQN!Jv&#kg{q#XSwUQO< zB_CBg*%?DH?Y!6k{YBMTt&Qj*8Dn(ZYh!_>pdg6h!$z9htvE>)GO9UCB9biaSGSWp zM6pa5bNsK)WR6DUnrVH+FW_EQU^<8Q_3r+*JlZ+0S6CPfnJf6WM`8P@S zjG}H58IJ@vKnS5IArh60Iu4kLXv+bf2{jHa3F<<_LJ(rP_ApIJ?EyVF-*{rl3 z-gZ6KA9`QXlhpdZ`Qm<^uXu=K1~E=P#gIJ4{8e`D%G-Kx@_az@-eTS?NYd-}SidH2 zz0Lqbexncye|+yK_2+jv-xLJ9DK&Y$y@EVnud@cgZ?E8;d+)dV%ccns3M#QcV?jLJ zN9NWG=yvdPJ8IS&PnTMPK zA5Se+4vGGXqDtLs=@O9!IeZ#Xe7<4Cgx<|X=bjbt{6ncD&xIA!1ZMm>^2NQpm9qWI%25}u8iCv|^AT+V`roGB z$yUGCtN9Ack3Cd^&kgPJqZH2gvvKJYNCarK_4O;&AAIaLVB3)HGU?-=jR)qrb(oNK zxr^UhP!=l}aHU-`k(OpzgHPRjSU+r32ey`4!3mpX_yKf!O$@?C=@pwO{mI@N0Z1sn z1zKP60RufKnoN1Aip1;*d~*l~Sm6aY_3dIv)}@F7ScD>cXDaYCIZN$LOnwfNO7pyk z`+U|WR5Tr+9m#Jwo^zcfzZCFawLd?L+8hYL5ZDd-KeYg4k=35U@X4HiTQN2`5Q}Dj z`4X|>zSUDVTdcfI1z!#@ha&AVSSu?7_B%y+zHZKuw$q_5jSB%GlUR2#@*cVp& z2o2=};qO^ljJvXCJQS&R&w=N_{2kS}30_0g(@i4^j^jfja+V%oVB{XsE)}}m2pB$8OoLEV#`?KgrVcnc`Eh>X7=*WnLW{!79U|{%XMuDocVk++x zwa6WRLix<~s6*gea1*m<31NVnG91%~9yv>vQ6SqbT zPlam-H1a~!5a3jJ$VBeg>X$%}QVa41{o?28z&aeq1^C$UtLGpV?yPtH{FijQWcwbrxM$33$ndT3?B4gJ>W@Rar*bMwY!e*jhtw8 z*2Sp$pY(e32SQ;W=`?S%7Q2z`xbC*SA^_3-q~>wr`YPfd?+ZIWe4C5{c@tsqVDj)A zP*y0P#!@AwYA8aBP`~57d6lwK|CT@2KNZ+jFWgJSU>h+4%00K?a(b0_+`f71?8y8% z>z(P{)U{e<3_*^<$n_k+>88k}t0K@T%uM%ZTuvZ$Cbd$sUmc&6?c4Q3bRgp!_@VH? zBVjh(2c*AopwqeH$YXD*Jd$VMIm3OW8Ti_vP91&0-%<8%e|(r9KH-H)4;~(W*zdC{ z=sEV`f+d`l*(T1#NM0p!Qo$y>Q&P1~BGqzC^Emi)?21(pjf_GDa1}8U!_a2_EGLJi z`tx_jtu+P=g-yTdT^1{jKLC_(EqfbuK04yAn{%*|8A`!=a#Y-U11zza@g<7hn!g8p zi}!MRwJ3jj?qFG?KTddB^Sav%|1s36mx+O+_a&U8VgO^@<2`|0$5`eLd z_V51g@8Ywc{VYEF+0WvYS6+F-7=C-hhd%V77*zh_FMk=&+}vQhTuS^iuhJ~5lX+0t zU}lk;=Nc#F8L7I#g1lmvOP<+n0p-c<(W#JQ9d3JC&+pB~7(w&gQ{o(@xUXR21pfOr zd+LL|{yBg-p7lXxr$xqYS+~ZdZ&-}J+n}<)lLOTPj9A#|3#*&+a--DHwEQ%toY4FS z^C!A0T32XH#+p{HhaIXeiu1u?VA=Z5cdZ&n1ir;jQ(pmGS1MP!n_uC4yKTYG;^R30 zfR4Qr{jFaFIc~DGhRY?l>gV$Z*D(m&uI6hABCZ|22yCTt$8H0(b8fSj7~Lnv`QUPg zsL7b`5ZpYEeadohn_2p?`6sqEbQu{pr}w_^V~cCm-12%FSXOOgazY2X$<4kCwyojP zH|S-D+JG{2!=}x~zIOX`i`(yHi0JR$vG-iX?K&iLKFxM&9Q+(pz+zANDL`Sz@s@Z# zoUBa#&5C1YsH(I$F_QjrD!4K*VGl$+;>k_&Yv#mx`Z<)?BZsEOLB74<;l_#6rh#e= z=Th)J7EHR3qMqw0!9reP+y=K%8$)PwH9;oz3;N|;jNps{hbu>ygq~tgQS(h!fDxeC zk7`tgXWN{3zLg`80So`x;4*Y<#fsjbGPZVzEg%CWDgEPpppcTl;A7>Nj;_t*VXSnP zHu`WZIMV~2BY1M71LtW|WZKYlLsLcLbKm!0hQjY6wzaH4+St-P2cE|_8EON#cgBiz z-+sYY>tq0A{Sb!U+MXHm0)=KNu*a$iCD#@H7za`F%NgWpuoSUUlp`bh5jFBeSrr$ZXnzK&-*j#}PsA|Dtl@3q@bBG&$xFd;|w zj322-XTx%aRVLe<4{H))F2;3*N;-HQa5e6`^xwKis;CV$$y;cQg>fhaZo_}TmwQkab`W?n&`yU2HD+y^gn;6-hA z8Bj%hTen&gJ2|rfW;+np_jW?E?zEZ19t?u%RciK9v!|UDNm}>6vW-0H(Fb{5#@M%S zV;+Wm{-&Wxx0T;SE4X}QpG6EZ2~xH~&V8O(r3@4D%2bQ~y2akHm z4@laT8sdch#IK&i^gSX+whi%XADaQZ< zO~M1UBTy<`H2HE&(n4u1$7+{BtWCK{=UdRkXCQoM-~L-ddE~;1^m8Q0$o;1$NGhFa zxkmpg-7os>(BM^?HN5-9cO7?5I!t=y0p^iMzDAcTiViEn1)>+=t?QK44T%KL|c7f&(PUvr?VUs z8<>-1jRxU+j6q1%o_621r53TBWG8$*uM&FP?hCc|Y`0CNwgaG@)H09nV+oNj0J1%C zPBs@=XYU<5Fjf$A4kVL6r+u$s7MzX)|0c(j zx8OHip1MGhJIU<5W9tojOpePwa~}1bYMmf4{&Cp+RWqFIdRVaIHZgdL+@0B{@^Sfa+Lv?W?b2P z2=ZIXBH&f6GVxvv9!38M@wXkDD{22zQ+8y)EQsn>r>LO&_cdiCBzaJT`@`$$I<|W} zZ1D_N>KaU}!Sp0trX@vW2xLpaRR6P==LChn=SG12h#eD@^O69W9GKF^ zEGZh>(J@3~$XRT~6=+EuMNqbVDBp-MUFWm0wC61468w_$!DYKV9qCceIjK_4qXSbC zLDx9RqI6_^eCyXYNC57fE71_1vaJW!p? z^Msx<$xiN5ol^NC&L^yDvLX4D+a_fHlJ}BF+-Zf|3vokkR!RL+6TS2;*A@ebwOR%QK`QSeVYzv<8?H`5D6b^mi|7tz=9*BCvcCc;BNnYoWeZY7U;{ua{ zsH)?HchA6aO9yo>{ea>;0Vsk*s&bea2_9uXrAZ$K7)%0h~3dj474}67EYtY^yEfe2i`;mMKC>qrAL|K>k=eS5e9(Mhj z*s10aJa^^&Q|TH0xQuNvJ6hxz5p`Spy;|}&4v|0cU1->NSEqXbJra1`meT}1;}<<{ z&Spt{<^+PrOyP(SeTd#dbgbo7!!}VM{3aFP^;h4BC*S+t_kPQ^ZT?PyZQJnj%P-?& zANv^I{qA=^X>`6}@a}iN8-Md}{!RR~zxLPg=}&(ezy9mLj<0>~Yma{M&k4Nv;)}R* z=MKL9^{)c}zV@|O@V+1TfwF?Peq-JTt&X;<$|L6hT?4Vwx!vnJyQcI{=IMD`XbnA- zN{IX-)Ay}Ot(FRE^;2o-I&NfHb9kjqh&wE@a}sbTU9@JU@MKs`Q~Fd z)_0qoU$h4XK?CEz7v z4cpd(pZ#0VWkC%~7xitYmBV8N^Vr&3(a=<|ZRAH$7%-mrocJKc zQYudF&$;g;0_nhItkNf^J60A*L-9I=TmgE(X9^;>1tDW3Hh|kYdT-daj=pbW ziGTD884YGV=L^!LWe^z%1s(KmNmdQ&jed&csbvc~1%0E>Iw_kp#K{hRkDe2skz`8y zHeWylKA`MnsZ*fab`e5@$`O2#GSLh$#=vqtGSO;4k!Y0xI1j2qAGqu))tl#)FXxi2 znF>Jm3~5S+fX|Hv$qpfrEBuVFZB_B|!SxnYR$vB=dHcvVj4w938%vPHd zf?jvS7+B6^iR%ueO$ah%vWd@y=u&7g+3Wh#8pbl~LTY#q-OV>AtwH)qCY%!EiJk>& z=ad9mvn}pSe;%Js9a0i=h0e?`D9`bcbwM0&d3LrfI4z6pVDul^{KC6@Qu~x2U><`9 z5w1&oQjKTPsPt1D;S=J9_tK!y*4e|UplK6a=ku}XmRaZy>cuY{>ry(-uPL3(KF>pe z0*vvSb$9;DbZtK*{j;o-fqE%b zK+Na&S>8~!?^8ZuM$< zd1N*jOna9v4>s9PNSgGblaEZ6y%C|1c0H7+v;%ilUH+$Bz#malG~3*>km);Cr8L5%wnfh)#l<}p#lRwjDxqrto>FmQt ztVb)8b6~jt1$}rarT$Z*1Em7Ek2|P3PE)2up~22xz}NGQ{}T5`5N;c zuAjuHvjbrC8t!Y$&-_7S<(B_uQ%>flKz@y3_H9Y#sR?1L;@_ig&EIq0N(d1#&YN;F zQ#td!loWegGjob4OADT-w%~*sB2osGUwrY!!!v(gVcRx*>xqMi;9c)}7e4p7&jA3w`n9j& z>8GCxU7qZ|K@$X`R1E=^UXKWTEoQ# z$uG8D_N8z1*@-DPr8cO%U$I~Jm|z}$r`5l;2DPLq$CzbzG9kiv--n-QpD7dfvry#7 zTQ~AE8?XWW(nlL?aJb6|PjV{2^Hu}WJZ*r)?VFt!iht#9q9Q0#k; zmCBuR$VV_{P)02MnwaaLiVm^uuN8>zA@Vngl5FYzMx8wiE;CVbWgq*vf!^#jM-X&$ zJJN|ewsCwXc(l{DHxD1TL(x>xRnWunt1fMApEqQj)VP#fRJvYD8q(qobj@g;I^5vF zXo46}wj@gy&D%X%p)yy5vB!US>{+*XBGR@Wa)ZDFTs(tm9$aQ6Qi$$~%aj2REJo9{ zo_he{3BA3{t}wv{#*B>yG#dojHg8fxZVN#xl?9Pp4TBhMMKodz=8c86O~$_RzF&Qv z;l!)0I{IZC4-BbwKs&ZL_5M)~#-`&$YtII9U=P3SWs;*Hl7l{n!AV!|q0G4MTMPl8 zi^M=S;OC<(-low9mb(yHNC8Ds*vbp#ijnxHh6T|Cx#FE26+aK99Q}-9=t2Ea1DJDA z>d)KsW_g~1*8DDn8SQ-^-xx5Z?!S3vdTeK5-!mzF$4($pS*Lj&wCS{h;MPH9Mt!0O z4=RUVF2{aseDDhk}E8E-;X;;WUR zK#4_zZ76*T;*Wy(ksI{nDg}pz@gmtU^WN*|pp1X!l#(PfA{6`cWL2<|xkDW_dmI85{zq zdr92DZKUKm1Y;K{D^-E9^`+ZvVHQ9pcPDiCC@1IM8A>ucO%hDxrMw$5ZwQVqt%0

PBK^u$Bzkg0!0B}oaZl<|HXUF5GmCUD6@dY_A_zqm2fZ$ceQJaCMY3$ip#O0v#GQ?T{(R(^g!A?SG&OzOfIs+^Q7z3 z8kCZd)89NOS|`XB(jonZvsg?IcySzawBsp5Gpq3L*)|kPI$>-9#8GcnK|kHkLC9yC zZ@KPimP8IeN9!(OP3r2Ef^(&lxZ(rrvv2bI6_m9OCGr=Eh#tLyG@nEdjCpX~Z% z(wnSDXFVJGiH`tPn|jBtccsB(IzsB_ghsNymgfti&_=mqRj031&cA(^+!EVtF5A3V z9lBkxT`qXaRx11U$4wI9b3Y~Zb@`kaGS+h%kG)jwd$VvmIAX!D1EZp;FDARA_DsRU zcf;_Qg-h_f;22*SxoI1p>lf%nqdbAfBx6AzWR$t-<}P~LxDsz2bAoA)*cQ9(%jURm zBp#D{zeN9gd^Y&dS1P~x=9{>G|32=&@dn0K^M<$P&->|*i<3g7IE*~<`(T1>M4(DygfAGj`+uE(4@upv@cz;U3}Gb8>_H| z2Byhod#=qVeD408<=%1KcRaY-j&10+YmN(!@pcR-n=Q7TMy|GUd#Ba#**Fs9`Iucx zI$}J~(QW%{wA=;Tq~YxQM(#Xtpo3^EeJdws0}w9ctU94WhLkU`BhT-o6Rn0{b{Lgw z3Ue&LK65xRHH^d7L@u}NtO~S12w_=sY*)rFPkO1Z@D|DBdeFv-Q{M{Cz4a0a-GYMk zhybzu4oURstR)ua|DV0L_qk)a%A)206}2ONKP&i2+Nj* zj7$I|{7(oXBg+|%k(>}B5s@Gf6A)My1oxc1-!)wXT~F0hJ+t;X7i^#3@7z1D);qIi z&Gbigb=6bV-PHozL4$#XN@gkpg%WL_(Lf3}g90_P;u!;P=ee$dP&x;2xWi*W`2Yu# zISbg~;HqQEOn|o1ri8!%4rDm03YoA%9po%y@(5RMDlfaoN}DCrrE*9sM@W>8#mjYL zj)dN~b(kc5Z=?JURcaK-*4|sD`8rBEuzp?oEcZpQBmuElVx^wv?m@;7o(F zw19ehKf5$%B}^Uy&yrU+y1eAaX1u4fUef6U)zyG|?@}dI;9@yp&gXL}s^@bltLIW$ z3&3zDU4*t6N1O4jikh@9cv9!ZdQ_6LR{Xo}36`p@=$4>w`=rCjb9LdALFF!E7e7@< z4-Q@sgjvZlBkzQE=vH*fBW+ujkAx?$6~60ozv&NA>Xf{Cjij@EWAOJ~3K~zDmR*bq_TjUu!zQYJF2^xq<-%4o{tol2#zvTXq5yj63V@FykFeh;MVRk_CgAi{6MX{9q% zJ_5-7H+#P;p4?Iq9lg=wB`_2scw87RTg&uJP<4_I>rDc)wUzf5mWk*rgYSflK-VMM zjv4}+_35jX1NZQmEyo3 z41~VkAd>;PSJiG7*v+8w>PA7Yre`f^DV5anwquuZcN(U2*mYp}KD{id-#xRQqaG;{ z;5X&_8b`hxd@a?8mhI(YBVOde9pAO?An6uCbC3G}tHR&ZrRCs~u5XWhRen}X5Zw;> z)v%KV@_WB^H?VBmgKWEdIey8bm4&6fNz?Z9R^Qntz};w#JZk!^ZB^UrbMwn?@32^W zM*4N)La|||=%}5|(w+(D(quTibGAL>_cNM}iR_CV!;(=~sZ)nJY`olN~vO?f@;lDtyzAb)=yc#3-Q)79BhXT*es{xofEcHfmdBGOz1_=OJj+k z&?3o$iJ5kMnRX`cRol_dOY8@ zKcn=WiJ~hDCE8QU745B)%WiZaWvVIX=RO#yl=lRrZ!El2b+M#x>8HYZK)27L`jP(g zz59*-|F%!?pY`#xKl`)z&ENb@{K7B%!Z%%%Z|oRj;P3w3zl&e~@Zs&l1@m3d^X6wLoWwrrS9wZXQEWisk1o@^bcgyJB2SI*NPnZp zpt5sNkrT6AbX7R?xSH0@@Zdo5CG2%fcH_sSJht@J7zd6=tWcbu_F`ZH#ra8Nnr>VYbkoxt6m%44KcXsTRX@7fl=>U3@#S~3D5V=lwd{gx!m&-&^o}Y;0CnZ)Zk%GJ4m<< z&+qcT_+gh((-#k)&nKSG_c*WXV;r$}$2!k>;5gQ*#{xY4TKk{&L)P)k0p)o!sEpbh z5lWx>Al{h_bJ4t6UNWbqOnlzY_MHG{noPTG79kxGt z1NxgGWql=~@91}H-*?_qpv(5W(4Q%owKCNJU4O2N%)8W7o*>($rRT^trVulsMb*JB zGOe7`tuzAiOEI}#7n?6VUZevG6iwxyoCGt5{}TbWW|?xd~;nEdqxP}tw&Gp%n3rfE98bn!)` zXvY@Dsi2U6;d|-E&&-lTHUX7ZPzB>t)Gqk zNz`!4ka$`3oNX)l@_Vb_76KcgiM(Y}sb5r#cM@L6Df)reJB~g*CIl_qPTWV@8x+IMn$->Z9P1-3=B4JQ@c~Omh_aM>1n>L4nYG9^ZBQ@$Ct_DO{f+JU zq61lFrOVD4wHBdYb&@_pdB}4R$wR+zf{zhDBu{#qINdUk_FFoI84nEY_t^CoZEmEG zsAy7cUOX3^SnSw&f+dLj>s)!09+ztrrt8>M)2i~(FUYz9sseg55g5ZD;REycPp3yG<}fS(+_H{)_8dKSdD4V*t9aqsS76 z)T{47$~1bkvSRqoKiWUI9vx)ik($tuN#(WL=9NO0-gHP=T9~v4^=j!wwb3eR z$N8M?-gm$I-An0z5{@wje(l$O4Zr^Dzy8xbrQ`VC_r8aJ{Ez=J{^sBOoA?j^;XmMi z|L_0(O&94e{LudJ@#9Cly}gx;15zeZmfT|{pk!jl6*-|eBhOBmpeFK3WI9YI)B=hP z(l_Phr}7xI3r7`q5rD-?SwQqy`2GQptt+>cog7%UJ{Xs0KEr*OkH-pq{diyzX`;z0 zzoCoBTQS8!WstfR1CAMyTt~LS46HUq=X5`XCaOBM>~Dsz&M^@bp5XHbyjzW+I0otKJp;F|2?Xb&wneyjTc2|tOZnPFH2~lz zFq|u%={KHHC*?#IeurT_YFz65xVge#*Tlg5&E%r>0qKOuy_Bzeb(S+soEZvMT$A&7 z55($H0|D_xXXI$g))%yb3dlZZY`VvuOssD@1yn%GQ5au3gy)Z}uMWP{SwLtere_rB zqjlvjl@@iBwUTZdST4bae%*>oDj58595zZBNeB&wiLC_~HJdVLZ-=pMCvZZ-A*LRXHW%T` zl`0p9zcvg)>1^RcOAyiEDr2FH)P7xxt@8o1*N|K($es7f*_i{(QBRc#wl#TGUuLev zOsA1bb`O7Dh74J`QA23@wFi~uw`_NoTZBI+y-b6tqgK+3H6ER^L>5Z>+}Bp zT)g#Idp#WUkpscwIM!_T5&L74&ZB5=K{H;t%RU)Camv8*PSK?7#z#4*Ob1i0M5wSA zDyvfIQvnV`E?e?7=;(vIra5!JMe?pEpU4HcuSY7}Xk+7MC=vrl9Yy8TK;}ywl_4C8L}U{`p~(B)nBTtt-m5w;N^M;vZT@U2|G)k^Y_|QL(kp@mDe8i zi@zzG?v9jM+w4!SO3{9oWmS;9QKg)A@m+N#FNIh7yG3XZoRPp<_jq-kP3Q>*8Gfb) z)BCyJuXI_sF&%y!7z45=H4Cc*+h`Y!+4|4+u;Q1lqvBn?x9R6gwxajiFPpBhy-dRI zDlb#tkY8z#QQs)TY7Q5ipmfQHka}J5g~xO>qY={kX~Qd!LFIWP-?pRO6a4`rZx(*_ zz_2fm7pu{}&VVwV*x;%DHTp3r>Z<}z4$h)vUr?lf8Ay&kpa8`uWrxzQ_DIeZ$|(Bm z1Io!CBj!?1LoQa@F<>lE7KIe0-HFo()h;{MC(h~bP8x;m+xvB3j2sZL)vp_#+ObET zIZ@JDa|q?5XuHw5jARZd57K5jruiCt;eO8>k7nH)J?u{5k(HL!(@`d!EPBfIdZctY z+rZ%!bxHPTJI}L4ZZWye?X)=P9CjpEUnFh|niSSYJMVTK>&v6r(^>8fb5I^6zNw$8 zo=E;{c_jt3?fGKp-X@FsQ5b26&Ozy`4G$LHtTyg;E9)ZVZrN64n#~_=dFSW3d}q0~ zvZ;MiX+mt7QF)Ni8@qE)zEv-UG26=902@2jLpbG2H@yyjJ&7NMm(+>d`B3`JRqbxn z-E|PEzG6QrZ7xj5{FAqD{1q*K@jv*I40MRoo|{oO9cZqyiC!087^8d%hKSrJx#mDG z=#N=CC9(SrFX);1@BJtdRMZ{rxx((Hh+c{s&-15)%0KfnKZD=>-QWG`e$p?Fzy8<% zI{x*){@3`UKl&s5_y7Lizv<%qg&x`;K7D%6h?_OeF}H~)NSPu~3@L%q*Caj>iDEbf zcVxP=;4x*$Sr0z`SX zJ(Xjnvik{lTLb1e!zIxDa92Nr--s{j$F)k66QbPKL}Kt4rT?w(LhUMIrfQez4x99u zw8jiGbNK z>Ei2=davpByhhs(nP!T^p?cE*?jZ^HSgE}25w8C0#ubrQVf6sZQ!2H1R+I4jW0xfi>^wPJLh8(ppesrE!Y_nIyS5ldvOx|NlAk@q za3v0-QFL`$-7@@_87-|b zEpd02CPNQ~6ER7)G<7)%ftb*+24ZF!ifWh|U?cBm_Ez*x7Kp-#9h8OK2h`KKnw7ab zvjsFU7bUM}5$4zh{34JVRzUR|4#p{P>dLwD0@KcR5+F5Kh>?B9lnq1(mT)e|;2b9~ zLLQPUV}hIab66p!~ zD&0lLv+gfwp_Ed#q6`yFx8$QW`CV93@dv9QidgH^_*G_*W}9v419GhfT$NdI!|OwI zQMSt_W2LTaI|!{MrFgdt@BCN|EJwrD;Iad2DV3nEiNtG(CD8d{^=rUe9~J2q#_eu^ z*^s@lFW{!=*08YvdhMn9+6>p8;aKyaEstMSN!Dfu);9$vzsGUb=%}xei;CRF0Ue zq6T0$FvHlh88nR<&#(+HwYCI`vT*WjHc4%(lHy7!k@~HoRzxe68VoY#LU?IzDj%k( z=ag_MC$Lr$V3o_r2(44K=Df5K>6nMfb2oc}zHD5-`*oM8H^&yTG_i*jeR<8_Q@&l8 zt=ganCVds1rk^d=9V64YZX?du3Km?V{mr}n`Fy{sP+$9eCm|=$I|ZzZjF!f_>Q(BtJ4_;Zr@Y(3 z*k^KnCA&+XkvcKYpjy#YWLcHX`uEgpl4iq7l9rfc^APgAH`zb_N{9) zz0qM{MfRiKNSPUJXa-nJ;&g7275b*}zjNPY9S{|VCC}tR($5;zs}1QORXa1h;^`xb zYBhD5yk^;;X2da3YvB!-yeYxTPK)1SRKRS&%_GVkBz;jRKw(t!dDd6{_Q^nI3#&~O zUXBEZW6>V;U6Gwk&K>Lb52K@@_(aca2I1XyEy~@6jjJCQ9X*&sBqCeodcBEU-zIqe zq^;8Zg2`8v~Px{M_NYpdjGyZ{%Ca8R-N zsUowEs*(C_90QNX10O!T;XB{?fbYJ47tiMtZ|}z%D1LjaLF2c_+Hd#qSc`eMg_$Fw zj;@F-3tPp?Rbs7NcqEG?1uxBM$W^X#Xc{?inSLwkO2qaBv0!LUJf9w0gFZ7tAQ!Zy zpDzZchncLnUJI8-tiEOisf*LM;Vz5lN}ss$i;vub%qLe`JJv)4zpd&o#Djo)zg?4dFH^T67f_HX(jq zzYVctRmZB$Q1%9Y-~Blq$B~~c>!#xbqzPBnkK8x>$d8%fILgn-wp{q#;xm)SO3#X) zYdX%@Y$GS0N|b4p0g8+Y94j6!zQM5YLCBzT(%m9C0m=pXqq7vMl!hqc7K0%L23eZG zwVo;A0U*O&cUzMbDq~2&spek>0~&Ztuo-}^n-c}FS?OjZ3Rt`wSl04I^GQiMeT1Md zkE#PD`a0{tGKWPtyKpBLUoQhm3^J2;QKJp&;9UP)uGf7}qsW06bhorbvU3ISHg}8z zI1rFM1c?K+&+&0!3>jEXl&ic~^p~zK4dIQhd$}ptbFikZc3Bs?QhDm^tOUs2w68Z;`W1%C_Wv%aSf$qtPxegaak*Dns$gMvN)Go* zTlW^!e4`-fsbZs%baQNu2L(j(yM8a@T0lzlox8!-QifvSu^bx8KX|4D_*%t2W7StV zVr)5~MG=s<+2_g`Ablml;*8t=cT*vBG)oqY<$yME)6}AuPXN}`lk+J zHX~E|U19*8^rC#R17*t+wCd#GyQZNbGK+A6kT`3!=fU!)<4SZu@gD9E+hjnB3x+UK zIy<_433Ee@?A{pna^zJ(+Bcz3^1Dg-_$fK8IzGg0I(&x%Zm|+&I2@yD^e<1#;y-|>_;C) zysequ!#eI$Ot*Ai4XPX#A=ma*dMPJEb_cl^^$U}qCno}}^E?YnK*}o}ALb-R$e`hWIGeL{6I<;-pus!dV} zM-qN%yIw5S$Msrn(bHB5b%*9FjG;BCNm(*EG543-d1*aQ2Uf|uQ4{!}y1AbbdI_-2 z$5J%Dqp#8|ui3{%Kk|D!vOmN>#Z8+WZB*J4vRDpn{i(cc94m6KhO;T_ijMWYTO(#8 zdB-B}%l^f$?2jg1?d3Vku;ZYpKur!W<|8&>m3rG&TLWmecOO)y??ghV>R#W(R-H@Z zqzg0@d*Xugi$-^JA|o1A-qN8EpF)(uMV_|p4?@ql0mB^5sBTt+@JZV!U&9nZmqn|N z=?ilRvKziu7LC$#gA?GSpA6Q4ZB6~3Z56-We31Nr>`cjKTR$rizUgQy&d<7Pouv;~ zZ3Rb@yf;@r$;U7K(l6n+e(Sd`1BX97?hiA=-~ao6A3y)|KaW5B!#~82e)OYnx=4TF z$LS|VMDbK#^$b6o_Q~jq)fai)WK{3dv;tF)_|1Tt(S8tFLzy%urtFyvRMb2U4DO+; znJ*7s9mj#U#{(Zee8BVhT|Cbd?vD5Ow~7#X9BW1MPdD!#@=R6m58v4Jr>ZD&0nxo|M2 zZ50pZAbJfbzdhdYowpA-9tYkYZ+ILJ3_BK|5c4_rzLqv>db-L6Qo$Y2$jWhE+boZm zG~roR%e1t?qS{R#=M{gfw%L4K#rr)@-J@>QMcfD>7U+X-68AwPV(TkCE%~hgadyL3AR~=!=hJA*S&MH%< zdpX97?mNBMl0(9ua$gJ%s3`@rTF)qx{zhV6>HRi3ot%4hx1bW4uaiduGB%S%n_ zF2z@eiw^xNsSk_rW;@Umz*pg}D@>uzfYSl~3=brOAiQ=jJ-fzDCda{kh4*sz%Ch<~U;j%H|rt zh_JZ>W6jbPJ$jKr8vQ`h+$3G6VH`91Y7Z4yaD6klFrG_ACd(x@%1HRBN8~?9z#S zQe_5j)kF`23HH<$^QJN-;gkbjH!k^8O=N+$rKz@E-D&+!u>IHK1GU9$f1|xBbmBi1 za9g=<_t)@B1V`G~E$tXTSEA=Jod~4!`d!WinK9pt2SS8t1E>lO*P11hzrTtb@6>W7D z&wcLL$|)Er>vO%o$WpYv=o`(&nR3xB+n&yG@;}%QAU1M^pKLa#Vq27os(!u6iaQM3 z=p%Jx{We)$y2xXvlgL|EqqR4@zEKbAfU=q8WHqME>x+Ea`@Q~FL}qU3leFO&ZW)~~ zxnG-1_&KenyvMSa94K1>y{tk&)8F;D>9f-N)_M2S@To4|NSOY8|NSTI_fVPfFKQR|O3`?JX>SCbfX;=%;9&c-<@;Seg3u2znxAMKp zpd9_LwA5cJl@`}zJ&D}=A3H~w?)fD@<)|*gimY}-2>b^x{YDt+I9(;APeA3}I`53!^^GWHX})qTaX_#cil={6sWW=F1SW*x;x z91~1Z7mLm(7M`-|g^MSvEd`g{VNT(HrpXU&C`at6+(_U+kE6ym5(c94)Qg$Ktk8ex z{C-uM3B2`Z)87i1gzZpFO#`kFe#=UMZ&t1Ws!MUYk+T5aKq0^5daGxQ64t70mJgCn z@?FcgfzR#odV~?=N-Kb+Xh}Bq72~1=G=)F9;F8}o9p~==IIvr@Ih$q)LA3T1Ff6H( zE-&+;P#ho2YZ745e~R0>xW) zd0Y7E)v+06>DWnSLDusWLC!pzz%FM&7MRH^@7`E9I+)i(#LWaqe3;6*DTSl<@#g;B zT7MBA4MgbR4=h&r$85Kpr3}1mA1aS>bk+9)5ef(-jjJw1IgmtN@W*(wli0w0{>)^x z@&qRP_9X9jpaw?`b~<9_mDj?Sbbu?bwi55j@iKmm{;4`EoJ)DGLV%4#r%>e(&lxt} z(VTAhi_dT8=4f#pP+r$(gudqf$>%x60o6a;cZQ%#SJ$yOwguTrJrNalL%+T6-tx>A z%9CTdZag~ALuA4)!o)=Zq+dkwsJJW@XDj01oO-(z0^4&#U`Yd${sy~v> zJx_g8($RjiI~CQSzhDof3ND#j(#E1&nf}?Dby^)UK26{BUTm-kkv3C*JJu{#$~ivi z@EmNO=sJYP_izVdK)L0VvQ)~@F#0Z4k}92|&bo`!wTtY2!IyW6^o~% z`(PNOjWFc(1CQen{@Ha*@yi}z%iDmm3@j6~(Kcy1TPx4@XM~=C5tB6wKbD@NI$Hon zg?kBh;{fI;eo6chd(vBk`fMA{-l#LSn5AZw{LHSeT6?jzc9t6(jpenj8SV}(SwEJ}KzL2$$VK)Lk7428C|BYJ@y%pk5NwdE zm8VS37vBNP)&?>}zG_Y~K4Z_1CxT_&a~+@BDN?`Tz0wD}Uv$;9vc#e}&)w{olu*{K=nu)5ZA-W3+qPwWu+V^^hSNVUy;bUSt){5%behnz!)gC$U z@5ixLDxV)_ZbLO9`ln9rUaP+y;i*{P$E*bs!zu*0;O<-;CJV*LOD@W>`_l>&XWNp6 z@=W`o`zx|XaB9$Fu!~)SY_6>-J%&&No@i-Ut^ne zzEA<)9Im1r$|a}wJ!jeKZ8ZiE1Ilm@q3$qW;Sk!4oK2yW(Xu(+P(qC6Yr0K0LqyBP z`jYyS!f)38cb5iGLF*G4=7EN55)cirCu=gKn0Pqv9aCQYMB7FrklGaOJ)sBxvfHG9 z6Ydltn(4Z5kYrRtCw1&a$f}WiUVUHrPXWj-=y=ci5BWQd6)ksnx@g@z$XP~bA5gj0 z*tUW#j682sNOrgUZc;5_R8|6i|JZ{m$=;c&&$Tw30^Z zz*3CB+mu_6Z#>!QW*0){>em)-%rG%A=89ji`px41Y`?64l%~8!pv~R}-Bkuj6&5>M z8yKxzRC30(M_**0sHME3+4u(Are~VNTCF~JdF}`0U3Vl-@B1lh=<5ZH2N($=XS{7u zA1d^9?dhH5@?F-n4%KvL)#Ftix}Gm-APTF|s^+!jdZu)W)ENkVniBRpRxQIBF=TTR z!hqNhCi5=URV|VKqOIZb&V+97WoB^G*}2qHmL#JwG0*cgnOu+n2R7s0Wde)r*}n4F z9M)M5RJ+pYg{!ePY$y}QN%V#hc-JJa>)`Uhkiq5BTP8FuIb04nlp`>nS6#O|uhkh= z*KwA9xw>i(wdwL(Pg8j6(lF$M>aQ*|Qm0%ud^gD{&cnnDbi68taI|ycA#8Nal9~L4 zEcAteCfl2ZirLe9=~QA;Mz7WXGlwZV-YXfm2BFWDMjG9VKX<3#FmVzmK}DAf8fp^4 zE=gU)^KL6N`IU4=!Mzj;Xu#7L)}*rSib(*E(D!i-M}|;Kqm?tV zV_)T#j$oQb0584Auv~zz)#G49j1*zGOyi-zqb-x6v`P2HJvFGjR(2=Xr7dTzOagDS zEs+i#WZ&az^NS{6S)jUSFEn$OGE3k1{2|1&n@j1xmKUa3U-GHGeaYc#mFfDsQ03bA zw|RV>U;Zo}Ux&1L$lxYNK>7MA`4=m>Uly+@#4ySWPi&I{@}=u9O2&o()b_c#=wC}_ zgYsEr_~uaSgKJ(odUw=>Y}hAtx^yJX>u!aoQq0=*a-#b2zyNTlZlx?bPrN^$`1I)$ zK7IPMaK_ujTda2R;pvQJ|IIW&{UkUoc|vq`K8c14QYNH+SDC-HUCKrZ+`>NHb}1TP zXg!PG0CI&Z=UXWjpnUB(VdE>&R@v5Rzwo&dPS&n76Q#~dRq&Or7~cn(cDZ%?)o!j+ z&PfE4l--{6@BK#bh}zJCp^D#;*tBezxj8H)9BaJX<$|@cmyQS~$wB09+oQD;>$&kQ zA9IbiQGL~83oEI7mBSEtQ}gV+lHWmW%EGHXSS9OiWc|!OO@Zn~?;4n(KD@R`+Q!-G z(7!|4AXVKa|D>$mzU!4s7+k@$%{KA7x9Zy_k8OM7C*kEir_9H*Zcv-^*Be3JGB3R?i!*$H3!p;Q4$?$-C@1Voznt7LS$6 z;tCVdxa{ibo}UVVz0w_H=``~Z1IlX>ZVo`p!h-2@QE7|#wjy~g|MK(s#Pj)%_vdp> z_Io}v%=hE*20R`Mvj7p{b@`Sbj|bk~Y6b7^lVJVMb13XcUC{gy`I&1Vd9F$4W@|El z`5Itei+gA3O+5#_3!_l?ApPK6UCsTj=bW(TiD&TT45s0t3^R*~1P{E;w>7R}wVKz) z0bQv)r(@WPfa#7EfpQ|kSVyEmgkLMiGX(<#X2A?xXG69RBVUSNP4;!>M7y-%gTD9J zZ1tH*dz+IhhJMz-@;t^GP<~82f-c7qi!Xzxx#;MaI-w-WVG@H=Fp0&#TH#E*Ogl!Z zonjHP7jo ziH8k(-Jd6*WO0oEvw06Z1XP8}&Xh`EsQW#>t3=o?zw+>2XS&_;*CqwDZlWt9T?^R>QOt8Y>%J~CS1X^GGxoWR z@{)ISFu4yb2Y^{m%e#wyO}7UE@x9&Uc|Fx7`xuEOLZOMjR*Gy9<$b+&Um>p3h6IxpIAf6B8KC=V-Ecp z)OAi6P`PLp;p(^k2}@%&*-FZDbr=igK~JN%7%E-Eh0ZC4va5T0FGCB>D_^lqOXL=| zP#ubpr-^O6RS#y+%|>AGHR+<&L~FGA&oUS%CI;62QrqMuW7jcYPPoN{pkP`~e5h2h zjIo@xiZnJ@zmzitG?V_Ue$r3LVnj<-XMMVparIN~A4IT{dBZV$4H~bs3aC^FSc|Y- z6>d!15su;&y_c9#t|vMV%z9r9z#1gM<#0#rhh-5lfptNtzto`(K^2Wd=478O0kY0k zTO?%oe^XAoB>UnZ_ZEh6YbL3r<9qaji)-GxfdZd98oof)DaFzD z{{H<%sz284A1j$3H;ubvtAunJ+;B+CI(aY7U$=PqZU`ezPPjr-uk@QvV}2W!Y42Bh z9j|HM?0sH8ZFWDl=Wnot>VX_+6Mc>?&%3;eio7oTLY7dk-NWzGw&*Gkm& zHwNHy)QV8s>JvD*Rx**o+tdXXL>bHDIl&-ZOo7cF6G883vnr|8=>Ou8Qk!uv^;pske~jAql+UPhLBJ z5S&w6KrxYqK|-K&@;8ze_hno7tZO=D5mQ4lZ8z0)*6Y9J+Xi+vd|doRy<2?9duiij z7Qxv~cwV@@Uu9nn44c0Rrj@tyTWcq8xwQv=LXKbj#b3m4|MqXU_W$Ye|91S~2S30+ z|L6Z4|MuVhTl}y8^}oL9;{3TE(@&fkrg!sO5U-%FDf`ps8FHfOEmWU0Xmlw0kT$tT zyCQY*N@HQ>_FpL0AL7G3S*Xn$UN5!1gCu&cJe$`IN2*pysD zg4P04lQPG^^4nT@OdqDZV~zDR8zXk7ZcqAM!)F~5yor~@sL^#}IKp6nm zr0E^2yVq77KBu&kOSOnSMy@W_fn^UoSQx(|Dr&>1--(0N8CBBMZ+qZ4mOt?^C;SnE z%HfMEAIofIr~jjK19>3I7~dHQv)MB7+`V14S>Ikfl>OdwP&pbT9k6I+fk{Qmk)3fKL7eXoSE0=|}DGdNcVq$5zBN7|ql zG`!brH{^G*k!4ULMhy`WoPY>X8lgSaTWllFAPZFLdf%gEt&FtFan-X;F7TZT@twy+M75tSwKfqmI4BppPX6e&E%=)Zr-tat(~`Oeb@qQu23ibx=?ag`(@$XItCt3M?M50X30`XjoC8N>ET@DcyFWg+s*=;IeYv+ulUL zdo-9jY_nk-T}`s(1bM#KI0myX1n`^>A;z;b|a z4V#vOUHkRkC(Kk*dKz{*sC+SkArxXKV7(E;sM$9DmGvMEG~}WsJlo zBZ#gB?mceq;KKAX`SZjKBWA`PN*SJV%p71KLM7&PR9zM04qM@}1}coSUZ*}MFGFOD zS7*|#WJ||%3AB8{GgsSB0K+C=L0`c&bYidmQsL>N*CJc`y66w!JF90x%m>cs^n(^GIWn?Zu69O(!~&q5MOVrF~{zd*Pw|*yQDfWL1pT zpZdAnR(FZn{$U@zBhP)P8>#T;4(mnOi?hAnc7?iOk+QM+>nay-lD!^{_BRdjRSDeb zm%HCz9W4LWc()i(%0j)wNA_aWZ=L5WIlD=_I3>TiTx+#11@wN;?|;j32^ItOY+iS# z3%2jC2A4}d-A^!5nbZbP@ASX#6qQOFfI*wH2xRh@+KaiJk3>TO&~(q;vG>i^I4B&K zX{9bu+!TG}E&)(JsMJ8-VO@%Y;>oKa0<>C4r_3_cz0$kHC=he# zO*(yCYk@W{t8}9>$oteLaUB(Jv?jKM!Ny#^0QCvvc56}=mFe!wxenT_E1y1?-1+8Z z&k?;re3aERl&vD{R{7{Y(#5O%o;KMX10~H=kW1e(FZ&yYcIDMFCBId6Z8CC~$_*D3 z4GiqliB?|Qe&4d=FaPDgjNkt4-`=)G`|0s#as1#1Kfv$(-tXbx{kwmMj~_pN)5ZC7 zJs1%}2aI>DpaabLJ;lMjun}PsE**c6Rwtx@;h(FByi5Oj8~Gc+{k~ zF=D^n$5D|aiEDC@?Zo4FEPn+6&+R|n;qDcFdpkS-V&!q-B(S{8zomW*;;7kl9=E9T zwID%;HUVT=IKl7Zr;qsf@e|(O-a-cq95I=>=s$!;is@FHt{oy`mFpY;j>LRC@9)*` zv4CTCT;uK-?ih38d7e1WXNK;cvq#g@#-%*3mAJL%^>Hk(Mf%J)xzFd1@*HoJy( zRuEx<2Y9YuK?sSCeG00%$+sb9DX!&=6Aihn(Bc$Qg&uw^cjk`C4>yAL)n5Tt6_G+<>&Y7R}3l{gTu2O;? zWONC;0FR2iG7-x+cU0Pn)QD_ZXfn{Mk_!WfK;Dk&;j~O1jnL1W`60l|TkBPMt8CRU z=}5s2wn%f_5c-|dQaCdAi)3dxB_dLfsk8m7XhOHV& zo88bUid8x8ozGW_ul%c9W#HR|_K{Oxsy}Uj+0m8}8Dd|~z0-;j>dV-1odLDA=}t4w zh|>KVW&j5o6-EDRTIF_d;Y~C?C2uLv?-NGK8C=E;stkMC#TZK(glBGNVKvOPCF-7D zS|d=~muhtWNas8aOr|f2B>MyzmQ7BUvCdN_d1aVk${?>0#zFE9>~<3@_fS(UF;a}A)Z_7J05Wl$%4^JQh*ig><1BnVsUhE`omfbI|wRZfcut4)@h zoK!uNtL&Jw6sq&(T`@~yagXTz*kxt)lab1{gKze+9?p>a<`Tfw41d{I*K5Ee0mI5H zL#Fg_>qmkTt#z*MFlDHFHfgQ}tt<@*1w?XI9clu~dPaILPUQW~!Ffxw=3MQVB9l~k z6#qQ*Jj1`_>XItC=sLh+?dNr%40MT?j8ev19Ps>=5ri4j6iu##LCuiz?pl7aT0XY`%^N7^i2~Pth(D`CNCYB zVu-vPWENeXdQ)^(?I~U>tVbAnsS8kayJ@Lxr+vUW>(U9hwe!`A0;h))T(A8VFj>IWT;r0XQ{`6$h44<)tHl-{+io#!TA7o_LOd zW1M*Iq4pV~YeWnd=3|vMLr^Dsh^|57C(07?dJLGGt;r8&SbHQ^xa*RUwg0Hfnfxw; zx6bdPqvt$vK0{78V&DZbSS-fpDnS44zQX6<$<>Bcqvud*>hl}cX)~E^tbLzzYXeyV z7;>rPH0mq_S$aqIQi@3=bDlYg&c$KX3YGF?75X|ELS#?MRb0-b|9(#d+YJG)w|asv zuB+-DPkl8+y1ZUkMcPxahi%2b7TMeBvjw@k{pwL=czxzGb-dMAEs49b7EGhbZ!d1ElG>_ws#9>3YCqBwfN?(B zjyEX(lgO;I7cF*LQ#TI_Iqo@;k!?i!7@c)it3A@G$?_y&qw!w$9ntG{^VkKGMmt2( z)mIIeq1y(10TCrah}XMA;p%qAUJkFx^HU~V&291(qiKi_U_m_H@2?!54y50KtHZiO z(-UjZ#v!3nT(aGgR#I}yUMLt@JfuXXdc z;Aj(CLtj?66^YQqhsdb?Tn}xR;``Wrsw%_<)Ys&rR^m!iZ3W^9%8SjPVP-hbQTkdZ zn2!@aN3B+-E#q_f!(!sboDUGxL2j*Cq(S{>dKV>_S4~)?v=`)!lqs*)=zW```+I8_iFS)&4 zMsbNo$pPF-Chc>!3$a#B5|qh&;scrP71>Ptc(&Er0?ft~zMg`mR}{N-Ml4p4ty-jO zUSjhA03ZNKL_t*kAck7!+1eO~wzY_01`gx!U! zm#XWCv2dvq0aSrHopsOzz|G*Yf=*{8xa^dLios7ZR{~^r>4aKAo-FIv9N8%al9>78 zY8Ih;o&;vny4r4Mb7uJP0KgZ`VlCTOVdLmVqB0Uurckv?*j8tV(&7KXK36l9x>B+W zKeWRLSgk1nlhUXeEZk9{d|_3PP@WFkMJU5NZc#rpwP{r zasej|5h=>vI|sLc3Kc|YTp|U0A57^ykjg&^EtbM2NNGgxX9Xpx`da6pqEd#l%YRr6 zLJk2PYo=NOr+rXCR}Nrq4j!g1yyHRJ%gQ+oNfBw3qUK6+p@=rFvzh!t#@1=%y7#5M zJMW3(=1I!bkd{`frx*RTPt^0tPv)o)bTX6MP=kr>qY3r;XS&K?Lffo{$6mMD9;5Lp zfLQi#Qu19%3x&M5MYg^6=S;)&CTxxxnb0T&0_Ub#8TkL756Qf6Jor#XF6P7-i2 zRO^sc%H<@?y0NWNrd6E?F15<)5Y;wX?9n;Sf_Ch~36!dWmG-S*114tsO&vT;Q=QdW zq{IbV7E;dkEP0nR^zF*)Kx|K-p`>l=^8v16L))*`ML`Y)(;lRmHZjM9&$0GLIVR>j z1SDQRRc9JwceT*6NI!V>yH#7VKTM3ZG87hr%vLK@oX*?QBbPRi{JQK!_H%Vwnr$;S zZ|L6jIs3oLeMy6%eK%d8I^o(>ssjp3?$;M`OSed^%eE@e01#!=@?8n7B)k^y42uEe z_^c3~u~Ij9m_2x4g)knzjO{rbhsyvWZqD$=Yjs|-XpDg~KI1%bjuXc?OUG^@(pSAL z(^%=({)Jt`BWXaL;=+FbGW>08dq{smt(uxC&$1zUZYiF+Zh!+m9VaH>%aP#IF`s9? zm(1L*J;hzWuDZ2;nQUVzlbTOFAFD46lQp)% zG7+CRpzF`Xeete^&KP6haU7VB19PrxI$!U?E_W6uZ+F?GsG{P&T@QEUfGIlJlQn&mdxfo_lQ(}2sI^vB&+t^HnZIgS}dE!>D?zco}?^r+S(CFkSERH(w zc{$LBkZlV)|Lr{f(Lee}_^W^QuYS|T_|_l4_=~@Y-}sH+z#sqdAAi$D`ExtkgdLPF zEp$g;jW^+1di?&}4)$SKdoKI!eJy+icSTCUPQiG zz?#u)p_97CFaeL$&!ih-rSjSj`pmXl7pGtKeGOm>Piz>H_+9-8Y$t-7X3X?VHuE<5Za3LwLvuISP_NI2Q2o;X4=Hv3=Ix&yl_qEA2u3Wyf{AHw2Rsrf~9BE`;M2o~C_ffSb%F z*7EXE@x7d+$s7!L%sCe+MhT@Cc!d<%sJ0Q1VA$9+6b_b5k~PftT<+~@dm~7tnh>oQ z!1Q<4N38^hzUl!4yD`N7E(V3yuP)c{95YG*N*;?n%JLmCpwfN9cf5q?>R{U{9ZkF{ zb^(vwc;-_bSSB6fOAiN{jh6zw5WiBsp2OA_+9zx~vpg9FR98l3xfj4rARYuw1;cv^ zTzdji789k!>#>!mQEvTa7K195#;_U!%>pvU@t6J)&yvh#6csO2yI7L<)={nGA)uf8 zpt6PIe`MLR9h!341ibHt=1iK`Ac_puW}BDrtAW4nPVm-uipK0eMnNrwZO)2D2g~-a zcJ1dHP`(Z>^Y`{1R-4T^Q-1n%X=BNcT|oIMXU&bwlh3D8@(Ik|pzT}Q#Ea)Ioh92a z{~<56jiDD;8QPw-gq7eb+`^qT=vclMX=~hsER9BVSljBJ3>PU=+P#!kDO7gzdJ4U#?rjCJ3Wkq*1H7tDD+aXz1T zo=@QEt$z^Q5gbni4>GGS9i3M7C3w9$2uG@j68Bw6-@5-q;r9O0`&<*;4K=y^yoEY% z@968|81s^s{EhDxzimR&^g?)zMTMmdU$0mFyR_`-T^y0};bZ~w@6s6q$TYX3@m;bR z9F937Brzup<$fnqoPM20s%@maS#xjsHPYJ}7}m;qJzT|Qi0e5k?D7C+^ou_5C}nl& z?PAohzpF!+24dfjz8si+P?<6o*Y;SA)!HUkV2vZgP?aN7afIkFC=wubtmnJ;1ASk0 z%ig#6oAdeLzIZhaAL5Hfdjd_2?hM>@vHV|Hp!S4fyPosJF%O(p zgUM$MEGMSAgxgi!q>+Mp1SK43Yn=|rES4lyGzyiMb ze`4ZH{4IJD$nAnO9hd7Ie2ix<_(dCalS0K?#iuo(y!yh3i9;(4ch@F-sRxTf?BgU2CuUOCy0m{y;4FG-ztpz56{=bXeRzM??>M1o zqrcs}+7H1`bt`Ib@_XW9Qc6=RnQK5ByxOEVRqA~K8=F^URPIA^Uq5SN)L^~F`NWiR zK-;|+rw=%k{@#jJBB49;;q)f{4_ByCOR^8N`)09_~fqgL$mL3piC zwkbO8dawqe;uu{gt)}XGxqsV^zxB8N7JlVde&w4k#!t-gtH1iI_;3I1zu|xUkN@#a z7v<08Xp=tk*zp0fAC{h^oUQW(LpPZvcBuH(8V0tjPr)>gO%{yIx9j4{3+=m3=+Dr= zW;o{>{JQGF@}tmC{tWp(j`GorG2n-X4}$o8y`KZb=LzS&!_CfJX{>?XS}4NnVFQOB zSJ+^{^IR*A-{0Ty@zYwR{N)#40Pgtk_J-$op5cp+iAuw=Ov`RNfhXIoDWbeyc12pw zDEZuNff_)s=(+$d=M3-d-mVdJT!V}&)#eKi$kyIfeVeQmgXc#~{2D8K_%SEu;|{Sa z0v7u~Yz5LT`HXE$!y~pPGmrh26XaTAtu=7ox00KN8c>%0kyu&joVJZ;(aw2Ze7mc> zWl0AY%eG!Rpe%#OvYko#3^~DI%A%LFT`Yo{-Qlt2TUbCT28Dh3+1-6xuv7~P%C7-D z;B(DOI18v>MlAYeYgF5RPyThu$dXCAfa~B;VGQ)PT&tjajnvdxcMs^)p+X~qxQmUc z|LFj@09PYO|J!%8S$0%FZ53of%cGo$KeU6n{-On&2S!|gr` z^iy&4xj87JnYFDnzn3$Y9l2coGO*0|sd%KyS0^xq#Qr|g(5u1Z=rGa&I)&CZZ@=B7 zZ`W(jfo0^NTdr14;|Q1n(6brHbbInr@hp#*vP?rWl)(zb?#YKaQ&x;33V4SMUWH&f zXDO{=12Y817!Je08u(m6q#9sRc4QvOebR^MC}KaN@=-X_JQpbNBJF|b-*qoT09gUlMCE>hyCi|$B zwV2Ach)tMqD+ayyIi27mH?!lLtUOkERCFfYN*}kLLZvC`HaUOEGpp5Pp7vFJ1whQo zoCxGn19&&9rB(GR%q*fKYwqV%#xRpKRV0=lvAAGQ?qI}vxgOA!m2N9qi6CwopIDBX03E>qTE)7K?A|o3T4W?Hfz=E zx(_V$bJxXad+t;S_caiF94C(BxRni*Mg2EqdyR2ma;44iOuvi(F>>R4VN6m-b&g5< zr(URYUT-vR^tQaoS1KPhfKryNb_?y3{n2Hgp~9)uKLGN(XB$v3yOe|1ms!ZMYV|me zoOykm1Lrt!juYoR8$C%MSQucjy~mt0`d-!lI&c$xgE-bVJ``NjiFmF1-ip@$U9wf8 zxvzn5N7zNAcJ%)YhcAD79hAU*@~gZt3W{5$@Ga1hls7D)6wrF_m6v<+{ zG!?FW)_Xg}vkME>dx_I2CZr}rD4c{!yh6yse5p!ycv6{QJ48!|E_$^`Tz|Uc@T&(d zywi?qBju@-O(G{!>J!HW4OsCOwH4R~mN$K&CA*3!sguiNL<+CgjO6H)zPq2VpDI@;2Y|xl$%op{>VG z3*PBMU)%Jqg*ql}PpIw{d8uXHu}*!ZuwtJT7;w>7Tak7lb}e%IqMYmAA1L{?CDE|| z_B{uM`<4%vtmD{2B`z$vg->+Iwc~L-*1naG2Oh`7Y|{~uUCco;aWD`x+QqI^mcH+z%kWuTamVZG?J$u05O0xP=x&|Hg{m z7)0jk)AKwr&lN_Pws(=`L-#$+Sjnt@IpTNLX>I#EvduPTP2DkV z^a15!(4{v$_`!j3R6XyJpBpq52Q&!Y!bo*+9(2Gx_?QGjeLTQ z*UM4ARjvxLx&a|aW*wc~t@akq2VDMF4(jmUox7OPa2Bh-V;O$~qqhYS;Tfeb$5E|J z$S1O$g88E}@o!3}vXQR8hPp2;?iOxrc$H zgUY^E1CHSpe%SoN`^qC}9MF+`&e}`QfiYr;+8mCUf#d^Y?bsL#qOJGu>8%yZ5WM+w zTqQ`%=m5 z1k5rx_Baka-X3^+`+&#e4Uf0Czzi032@q;~c(;i*$-`@K$2TCkN>O>{s_5eYLt-aD zF>MR-m1o-{>U$&gJ0L?j283EgkxZ%>ASY|-afc3p1g{k$=lbA|9`e3rHMSQ-%4C%b z411Ijc@6outvuLePUtT`AUt|kUZIxXoTEHG>~JvhJWrfrfaoCk+LsxfhB1gbVjrPH`1tWSVnFf*{Z)2| zAvJ7Z9CI0r#~M^VjsbTWpeApmk#J!(VWG;u^@II7(%UhV32-f4RGFp@#a^c(&I{)$#nocyHo5PG{E08}+$+3ylO^wMQPrA#FPPw#ZHNa$&uY3$kE(FE zNkG!Q;Lqw@?fsO!dha5H)|Wjzbq{e^c7pD2e0MmotwgSKIVrFA9*+}m^KE^Pl|szn z`F!H}eBykbdHp<3X$OH9Y9kH9m}|oNihMKT96FG8l$S$3?IT`mdsk9bvVPA2 z2?rd9$RyG^_Q2%id|S~I zm;TZ>U5uZ& zAfFXEC3u*TQPNf!Lu?q)3u5yM^zYftzHJ3RGxoaeID376e}4yj=3wvh`HuJZ_oagl z!*LuD2AiRhYe3Xddf|CK7oEo%5FHi+%dt|GldGogH+-&1*hQ}OEGOig=L%i@{{9Ic zKYhfZ9%?J-Oa(z+7F!13bqb@9+{2xeD`udFr{~Wm2b<8 zJ&Wk1BmBbV41-<%zQS@I6AIWzf-#d%*L7b2x~@m2Q+7<+H`_|(NHu)`M*4cETZO&$ z!hLFc$%3TXCUw-dFtkxG1IZQ9h4>A=1}-exj^(SZx7fe?qPfR}W6Wp>b%e1}B%a*| zoW2D-G-x-Vu8zNj(aOMn3EI&?&aF*zW8~xt)2&>BmSkqULi}d_)OYWn+9%F)KV`!L zq&5u`YX#k_BOCZqi|#~PGf$4bQh6f+jq(;+@M6?_=FrCsDae?NDjo7g6aKFbvkZq@ zq?ebyLs%9!%!}%v5Z?J*@teG4+SdNk(1am&hm5DME}b2GcmD6RO<@{IYlx}{`w#P9eC=qH6%@k|Y@mRLesm8p{XX{JwEcOVBrS!KLPO7+?CQd|e>Bb2 zA4eFgs6p#wE8%@$8;q^zs+$;aan@i<<^EXmm``8j_8MSw5E!H!GM!0S9q=00*yu%|x99b{F=f_7%OFDJGX)4sepW(hT*1IiBf zBUYpwI1b#V#p_o`rVY^n!V|D-xyV$RuDny%2p!VpiR!w%7g0izsXW3HWy{N;auyo( zJkPwZSUk?XJtxj_Vw@+&JTWcy`krf*@|;Iov2w?^2E)mlfr&QB-*Hb4C{7Rq+o&+My~_`EXJ72B~|z_vmuPkvR^ z#i3S7uwP!&1a{T9p-0h4cLT$B`73tOiBJ9ADqkOjMjtezb41U?elL#fM-s+uq+#;% zrTg2;)7y2|H3P#)!JRlN&!v<%EKR=8@1M~{FN;q*oAjiRQ~Wa)PJ|y=Tl__c<8c)3 zmY$dWdDB;!<;#*?sR6-w{kqp-uOIhrXMb$%p+tiLqsvLVU~Dg}Dji(K8SGj!t<9xo zCVi=y2#UwFFNWwo>P_|rfA({H*cf2N1((1js77DwDGJ!!bZHuq=2{}()B?RtM+eX;bzwrkU8&~)}~ z768U9-NG#l%nZ|S>Rc4Uc|Mm!nrkoOPoF-*hT-uz@Wy?MvHTKT7!t%y9LquJAt910 zI${kfx6kv$IK+1(Q8|F6b2!NT>C;Di{P+=He)$C+j|18fLXhxS7Pa5yr$R^sXySk{GmaXgZS z4NNm3^zYh`pyNW`X&wWs+9)y%Y?h1(U>ue}yo^t8jhd52+2{9mr86m*(E*HO#OJ_x z95^0zPT;Gt#D_`RWdiV<`&!Mo2BEBgWPoK8hkk3EDrKWj>)@;>);>2r0PV*by0Qux z<=ueo-eD^`JFoAJqxC}x)y!&otbiwzdOE53y;{WOeKB+qOfKKkX(;8L?rFg4K$e2f zEU?&=jf)V|vb9Cm=8x?CbIOLl^|a-oL3(lBNj)h4;Lq!z!i%#ohoKd)fE){6xF>}G03ZNKL_t)` z(usUf5M1ec=#SoKS!a@Jy03wC?n4svdOn|kt(9W#1c7Td<)=>{@#)hiyuZKW{rSXs zKE*l2*@rc-llyK^7qXp2U#;2y>aYnNB&)tv7p>~=!IHY%v385Pcm3!rM4%PV_L=ov z2-fmR=D~J!Lxg^z)t>T9dtPNh2V>RDo}jMI3KTtmtG;G3DW~%z|Jc{GuVj@jb=xSN z`SF&cFpS0281jr#ayjGHAk~bm5#pMiHD9RyO`{==HI>=ILndtnJDCh{k~UU6 z+jM)hC)G^WXO)LKrmvRr>|fgE_jFM`VK|O6LTJY-{@h#nh5optTl8VQDF4bqDP?MD z32$P{qosi@?_RGg0~SVsFML?Ir8;cupTdw#BilRMz48a~f#3kMxdu?j3Vl5f-Q&Gx z%qA~qi%|#GY}|96IrI69ndOD^_1a?)DU7WQ4<*~kK>CckL^vrOUyOyMxAKT?74R6f zGJa6!$Ua=W)Unnw$-Nbu+PN>R(jZZ9A9J+IIK!HJQt>^bfmqp?WJ`P&>d2 zZ@Z**e^}$2i`ZVuKik6Y9+L07t)a3r@0q-68=s=Fbk?)VQtwG7btxiVU`H$Oojz1- zLwu0w5HIjKTX=|aAy=d+{q?MTyy248X3~{;$&9 z?M{s}l6pt#TiCnGR8k5{Cjjs&n*M~nxl}gTDUU%Fn*Ec-vTSIA4 zL_D>8nyius?NaM2aM%YWFKv^u0#S8@leYsqysAr&=FJ*S*Mz zIj>ugQ7~kO>Ta9}zZE8-7;ESLQtJ!ZC-`5#cY)FK&1d^rHRqn^?oYV%z*N^>=POwX z69C}^40|VhI>T#1?V8B3&To&$a#lZ980_P*bmmHS0QSfgW5;9Qamt`FAee{_`%$Zv zZHV8j?3j_)`i!-`EK`!J&WKX6EtbD&E%30hqPkoT+C)|#+p0i_N(khe}F&wqd)qli}GiByuH2Q!`s_t z<0u{%pGfw((y8&p&2`Cdesi>h!8b1N$+?BK4xOHQ%!s4Ay?=jrlk9qL!T&2ZH6ZY=yT5evf1A z8~=EF;KPRx_|A8~gYQ4TpA)pd|NZad```aQzWd$p;yWKc;PLjBkz^k6?h?VPe9y-t zRw^H{sANSd1c=)gVe~qIPhvUGduk)5%x^1^sk2IkA6ulvwT)R#B(P92oRDNE5|{Q5 zF_9&9ppIY~jR(b7(rk>xzFaU!Y*ruUS(v%-{e_t_X>a|f^&R2iRX4!7ZARcYT_ltd zCe+FVA%7!>-x99RysgtOy=8Xx3}qn4;|OSEfWA3DB~E zMrZPa!1xGWH5nj7GSnTwT7A)CP^<=*r_T~JWCk5vZZs+}+<>DaR!CKTb2^5FQ+CXl z5e;CU2|`)18TEw}9dtGAb-O2ITr|J+1f~^3qnrX{;B-}q@ufzWu7*%&^Ndv?8j`6Q zld7mX=^%F_uObL*+nl;`O`(c#()K~+NSk!B?YmrC(33WM8ksa;MVMb5yd~82e{Tt` zkJLGMYZ%k6L0O%B5`_(8XE{I_EkEY-R;a{y$A`CBC~6SWSZIURL08}CRehawS~S=8 zV?O)Lm-VbESaucWl3Jcrhv2`XOoJ$S`+J0gK}# z!x+zOuL={=4b5J^m@$>D*Xx^iz35#sC!7bITyBgQu@Q(=kQs;nhrPGiwPiV~g-2w~ zwa@YG(6THWEb}8FWWYi~q5%>wK!*oFydW)P8g#G(LV^YoGIVH|YXpcM287)>d(FzY zjf@d7BCFQ1`E1xe{-~w7s%p-v{E3W=7!jEn>5zeCm9s`xj=ZAJO|LFsz9tVmZLJtv zI(&i5=i>=?;Q9I*G5|Pjj0&2(x}PSi)R3zoQy(^=Ro9HlQhUUrzlVK0hfc5j zE}Cha2gW$l;HmyWU1Q?>s{Q8c8s*DCiV?^f41k81fj*5tA|Ck{Ufr)k(thB-&^HTg9|`L`8SHRCy*3(*Ivy>&2| zHo$CsCvh#m6CTK(X!)=6h@lzbL$A$BPRRBmn4Ub7&Qcq>B}B=jrfJ(ia=m9&V3nm8 z5wdANFH0@gBp0wrW1MI|F|AiWy2wPKp{49E7P>Ey=vr$^qU06yYmvJ*DQcKpi-PQZ zt*`(Kpd3ej-U@kJ@Eu{-Z~JTBD^YwW{VYikc815KO2y|jps%y@7e7~?oi=?iR_utZ zGdhvwoknW`X4P*bPv6e*j=yn!qqLco+Mel1QT^J=-c@fVpO_yqfm?Vr+gjQ^-r4n& zI`X~M`7M5g@Ix_1_Uo4RXy>ZP;sIyhZ1(z=C$pfOSDF9DS!t=V6U+`p_l#_!QEwo! zpXpMBZE~{p%Z-j&6W^+J<(HkGOBN~rDPOBxz0`lpSpVl)0hd|j=|zXdTL@m`J0;ed z(@b0CH2e(g~u?5x}e8?2{ZfcQe3(ZX;U0#<+gTNXT4YIhkbQ8Y& zl4kC}Bm89#MqfGx#$zRT*s&(3pN8XMIL{-ZlOQpLCC)(FE#;@s`*=R_8GpkzfgQQ< zqkvCRD$f&M;Vj*k9U-)8{WbVJf6l%@Ol0tBu;FnI+0K{b<%&pR6{0oM!iM>fk)(+A zONSMXNR`uM*WTvB^Fj;W+GqXs$e0CgIwqeliyvDK8bT}T5HK_dY^Wz*)HZvJi{s`~h zy~E4PBcjQaPkfba`3YYlB3N#}M;kLJ!@r%b1*XN0R)34?GS%4^{A=kCuL=AT{YF-Q zd)Pr|L~Ye5EB@A?a=R|PTD}pfmDnJhoX)x`@1%V)34RI_4;w-+p$)n}zWmmTO^K-{ zoLDRCt@smd8(nSJoMdO^n@T!F@#pjvYJF6MmB;hI`NByOnQArlBrb|^nu!eAp8mS5 zpJ)58v$wsbwO<+s&$A&64*)Cj%-+e{(cGSz zmE*zzn`=cv2>*-ev3b(e(9!#BKLMY&VRkQv%fJkRr>`rIx+4KnhBCO?N@V?R-y6td zUm)Q_HMke;gof5>sYc!13>0$$ZJTQGatFn0cH7JvMvFK{=Jqb6QB^?o?2`` zQ5K~mJ6chJfWHi}8t_eroLhwKupAs+<2Gy7Ez;sytKi&SLWoh=iea!Rma>vqa6P*K z6cxazG=>ULS*hIGzo((!lP0Yk(k2$X-&Q&VzZ8GgjHz>;tLs}SoBcD=ShDOJ)%a9G z7U@`PH!ZuTvl_8q2QFb8P0z`{RZUjEo+(MwK;{Mz?e3RN2khyyjs~j`?<99=_Ee+< z54lQ^PW-W=Na$Yi!grkYI3?^19b#+;by|%4^g-nU^a1Qb zhfsEx^y|7h^<5K70yGn7RXY=;DHh1ynliE}*3MixTXkRr$fcg`+*t2-r&Z(=JiVNC zWh}JGRtcKxoatqe(p+VvYi-ZNLzn-*OUC)V&Os;7wx2 z3vIn$?JR7-80TfOMHz%|gyd@bKdD1Z`AhU))LZGH_=3-v^=r!Tiykvv?vu31X`XEl ztbURNGk`$8%Wy5TN!hxNpD@^n0aSPRC?|ML0@VBKU@zOa&Bm3L&DTNY%~7A}_A}qP zJ>PN$E{cq|OI~vI`oLku3n?>ZbyRn>kkc*vG)PmSbzCJzkUf}l8yois^IbAZA-jmC zo!eJs+*f~X&roK%yf1Q2Fwgaf#6;HE%~OD26Ze>qSj9`VQGlTR-g09`WHgeKQI-r=|=GdDR~ zwdw7Me!U;rhSHuYGqJ5!))ccHS@o3P)YaM%dR-EKY&s_~furi-5-|-xhUcaH6`N+s zP?u~snyF4NBFN!rJ;#NvlJbRb^IdP{ZujwgrW2SW_u0j0(PKJK?d|F+fT{$70rg}0 zSi!FLPF^!F;x?(vMW(iU&jEf8^3&&`wAQ>^1K4)Y%l%W?T^w7y*pHNdVfZ;V?$9ns-pI~5mha{pd4e`XIe!*BogZ-3}w{O^I)7QgWu zzkxsg<3Ik;Mfs5(kC%6ArSgVjR9%$qx4uxkd3%`lM19uUZI2;$Nko%ccaFd(%SKs~ z-Q}T%oJ1NW9_72GcS{IYsoGuIpl93bcv|?{1REuu3L1@L;FxPrnOOGDpI~OWN|}2q z^K91ra*S^2UW3XEzinnXj)9Lq{v1B{e?EuLef%*#`sf{AUS4t>%givxz~PSRN7~_C zoEJ7K9i{ZzsMW>1(3Wb-vdrn9m*^85fFB!H$Vi?oLyx_i#XlfnotF(l zRBPCXiKp56Ok44lV9pJ*5x1Ew8{Y?LDvK=?@0e;VG~BXJ>Q$_mJ`Ta1jqkF`ebPeH znbfiFIc|2I6K0Fgo}AbqQ39E;M_;fLg$WGM{jzg{W11O>jIP@=3Gh-3hT!8hsJw#I zZ^jO8j%vCx*G%#uz*k1n{vUgc3c$-zdCpRtbA^32#GWPw<6f6mz)Im$f|LNIxwlS) zzdjxuSRN4qo1u`A;Ej!XSAP<;Rwpl~#*7uq=>W+=JwvYKgn-6OnsoFsev;`|dwA^k zyC$UyFzqt5e6@(@?8x<^AT6N+UR&DNZ9k zzDpyR31~QPI^K2%0kXgb1IB<&Un9z980={2B$===R=9J%mmdo8%}8hi&5&7WY!6Ft zqjl%GR*h|g%KF@P%|3f^E>V}M%-=DV(YDgrl$Zh9uBzPXp6#T3R=?F@awX|)O7nU|y&@)-hQ>-WzA4Xg zYK;gLQ!@ZpSQt)nSoEsg9guJ{vSNO$)nw1ux8yv}RjTQ5KbPF}8Ivf0+^bvKZRZ)M z6CvBx({Ww3u8aECK_@@+xLg0P&_ru7LPUJ(b7!d*dgA3RbAh$`d#ey^wW5&4Pzh1jO#iSn=H z5fcDc-xHlCC0sJNR>qBxDaA*gdFORe71JuR0L!Ef^pZno(Q8gvtXQ5ddzeezlb@6h zuklY599czGb z&_MKMP$zDv@>V~sTCn{Ed8F)rI-_jMjwS)Y2{bf6-G>|${tL{JxhV)im*~^Jl zApL2RIh2n%aAt~;l^Ac-%L7%IH~{unVu6x?&w!WtBIbNACF3ggLTjr(73ZNS*~E%7?UB<4MH_>sn=e>61-IY@f!H zGC4tWqTEdUD3L>|T&lB5jtT2t(#`%V^ef)lXq@+Xb>XG<-6od|2F!41a;1dzg6aF! zr>k#7qlq37yM*?1tC%*E*fOe%GOt|^7EP{80KCb7wtYmsB)B3dKt6W4zOCa;9qQN2 zpvxNNC}#b;v%m)kSru;0}zsu4h#X*12R;oLFCqFY>=H z|JihlFdvpWfHsQ9`)jh2(2;$8_=?g8i@{}w4k}yZOaBe!kVZhn_MxA$`%#GV>U!NR z-JX+H_x7bC>QWdSz+r!O@=Y?#)`xl3n5Oz7!(f`BPbAA)SGlg+5R4cQ*fCPvx8?Ld zIma*m@-P4ID&-%ev&US9C>@*>{|=(@p`iM77(u|ISJ zl(EYIi0rt*Q>i23=XAW3Kr^e~yM0%>mV%dh_=4ROo9H;hJaNvosKZ7Nf31UXW}I{! ztBS>Ts)<#7lKRP&(7Hf`KIHVnS)SpDklJDM*&0xOygcynM;~SQ>v!)y!b^nA9%JoU zd8CN;qLr6_D{XCwjMN)RAx_mTQxuvny&dmZ;Q-pqc^@m4$3~mA?L_Hn#cSLfm@AYy z$hrodP55=4J^ zJ&{SD#6z;!K?kkVo|N{s^TZk7y}Udsy5*!#MZ1Ldp;h~egjv|O41k4m7W|B{&#j;B z2*R-ZJ`Xx6+hO%!ODC(AZmVjXEIg2*byye*5l4igsqmb`VphmlA*#SWs;!Jep&tz*>mhc*nflo%tK4NoVKXF| zj)?)%XSXW7S~ZdjNXG5n_NR)4mvQA{P*TuO0Ul@0GRv7!DeR8`3Bs20f8|xq$abWB zu~u$pSd%bVEkc9rp<$GEP1z@Hs*RBJE?(VBtoIgm%ygJ|10>O#yf|=oZRc~^?`2>V z7QpOr;PF_1m`;jx2uQY95Vs}7!yjGDQMX0clh_K|AtlkV1(VqDSOl%2X4e;f=&4vRwLv z%9g57IywY|@jCHC8ajpnhmX?JHK2SGs=XvmMo%GM?GCSYNpyOnBf+}2L+b=^;{vWz zu7Ry8Go_6CD1h6}K_5?Z4RBxkyW3pnX)9EY)ynDpGqU3>S$W3UrmUQ5Wax&V`)r}H zQxB;u6&};)YI20oP@e}v%Tg2ZH-nCfyNHP-zny{__Fb><D$J8t+S= z$lvw4^|h|Q+i!eNzE8x{z<^&XYFE5)_saf5Z4os}_sf}SXEdb{g4e+q+6ZbZSZmDQ zmwcHz_1{qU?&|4!vEQvM6AQM|goN@<<^>p0uBquRbOLvMdGMid+Aly3VdyPoeG zurNhmVb2NOignvQxIn`xaa$Q#a#QOo_5J#-u#KkK)35W&E9ncNH#|d%D}4*iu0JmL zSdLqUzb?G2_O-)hPQOX}g^fF6@*M? z@zF;g;g^2tm+-fL`?nvuC_j?p-MbgOyn9z2U-TQ%*4 z4Vzx|z9oL4406F9JIc7x0Zj<{_L|GvOApYed!FYCf9=;jl|^{MY(-PV!LL`@$`@IY z7IRR!eU=Z8`ndZAR^7P*+Qy0q@)CP2zx(JT{GZQ#4j+H+W4x1<%7^&d(*_OMt0A}N zc>+1u*y#sAbXK*E+VI&%=roj}e>6?zvKVo?9qu7|^24NffxmN{Q6j1z?FPEb$i0kf+4Mt+(PavPswj zc2VNnogrsy3ySS>jRvY!rUYTZSP62&Db7{IFaRgh@a5>`DyqFW-b!AbZVLyx29}T5 zQ#tamth#gkfG(Z!Kkr|m2_tg?(o}P>ZxG_M&)pmD#vB`xQ zv_-n;*S;6Px#x zJA$;X0DXODr$Ph71F}C3!?r`hws7e?YFBMO)4Yd1S8X~?Bd04%ErLX86d!a?qScQYWzGbGZU=*o9^-+^E1G zYxU6oXxF>ZQv_xCT*`XS&s5OI7%||>P;8@SjFt9{hC!woE!KFko-G4h%epvX?Tx`z zz}4WwvpYax3h9g>`OARis_#Y-DVC*zL;@BaRHj1C73f>~d&1>(nK_Wa0X+`X9mf$M z>Lo&}gtDaLCeFGaiEdwTo+k|NOTnIJwJB-hB|8BCV_-M{j2wXJhP-FLB_nfowhCOo zw6)M_dqx_518#cu?sVpAW(j?L8F)05vprW5bTGF&;znLX1`CMVXT4YJZhC46%?IqsP@Ze*7XH07VL?G zJXiX66I1)E&2EBoFsVY~&AE*K#fLiY^$ z{ia&)B3H@j>)Pe}H)V8YAMajp(~k=n`QeW*e({U=;upX8p^Ndq$9DY6ul&kS%%Jj1 zgugEQr+RQ5+V@NaUmU*mci%3qqTt#`3mbQg)pb0JIKc_;>-kOoryMJv1*JM}Ke*JH z-%sybfSj?fvb$l7kvED$N1=@kMYsFcX+%!+GO|i_vb|Uv9{EEKJjXXr}wbW9TpYMsvNz& zR*RU(ayGkd_@H0g=(r-c)S3=%sYxGPt)}hg+!{-;wVtCyu-H$R>J+ftVi%5#{zbpD z+DgQV>ou43rLWAnq7lo0vIk!#JZ?HWYyh(y`6MCwOk z*jR<_?wDh)sGaj%^`;yUm0RO*#h>67k5+A;z9aNSj=VPEr9u)NQ#`UlYE9Ieav#^& zo&|2MixJaKk~FT)(rX@dS$P!*T?mp@9>ZV_~u zS;Z@dg*tn$;jZsLyMa%@YCCbHaOLyytVge(r{SRnKoMm-sNA9f<+ABz-`{DS@bcq& zH~(n~Y}+?#_V#dckqDc-9xa>PKYP=)9CV>!wUK&D7%y*+zh%zv1SZ^h#i&yb#YZl=t&|X6WmERYjHE@>-oxq;Us@!V^K4 zbuPe^JS(!T%0`*tIe0~1%3^O58rS=m8R}U^DHQ7d`hPoA3@Pt|{JZWfDzkLcz?gGQ z_}TeC^(~UN)#+CGYQRkfoc4FHy)Wrje%`Lt;s4fAGNSTGUK4&??ahX}%IdtdJAKq? zJ#1i(S)sCFfa$*EH%=mjI^v&AnzS~LlN*L}3lWL<((0>eAq+5+Fv=bVORZ$(KBeX^ z69g)uiB%n7;?%T986d}T0KR4@uOb&#_2@imfB;Atl!lKQ@hDbLMEC|R(>Tgx%7K?S z8ujdDuvz-~`m9Mb7Aug~HNdGPs~tv8jvOA7ZH)En{abtb>Sh))qk5EsdJnPv1?ApA z%EoBtj??73Xqf6ZSNU6V_AbpT5!xA1YSMYfP`!`aO%iJNRNv8FB;WfMVp~2w9VgDn z!&0VXRHxMMofaN_QK857Xr8q{wL|oA-Z#L1^Um8}|3F7ChTSA~qZmaYWivFpLiO*S z#wI|5FH|SP?+2CLk#eSoXY-V!U5}aMgMRIPaWv71vW|s6=A=B`!`NTzm34LPU;J(- z!DgFXY_$!`@H*4F5y;ALS9mMgTmHZQUF+CyO75)-6P=v8XB*5?{{qli`t1^QT3_{} z;HXbY*DrTPAGYCMPgR|iJbGWrt-g?L(%5Xn1zSHz-P4Wu%HK-&R&@Q7J2Z|uPGYT9?#*zctc zbtSA76{L}Eg>gjZYn@DUzal7B>G;6lKF#7td`sl({Li#iPLy$D?jy9-Fyppzb-I z&*{6k19F~KrJve;U<`;p-u%@=q};TOhDf^XxOxq&)@o7%qbMH012~RHu1qc;)D%6mb>tzc7J;7+dmW$t_XY;Mtc2B?Z1Ge@KO;EiXBAc<6#E|cZ zW%M47J_}FxOp%P#p$}~PNGi_+C*%x1DV7cC0C^q1<>yX#b(p%&9aQNP?%qN+Ha|iu zRIx8-MWji@&JjR2jX^e0K6b-}($L!ANgvh~NatYl_THBD?t9;K&H5$xM9$e5vTAMT z4;uP&M2J6j)IR(3j=j_=+ccrz^c12!6-WziQlxQb{gCyPJd|I_!;OCzow;hcIX@!7 zl!fe+hBkBEwsuiLXci$DjG<1Ep}=w$EQQ@RINg;h>zgTqjuNxlpKG8L3sC1~WUL>c ztBN%=8?dgYJKQGb7&!sqYB+0uybdhqC)=6M545sKAyG<`KdC~M{uj?%{ItRvuDr)_ z;02HnAF-Nk9BXiytI$~%qAmwAEM*v`6u90SqZl9?{vxPS!4)4bxnpw%4X%R66F_wy z?fuv;?^!q&a$kFC2L);+pB$%GxRvMgiSvBo`8+e!Udo09Y1haRYhUHZ0wr?-XUTss z1UUbErsI`^Nwpt@Ol}dGX$I(i$8fBbl$bat4P*kW$CjVSsC){QranNVtO9I>YR=-91|16=|EX>q{8U;bm2iOCTA6gBp(=t zF=Nm%21Tw4vS@fWU`82X$&dtVH%Cizf2KPln%pEc{8*D?##jSqW3KXBuFH>IN$%8YOFNEkAasNzl5YMUiC3p67*1-{jM@t8A z6+1bKbs57|nM3!FOI|k8y)c03Gbmh^_~Js(!ZeWU(v>J3nD1}DxBFQkl!jiLOun?* zDrSt#7fPqrj`wZKHrn~u+Y?FI#7C9Z1?XP?tBm!&1q-MvC|`rKnS9zDq8F<)R_&3J zL`v+~zU>kyf8F&u7)3xrZO6T5v>1;cvw&t&AJ4tO;{r>*OqYtG*5tevrI7 z9)<8`m2;oeoY*@Dm9O|tdRniK{&rO-?Y@QX&Z`0e=7nJ^y)6$d*sUzE%m1w8_Ix_` zhCFIWJ-qbAlDX9FJ>`-uzJ1rTQ+{~skY4?%eA=k5Q z%g>?}WgKy*PQ)Z_#|iuyp824 z+dB6ZW=YH|dST@ilcw07^A1CV4$;1vnQZt-WC&YT%anb)wX^E!U~wB@STjbr8*}O0 zu{mXFSJ!@p+oWdr!Yx|=xwU}hzm7vvhQ*zRvlP2G2bE8#0gWmNTe43al%wB zdJq{V0>#X5(ifQfCR0B|S=?)D2Sz!3yx{Tjf?Q;#AN8J}G)*`tHL}agOAF+Q>Xrufo$jJv|l8Kov`v>{x z2EU~4@>rZ@Nt0$#xbg@wv7}7|YSi|9!-?fHn}zwZ{4Dc*zf(NEK#c*zBl69O=X3ew zpU+pkzP={UY1B{-E}Ph1dd&)^{W=MLcaR|1g72NcW3T}^Lyj2$N#uT9@9pk4mj;vl zXdy&I(#9kFB4jnI8sgi#a$UeSvLBFoKpJgb)AdSV<;j#vst(Tb`*aY9L7}Z}ByiNt z3f?jP_+knJ!;z^$mj4O163E5(Qvool!J&068<8C<+g|=_lifqPt@a|=c1JE8TewLe z_wTd0emhBbrSvL5bG%09Eq^A#FCD88&|1pCNyYcrrw^@A;B9$(@;O)lURiw*3w0NF{@=fY!Tx z@R55UyM+pe_qo2>)Auf5C4Sd#vuiW7iOhE=ce@0^p5UseHtsGR`yjyAz2Hn+dNi2c|6m zmI|`KNrH{8y^BYy@rO`T*W~qRv5IUW;t+ ze64&C@2Y&po2X;a{qPt}9?8>l#7YH*#KCea`Br|aCvIufBu=bu`nY8}FJUtBBeP9_9Im27}jcVO!3y*E!`}0!xKH* zlFMTJXkWcW?( z|C1hQ|Mq5*#_|>+XE#bcc|xw?F^Jn~GBSU~ozV^u8tZ-InytLriMDW#*P^aQVNu_%TyD*}^uDcUS33B z>HYgn&Z)NS+7lTaNwGveW41yYo7yaUUUF^wGy9!gzf@k$uSCA2J))6TqJ(-)eAk$& zc6=E|N~G&Mvr2Chrg+_@S>#&l2mZo7jGZdV^WD0n)H@Ch>s? z5IS$^AX@f`72jBW%!I5)n)#NOXW3}iwEnm2zfI=rL2aw;!LDC2wZB);@QRyf^f|Mc zb1o{Sn3*7z8_f5{3+V9 zoZ0lHXH`#U1-!0J_VJSeX0x^0dm4bScMOIMvLO97@k)eex5`FV`bNL8kWLDWTqx;9 z;kLSbJL~gXOTd9`K-pvKBknscPHQJ`V6)G>$|X8SB>~ygtBPPh^=t?IEn5QJ#oRC9 z+*0kTYm>jSy+g+&8#^?7Y?fd2YWB}P`Ay`nYmaf{aEu$^GO%pfX{C%>UN2;&X}A5T zMW+2E9AEwFSMl!MyANHAANY7apZKf4`YZh9U;ZWj<3Il6_L=iM@xAYT58wOV_wcX( z`mgxAzxz7?z?Z)CCH&TJ{T9CZ)vx|f7Ws!dzWn7c|3nNbGxBN(r|kDoAB|!kDh$O> z;V)3zlizpyh>2V-n%>EU58SfUcNtXM38sEa{e!}y#0D%s_x~}#rokpKr=<_TYz-(^ z7-i;TIL5$nJn(ot@bdBkvlVih+oC3}zvv*LTb!rI)+5B&fs1sZ3CFHhS!ydgZiAE2 z9QPc5!Txa^4SSf$gtv@R zN>&rwEb+-~6CkJD*PhBXIKOnl8cbIFjHWNQvtn5FMGcpFxhQHczUDiq>xIi!8j0g} ztiI{2#8XuKVxLzbE2Q+uva0W=yb`7riw`yUj#X_URwkdZ!0U65_k(a!nJ*#657r?SQ0|3Kgg~xLs!olnVwd(BZdtm5cawWn_(~Iv+(oUc@ z*W5gi0BHbd7A6j{?i>S<`>DW?v-ZjfN5-k^Y!K8wlKhU1rX57T(?;z_HhVlIfWA;5 zTWo8zU6|F(zx+mwyA)p2tA6dwHt=3vz214g8OQ>lDJ0sgn{wm=h=Dj?Go#s2M)1@` zcF^5Wtn91vs4+&vwAyfUzLMjl<6HlCr9swT9H+2GL-J}fno__}C`CIZnXAKfy+mFh zO+&zBL#@Pyi_vhQ&j?Uo(;~93dDV|OX!{MbC8V6#wff`VJFL8a?0 zjJDr}4a_!Fp<2du4kBAAE>i`zEI{d-%xhw1*l+>_bfHMylJaG>joH9q0LDm~ndIB| zjY9QpC7crq#elMNU^#vZ<28*!Q9#vcM_y;&vwA<9Tfjkr#l|lvxqJD(zU?7R90w_< z&Ipkm0C4)m^IU_TqmsjU1q-6BkFuU~;V#Ed*T(q=i- z+=*;k*C$Zuv*c6DT+5xAftkDI@LIEn!3?Co#begQjulbuUXb;Gg(EEc|h9$7;_CQk9>2ajAD~m zUP*q#Qirm(rGp{YN|&Ta_N}|EGfvLb6@oTYCHP4{mMgwjyU%mN$ISO#(`p!s_`{Np za$EEik{3S<&dIvt&FjQL(07qEqi%smHfEYYR7a(>j9Z z{=VXXd{5{^mQl>uJWPfV;8KUx$R}T za)xEO@(FK0XFqG9?4g)cofd)tBQf8uDMy>Fbwepc`YUS4HEebAY3pm2NnCUoAbc0I z{t+Q;VQW>K)U`gQreu6Lv&k6#Yoc~stP za^BD~FTETbBl6*zw{0Zbci@%63H^3XRUO%6d{1bvbzR-GS8JAbeM9e-j^}wV#BS5D zUu0Ny@*D=Z|EoF_7e&3v=0^WunsSoPi{mu=BB~5!yIJV?k|XO>h#>1-3|jTm@Rr$!sAy^)xb6RH`)GI7GPdCv*pTzJ(^Hkzzf)_$8f=F)4H z`eMWZD;g2^mHbv&-dAX?Z?=8mvA;tZzj)12tCgqAR7aN)ir8}EGU3;I9_-vZy^~kt z!jtww0PF8u?(Wnns;^*Aa}$M^bcL3RjS|PDT(N5+a_;&e>gS&PRb}+(bD<;o$}YA^ zBSHliD~{UjW}_=~^zp^NYXAK&`cx9~@Q^ha+FCV#)Z{G7J-;;A0(E#+R-aj`u(&jZZ&y<`)fa6U-9~S!2Qt%lJ(lS5?!|n4qpak%095% zQ21`27~pfRJ(aoAkc^sO^fpdQ6$BK7gT@#)vrQqro~eMf zG+JHiYXa0Ic{RwQ)@yLz)0$ffVf6a#{gTEB4J;~mXuAacLO8q2U^3s$*+QH4a?=0) zQ!}A-UqY)oZXvPUJ@(CXX=|iHRb!DMuHcZFI*#9_C z#(qy#*_|KyfynJupDDnC3VB!9dRSu+mBV#C#({YZ9EaoZBcJ!SKUPndgE!CTiP!Uq z_s>_nzP?}KTe#oG;TYq96M*dDuLa<-=ss46Z?eZCRm`@lG3Cpu#L^YT<2wk>L8p_0 zPir=yyXTC>Bf_2ISk!WN8Mt2Wv@q2Ya)#eY_ityPECXjSPRw&%@7qKSl?lpT+OF|Y z=K=ReCGl!y@dU#VWr-wBOA7#fu0+f@B_PU>M6uM#l$TV`Z%3(_ifo{J(*W8}CA_oi zJAHqd-a**h>%HN+LU|N;a^@+(sB77zEAz>on(|cS{9Pr3h-N`EfyKKBygQn{h{z5oj$^nPFr?klN%Zb;;yj(hI(aec>n_3s-Evi>a3$bBR0UG2lT$xrGh zbjYX_Bih{7dANqp8ZgAFH8_S199t;SBV~saTXg(oAlhnma~JIUca=u_eSbclVffR^ zCmwN13_eRusPA`vN}V0g5UX8^9P%=Y<8HUALcTTct?arQF*gtQetf-F<+%`) z4n!#qP5XrQ&1N(DE&~KJLej``r5@=xL9dhVZ}_-lkh)%&V)cVzN6Z;q_EE|qcylfl zVZ2{&hc=CQO`<6KlMh-9(4z*F`Al}gHJz zyQFxRvVOCb@8-EG(3Qt^VikOQ{|$e@T^6mKpftw~fjql3@oe&UJNMsf0F2c!VAz@@ z+H9cc4>GRSY>pIb>}%Tu%B_!SnW$ed)?Rp=lyj#eliW&D{xf~RHa_ThKewgfK%oP4 z+?7yOj@2tPc~EwvwDb+TxujF5J$E%2001BWNklkid+SOqS(U8U z`-)}dp{5s_&PL%eGdShh*(c3-;J&tZBSTuA7BoNMZb8$Y?d}wH%AaTlwx>svX4va& z8fh2$F5swv5bAdMU3?pb#r2Go!HCIc@R2M(gy;6?f8806mTs3SU7EGMmz72W2kyH1 zdd_=tSh}wUWtYvuM4CSqvgWzTq)yLe2S&eu)ubY2M+FNdNWlJKaqsYoCa@1AOO>+h zn;J1K>gt4P zfB8^;jE|p`LFF|Vd!P*@yCeNHbL}5bJy+uefnkAgh#Mrl!bv-lbfmI6^|z7_j=d-#F(}gq1cJ@j3=FgqM_Hqmd`GRil(YOob~l{iIoN5g5MXV0f@6S&SAjWr>fpHUtN1L_*<*^n3jmIN`0BaP{HEDKo(O~4~y7-3jJ=?JKt1brO;-CVz z6JDEtZb~ZJBPL)QbYW%iRP2Qf;mh_ubyF~;o^@cmY`!bbcTL-?T!P31_;Q350mQo9AfV=#Pz zg2Ziqm-|8G0*nwQYu7i3yg99#VdT9sOrKbaLLz2P=-I>RKSAbPY4Dw_mwcMtEe1v0 zq8ltOzu#CH*(2{PkP7GGD<>@@ zKCQP$ulx34KfLgPS9pvRh6EibUP}c_Udnn~lR?e+-98*h&Q-J~p}(ev_gH%mtpPoU zGi(L(WFOvUX?yg)fdj`8hJ7f}ag3t5HuuJ`$;l!}Dg-vAhzI5ir8)(C%X<@fL3W6p`=G4VJK zz#fs0tdP_2*D!bs&R+jK%%@e&6KECzr2q?T=9T>-B-$3@}3oGH(kDR@4Y0`}>6 zJQm=@gJ8>|UwZP{3R!asfUD0b*Mt?tHWobVI3uLya#H&`pT77{0HHu$za~n7fLmza zoRKNtN|#Uv4$i!9pV(KqvGK+$)=rl%E&8vnL{&KpEpNvjsedl3F{m$mg);UC_ozU4 zAbNn z#P>dxdY5O?-Dg8*&?aNrmd^AMS`>B8F6 zPup_quhO4N-lFJiLRZ0IpL$JP2mM~KUel$suaIoCwf5e10Qrjl-})WNJH67YZv(dC z=GTM{Yz}O}UY9Z@ZP0P2$;T(mC)`Ir1Iw9U5&n!5nsQdUG#25okq2r}Ih_vjTw%KU z-Y8J;*0a?QPJ{zslwp@R>a7ixmD$J;L8CGgRNspnNffMi$ahN)uv#LsVAZZY@m@B4 z#&?{5MY1hyaJ-`uHGc`3-q|oqes^pHFZ@{_*=k6!0`rn<@>otjW9YhbpIQFKpt9IY zaKIlEz+>@!3Z`7nz8AZM3f8;fC5yI`vTIwLw97S(&eUu- zvVN&RZ}&{iY%kG`zyv@$(NPnpipTak=}@!$=KX$$#yts@&!Y1sJ8hGG-ScTWRMIbz zqv5@`=cMdc8(eJxd9IgEs$+R}kIiM=0iSE|h7LE?uO^>Jmy;@M66P#hTF$E<9T+h$ zb@(B^p3)J?yEi)B^k$PgfxvR0U-dAZlbMP~1}@y3V)-fyuk=;^)y@|5l=A4G))j{3 z@?bPQ!O9Kyys>X`?)7*#s4ViiuJ3g=uP4JJKF@`Do@cI@QhBE75FfAYhXCUqevW)D z^?%}-(kMwM_9dYYv3Szag_1H+e2+%bSEW0&Me{xeGjf$OuqNF^3r_c3InMr}I)m7- ztFvme)o5O_eYxUkNWtjpZY0$^wCH71B#hq z;5^dJ?e@+SFEhel%bv=qLo22Hs81&G2W4Ng3kyH+yPWVID@b3TuXw(`;(0znzviyj z9>*hn5^be&fW4LclzdUXNC|=6bf|=vW`xJGwPhM{;?$tq7&wj*TNNJ5cCq3c#dCyT zv}4w4>sf^)ZjFC!pj-X&2tDmT5HM+3iW?SM3AMnrQY0`E#8Y4?J-`j~q}w9^)F}Eq!7$ z{mt_jYXQ=+sBejhN}lW!CR*L|+F*{gQaM)>0QdX5L`Ya=j0B6=goNC;CiHe{P_X!} z7*L<3!$pDh!Oho?v1Y5d)qaUSF(*3>ve78yDC>4ZvxBfYbG8URl7?2Qn}@9sX8m~-dn(}L9*@UnBg3%8PjTT z%cGq)ynZWt#PXT6;fmp~hg+<)O8J#D0vI8HQyG|qm)^r+2HNQTpzeDc)j5kJf+s2E z{Zq=iz(jZ0>0ZCF@-+sQSCu)CywaN!5%SlU;Ttt|c&+H?gbWVG&P6Ya)l`6CSPgcv zvAT07DkJ>YuRUf1JGqXfSo9A$vHD;cp)~|uy@Oe)Q?wLttsbZWxEfT}K`Ex1U_s7i zhGQO$S9r9!rq*xPfO5@>dVRj){p%~{oOqGGHOE+!O}4>A4M}5WFj26nhuyK<8EYTh zIo8UD$rZmb1FJiiNI5n~8SB#-qWZ-1S+lv86Za*7l#Q{%-r`6dS2OBla8Cx4GyKeX z3Z}FW1B-Yw_PyYsG6$5;b7J~|hc9hx2KXqDQEIF7mJaf;h1ut)cC`G&zRC(pROMWb z24xB-B{=GOwG9ZN4*;}OlLxHv!y*vin3$7w^{i_S@^E4TSJ8`1w+NR##=uk^pt7pz z4kjV|5@l>OW*))|u8#jkq)La7VCy=GvvyLaTx5HOJkK!i5-y1VG1GIlh2S^=#3uKp z^oM5OWcb}RfpyCx^uCQ+8Du7_Afg_LFHn558%@iUL(I!Q%zwc^4SVq2P&eRVHx(K#rXm`$Fy;!W-Q`A(+S zpDTlW)1_lzFwD|r5*f0+uKL&HH@30%se!h-IU81FmFeEuNFma3pKdMDG2ULL8Gk3v zo`xID*Thnrwgyk4LmuPE0p-V0*JgH+O>D2UFGN-yG4VxgsT+LWv6*aXR@f}DIB*^( z#yKvSvt&GPc5qnu!%*~Juw=6xF2(GajO|qvD?at)IAST%NCn-^^+wjR^Y;9`?!BH; zImj3F9ojGTUdLyb!MF`SsiEBRi^i>e&}vXwS1Q}yFQ|iW@=4qmyj1q`nt(HtWRxv4 zRw|dBy(?<7;zJ9JV}=M7*V|C#P~R6brO`!?Bins%Z)w~ppW<1a3(@31-ryALDfc|O z;-o!!f83-({43Y@*>s%KfWh4>8@?|7=?U*?U9Pst)y{V|Uf2ES?lAgY%`E7eZ5a$> z2>UeJJZ%sv7z!RO6)|tx`r+sVvMjCtnysBdM80|^Uy87FTGgX?WYIL~VL|WU``+Jc z6~=NdM=IG#cEyZlI>Sn)r0ft`ko*58?r8;W>E^Ye47zTT_7J&hg4t4*Su|30FjV5!b>YV(u_u0a<1%9x6Qj|HH zkVr>3B>LDU%c(%geWh2VPwF+9$=BfW;C`P{($X8MKQ1-xe1n@qR&tXzCO-i+kX~%+ zSDxDPTF+YDoBm`l8Svf9-Tu7#9Vc6_=CNvq9U!EDvrLhAR9~WuJ~A*?F*% zMY~=#%8FqT4ReJzKCNv1g9{92rF)5xc&x=Pj7~^>N-S{2bcu2TpSj>P-DYb$!)xIm z!(KBq@$*@_4T7cIYk|rXY@>Edw5?vxGba5JpIs)=md~4mw=0xP;Qkca)XgZoajY@6 z$K#=DynWy&&LogGtVmwmk6SwpF=3Ibp5ts>UebwqHyaI%3sN^GZs|DYUSq>&*y>HSM_JnUtZ^N z95{SUyz^sgqZ^x=_IA~|Tla{XKXgDj33@Y2G9`m+5|;0D<5WNpt~{lX=W?}m7{8T4Lz7mZ6Busy3gIbXY?T@=7)J%Tc8)h7iwRye^quFMHogtI+zRcSLA-ZJc? zDB0(?PMmeI`xkfT_vJ6aZLIplVtVphtQ)B33Eou(y>awDz!}tR^Ki$d1zSQvt1@h~ z{8K8X9o zOz{F`sRf^L<|0=^dAzg0Fda+0KDdI7qJuIzxMzA59y=alJ$(5-3L-J=1J}F>}^lTUg4MAIzYSb~O zPAAp>lu-#!)2WGzL03;!5xdCO|0(SRAL&Yx{or@CXT0kAMt7wr>`2q$F9VPky;x6il>AKK~4mB||6pa$ED_c~U;1E}o( z(YA)bp@5H>+j)$Q=KUyCw18SL48z+o5=X-sa(%14ulyvaFJ;fllFXQoNxi+EO`Spd z$TN|SOPYzV;M+UB#O=DtPOY2RVwtv-WiyETpt6>==(y-(qWj}M2*-0TWt@{->|1r6 zGIGxC70#|7^1bJE+Q5}mzc$NI!#Xizm*bYJ;(DK6|%0v4(N@>%i@V^xs(Vf z*>vIakWUcILp$J^-!*W#vb>BaW=Zjjr8vJBy`qA1tG;q-CDrKbDlq_0d?Q0$Pxk@=AH1V2y#jjdvFT)y$K4%O_*BJng z$AQN&uy%(YFdS<_uJ;vKs}3sPe&O3v7p!`@5J-a(D_~b}8b`l3-PF)|lIL)!==k7V<<_EHAs*W5UBLKI_NN z{_M|w=puaT@$GMa`$rb?`mN(n|MXAsd%yR4|E+oau*c^<_qh*Ugdh6&?svb7ckkD# zc*O)j=-zqOBy?HDYQtd`QIah7R<#v1b48UJSZfg0S>;e}fne4D6*uo_uWU@z*-C~r z3?Y|oP|#wq+a2x$%kNtgi7Vgk_beS;<>Vx)wlFTE!R=9LSq{OMZ{jsic~;j1ftFDk zkHBY)qCp*&1LNa}iS(BB;eNSg%WRWZx9G9C&?xH?v>sTK{sy+tWidn>i1j9Z0gmEUHGB)HfTH8S|uKz$0K~qekP_S-ktN z&l8VxQHq$l4=hg%pP2Jl5gpHg^O!4ACVBE^KWtX2L4Tov8}Jx55)6Pu90m)=Hl5v# zxzfo}ASJ=dZftaV-duaX)=DVj&;c_nnSzE$F$rS!#lQj2kasL^4Jy}w%@QKlS&ZU zZfJeSuxO1Bc^|3j3b?%1d}+9eQ6;uwZ@y4CvZoNMJT3O8wNMo17^7xBExwcel`eg> z%Xz;$N1r!$;H(+=`<|SoyfuZb&!Cjwo&3sy<=meD0elV|#~A~sXU(2N$+whAvKm<{ zXTc0PC@0RD3M*y8!Ugm=aC9AA92^W4WQOSj$DEkQk^6^a7;dhXxN%~!?Pu^&UHi5g!M3oqB|lUq9syD>s~W6U z=y#4!ZfP>kfZ|OEpHimexj=HLJds^>ovCgzn)J{R$6Td|EJFt}aEI#D26P>`^w0Qk zb=i5Im=WerLAio$)?;_*EjkA?hb}W%g`2u4tKWfAH_X^qulfY{S~^x=ek{H6czNI@_IiJL;e?Hmd2oW=GFHU+6j|(hy{}~GUnO67 z(a4kKlHSMNCp3@k6p@V>9JJ=Y$dBPI{7?=#wo^^a`~uC(Mpat#Prg}+`BQs9dB#uVdO zN|rK#D>$7r*jAlodtUg3IB@Y@&aen0uJDjx4k%C7|7+JSjG2z>6!_NPZn!4ySq{|~ ziOK32Z{L@!(BE5K(Mj8=su+HwHy_CREF-e4C1WK|#Z!TuIcayb&&*#xwOHUv58WyT zY?{Wm?k*UG{R{b9Yl~JpxsE)m#r9fg^M-zk?SBwQpRN0p&FcFQqZ z?OnQsX}pE|(CDPPDR=~$PGQPUppJnqw!8A70k+reLL1FX^!2!VmcLn+gR<%L(61Ah zIvS)6dSioIK_wnOCHg`PDic#_cXtQQ=Zrz+Ss}BZ6EMSzA8>!H^4YQUCB-Z4)r)M8 zvO0C4Wo8bB%JhrATwfJEs5>fc(Rmv^O2F>2!K$*Ab3UK*w=U9aJ=Aa>74$vJN(_uFlNr*v&Zrh74E0r&b(2qK>Q-Rleu*iRbI{TG`Lg@$&M5&wu{&AG#QC9_M-D>tFx+e|tXP{`R-={{8zO z>oDLSfBf->F2WCe{O5oECqDY}vc zl$cTbexi`PE?lWTm99@qz$hF{-_RDFv}8W7M=azJY$|x*ICR{K*l2CnnOoS&U_1S# zX)~b=MX$eT}XW9wkG)A`x`be*sj07tx*-)QrIveeHUv$c`fvsj|1lf z&U4Xg&WJ!DgURaqe4gjR@PV&$ z>3i*4ygRBA(B^ds41)IWN87rt|6LGz-nUk;TBW&|6tV!O8fUF+w%}n;0+(S@;hpc| z+jFqFeLws4icb${NI(^c$t;jPYZS;Sk2O>;-(8rn1aoJ~yv*OYsrw4cYsLEsm__JA z&T#RO(cm2d8Vl6ZIk<$;5yFTHW^ZxD&D@_LXfm+&RTbKaY>~Hwh8rE4Em7(*4TLf% z6jm2l&JeE``QX>DsulqMGaNOd&D&d~H_JM5)B7w(%Os?we4nDLfpc%K0OANWL^DQg{}fJ564Xa!0ecGtgJMa0(|22IhOOp zQG?3wU-ABv_xL1MEx)|HV7jl+&UOp>~1x? zPbM*FUn1|uwd#y4Gpf!yP?*nPuaD3ubqb^o=yjF%v%hZrlOU`5!OU=`w>@_=UV8hl zhv)=jy|VGD`@=L*7i}RrFh!P}Z`91?yQHJ-5gj!DtspItlD>_{s=Lk=m@>4% zT^(2zV>2hQT=ro^T@S|?z6O?ur=A((fZ5Idw9Uxn%B@XCDE$OC)RqTCuB$9-PvyQ! zIR}-64-)|jcIQfB8hF8VtBtrqSZUbqyAIJjfMu}OD&=Lo-|36aAPWlJM-y}VR zHgVx{o$@doFz_|N+;Py3HIceP3#UD9y))CXRWh9YEF616|0#Fy*AAJ&4 zh#Oeto)?4`7db2CkXKdj=UKAX-1V$^N%UA~lUDyEmO!f`+a{?j?cC3M9aT@)HR+v> zImIPa`y1U|&$s)Fupa3HRC;$K;07*naRO@;zICy0GHh^PT>~q;=yhg4uSn`+ZL+Y*-&<}@+c`ljP?2N$Kg9D=pdE@sfliAvUa`}M?Kn$LK$}Z!Ju8G~!{(+R07$LTh zHLR@x-77kuuNAv<>Pls?7tV=uaF6A_Z}OaTYhtz8gjurfR?+*y6`9064%j%FEkTaB zJ5m!WzBI=$U#r}SVd8S}2hRq@vCaPnQ7yivoY{>1E`4CmwaAR^n|f~3d5R&E{xD!T z93yQCmVb=3O4-MX2I$k^R+H5)m_PXeqQ>p|q}WsKoMmD;8A($XK7=o7&I5P`=80%m z9*e1npP%Q%Lk5-|-uYY>K-m~EplrFV;E2hW%@@o`s_r?--iGqb;*YgH&{z2D5kvzJ zE0x0mbku-D3sol!)R>ik4k|qMgB0v!Pg-+S-D!ZAL!vXxa(c6(x~HFw=rl(U-Mt2n z4aj{8*{LP8UJ9=QuRuM|0Tc~6kU}twt8&T>>9|Xnj~vWE;b-mriLL$Zdx$_+hl`Ro zfvLj7GD?_6wp%l7yxzRX5|@5avg(Yk#$v zU1-*v-otDujBUo!jM?c@OueWupNEEnu6-xlK)uLCq(i5`)5tLFrOC}8tBYf$^79E; zIcY!nBnFi~Su2%4`D6_$>&k3~11UXF;c9ZFa>(KqHjjLr01p5tqg`mA5f&JRWzou7 zgR87o^yVc$ZX8uA^Y4;UV3GGX&Mtme`YGBb`GroQnR~OG^IU^#l<_JjVaaE;Kb=gV zLh&vn&y-&*UtI6OrR$QNe0g}N@k$qLF;&5cj(^4=TCrH=Fb zL-C$(;jgXae9MD4W8;@B+*Ta&-xOY)#Vn!=)=ItDh;i|Q6# zMcODT36kF~?f(WpWNzKaSDD{tFUv~E%b;;i@aZATr@U8oR=<`8?!`?ESU;D@Sc+cx z&uj;@*cM_*evo$5eg&(@MuOia+dcbuS60PQD$YtiFmg-{wygZz7zIQ4tBTw6L9br& zEjE6Nl6dlh-@M(^E*?_3Q@>NhQk=CPtZ2u6%B_cTC35D$FNlpu&usLhVbxRmDr6uX zYd&nzPvkPLZC{tW3@5)+Hy5_5?xfOL@r&qyhRt?9qudv}fO*BEkC^Yz$ZTeoq3O!m z6I%oUrRV0FME8scEzjqfGJMHFUy~z8$wAhI?Na@cWh>pj>QfV9D&3LgPx?upCIr#G z>p<=Ym=i;D0$}e`cZc#M;HCe3IX2S=vud0Z*i-jpbefX$Cg&S&0W1bk$G|uOGlXrz zr|)n9897aX#F}r{v3SJ61rCcrqF)mWvg0l80PQ0W?kIdOt5tHsWvoQ!;GaZMI-lXN zJQ-eb4Nh}AphaU~9EQ^e4qcpqZBkK{x-_*@H$pHE+cKV20bsqaP2wSu%O=lRCcZLf zDf*^x8}Y4Z*_))oWE%osa_tn=K}rON>kOG{&HIMlv*&mDz038+mnQX0csYb;R$p+2 ziB%cax>zfH$m*b%Vs+jp4GNDWug+RMrCU?go}MM^IOzO5&(D1L>!1GVpZ?HAc=K>~ z{Ka4V#s64-{}Y4PKg{tHF{u2X-^Kfn-lt77cH0dQJciUcw87?Zj0k`2W5SOk`uDje z>dgU+4WpU$0Y?Wokjw!?3M0j#+_uY%@`biJQ1%RR8y8{=F0`Siq3i^Q`@pm{a8~wr zjUDCItw7+cS+>YFk&SxMH~FkSGnN5W1*Qk^fgF&Y%7(0 zI?j2*PHYQ#&RjfHO4LN>i6W6pMzPQd<2&!vV7&0rryexK#tNqL|p2FvASrikab@2E$C&6ssctcN~G-3Uy2iUr=F?^hGe_bbTgFNE(J0N(@3a=l>$cUYcw#YF@58TEmo%}q{iQ_!i2YupZ@eV`a zTW)2hTMO>njq!X0?#*$7^qWUbJO-7QU=XLuU`+2CC9eTe?z1KYx*?O(#gS+m5?~M8J=7X zSe>!LX!732u{zj@1>wV#63LjRjOH#3n21%Fv;=(b@cu>z3z2BnjOzsVR1o|wu19%9 zu4ffidRU(|j%)FuhDAbIF&K34A-ytXjooC*U+(_4rxaV;ozN!`NGo{_zTzh;%qH!8X` zi~L^($x7Aa8n9Z0rRlN+-8YcG(qtj6`EI9U@s4}{^xV2EXSc$f6=q8Tgo1gMeWA%mC|68d6pcKO)g9k@| zzh(f2;XXI880Qh!p1dOcHrs~)DfX7cO7D~RpR5(ipS;iW>+5UK+R~wR$b=)^Pq{+G zIK~J*wwP?h)q8AzmRWdO_dlZnO$OC{L%WtYeEZRgD7hnJmKU|X{a(# z`ev6+dU*fBd$bT8#a-x-ggboTly7IIk$s_mcsKc1_79z8$hJPl_t z!x7N{oI0?)ag~-MWlS|NuhY2HSuBnB|n}JwC(y-SuRpv+JnE_s{-4vA3|?NqA!mA7Y@j`ZuM{{@dUnY%|+*}tm^OKrce zyFR4F%SK)QDsum7zYO(E-95x6<>UoaJF5sx`nu4`U9wxewHzayNVg97|6;Ur)R~UtYuAAV%wa;ja3#7M6)04*ouS5=RDhF*rHf5<6ZyNG`T|Y<)H?cG%31M& zkqTvfnDF4N=Y7KcghQO>iJ4~(=&eD&<(!(ZxPxh+qUhJbl8ZuSFsm+Vy6Og07l_#-a^ED5=aZxm_}G$(Mf5 z4kQw~;p6&d)p=jTYVvz8r;D(wzSFqyQbiMa>?v>4|3JaJ!vgPRPT9FZn})lB=U6kv zAnp+G zn`(Huy7x&W6u3xXMrOyokYG*SUB_7TLerPgx(tjn~aryd_|X zuh!v$7t0^)?rooECKjxD#-TNwV~kb)FXj>VRIV&#ErBhW3fx~!R)m}U+l|6H4}f&D z@M3bx=JFX;cf-8a$^^#+ObUq^wZ3&ws&u!O&*R{;{53abuK;!tE@7fD-2&e=nYDJg z29a}R%`kXm)(t;U;D42vAXDWwX8D>REhrLzO^sOvz2!JjAyf;PSl%B{b5& zi)$GrN)U-or2gA9Cxtux-lu%e*_>Shzi?i8I$-voZTXGzjzUP3(?bBlqU{D!BshFO zs3_`tS^DYbXhLgW?Wa33Q<+dKEdA?SlO4Rj zrR*xFiu6Y_S6zbIqF68n#=+TM8or#1%aMU;z;$5)spK@6{Nw29CDz7Y*XYQSGJuf_QVNMG^TZaAG_xgS5DM*kP)ut+y7|&7O{Ms^=HAqXDMv#{> z&a8qrL(Y}B?RSc2Tq;^kww2u9v!| zH#1*P0GLrl#8aNIA8e0je?;fy#V9YSGpq-p95#O(%+9HNb2@8*811 zJ-Ary;k7bb`6B}|7h$Z&6mn6gdg!>O_7p3*gY_O#$%A^RKj0q0vN2;jX7TQgNByPC`DQOgYf?=Eaft9Eo3eG?Zew5CVeE1+c8+) zqTqt?Gl6i*w0(dvVUHQ@Zd=f4c{id9L_Bg{nmm zi#mp-XAQxpNAjy3%jnJpVTr;~*Hs+1W4a~>cn!l0xxQqd$wJAP;MRW|Ef-nsNwZz% z=#68KR!q26jBI7Lt4Xl=`~sY6O{I>b`Wk4EaWA!0m+12Z=- z)pphQZ)$?$56eYj!v-eSwgm8x)YeD1-BXsrB^y`ew8~3P&S$W5HnsR6y1BV|uCn{5!0jh}!gH6((b9CXs_vbr4eR^Lug6QNqR_1O|AwJ{$x6)H| z>`kxlIeAO$mg8948mwkrxZ=88kW2p7HKza)Z_)|&OlKJ7LA2337qc(zd@QH!q>aK+ z54^XoA#!=MyL9^@R#)oNZM`_pLmns2^T6XgVr|Kd8Iv*?KpvoNT9J!fUSJmb9xD9`PARkT_e%ExKA=D_p=j=o^%Ri?6T z!b8%gi_~^qUgN5|5(bzXrg7fJzQG?&@$F?w#0)4$>BbF@Ea3%|?+o&K*_x@Jlvm|W z>nZLkKtVHud6*8R$kXC`?;k*QOSInsAFCv)WeMz(H>pD?({o(Bn!HW4E{B6Q3Nhn( zL15bB+=KfHpTu+7le<45zW=-?KK-2Usr=pVe)pSh!iUG7|M{PPt@nHX8P7WZ;)^f7 z=_dS-kIz5<9G^aYYQDfR^t|i0Beox!kuAF_Nv%CqeJq?ZY%O}#Bo=^TPHm*hYV5(} z(qlJ2*r2ay&3Q!PRc%K=jDm`(1h&xoFOXHg=tce7izhx!fcJSqr< zd_@G!A+nX%FTL8UL#{29>NW2-reoTSaaft4sfU~u2bZ2(IO&;KX~wywzinW!BCceQ zmN})iCKqiF`M&rP#9qA{qjeN_5LZR{>tR3yWz(ii97Y{3mu`w=u0>Er8l&i$hm>aL z?Q`6+IoV(5;x*dfiHFurX)moLf_TJSSl?ZXCTU=J>d~@lUfUR3|MGnM?$+K~SikuH zJPusPf$JD}K1cKcS<1cnZ)H#A;pN|5dil!NH~$xPJ9@GfgUdc0M=YS^LdvyW(@}P& z#h%JGIC<#MC??V^Seqj-jCytKkiGQx_2%~0E6`*C*ro^}4A?}a)u$1zzZG;O1GGLV z!@%KDawh;X9VK4Ik2+(^;Sm6TTV;Re6oOD1p#sn-5JM2%Ts%3+C1RndO;K#`3J-+FnAqET?umWXpEv2ze7^ ze!<@C+SXRxH;TV~eeCAy_8WWkZ8do7gS%lISP(-`KtCp)Dvqbq*g zg)VHL$#+AV&lF@;8rIXWk{-G?Pn>2VjFzD73O@}RK%Q$(HieUYqwym%Th4F%AZ=*Q z$y7MeFMgIQvzKsHbX^tXMyKy0qXO_@m1}LujO{iFU#vaa=l{(k>5<(IZ1Pg2%L1?M-g|(j#uhhrHE@=c zgkIyj$<`0fk=jaVMC09nGE~Y?zxO-vtPP<7|JYl(S6r7BYv6RIa@8=gpKZ^Ld`R6j zJu};+AWM$zgk$&$D4U0#C5}(NE-Y&XOztZ?q8yvW^RW7iE9-TE4=W6~Aq`q2+J?i! zbrC#kD;QbrwPxV1>ck5~&Dj4LfnmuAi;{)y+SpUiqzqzvfXpHai_m56G_2s&ycZ1A zMj14VR?xJwm0vhM(#(s zs5<1uQJg2X3KZw>m>+`OwHp?=$YKGJj#;4s2eM3{2J^01*sxP3RtsGk?WVIu`Ge z5N1f6-<-ZOpO$m&K=^Z`kE_q;d1OhxGqNa-Hn}%NDmlbF8nNXNU*rZl8ZoNWcyzlqtykS zR`t#Q-qGmUY*u6-xso&1>tp2(Xn#c)Vd~7KakUj8rPVKwey6DM8 zj?7YSQ$7W-Jd^{h?xycoI@ESFF~6a(uadKh57v$E_X)kRx?+jyvJd*Rb{=S}-Jf_+ z#h=G@g*TkvRFr25A@!1VZ|FE}U6U^+4pRJa!yeL>Sj?N5O_N#5T-Y8#<#!1xH`{If z{<=z5|GXZbefHTm-GrCNoD+Zfmw)-S-t#-(`OepRzn}E@(T{%gO*i3xe0=_2pX2fN z$c0eH!D>+?MDJTJVzKk<0H(uvEw`*VN!)ys4 zR`R%Xd)dHCelPnnt87?gDn;VNJw74G5~T^h0*T$sFI9#*2R!YZ_H!dY4VbaA+(hBeDM_mPJmq)T zkk#TjxqV640`wkfMG2VhiF#TtY35FB}AN25{pZAsFdHpzN~h zVmF`1gn0xy;yre?92W(`^0gwYxRhbNr;f=4zHvptwe`DrXjDyuTYSCuaSw2IJ`o2^ zp3f|;2_Ou-7z_&5GBu|XNSMX(h0qO5O0Lfglje6!>e z69%|i=Xyp4uP8jFcXKjr3bGrjHRR=hErpUlNl>vFw&U&yq4Nk1DMM2<$xLNF#(^`c zA{@to>%4GYXY?}^ND3D#WloxDAa+!#VzvR>*y$=NRd~m7;PF^arpMz5KpNX8+_HW{ zK@MOX2hPW1X^p6c@i;Gh>s#N#+qd5E_V$Ly;|=HeSk5a49;c_1-+al6GcpWP7B`1k z=l?uUddOe~eUzu}z!R(O`E0FUJ(%6^Pe79~87U*G&#+bNV2o8-Zp41tH822!9|@$h zZL-ocb@U$4UID3yvaRF|)o02>v({$a^Dy}ZMQ?+x05iL2r86mKvu{nF)pHpocbava z6Q9S6S8mE((8om^`+RbvZu`&@7GwZ=R&!avApE}3Jg@Ha^br&NEEAB zYfztOyq}D%OKIv>4y#uj7nW&V06b1>)}A`b>w5or8$^o zJTtJo#zz>oL4kkTRtMU)q@|}mw9r+ZWfhUi2Qt1B zXsdm5vbEo^p%&DI!cc=Jg#N zt0r^ZQQLTW!`TkO#xed=UKdF?7wg=8z-R2$K4;?QtR%*)*TDH6mB|`L=~TJVu>Rlm z5%sl^f3kr=7jA(h^e$q%P+aB}b%FC!i$$3aJRZxjORP^{4Cm2x%~g@;`NaG4!ZS)D zX8u7&od5tJ07*naREBW2xA;{vn09PyX24ho1~IQzRf?H*#1R?2=TVMqI3h>b-HSaXjXc{aNzi)us!jgJ#M#XZ~n(wQ=GapBQ( zVEV){S8leJ{BI?z+k7ctkatjgr+{$mT6SbLy9&5dV>le8L!8$l?h(12uSV>%Xn>hx z4qzOPV=kN7^Enp3&gJ`hyghI}4jktZ+a-~3{m}xVS!R0cat&&XNmq94n*l3L-%xa> zzH=@dt1Iua|9;PszprueW}jf=L%z0#@QECNJslT$mUNe`%} z)o&nrJa6t>e_Xi3`m;Xr9_iPcwcqD8qf#2Plq;~j<^rIL(`vg6$Io*}`P<+AHopDs zZ-3LxczOKeKmOy_9z^}#_rCYF-tQ+p{$B_x|L{jY#QA>S0=<=R(klv%p#f%13OOnx z@;q=H=k57uy1ptUW)(WIm3IGAw(W4hM+AbOxUOeZ(p%^5j`RJ%`8e@7Pn?gnaP9Fp z6ECD*ZCmebrL5`f1;D`R@{t~RoR4*%^Tc@+uA(1k{ir6PX8?O)&>CNpvHvij{9y$~`VbbzneOn* zGOINyaRX<>?>(_VefXV*4S{q@)5k;GYFKU`tM-N8u2Gm=vDA+CcXdl!gnIJz_Do3Z zUeKm=s_Izv{i{J$IAc8zXT{H|{5dSf@tln zW{BiqBu@j_`tI^xRD`irAs~^N)91Qy+o?egy6noxSwy~!C7!Hk8Rvu9Q8Bm{Mlc6B zJDr1AOqpQMc`jK;Y(r8{>8VZz-oLKTiJkYi{r9{!8DYI={Jqm?qaYI^qny^qkYL=f z3ShhlC|LCO4l@bJi(wMLS^cIakaSuZjQwSqOPU%E^fXo_Mwbri)VVGEG|O^t2Fe6=_!mZCajQw@eQKGsw9GvjR^6fat)34;W&T`&hyAd|w7n zYg5wkE+2I9iOTluzt!*Rdg`dNrZAB}Yzr!r*L0c|697O9CM&>$1J`+lfj4vC5C$ms zpgWx~nqm7g2rF`nbFyXR#^X3FfN9@rYz38(_cJIh7U07P7zkR|-Vtwaj|eC~TITa` zho6hKt38W`*LmFKZ|lzsK$MfA&kl~Wf`Q^W$yOSb^>NEoZV%#;(l=bBl>0=}@gBfN z9S*|)W+vBQ$i%mJZ3W13&%5A_ri)TW+RBWEmtkOr86}ZY4#+&{4)ZQt8+ej=ssN<0 zMVA1_KsdhPUs{B&V)drZC`XPl||X{Vw`R) z=7>^OGqZV04dCYj2%Dl)7f@<@40hk(aC_7P>Pwt#Z(l@xo5xh+Eu zT%9fRKV`k(9M1Nnuwc@}!otR7S1Qlc!Zy{>EN$v7u&g>;c_}1E%*hHS*DCQ0%)G!P z)k5oiAb;}v%Aei`R_(@tfjQO>Ml1|onAR~+tM_jYY(}dxkH~MnZh9bxtIHMa=j1Vt zW@A~!IP#YF_bOak9FW)omUZrxzTTdDc5^ObLd|wPC__@>n;3F+58m%l_y>(2bpKWMS%x35$Z{DIHUlrm^9mwYX}~NI zDA{i{r}edc@QlVc*nYb{EQ+u7&Z{_Z@LNpp6VJzq$F+jX*L4Oq;C2#f zGj*Z1zLQXWmnk} zabDX*8QS(p`Mn4zW9{(^Ad@79X<&H^D&LYFdved%{a=C~^}hgmA&vO;(xxllLZ@3j zYsHR4+74K2XH?CpZ^+%1uH*yT&dbJnsm;B{??e04OZQ^`9H{@I$4Z|S?j$di4`i&$ zZCN2_z8rfR8RUUiU09gZbCEhTmFHnCxl^~NGFyC}3-hfONw><)?Xfb~0mgzIV-Vji z%(OCl8j%W)+T=XP7&X!iC|||E*VR`PF@Tt}KnX9mQX#tb&Kj9TQWkiQ zlnxwiv*vJmeenG?5lr?;Fl;Nj;{n8~o!G<5eZ~qUrCnV^?I+#`be_?A35!5}mHjK5 zfIN+S=6f7!i|OgiTr^$1T{pJ*nlD2iG@IWy(7G`7$;uDx!p<(erUi}(4=Z7G(X7q1^FJnWiQABa7ULnnJxlkDkr{iuwl2de>M`5k#au*TjA9`V z-BT}{rSd8n=zq|pu}U%$kV-k*LawTHlvIjoH!70{fcR}L*<}LwF+!{ z-n5S&9m;E!9iavc<_MlycloM-SkgabhRjXkww%AE3*=`>YmUbLyl6g({#&Lpw*NOe z*lVh&x?5~q^0IW2K3->l>R%VAo@KY!9EzSNUAioCTP5>7%0hYEe;PBD%lM&@JTibh zBd9zgsO%A#c6eqvd+g7=g30SUBeSm-L+w&*wVOE9O!n&C@MRkVEQdc(!LxLPOF&t( zlrx}`IKwMxOYlXToq27o5VZEkZ1Q@OvnBuQ+2S6~arEd#0wFj{JZwz)IbTmSq z7%v?`WyqFJOIM!f3QnBN{?@*2bhMzkQLcbIw#hYKL$1~6@9Qdx_viN9u#WM%cDrBW z@LS`FtL~%!@_2!->I(N?=P=y9ulJ=4X4i!m*<|3BjF8u*u6Gok@cg+5Ys=3K#B0d0 zQjDJMi)(6@axUcA0?8R*zAkW^ry4Kf61MBdQu=51_|A8}lYW+;wd3#p?(e?V`~Bu` z{^rjl9eB(+@lXHsPv3Mi{>R4;fApgYCU5xTRry>78{4)Ov2ZQ7urTYtiPw+w%!O`` zgIjZ)lFdANkor7u%p+{o6;L*;MQj;V{`8JdpT5MW_fPor=@YK`#N+wE<9XuweBgSX zxZWOka4U!0-s8eGpUaMN7vIpbQ8PdaWcycg%egX@;mc1f8Q2WQI}qD5EE=mXDzSL# zWq&QqQMhHZ0PLlCx zPJ1@(S}IZYJ8kUp3FT{#3Zan>JWR$A3rCGP(S4FEiM_s8wz+iIw%cnTrl&rBUKV>Q z@BV?MyDe4{5IdxP#lwi;)PV_f>~4C#3?w5hJi-vVuC>R{`}?~X zS1(8uCn-xv;F$mW`K}_@|B{Ic*ji?CbKu`gC+~nUOV6ak>8QZ%O$n@aSBY!6KA&aI z5CD|pXp}Q5-s|)dK1`(v=|hz7RC;cc{WyRzj>gjt?%gIU3ZoPcoX&O$C?5yrb(Nx& z{w+oo9U#2iWsv}q22eU+v2}}zk&P9AT4mt&F=P3f2qN3ASGFu=$=WcM^mk^E)sn3? z2qw>LkFN}5AFJI|A9SyjBP8a!jo3pif37>l>K<661_dOdH9hoXjw;+ElK*-JnwR)I!TlxM_Qrf*e+9d+$^d0YzgO4(Y^e6E=8Wb+ zdbL01EHRqObA8?D)Ls`(UDqVmwAXAA@^lZd3>r41uNpbTtE!8wzwT{ZLHrgF$fmCQ zIqzi=M3%s^1~67|Sy#wgteE#Dm^G*z_@-qm#{?68lP*f+v9$c4^6;Nc8 zRcwfl*BoI$r2@)VX8o%%mw3BofXvIqcbsz-Sgvx~jc;2<5cxq@!bkROuF&UsEe@%n zjQ4fo#$K5{z$p6k#sAc;*{}HICIU*vCW(_DrAhm#j>RTh@^$jDe4KR3*%#v>{26nA zj;L0;b9N_8!$w*aLun&8Sty>3ZJgq+AURmXTsehjlQ2z9MuL?yXOQ4qx-eYFYvKdMb zXQxD0zYO3)WjOM6W!d!1C3nvfmvktP>%uj;M|5#gi5Q({K_AsUw!JU1+Z&Gs5^*nC z33ZezpT~4HnV5O*`90OciaCq#g@?({$uKhB2IRtGXxV;#1~O{;+QtOtp!1WBTy}#) z{VUR^QY26bpt={K1E|wswic;-J^0!zvfamstap2zsoWky%e*;~uWIC#P94+U#uMEp zku;L(2LzKq@6&0aZ7N-KmvsAKLEH47HeTie5cCi3cm9g^oE`7yB7cA?gI%InWwG48 zJ)01BlkKV(`Bpa2xd%)HmSdp^#}{hu8F z{LlZqe1AXZ#}7aMVJ=2We*yLLD{P}d9I`%v4@-DX>JdUE)wb4HF_wF;qPxd)=?I{6iBL_3m}m z<%#*WEM=JG_EKYI9cKFqb8VPWwgS?vp1Pgi{ml-C&B97+JDI3JCzvDcS|v^E?vB~i z_aoS5qY;4W_b}K*_?(C6_4o~M=xEr-Fkk4ByDl%g>xGX;agRJB-+%2B(`{aS`ik3e z+mE2bh=qa1K=P=v*NUN=k0JdGcI!)G+w?0hduTkbRppJJWkt;O{S~^M7>I4)V&Nii zUsynLvTU}yBzKVi1DBj5$`lUolc#_`DxSd*oI zUw2}20ELhSNs*_~5xixUB~2Wc_g4nu(9p0KqlAN01GO@4-5nSMhfM5bfZ}Zk5t50L z5&BH30g2BwsFT4QKH@u?FU|5i?ub&QEFH`#vwFLnurecb&&cev8vW7-XFoXB@!J^h z1cs9U)2sCLb=}9GhS(;M;B`Ag2vDKL5&PFI{zFPsF-7WsD#Sjnd$+PDTadhj?Yb|s zK`Y?8^vS>kE6d+YUyIBWqe2Bzw`W8VHQ|Ns5Y$fZ=gHfa-&JF`o?ZC9m*(Tjl~(e- z-TwvhPg&57ygd-TD;;;wRrQ(O&OI?)odRg!r1zWhB!V@zGGk9a0Yqk_7428~>$yP9 zr{~AC)G^yWGeeY9D^OuvToYd?#mAW<{LB?)xu;n#MWO-S+?&M2d7gWtL{ORKug$i= z0>^oGpw+dg^eIHkX%>#B`0gQ(92b|lyvg>)9tLO@qyDx)@$SU3=o=kiN0!t<=$vB= z%yD2ItK|3EyG+W4+=9wK<^kbdX#bc^vzUmW(grd>X@@mXK4dJZXKMN`@R8+D)w!Cb z3uDFF2J^X_YXRPxez4^kpsna_aUZ^r^IJdq3nVzzWNByZ`RlrSALHN$fopZ>p|4F)A9o06!z)KTh4 z4YIrnU`)y~zRpZ)>e9&I<5-OeFr#m-pz_Lao>@YhPKFF9AC`EiP*c&;;cJVA12C)= z;%k6MRFl}TUyPaoz!k%^aKpRNhMVYX^P$Tl zcZX%=C#+x!O{cYX@|;-X92O4K<>v_rkakPR*D<3AX(?azSL7q=joXpUaiK>R1O>N90jSfwZ5^kwhtGruja?h-+V=Y1#n&oZ ze<$)hs-exyQhvsRf#fNru(^Qv#dwBZ-0hm5w`0zUfB*M?$G`vkzkm3hZ-4vS_~l>z z<=f*s-}w$c`|LA39*>`Do387^zx~_4{Zy~{(J{ur@BQBIeItSMpXB(5fB1)Qx(Pp( zmAqi7J=hO*h=+yKA(8M-lG!DJKmp9 z04@RU6OYFOb|MzcdFrTRtgP(Ewedp#>VUI8xnrVAxjrx@9T%YJacVYi5-c;8h8SLHxq_fcohYuL_21 z_Ixg!Tll5aouQ~iY;INI533}!&3H}*CobA(?7kNC@_XtG``}31DIc%>JrKJ}aZcGY z;vb`KbjQTN!J8QAG-Q5;?VXv*J(%1vu4Pwz>pR=N#>_Crg=+xQO)6o^^9-CD{)?;t zshP?zN?`M|9EL_qgZYf|*QC1uon`bzMi?ffP9T`2ydYdLPp|~&mZ-+`fI`fM^*ffB zf>%aX{&wR412m;taJsOL7Z?Weo}EFIA_Z_27D7&UHSZ~1u znZIK87mUOJ^BS~bD8kuw46H!J`lJg&1+Xu`vrL*!>uRR6J6x6NURldIkx*rfry91~ zWK3sA4p+7`#RZkn5-rHRer?``e!IfQhkbBPbC4i61v~|xNyZLl{yGh=S@-7{HEQ$X zSwbr4p|Y`+t*(8jdWqO1){DM`5t53$KsuE`HJI@{(kSTi5@H= zbPEdcy$@J9?g6(f)Bt$2KzMl;k~4X*)6D??e&ef zH-)=9I^9>0NhK98+a3d*_VQVYR-ubeTs{kcY;79>3Wkqt#|~@=+9AV8Af2V`aKNnP zQVF1wxl4YR^Ii6oF4fOVb}n4k`;oWIe@bK7|8LecQg%gvHgN;5@nT-4Y)E}BV=?6M z0{j=4C|OfiivCfzLY!m?6n7x4j?bM3kUYGuZTl=6jCr zY!u!sZmRQ~vjH^oIfGaoV=3LLUoscT2%Icfbb2LfwS7qz5;Np)&bcx>IGKreJ_t zSmo2#K3~jIJ`kDk!pCYeoHdSXA7&kMq3Mg`W@zd{jKn*$&7T-`Ip;g;ueK48iUF-^ zfL4O2(pR+|#wr1Bto~plt0B?+Ph8VSduN@O@0EF_j7=P9(z`pRd?^RyR{1-XlQ|bc z7MhnVtR~9+Q8I~`E$(f;c#7Lp?D~d0ItU9uId6$en*7;xLvH0Wd1=+X$e5Ni`@_Sx zbMmXowHpC%0<~Yv?1Q@+mB)4+=|hvLJ66$70*xPA6)P{C#dfLOPrubm&@3^E&V5|M2^I#l%HKedtIM&38?s;EsPt*2YJkP+g zl{G_t>DYvOWdm$ffUbrrUU(b}AIjDwMC_jJ7uL0p?1!U18*hW^Ts~MK^Kre^=g{3o-j&(k# zu#Ji0UO%LtAknM!JFoGTi7SBFQHXa9g96bCtahD;7+>OuvO}70&#J#5&6BtF0|mi4 z*S6oc>-0}bCPnMB@|?r7YKO&s#VLS__#cQ_ib=CBM^&y8|DuzwuD>aUWfR1kt-M8X zSN+pV{5X!E=zjnDum9R?@c)~~@BGg1{LJsA3;=)s_kaIQH{qvpeEG$f0J@!^ zhX$1C1FWEO1%-LeMbu?)mOgrufbtsy%Wo^7{H<@jum*peX?rCOgm~FOFmw) zpYnU`r~K*Dm-zD2mw3LvhmE8*5no?x59SCcKc5v;o^O#*0%T04$7vSUIgbPLTp_Ph zN_AJM>1bn8&`Yc?@KyLtaGKIqgSB;)lE>6n^#bst-cLU;%6Cx40b3W?1EcLBT6`2xPXBd`S;_Q;t<|@${*ybt+#Un5b;p2VVlZ+E&nhY`C7#=Nl4C-ruI-fq zA8(j7b;+D-dyur<3g7jE^xt)>_*lhGDc<$`nGB}*3y`#{@gz2Bmx^c0$DL-u2Rbe} zKHVmFR6CfJe9cu}eYiDWf%>+IDdAWpaD-U9KU}e?3qR`p5vc8)j1I>=#sDN-oq@G9 zSSEvd)xbL=#R~p5SlX}%(C$VSJ4DLs{4ralt|Ai%J}_-18}xxtgRT12NA&h)5^TOC zW_^c1su|u1?)&RBpR$acuf1@_1bo05R0)Q0w^i5N3Xn!>7Tjvjo1~9|Go|&-$xFd5 z$^xE3zZ)|m@W24Feo{L_T3tQ*?KMv_V~eya9!lW94FkW+% z8fXC20Z~Q(=`FlK_M)(A4h_2nv)OjYn7k6!q+{JK_{}Vg8~)bPHX^SX=y0p-;*{0Z ztJ^8*_c$Lo&U3B852LtvYB@z7j}zDRwn|fsk<_#{XM4vQ#e=t7ph^`qIxW|H7@2WP zXHI5Gn8}_NRW`5WSUvNawGSMJw^l{fb-I(F%3t?q@d!xoP40;hH6>4E$dDyq7L^g} z{8Gv$BecCL=`s-DMTfzhSgJ#myiwV&@}+G~&AwOr(h+eFA7bz$<(108l!e>*dR@y+ zcEHJNF2Jjxa6}Me3V<64(lP+2fYoG$IA zgV}!-^G0CDvEsaO(h5z6q%%OB1fnB*xz!39k85CD+(YETxF#;Eg<2)AiWk>Hilb!{ z(P)^{a50k`F~07y08XsvqhIZcgIXcYeQU zn4kK3FUynj*0HasLHUq+ml%|@TyDTS7ju*}Xk%0DFcR&9qx5=>V%IUflN%IT{V?Gl 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

u(` z1M(YX{Gp?u#No6xR6U{k1<0br)??`(9qF&xPwP%_Tf%5h1ZPv-fUVfS8%r{nALfn&~eNlQ!aTtmlbq}BCA~sY*gw_Tk_e_dbObwiUk^92Po?%rS8PVj*ldrpZP^&hQ% zJxU17m88Xl4aLmEA(DIjW%>f9B98H;l97gUp1llYpvI3qlo`vTpMnczM;$A!OpRZ; zrv^0B(c4}yIj>AbRt%4f6^e^q;~Zr-t}DMd57LCTecsTA)oIoJJ5YKK*>PRM7pIYL z34@%SM%E%|xh!b*>Pm`;aF?WdRHv2x!K0KMOt z1_-5}4$^*uv%pH>n`b871W-ZVIZ8f;!>cd>Je5u=TUoKTN@<X?Ji6yyIf?NKU?Bik0m*oO@LQF^;uLxo7k%XKe_Iz?X?L zH3@_;Q@tQ@__9dFwy%ZDuj)^17jaF+wk6Rpc-ny$GOAV|6;GA>odRO;7QzVGHo4if zs@kZI^YMMI^%cGa!q5)#JKK@B#ssSd^E?yR`k_N|jpB>HC2p1jR=Qcic=$-SNw%Q@ zj&A>ykMkg1p>SbuFF2J9$8V08;VN26x#TNmxpH6T<(ZgrEm=9|*tSBU6}_Qkn6-|n zbl>u=Z;bX9?to0K`99ENc(gxm>pHGul_Ot21wF?TE3=+!0+q z_+2q}RQj);03!~U;`1#hg15?0($*>aGG>~U4d;D6ZBXI`>7XabJnhtBBbAtocExIM zp$i!$*mRhU{ov>nB0S&f9HT!+cLZ>NlJ$C4`44ElR(Juyk1kO8)U)om)pGJ@`K5S% zzK-{klxm$d7%S!cOWE+zT`dAoDcf;P{})O-Yh4T;ZQBx&-;j)%|hkHUU?AI1Ns zkEXj`k3ahU11~=KZ%?B}T`L{vJ1o^;y`qTtRN^i9ZrTR!7W&EUh?JFCic$C03S80` zJ2(M!FPcAvoeaiYI|G5`gXzSu2j|6X0MT zVqOEZ>-2#sp1tsy69=jfC%wNBiWcw#slQqDW5Un*+gmY?%1$+YbiuE}XB%bG9|n&# zYHpl*(EaL_;c{L76gEfw4_0N5YnGxJllbjqPcC>c%gADlne>G3dSvUeu@o7Gi0OM{^|fLF_^k)%PWZN{SA$)F1}l@SDmQja7|S0SkI~~A<4VNv@8AN z%}4>JtQtNm>Tq&>d|IbowJ>K{EY#fFfR%o^T6owWW^ChU_%GF7G(0o%R%Tvn{eB(t zAF3~%HcnL}x`ZCgS}3p(5#_Lwt3QCXt3W?gHjligHcp!ss^=xNZC3pwQf$< z<}uZx#y;`O-5igrt?B71$1r{%sE3U9b@4BQCBG@Ovx~KMUBG=pPpr97K?a8QCL_R zJlMI)hE|PPPHI>V+dk6RGdQNss6_1nIfxnn!QHopdd=~58B0Ax0df6)^tPm23k>~e zAep?(t3W)E^tW>+p8>Z~DEIRWCTohEExe1f6iMTR zb`3ejYZ~BrYUKkv9K;1PJLpqKfVe1SY!`uyp3zumDw7j3y{dll+G6_kxX|U8M0L=s zb4F&$i7KMG63l942M<+P*OLiMleyh%5i6L&y4qxln`kFU4;vCI(?HkK@;!0nfeSh*}(zc z#?ObN`C1#Llf_&0wL1)gS(=@#HXVa0-}GnwUiyeHteQt_m3Eo?_||^D$Z_K&7$(li ztyhLRM;Zr8-c~AY&ju(h8v`$*Cs_wV`F)BELQp|VS&YMO^Rb6?2bTTueb___V;8p!cO62Fw%-FYO%NFcA+T z^I85+uf1HI{AT>F{9F5c=s9b5OeiO$-rZrRVTOc4$FUH+M^ixUB`-T7@$2~Hb3;HMgX+^HD2g8 zoigiA!?QV2%qj2LUdqK7s6{-@J+j?X2L@NhH%>CU;3MF z*lavR!k^>M@#px>@%8l=u1hBFS@Yf6PE#lOzt~o6-6PC_c7OT69H=ZH-8t#s)Zt6D9*szN47JAkxB5$6 zHoQW>IdStE6J*Uc)?V1QV>0|&xtPama*l3wQ?`Jko(BjrIlM^VX49GQLEjQ3{AC?n35$kPy8P}yhwGIJmxa-iU8mrnj$JMnh zV!KF0s`Lj{HF(jg+tjf*0nt+@cX)0;)w*oy5-YL|*I13MI*5|iD8*{51Ig4SnfbF8 zl(~F%#E?4ewf4I+Q?fowJc`A+Q%_{yrw-+h@d-Gky|An@7^kL<&$>RPU^hf3Qom^k z)b&cyF**!|X#E(NZc!tKB?F=Y#xxB_P#KQgTb4oO2n_DErPSFzD)$;#-UCSQeM5p{ z&fMNLIAqe1Hrr!H5BH>GZh!lZ;4l#bKCY2QTOq*tsa0sXqIj>2n+8%=z-@(6c(cuw zdbgbqOhH*rla(p-B%oZ^RJuiHw;rrUkL}pj>kp@@dnmpG%=*+DgE@y}N+SA;^cSvP zEE!M?hs@&~WTyX1CP9Yf&3J!8i>cz?Hb zSI(f_IrB7kV~-Svk$%`$GA#E>n>d2`f}ffSIqF@-=zj|z;rIoe1+2XQ1_Thf7`MlM zSe5E6+p$)(yHYa`qgSCPa3rG^$Ah**1xs8wejW#Zo_zk)o%Lp}_Wse-X~okh^ed3u zxWpO56-cf<mD*7Qv zN4qCerc5=Y)B1{8lzv43`IABANT=DWk;!kv0YjQ-`t4Q91^OE6pSh(xEBEHOb1bSr zEqsiT3A|Wy{o^W7;W=luU*(ULZjZ!11nj=+gNGB$trXx(da>N)9!hF0rCMf=nlOyretgu;#DFLvPfljk%) zJ>Av)%rhg=fx6`gKXH{j>H&x!0p|#X#cN4$!b^E`8buDziB2v2NeMO@BlT)sCHsJG zE9=@)XNwW#Sxy>Bp2bzlPu}!Io(H|DkUPJooJ%RGBDU)|_=r;B`_Uu#C<`hV*k|E6 z#RO(q9fau#FpXbQNh;RMjJdZu36F#*DxE1M zjzv(pgUP~cWeio1fQ~89*$QBD+XX|P(TffqG*T7Qn&-P zZN<%%$@yVG`L2L6WuKQlsNgRyU1U~L3}sWL9<}P1KgXZr&+&JTzyA8d=kuAmtgc)g z=avNko_a-oDLVxG-uh3+CaBYLLdjE;ebDl6Nn14BTzq7hQk+$Ury0snmLFn9V0~^yr3-4rLxpar) z3`C!5BJ9xTe^|Y2>u0$86B4Qo6)*SB{^YA=QH^*luJIN>`CauQuj_u6cD@UK0lIrc z+QO$!ly!?LQ9en=`D&u?=wJ7_wn)VbejIwM%bUCkACFHVM0@&O$WedF z$CjVveQ!GSSnNe3IW-Y5s2rZJTp%j#1yU~qVr4NR#-e)5gf0&IFX+C?iUP~dF7?hF zpU>x-K&1o5bG6i_B88!&4bww3)&fBdtc=d+Ac`4g+? z40c|ryvRtDiBgE2htZ}e7zFNFuTHQg46g@U=pmV#8k@yNmB7vVHI-B#(JK)uQ;_HD zngqudw}IvQwjUoc2zteCPf`f5rIX&g_n9*aX3Ny@xzDbyQWMy#^QeRa8O|^^p8~p? zE`1&{I4>49_Cl>x-ky7g?`(TH+2mU4?d!yYqNh_K=x6%7nmWX-Vn{d&Xav)7c{>cF zE{@gX!}gc5C$u~EG)yHWQ>4%lx%b9i8~2^7mW$Ut;qTHMb^H)#n2mt@X>Z@Hpy*1L z)iak=zfzoRek?y4G98MQjZctvqNslDyNz~e&fEs`}6(X&ym)P z$7Y6&+MA=#<;be>rekEZ2In^e(nz_udkxkyd@`W?xjr&uvK=tB-?Q~^fRFKRX<-6K z(HBq;wX`Yp@o}0&Y>(?XPU|^tm9_||^ulk&w)i^;=k2g@rCcXOs>s%10A~Hpwula+ zY)xb=#215X3@EO}B7A7>4L1pDxtp8z1vnR?^(C}^!@zYsww5IyzSPcHHS`%9DR%8GM zDv_EB$DDmi*g3`+R}YVziMAy>Rk znAnNVvo;k%A`3Z_S10QLC_~Tm+8}{sF$RrtuB1Yiq5PqF(U947k2IEeB6~kEb&$;ri%X%PPGF6b?N`?zvAE?wshitB(ooab`)(qE-_JB;B_Ax|^GP2@`11_zn_RA)Q-S6rv*{L-8&@1wbz(E-$_1?!zv@iQc z%TYA%ZAThqBKvw2S6w`8xq9%#E)^zr-kGd>tN>~-XmE7Mulg?i&hesqg})u#y$=Ue zuO2p_$T{L~(H@YilMxeA?uOZNpmrRix>NohuZ9@w=1@DzCt{ALV@-u0`I8`%{;WQ) z8gEm)ZFwV_T}hYH!2p=8SuV>e-MVc~KKrCPtM?)>dN@9l{}nF~8OrX4&7N`qq{PXO zM*8jSLj*WmcdhjLO-(v{qC3| z^6)7GrHRIR0FN!I%+SjJlo@#I2U>@yV6u#r#^GxG7UeQThKf?%qb7Dr>T2pEEq_;G zPJMToaD3u|>$CKsCT1{@JOawbW>Gh@wu6oZo$5m<#lS?}&DKb+q-c%DwxyiJ5sM}) z#-wzz3@FQF{L&#y^d6oA>#>|}3)T5JrkU!rp0ygUYPT{s^Wp2wD)q z$ERuRS+DBzOubpNLlcd5rYtrfjB3)>LZ&NA%ogT1h1fJ8$;`{?VBP+L$sveNFg4=| zd|V3=P)-LxbB(HA zQ^+fQ=0t{gYylSgBt}4aLHCL-q0Mm-T+qHqz5Z z!G5MQqgGiL$%t?ru+gLYhvPmSuqJfYv`Oc6%CAg$ecw0k?{9p6|H6HL=Y8(uIOQT& z4zJw0;aW@5C!yh4xZHSMI8U-V_ij<=e3dQf^LyB;t~p_>$IBc1U3WQZ6v}x%XkbA1 z{64tIL>Tt6`dww@FFJvM=<+E4H;3ShRa@DT#`Wecv)$ERiadlK)y||TtJw#qj6CIA zDnwUp0?evTgO2oBhEmaV^O|itK1+hhwrd7+PlSs!9lI6F zK|6cj0Y@oY>sQ$}47vdt>Z;AJemW~S+wVFwp$3L^`P7|DwNc$dhs>KM3VTmRDE(1B z_CQ%buRY86($)2_W~?ve*?HsH9?D?yHx^PxL#J*6n4~CY*yjDyFPPYeae#)*OWJ&n#`LHqx`sa zVJQSI<6KBwJsofK&(a11Y+>2X{m=skYlZUFv;42c8Pr^%Jg6~eXe+O$bHcl+!pq9I z2m6QEHmK5G>*tC|erAOuWGls+Ye1<#w0zw!A7`c+XI7{VE|VT>Oa*V%8PvGd{jrq0 z^Ze91ha*nMvw9)8S3ezeBqEP(Xlj%Cji-1cWZn$%Ro~EOP#>=Vv{U_Rm?&(9jCux?xsTp#XO;Hg z8T{T3;)ZieZR!Rn+Hox}+oh|X1Evj`K&KadQA0oTv9fQpfwe^Md~Nt?o&x> z;>-EZC|k*cLl4pnG0&i~jDC`FySm z>slfUI;gC=WwYYcuXoe=ec!loek-x2dSvrzqYP+LMd-`nqcCPFyK!sr2#Ox=6FjQ`wlXc^)Nfhn%Fp^kNpu)!X_+R~bY; z0?O3}Z+lSmakh~%Qe}Y21?7wGpRKE6yB-XlTq7@jRrR;F-Jh0<8|bRDh;P;;;VxTj z`U3itDQ6J5eAepUrHZaT8pYd$N%ukOpzKPUD@At$jc8~6G8aKfcS`p!*eL8AwEza^ z?^N@0m3@!L;Zy%X)`k6Rqvi+}FTlAVD>Bb+ZYf7Yz%-y7tCqDIo$OZn(`d(p@r(~8 zt;_pyy`Va=8}L~l1N;J2`wCcbh4L^|4giR5C#X0F8GY76nNGys;?7v5tjqYRtO@a7}Z6NmgNXDgsvQB-y+prg)dpPqGiFFpjIS|N!xbHihBI&p=L+4fRj9{RO z-gQL^rw$;jK%AZX7CUF0a%RkxE0rSv?e_|1YzCC^MCW}9ozEwN%0LE`Bgl8(!Juym zTy&efoNi=rjxE`vWNZhHPG?5r$t2Qd@NaBS3Z~MnHig+RxOmmfVKM`iX_F3p|^bWwg?*D@mIX2hBua*>mG!3p#pnFlp<{p@J#E znt{x|@Io0b5C$Icp&lh1sRx+NKr$i#zRDrkknn*gEYsdV1wL7D=BfJK2Gzth5n_cm zg!~*w{YL&Uo+twsVj#`W8s`osb9D}BIo|IevwN>pj>#W8Q@~E8L=}Rae3rb9_mKiN zl*Mm+f8TXBL#4iMJd;^(mt14j-tmLtOzEeg*#Tw?<gF zeFngR+Nj$<^PF+Rw9Sn};|NARZgg7{xvX^vXCF(j9;J#?t__~%(8}Z8`upr(m!Yzx z`$R%%{!`A;6$Or9jfG;p&2XE+rrw;gU7z_75`cdiD;ffF|G{lE>L?;%F4wA}tPXm@1zK-1KtHtTo!oK`#>OCH&!NC(f= z72ybjdk2(_Nv?F%QFkF$#WSr(8iTlb@6 z#=7yj^Z@8N@U&zzWOecpSVr1sivKMOrocJ5NXvF>WQycjy5RCjPwHVy7v&S?sjm`uhtE3RgY$#NJ$O&L2-skvfFUyT3n( zJ7?Q}DC5wV1~-?NPL<`UsvUN)9W^nV)4h+NGKdz%^OkXvtF(c-%uD-KcZq8E;A?i! z=3o#nM{(^@M$4O$DUSw;-D!@-2?*Xdj7ADfNg+KaoAh7nhcHubCO$+ozn0rM?0g%`H^vzaibnRVRq;XUHnP$ zS`{~UjgKgSQp*VhOC_{TrOuk-oD=lbCD34cuaS5g-uKQ3DyDBZ^$ z*!PWJ-{1KD{>HCVG`26q-YBX^?$&$Bhl{qSJ=T{e?|C^ED%~}=8Jc@4TeYLpU*Fi9 zD>$3~tDoraFcv#5CXYO1rOLK5m~2=0!`2o4d9iJmTqD4Xsr9NEEUaF>9GZ;mgLfDv zjb%d*Y=@&bN%lO8Rq@ohQ>g%YRi8Sbtww;v?wK2$z~yY2er_PEw1qRrhm&a-*`kPQ ze}v%9pt7IOC1L|X9BXy%^F<@uk*UQue64H?_fNLoS70}ju3<^F^L!_8&yhhbj~a&QA8xI8yVvkfcQHr^!2zflr`n98zAjALrV?1W%5OV67{TmU!jZonhEXuj-kYy#D<-SL zYtoE%!^a;oy2QL<&|-v%v0yd>$>rGB9x8Je7&`l?VPUb8_v%CQsv+C|&0%LF z*j{VOI|Fm)zEaKGbf4%2t9`6^9ZFIJOS@vKF!q>SuuC8W4V!dw1gdEq0UgZIs8qQz3m~pt0C`Jq-ULOSNC@hA3vWM}|MYmjajn zcCO02H}_n=B|VTAxM+0v<`hKvKpiU)P|@H@&5F%QN}rstR0^$@A<9(YJ^HK;E*-78 zf)&G&1E6LydX8ZYnJA2a;6smyg1WyG_H{N0IZ65Q+D|;>!O-4r+CZ6~_|Pj?d+1X0 z?MBnHRL(Y34WaIW80}*uP&A-3O;g0cR@31ioiUgk&#D$R+e2`J`bs%NEs?decD)|e z-^_Cv`c&_K%VOBZQtp*oX`B2gdq7D0n($&E+o$9Dq2oNpBf&xD`SyXFIXZ^9a=uPB zCbT`?KL5{$`;<1JS#O31gy0Y!7pn99gRiks+`t6s>z-y z%e>L948*}pgP%4M^+@ZeZ?_1H!piPgBmxZK*L^-W(1b!Dg`=X=!6@%CS%tr<2*&D<*Ed>qsLGuQI!5xy5s8W+Ct?Gdxx$Rg9;ua|Zif!Qq}X zv0=90*vX>FK+-xYe#sXfr@T1(%ZGfvTlYzP|Gvis&GBe!ATO1d&N1 zhrOkG#Vn6H?c($Z#Dt!H186|{a_Fk{PE#~2B# z_E3lA`{Zk9JGe^e6_#Us1*;pyv;Y7g07*naRQ~Su3<_@Sb9brF6R(vCc>;`D$;mTR zOYM*J!_kX7_}%(lr5_9!G+z%VbkZ;Vpq#8h8Fot$=iX8e@%ib)VHUjsJL#3x+rmTs z9Dj~K$KN{s`uf7xSM00&#h`LtcLuuFZRz9FK&9%0H)9&i>UQOF6`9sG=j>*{& z9)-zAO*q6uQxKmZPF}q9z1C@K;%4w@TlvkLKZtHt@`&qF?jpgwr&Il3^nr{m*z-dXZT*jJ>@gg;r1*8h zmDf`ZY0cjtyS^C8#-V z?~c$bv(UjO^I#(ifzWAEGRJ;gd(7CrTPd0QO(hVW11VY_bsK~MzZvEBSTOBKBRJbz zsl{}*xHlwfkQq?UHbyF!yyjjZsW34;_SSw51xeBL!5WKk6ilUzm_+P=$KC+|2RDPp z7Zuh!7{v~!KW8A{_g#F_y;D%!Im2j6YMQsyXp2nSRkh1r2?YePxhSHCFc{qzc$+>% zA{Qo8>{Slc+ikqAx5oW5@HvX3vMY-7Db&j0aS%<9&w=;Z{znRfET@Az>~*)Sz+@&f zWTtUB#cjj2nQ^Sp(soS&blf}3dSPVMQe}i9oVzy%d~3kSVA6?Rj0WR-Oq6gqdU68! z)iAzP0eB&?8UvFyxk9?7KrvcPQ9O89zdIEjpJP_nDwew8#jJULGth;xsgT{ybpD>J zxSLj5sbD|6+7FHy=63~MIAOB?g`Y2j$#)756D}Lr>7*+7JFt95(0eV|inhBi#G(`d zik{`GBtk}cVb;o3MX{>gmmfTMo{N@6`Gh!R&U&Nql$ggEgo!;tm}Z`yMk{<2uge ze3F&Lx)F(!g`BrRpl@9`_C5w!1hRe2}mA zq^iNbjz05QT_x-A*gp+ndxp;4Jg9UP(y}cqi=m8(@?rF^1z_)*TvV)y&?`q8_djd7 zO?8U<8@un~SG@|t=hi@C$Cf?I0CPIG?!31>?8>qPm^OT6upF2Z`Pf$Lv3Yl3^>!2e zX+G9cibL^kZxs)ytPVjB#p7QcX9qt!5WQrihu*R*R-U^zo~WMcM>`)!tyK3C7k)IM zu-XuNt|vP;;z{XV0vFn9!)Ds!dzCKl{f>?IL|35goW?f*u)&;Lvv`x|bOMHds3*;H zta>P>aQ7NhL#!8A2W0#Cu~m&tSXQ}Y2`QH;_9ra?irKG*fLS4vv-1koRBkVmKZ88<|8IcXOJwiQu zbceVjw#o=5Q|BZ|^I{a`^GoY3-IlsmzQLhkAruWor;vUQ!U;LaeKX1D$OAb+g%X~; z9q374MY|1yiBf@|)2DXyw_qdig7j1SYz-6DS(TJuxlAJeEJxrfZzvqk;Is0_ri7Dk zym9A4PZD9OJf&s9LQH0h%pc1TM%p!~B5|xHBI$xXmkDt7< zZI45%c~Y&+gw@}sFRjaa*gqwTEOa{O+%Y+j#?}eenW+Wgt@jp@P5-nQ}JqJQv8=axAQr?I;;f zJqaaCSyx}jHcLAvGfO;?eRbq-DdXMRKdmdW-SO+!zwrJ23*Yyz@W*nySsAMa0oy8{ z1-h1*WU$~(d!nw*dwhmkM;IQH3{RCS|BzRdF623-+uA>@w)9D3pYI*Z@^|*|5meqy zc+hKkc5jDs|BbqC<$vEULFFuGu$WVkj(WBMmMcz39Pv5_E(vD$2J;#eBL+Cz%Aoi6 z#=ds#5wdUjUa89>utJ`JfdUWJ^yO$)Km~2P#~#Uu*?|kOdT*J-w_z{Cg`Neuav#C9 zcl>_exY?)OL1uVg?|l~$=spV5*l2hg|NYb@D)v;VJrKCkSPl6=CZ-T*M^ZCSYMn_$ zj$Sd`7$J~+hwFG?FwO2Os;`1=#gLB@TVg#0Yz|`VEwCm$NywxPy)FjGS^0$K8pLUj z4vTQ%r_x4D!SC)|zls{96G%?xBg5wM0hJ0-G#>EpF|+JTTr&;vWZ=Axr*HUeG7i~( zF&yJ0!b5S(0`|_-#H4-cjGDVNpw#BdDOiew0GfPy!L zk^|TbDCZst-rpZiuNo^V8#zXzj08Aha3YgKV4WT5d!q~SPaOipInryUE9f??jlCmS z&5th+XT6Iupt8+aTgbKCqAcL%OnYS`m3r|D?^NC+xXiIAEkw`67ec&1Bcmzj7ry5Q z0+f5E91X=a>smDd<(CQ7c8I<$J!^?Rd8aC2W5$n%bIcZOec&I%XJ7FB38m#*Qexj1 z?(c7G-}qb~d>sMh&*!R?)h3QLtMsI+_Fld&X1kF63JI6`-thARych2|yFTFU0*a#h z6HXkLQMIN|fN%!z5dWoIKs1CP#?jMOrTRekOH7RN%E z)hpjp19X&h@;8+1cR=I|1vUg$t#i4%@cmFopD5q7*J*tKfP2P}Kcs^DkjL4Q;;}uc zh1m`^`S6aFP4^DmogEh@hg3|iyf^$Pnsm)s&Rl58;p{$4QnTZak=4J-q5)rXIf z2hO^Gd)V`<|K>W!v4S~t)CA<*>h1?4#8Oue3b z_xvz(Qg6wT+pBCKjA={9gp7~kx3^(T8|k5X3NnODPkjQhCpEWyQXb=i-(?Voiy(`1 zNn9{jr12irP6nq=LzvCl2;0=*If)J(M6Ptz86cMJCo0%7p5;WiUBP9r`#|f)$q-is zh-J0qVOPwc#moN5)R#pKYS>STD)|LAW)gd8F2ezHeE2GWo((^P*_4sde>wtPXzrwS zpNd1R)SvQAFN-T1C-b~GWb|QjpB(rpQ$)wDNtAw8+kJaIC>#1+eTybdaXh2U^y4FM zscycT2MzoHb&Fynv8tzvoLbD;JXy&6=t|L%8@TP5J`?pRZ*IP^%4WNoH+X6v( zz|UF_kK!;Q=MPR^*AH%-e6MYXy z8?o%R+|L^Z6b(>sO22w49lh0&WcSz`zkdC~uV26Loj%#U@0y4lSyD80w1+yhvTw_B zyQcWO3CL=bf~d+M=;p*@_V=_8TW1fv(kw2p^wmRRUxFd@wE)1!?{D0S>{|xE2(_;} z*epuM-9YpTCih-9wW)gsm}w9YfhJ}hN^d$x#%u@22sBLP$4i-M`sNB{$6k(md9PBI zeM0tqLuaI)gg))Q0y|o<#(1b4(3YLrhP{vsi~(h<{o-r~@>WoJ8P;v^mHR7m<$!k( zIrgM{5;c|L4p6tlV%dtBIVZC{n6he!b}*5e1+D@r$l_qO!ayMc3!REj6?no`zM2tr@AFEeScjgNyb9r)V=CGb&rD?K(^&nFL_tu$B<8prJz0wM4xt(*Kk${Y`7 zU-5#2KYU}!48S}ps@EC-KgS?XPDO#;azlS*A0~Gs{m*iXR^??rlI9c~xvj?lP7A!! zl@3x+<8o4|ylx@hLWIcv>t@3!(Vj#(sS=1b^cs zn&N}6gN4L{R-{XJ` z|FQRO3y$Ns5@3LGR`36R?3u1ki`WMsfB@y}nce47l~b}T@dDxw5|mcEw*>igC=YoK z#ZNeQK0=yf+w~gIkr*U7bd&{Mt?bEv;%pQDv>AnxNj=^g+NYj|&eQsT=+#lo)^1Hu zC#wm)f~YdrUV7JBPKkb*sq`toOOAVNBtn%`CvA;A+{IbYL1ncIynerRAIiL*mVWAk zoB(5z`NNKMwTJ!2wwG?L-#-vgkBi9ZPYl3XG2trIvZHc^fVQWqQZgHO4>*^xm3g^%Z{h<63D(*wZOAtH0f|3EKHKaWlDw zYXSPM*PE)Jh7&4AskX|=1w2a!g?c~|lEE=EBw!@zQ62O!krOqj2-wV0E)dKgzd!TgKM1n<+W!j@V?bx|b zeDk!ahxt)u)2~k5qOT_$m5CRJ%zoO9PnZwC&EqnjeUje~^3=Lsw(k+}moYGb=}&yR z^bqP2gO{{#M)^_Lw+?u2S+#wTL$)sH0W-S@S1EJLC{EJumC89$qL^WWrlDq&jTg^B z^F|3K*J@=WjW;ay{qFZ<$2iW_iXVpQ&++H@b3BjOf-EN{lA;``qvGIac`dYg`o2D9 zr~T=8s+{8|lW+F8Yol_Xf9jE^tnA64_fcs3 zXK35?xa9Bj;gkf#XnV;`8FS>~mPjolLC`YXyA^KMtJ<>y%0g6m*56BqZX1}tc~0OA z_kzo!0P51x2ZT-)cHdo@kK=sf`}-e!zrSOVPp(v^E)@ag-bRucU)>_URI8LZ5we@n zkUZ$dLQ$@=o}Zt$C5p1W;Av@3aDVd}AN@xiR&YDhUo&uvQasxk>@$FTrb3aF(iKoX z&qu&H8oE*eOhdM?Vo(*YSx^00U=$w-a__Zqzc=oSE0ymf4fgJ`sUt%g zSTlE3Wtt3FjdW#;$i=)-X`XHO>@C)>0!tN$39w-1aYu-WIB4Bw zxC`PK$`hi5+@y)1Vq(uHxV3bCu;DbuKQV1Jf}6hDnG|>vUt+CPTXZY?cutmShd{$Y z@d3x!Ae9eMld4@zcX{VT8rz23Zg8*P?R3Z|pUR;#GhEjNUyjQ!_{FL-og55U9i$@d znJ7fDKbK>16{GSnaU5{f&)zW^XiOv$4&XADOqNKeeshXXyjrdgKBLziP);wN2vy~u z{9Zt>li)0XF+0ER3tc0}xskeh$KrcsJYe=h>QvbG8{h8>*Y`KB_l4_w;e9RaeaF5# z8Bo^iD^pjmb;UPG^qs@OP_v4-3@3rOF&uu%f2b86Xbq}54gSB^R6cdM!RvP&!x!GAC3TSHP zHH>7EmplWY=yl!~o*T>xrSq^I+@onWq75!7$7>4(&X8<=a6+MqD}7;!3`hfUnPjlp zf{!gj&+FP|W$%>lh^#UXXtOcY0adEmb9nrD`5eK9Tgj&ql02U+5hnAYT>p6Y!=bWN z+kc?($vydqj%SHl{r&iyb)OI%P15=)*YnpMq95(JEg>T%hOSLMUF+T~3Uqj`#&y)b z*sOAZP0~CUkLHkda8f@XK$sm7+4XqtGY9H}G{&*wHP78f4w^c)>5o{(PwN6}t(@%U zfaf$4i!37CQim1HaXe+~!6Qg(DbGL6R8Q)w_VthC?Ix>}rJuQ9nevJf*PtGR)=zVE zYW#$*LMG8!^$aYJiN@5&{g|a*`zv#$GIc_fjaPxJu|Kj^h7gmskAO0|UjsG=jwD!0 zYj`L(^@7!QJb+iWm}xr-PM@}Bp4FK}X=zZA)_n4b@8=yKWgF#+^~z;LS$s zP|CE-x;|wOIOy;5YUNS{27jJ>?V#hd#{fdljq_J`!qMdO`doZA5s8%p)nnTgmbU@nRDZ^2(`pVHI{?V*)9q9hxdm&sW z8OZgI+DQCLS?n&w$oMO+Cr^)6ud857LGhei6Pf%z2sLTB@Ef0Uryo})-}I{iS`0Xg zKS%gq+rR=B?{WuHKM=ed^{Jj7PaiTA$;nP$t^ww#$;Y``dB}c8j)U#UMzDhq$6kpI zzX~X0<<<|5wRf#jX4Vl-05xb}*>Woa)EqIZo{&unLn>TpyiuH?50~3eP<~YJeSR!` z3O#86<2?D~pX1N*=lEX^W|7m$Htn0(smiIGLOnN+;FYQeO}jG>f4o(?L#cqO*c`e; zTxqt+F7)(cQkc`Hs)m6hvO3g?P;IlcvD)?FP`@|*)1aPTFSmcYY#E5{z?Q3SscjLp zPlLzy5= z(2A-KmBrlN=SY8D_KBYjPMyy!WawJ3jo8j7%d6DSr5m=sqrv3{uji!Wv#*rn+4IDM zdvm+N8~3}f|M~vL`~8h?W{=!=Og?sp`G$FHW6=%-`oZEF8eC2}JC$lt6y;NN&$Qql zRx2mbOF1U`K_U-*C)WM|)przSJPr8Qd*8VVWw##aqLfmh_z+NLKKr>ES<+$G-pXl^ z=d%Wm{Ypm;8!KSiQop9zmXu^oh|^(^*jb9LXDaJ`hr?vAPJ4Ybpv>U1r1+^Bd7g&S z!$4H>5QHmrG3%$o<*6`oHH&feL^FMO?3c2aVPC7#tZ~+dX@$Hr%NaFvOlVrCMy4?v zH=tIEV5P_{L!iU7q~%W>x^>SoCz@dkqpc2!g0V8l{c?*2fS_B7UtDV@edJ(PX2 zK2OmmOhSd42vM(~a_^t!nG2nJ2ANw1&sOabGy>c8U2()pmZ~9o5~u=J1r_%aEapsM z28`#1Z2mA_k81QIV|l*dK&6(dz~r)4Y;^Af((}q7;&wz(Zi^5)m^Sf&vy!x7(k27( z@nG=P%0yVsh%Nyx#fo5}%S$oerJge}rK4xfQ^<0)$s@2_G!_Hzg4;%dUf#}bLZ5uM zl$3;7cX;9~c_?{d!ZDms(Z(CM;9Y<2`>ygfX?CZRU6cwc0KPo}%MM%);3B;NOb(V? zigYSl89NxTJknnl@3Uj2&azzb(=!t1YUM+jgTpgOu!T(*yokK+axR2!9UAgp!YxwW za?j)*+fs^@kItSpS}M;u`KUS?&M{ZKw*GF*G97jAdwg9B`+np4{>J;Y@czE=eJxzy z5meUn*R@*tx^l+rg>_xHR-Ug5*Y(2J*9*VCzVP)Gt3iH!ec|gBX|~_*JJY)4`Q0<| z>^2x;#w;>5TMIj-Bs*wPbdUpdkHb-T*AA!ovwmy*#7|

#NEgIutJdo^{yc+sENn z*>K1r;@x2%X@^IxTDHm%tyiBm>3*LxpbT-Q^EX58LCZOnLSN9a^5W?LLJ8hp*$3ca z{D|xx^2(AI3(sm}l5Lmo?87>H>XF%Hk(OGY+xdN_)ZZO6)1Vs)t2(1d~F+9Sg^YVjnHjW?kJ;h)Z(=nb?zP1SOr)JsO zR>kg?!xXf-!jWUJymn~pRpO(Zp zATmPvde^X6d4DOMfqt|NbKx63{#NjBzSV7Q`%))L(cy;f3!1-hb#hIz%6HdVIS#2K zm94gppfbO!jmCG#3maK}#uRTfs1YKR1eqQ~mAF~;qHL3Zo!DW+v6c8>(m275A@EJx zrSx{3b)p2jokazmS8H>KMfO{NKiI{?CVhU=&f`mFA-6t7LbZ>)elW3vqgiGwIc>cU z2ggh&&+naS`d6ZU>~S;qRD|jQmbN=HT`9{Kdul2Ja*uR(Y75Qr;s*J&4Y+ZF=f}{`s~;)-m;^4M$nGQ(ag76o|wY_tqJaKIEV>igN>f?DGLw9!w8+Smh~w0iV~ zV4Qv#wRKK0Jcw5yf536jSY8hwfWxtC%evxOyK$W`GoxdZ`{p@dEB#Kl_@9~&R^$BO z3yJp{RBg%Ka1}@&PRgf!`Uv)-Ck~GZ_3knTI?#f{2W{~D7+-Y1ED5Kv-(~#ma7dSC z)G#PrNbCl0zP!!T-x;>%;$ED7$m{IIs`S^fFoX+4%q;Eglp~n}yV749K1`-%$(Y+@ zmCq#F=EnyPV6_!Aye2-i&Z@<;j_QfR`5?$Y$DiZR@vp~g#X^87ze8W3&&?10v3V5V z2`Nn5@}aBqxkE-xN^Op7d^UNF3PkmpH6KJ90wVj`448}O;vFxYX7~g}jkN#DOHx9n zZN%-aT2D-#o{KCPq#u65+{SeV;vLA;#>~{CFit*kLJ5b*b?MMpzD}%A;C{dO@ngCU{0(JpNfo2w#&~x85&1_*iYGYrLr8QKkf9< zF2FoDlyqcyxwrD-new`W$`xF;SopQ!SnlBiD!dcC)sH*g6;Mt;=>5j~{f?E&8Cbq^ zwet2zfz8O#wp>U?S!n1L%7#jR-Rc!}dEy&kA^MeJZhbp+s9ddFI6~0MLQ+YM4s0YELeT4gRbEr|f30IM=YkV}IK_stgUTGdXoSdDCVuTCP3 zxO0VG;{zOH1$9CHind%;?wvtI09dgFqGw|SPOx0{nD@w~i963Pk9>98FR{*=>|71ZC(;TuTsQSpYjSQH-DsKe(aT+ z`{rzo-g31F>n$8<2WA$ScL#Vssu*=&M9+f~1U55o*7YtvII@!<4A>?WUOD0=1Ic^mDyz^@N+FWyi( zDC_q}`wPxM(I<_H#{cxOb(2XS=vMx>syB zxC;)RyudM1>pto|&ZOyb24|s^fiXOURralT(}dO8+vd?f9G25K7vR3I*w3|w-e0~D z?t^y}6L$95Pp$Z{#>g|1N9rWkkHOCRw7#Ughu5*MGwqwS71z;w>K4)oU9HM<+kPm1 zP_Ury0vnzw{`0X6TzaLmCLdnr*o%89$BOV}fbV(y5@gpP8D5l;JLP zD>N=vNB<&^2_tH~jJM@o%cw>2)?WL@$)8Ui_1}(SO~TW_t(4^b;_J)nx6Mgf%ERO0 z0k?oF#i2ocEbZ%a$@F&%Tv0XvC;Kl`Sf)@dos4>Swk74T0hyU4Ws{uUTW2Y4y~SgX z+9ONd2Uy~e6T5Y?PeP|{*NjMs+#Q)*%qkdMo%iDdj)_K z_LUnO3s=S#tZl&jeb58ZL^EsJaN^ZkwQfBvae%J2K`jE;$KtDy%ohnvG!D(t#S z`N|>R4etUi$6m*mE3#{)a??>yg3PgK6~2!Gx3wKa+!R|tG%a{G_4Nru+Tiz^={Q#* zdj*cU3YmKppJ}gaW}6T`VC_&WW+e#2>*7fbs0AX4V{OBBrgdgVdA%xy=XEWt%X+^Z zi_Y#HE34l3H`aH&|9-#m{r)X83u1Hav5CEGX3rI@YO2K`PUGNo0?=l~7|?KTc^Z&@ z#Z1wCFYI-`*X zsmXdL;jX&Y3z=iKw(e(jK%gL3jcsGEfvYeg6Gia@?I+VE z7f^OHHD&`~nh$MKj~mBeswh9T{&Ckagm$^7D`%caAWfzH2{L}Rg7-X~aQRE}8KYD1 z5Kd(fOF6TuWpm%(ip&QB!_g6o~8>#}gLZme*sSy++zVMggy+hY%U`cA-1i&4ccriXx?Y*qIU4QzjEc~INf#>OYr%37$Q1D|sbfAiB6>qbZYb~a)T|hvJ2ZdOCmyf!+K&g2D=DTp ztd}eJ=Av2n#7V+tD3+P>+A26w(!Up=WgVshA84Qy{11659i_;ip7nr)+@pRK>fy>X zow4d^`3joORfgBSaqpPW65FBNjhpbn#{v$G8ywI%2B*&AB7f!>MY&exhp1F}tSVW{ z_93eddEoV`kKdc;b=g%`sbl{_C;6@ht;je1xQ^dU+HBousmrCeXLb-^phwlcazEtS zb9AgYRtHFAMOvb(kH3PLI#x{W8=^E3I326A3v>Wr+&$rD2iQLyEi3e1Ptb5p^{#!D zGCu`v#&r+Ua2}0G81iO0Aq)*?$wy2hwJ6?9_&CZ*u+nK|+)G#jES-kz!-xNr_bWbS zgA4D3&((c-0M|e$zvdZKoFC4#Z+i!ge0GfTLZl@H9slGBpM%EhTAdDeX`kj`DIPp@ z)nsZe*U`S5AaU}6IOxI4FBr|;q4-wnRe41$8HhT5O$z6yj#Hp4|K7l`U z@Os<*Xh}a%!QxOnkAILWd@6y>&1)+jX{nDqo|Wr(<#6T}oRtr=A16#cF$#ru_kdN7 zF+fZV9b1RhYeH`p2{v3afKga*qLuH7nf+bFc<<&!Zl$xM+_y^fje6={- zEY$Q>d&(iT=1vPNm8mQ@C&nP`^p%?fzUXtzwsW!9``D*;ZjtxM?@?`RQ?AoayQ8*0 zr44}cSNo9D12Ul`wCq~BScj9wEG35>?~6`KzT6O_rXpiSyUZcFg^l~I$H=SNZX$#e z4z0tNo-G64ed}`U_^Y;w`tD|b4s252-TI(!0yR#1?@o9pHnOSP=l+!A`4>e?m6^%+p+9>feP z%Xi;%zzEv?;1pdao-*~?X zILG_WCF}P;c;DZ+cLbHU-|cJ|2FNbsMyZZV!iVx-h~9`X4;mc~c<=wb*T%N{2cxYS z1RS|1TBZc|4c~y>j(cs`x@#4nZ;$-$xk5Xxx$?UTAJ@t#+K^MTQV~8F1NVUG-gjE8 zsbCs(MO*o-1eRTh;0%e~+BGl)>4#&Q!TAxv478yDSPF-!I5UveinUfM1(o~|Z;Kj0q`&!uhb(Eexuzs&o zy7gY|NP~C7bDJnHg~Vr3@T{-#z{(I_xo@1gXC~doyS-$OFN@%h`G)Ns^-+;>w=vczTCH?43j@`TB*dV z9!5u#^e&aWdiEftQKt6Ifng?6U`pFtV96uiu!&WznPSiwkc>Xw`^H`y_q}lMjkPx} zTfHSfDc)#4tPV0A2q!F$Yid7|4!rq-bz_}PD%F=JJ#svC z@=JE5iyL@S=h3%WcHK))hpX506CFGb28t-lrKW6i=sQ7=0oPocWfk5VI%b!nrS&+e zF-rMgyqGcqku`=v=1;SBKXhfl?bGoQ3`vHe3%6q${+dtCn;B<;*<$PG_xMKIM>pTA;nGJ zO-G@8gbOr}wk90n9eoV!gTSV*MRqIN!rDP6Fc^09kM@~gGjqB&w zQ&P(VhOR7}%7%tRGCg|k|45sfx>P~(F+R(30#)Kn*@T$mHh79BjE+>crGfze4ekFN ze~v%LzUuU09_RpHZ1vTZ&-A9J$btTi*B#3Q}Dhur}e5t3GR81&VFm z`w4@*k2(>13P@S4KK$Sj4?(&&(&7ybdA%i?9s#+}PhywrErWg_q0yL1Le;KF9`_1#FRS*o ztEkIrXY!$x)*i#087_fkL^_{)8|((0-|rj$y#K-b`=45^968YM!vGN@l}L*Yp!8^y zNl0JCVIv^Pa&(3Fj-dUzajP*Z7-|HW2h(ZeY2$VPx1&0$R5AbxY{|ct(VhL(WO^;E@ymB!zYdTBP5pUPTM z_`$d4!~!c_PT83}T|7H*m2k`T(A-Z>0=UaoX5XMZTQ#$da?Y$%7#o6bF{u12(_mL% zIrmHc`uf5v&tI=EtW2#`E5o_MW$y|qU(0Z(a&#@iHF!N)0TsZ4=(boj0jtRKm~9C< z|MCXeR$EI$#ykVd#PWWnaqm2MziGbMfSik;d2)`#Np4R-5ANw(hm>>j`BA;JleEF_ zEH9}sy0On`Pm zg2UBxbiDEq8$F`NNi6ka z%&79MJ6Dp+CbV4wE`q$QG4RzyHuQa-&5jWiLQuU++p3U1M`F;BvF-)c@e_&aJ7w&| zj-xC(C4dZ6o-pf)MIes=+BxFVA8jk;Ov(__HECVL0szx)pE`{Gm9c2D6Wo;}8>{8< zb=}#cgGEoAJIl1=o{f9SuL`F(+!rb}^;y#3Gs2CE=ZgI$x&Z*VbK zJI_v@H;n89aEsY5Y!E*1z;PHm$YJI@nGCl;O} zz~o-Vz-cEJFB;Iw{(lg<#j`%}?!rdmz=TdWN#rpn`0}oOx(1*$Z1TJH@74ZvTy`3& z2k}8yJhm-<;Hr#H6_fDO6vnq)`_9SdgSNAtwQJI-TdZc~O3u*bC( zoQBhiPgZiXqoD_lsy@izRnv#3vro6o@k(#+ysos+2M0WvGJ9L>%nR2IHUi3K4h<;p z3jgf~8#5Wd6V!E!dO_l?UJqIpBn;VV`Md{DcFJ9Qevn!7JhB10ZoTFH(YZ()lELIP zg34X)3>;6H!DosQJe@WJ^CPgVe8{AXDU$@q1KR6T|Jk3yOFl=AK&Ct;u|CH%o(wGS z?J(c5-9y?{^ou$)GtCg1pr>K?6w#K!iQ@nD4&|TaAiQm!Lz1Evz zC0As7aZbvw?Y<6qhK5;{L*~Wt&M}!<<0l^e=lFB{IX)d7@KWAEq7&ZCvpt{u@vxu2 z{>V5Sgua5FA6I5N=4DqQ{*R|J$CImlU>U57t`M z(Eb_k2P}p><(O;vRL5+TI=W`HGO)+Eb=gDSE|m=|!vWjiHa%(x)HH!ZzsGUi3T~Gj zF%yqTbGBo_h()H*Uit2VOAwpkv!p7K4eFP>wyVts4?N*yWBPB^m)lvarfh9}!(>?d zuM>}0UydJj___|yc)?ixKrF1R7`0tge-quU@xbv~0GN_04BDI&x6wgmO>2Fh0cFSi z-q?5VNnAnYs!kLOFjbhEb{wdoUCWy&rg$o+WR$9?Dln_#*3ZXacm|OH20rgh0sVe| zZ|r1}Ikl-~ja8sn;4vI|KnI`w6mBj!3}H09HcE?DV2n7~ z>57Mw>}gFMXh8`+1&S`ZAd|Q8PNyTQVKy0Nkh2IQebk>%yIeQW+VIRYU&`x7*QA}J zOVUn|Kd@CLFO}h+ZJkHc4O4z3piKm9%QpaRP@3{Vs{Xy1kBe1;rG2k*|_WNV>Vw@f5 zwCt@v3h6ao)pnbz&=#+0Cy(j{#g{~ZtkE$kkb~gB{2~GXsC1hq@6b5#L^c`>dMN!o zj#G|id#l|>?qs9uhk`+NdlY4v%>^CgCU1D~bW9xfZnq1(Yt@+`Fxy%>*x98=Zcr^h z3p!SKcOU2N5l}w(`gq++k~TC{arwZ9DE~%4d9BRg(knblhEWgn<}V4cHa-tHQ|?!r z(y%>cQQ;!T4`sRv)NJ%{tX3_4+;HKr4>=?4DP6=-dfqx|M9SFCzABKlw{uA9T66*c zk^(;aZU$}|q{Hl7;Mr%^tPkkLp7Oip0Lz;w0jGASAVKSzlLo_PE>S2)!fF zBmyvt`j$N%1H%~qFfgu+`$1y>SBsVJ+X2&BY9UL-{jpY zPOC2}Q%QG*hi+8G2m=^$Nb3L&JjH1y5Ih zZBpcxXBgyMm0hgy@j|^=DGOz6+*78W5 zTrj8x2%Nh;r!2@P3f3lLTHPZLAwLm5Qt(w}r$u`G#t9siQZOfx6(69Zk1Lwg8J!bU zJ+llnqLEkhHGdQCQO)8uEaVIA^C(XUYQ^&4dp=H|n7emF#HO1%2YjX;J#Fz>8uexr zjuZYtyKBfpbHe=;d81=H>jLmNcOUa?{ax`Zf~mYS`RnQUJRn{Chw|kN zFfUVoPd_6X!;m3mhnbB)NZj8!0pEa3xmuY4#TF7lHsvv8SNaHJ!nlK44*gKff^i0y zYkzI}cJJ8U<$kyCR%Ii{qW%?JR?%w(md}5Nc}|=m?hGvSdH!?!IsP2~KM%@G9Q;&t zm2vpTOD#^WTUl+ne8{ZN3TvOoxTKXdwl85Gb4b~Cwd8|}kH*(v~yYw5zHDW8g z4D8C5XQvEpUu0%*kifDo>gktNx>9!Z>e2`dUa61fG&1VBs(YUDy4o}$$Tt>k9Cvh} zeA+Lq3!qaakAd|nw8Z(rf7PF(vT=@OE9SF!Kl<2mt)y_t|JEDk)(BQ()8{rEDoAe) zx5va$m1EMeYP}iUHf*n02xDG;RPJxB6AZg7{CLoBJM>KIPVBaB`!oW{T;XnlO9B&q zNZpRkGy`jxJ8hYGD8_J=P8yTLgbygbh+$U`cTBNXaxf z*KSzk0Nk&a!rQk zWb%yxk^A3~>8~57SU>lC4yBz82*5@Mb1YU9Zp|8Dgtr1HE~@V^n0tr)y=Ojap8%9o zLfxED0hHG@!x;zs3zxjLg&GbKOx4OczQ8$x(k)4IS?wt@6Z+&>3qw_Uq?gHAWW5=+ zb(H`BAOJ~3K~zs3#C`IvA-rpLzR1I(Q^;>62skwT8+k_E76)1h=TAp<#jAcFbk@AJ zd7?p(Af6a_*+18cRmupLj26ji+534AX&%Rk7Mn~kZ|uX?l*(c+k!7(gZnR=?}1pZS2a9ZPt&`Z#lE}gJc?$smCAQY zuu1BEm4!k3Sb$xrj9d&DUlm}klt5gmycS+xFMPeeDsbE@l>0Nj|N1pQfBnL*Utd_Z z@b^Ez@%#60{Ql=Re*gXlzX5E&E0}$Lt_&>;i{ylB&oa=CnSKd_Vg?lVS?DRzufYmlR4|$%Ky{d3fYSAwBF9SvZBZ~(_u2kEJ5zhCA zmI33tT~x8pURB@WNtXvIC7yuRPvDz;QZDuw2!2zK%MAojP6A_0?aDoLBpaaX?igV@3D8` z0OVCnZACfh9I@F?{l?%Acwm!X>3T(Hs+)3fbc{=W#O2O+HqdyZ@=XWAswpV$ zSM(?^6asSw?H(m{W&k{3y^wpjb0t-7|KXmlPSY_D{wN@-{?D=j8i!gJ6PN0WWjj*- z>i9HVZOU=!_77P6Jo4o^V_S+7MzZj4Wm>In;`<*Ba((`&hg7S7HJChu1_12@>Ki|) zE!o2fSz_fWpo=9({-_7&MlWPs2Tp6yY2bPGwJ_wP63zZZz0BQ(yK)ybWLdR~ z7%uQ>U&X2|T`S9H@YHv2MRaqe^3kX~RtAzU_vCg6=kCxtBu#i>hB7#pc@Y9*87Dgc z^J((M)4@g<@H)=r8(PilE&2TjAdd`fcTL<6xEOA`YbyY1PU@%d>PE}-vXe#6j0vth zcObdWwa2&@1pXI!pw7~dlYk&u;Dm3*jWduf&OyRoAE=Qzjnj0} zDgSFst0~hD@srQw0VVm`Hi6RXq6nUkJBr3@6|&8h%QHiiP8jB-!d`4bcy~Z~;kw#q zdGCfR`8>CWDB6}zjY$KWi$wO|XO!g;=H>QTo$|Va%PGrpwff#3nRxDv_kH)mq22OV zC(rPD*{aM%GXURPSkyb1*8n6Hd+Vlz#+>l*=lFB{IsWbVtY@}3rcP2aGM_nKJCs6` zzj>#WkjbN34>CvElb{MX`ch03qYvrbER>l!OqpnWv6=vVLfbW zCx0=t81dATTYyyB>thlsSdO-wFonvCmVW`Doyj64b1&=WSwBBPi#Cply=LbsWwGVu z5FR>t_T3zj{(9IaM6?ZADI3a%zjg2A-b1;9 z%E$SfwTD#rE7v{bb8;mb@@e2PpnT~J$Xu;eEbUNAYrqA1dc7-n&<_=ZN+aXu44B#n zrHOhf@>3Z%QfDz%GN#?Z-zMKC5cgcE95X5QZP<1vA$s-~fgFk%nJu+b)5JNH#u$%x zDvB-_wxrHpsY&vWP=w*^_nR4LEIn55$f z$n*t^J1>0@@ZRmBAS?Wl&jt{yl@EvJP$UkANJo5HE>lgm|`WiuH{rvUo7ykO|FZ}u|&VT*&3%~yQ3ucD@`_KR3 z|NiqIy#84!H(CXEkxz|?cgyTcobPs zz-`_;27SH^LOvWee>?f#ym7S7 zKR-)7urP~YEj%3!seGUG@Yq9suDmJ#N$wfgy!Gs+ zcLt5k3Cz2x*Cr(gjj8;ZX}3AaV%ia?el#tkR>@s58LcNHMlg71W|Y_xh?&tE7t2=5 zZHf}Ls&4mK9ksZ6Sz+uhW4rsD+v17RQnGzalg66GsNEp0XdBKKxvr?AqH?t@(5OIsP2~%TdCkG?vNBMR6#;pYQxnlQ>kTk&9DJ z8BHoxbn0eh3-RT_^Vr^=jB{NJuRQPEHlQYVbCO)HR37QCWx}`7#z}6^>U%PE{3)4` zTL5gCyrX`ec>2(TPd=D3uSbR1B)Yx=)OlTX9PnhDH+6l=htw}sA2r=vu6{$m#4F%+p+)MN==sk}PoR{D#i1UK3}MjIu^ z-+Jr|4!dJ~A-7W}wTSPXZj1A3jMt$X>9}EuUg-|MfLD%48*hFXVVt$0X~U=msJ)*f=~_g&C_!9=sRQ=--o_PQP~)SF-x_(oM`xS}h{2RpX&+{)G$O9m*+=7cBh!$Tp}`c4 zilO2j`2;JQRkfixvDlX79?kYhn@t{}DXmMXIrN1x{H+}|tn>5XResTL!gA1|%1-j4 z6rSwjV3tRn?5)T4)BWB7GQz}Kl8M2pdm>y9L0auBo{Hi}9MfvPy!zjCz1cT5)E*B_ zaaPa*6h_LMMImB}E95hTg&}70M=?1A&|DO!qzdQLeciCgx+yDm^ee|Ok=?8b>a1TlX~b_xpv{KNr?Tg-`cbmK5jVsX*}^AA>6>=+DfaKF8{D9{eqzw@hhd zGTYx<@iLJ=<(NAd1EI~opj2<|4K*z2GM@Y^WV8EfM{)7lqq8d41Q%Ft%RQ<8C`#d@ za#B#}1IB)WE{fT75J!2{gjvvHz^AfH5FXP0qUD641~%sL2`(M{sF67t-Ut3T>=yuY z`Jl)p8B;nqS2{P{9KL{S7-m)}3vC@47iA#FL`@>*rL%FX)D*}7=gd~Bz=m@J!T8?fv%_I zG>zE0TkG3LziDjyfe>6}R1VQ>gJ)lrrvsii+mrtc+|DP~DhH1BzLXzva(sC{KzpUB z9L=-j-sgkkkUe<>AO7uKZC2}=#S6~P*u>n)`C6{3v1yn(McHoLr#0)W$vKemm3ENd z>m;Yyg`7=y#ep63HHHokpA4Hlpd-h86hn9ihk7A?x3=d zhIuz{Ck#^7Eni(R+ek4h@19-hzJD4keXt=x+L}Qs@k7)?g&d1oXmC|lAL^~Pb24f! zc8FBhQRdASiqJG5{6DKF+q6!2$iVy-J1&F_txQ=)-GdeYWljc|t>vwN;Gk&5C94+9 z)@WUgQ%G19JPYUMv#KLaeW~l!?~jQTN+*)Sv9G6WfL@RBp9l4Pwd)@Tc{l0EK70uG zf>~=S9Uza7ZfiDqK)Fj{gqA<>(&htRyg`dTeb4H%Z3WhGGRyLTocdSRP?@qk_CE$N(p7JnP`Z{K zJ+;!gs7Mce`SiJuPxgXpB-&1x?`d6=m!ry1eU$BJKHJ)<=&)>G!ny2tg|jNlyi}Qf z!r})-Y)E=x-)e!^&mx!35`bg;1|GFOJy+!dDj#r+$wU1uu!%obaHQaNT$@?gh-+bO z&rr&IX827$LQ8;9%@Leo%TgHX_Jw|3A)~LAfmzXKOQ%JCR~=65Bi#l`c>voh*P3Mw z8?hA!aS5fxaL}L`@`_}v^~t(Mw~g<;_U-@y#6hex#p}h@LBUYs9ljRU+PJQT``Wng zD~$Np24^^ovj8{tBJCyMPvrxHFk_f{-!yk_&6tXkS&nbdN@UjxQS(}8CY{^{^K_iA zOwsI?shX3C60aTT)g`LxksPVGJNC|59$6l2h4E2uY5xwSbe(Op0~t6}$kh_EHzkb- zSU~+_%%x%V^%D!goAt^f<_p&Fnh_X zob9xNAMdlC*n@}n--BURx`s;Iz|~VOl1TwTJneeRpwE;LW~hL2$;sqDPq9?M>&^bOq>BRCgByw`<(=o#Xcphr$!!-Vz@BaVH!G-dj%!*g2a|6`C@-*8&g| zeHh%lu50d@ajb}DJ5`xFXPXfR96+x)*dtYmqF)J0LyZo1O5j*-GpsDf?hP}3=!8&u zZm5~(HufZ2hon)N?1WlqiX|B*Z`>)~ZOy(dS8QJwURMW`uPgWOmVWU)OFG>it5M$H z-}oo*`M%-zFW}b(d`D(&1FmIQ?|1U#*yC^SjraWx0QkTE`@dSP{P*8~ZT{`CM*kx8Uw3^B^niCcGsyBxtYnlI{2c(UtLef8Ysw*+ZON+!NPMH(_+6Jc@kHGh6(^NP=>Y$?4;R*CI2GK!y6 zid>(C*C03k7%V5%BsDT~3k<#tYo`-FH2QRsjv@x$SvJZoblX+1$@bfPo++qAU2hwo)Xy3T!*KJU7A)qA(- zy_zZ%C|OisI$M0FsGG0H6 z_+NN&HmCK>?w{GjgMWE_v{$QkX?5JN$I~v~AJ>D~x%BDTxmZo%o^5M=r|~!?P!~61Mf9M$eRM#w~F|%6V?Rp**8xZ;e}DgZ(_@ zdbffgZbH={VAW91G+jU1aj^KhCLet8Q2jjj1oVW>^qYOCgX}Q*KC@47ugsy-ek#No zZtB#2@)^MgEhBjV+uJ&GrZU~uKC5G86adKp5cjU-*vn!>cawT zFRYbx;ADeVXXH8Hy^b@wO$V*IcTncOckZ#Ai%+;he9Jq;0Yl@90gdTavWIq8S~{YK zHP2Hx|2h5~e~$m{@XU}vnLwS!27fd3YQ`i~%Gp-Ppq7lv$F){uEXmK>JNBSub_EJp z0PT}`dQ(&$u@aYSev5S-y+|o|>`ESWkL@eQB-7m!Oviq3Cbiq`29_7mm z@R3i(W3|nnha8IuNMehwg}vU$(Tu~Duoub_J3@7<(saX?pgnOdT(1jzzer$AiIV42CsKW^b;cT%dOl*X zz71IAp(s^x#3_cdqd1h;hdMs{I;MViXtp*C2OJwO=jx&?Wo%B(p&>pRm0-k-?u_uS zD~$N-med4HQF6ER8ZQKK z*8a(!FiN;b^GY+FaLU_A#%Z#}=w>_7dk)z$y!HiuIUK!mJecg2DJQR3Id@%Huh{jC zX~j!eq_ao`TwM#(&_q(T%-(wB&T%MXw0;{2BeNC)6}1wHARQ$R$6K#%RmN6rtv0Iw zovcW?M)P_y7~&D2D{xMug)`1eP@SK8cDAGCM#HZ=n#xE5#1bx(hI)Lwc-)gYRaevy zDF~z5PM9i9sBzx?r9da5&ZFBRY|~mq6ytXQD`CmKb(u+k4hxDD3!X62U`dK8GJG0S zo()l_Xa|*jI6E8eCB8l%16M@S@JE$EKOMt)HF23Mb(!a#2C85yIKxYw!PPu7tXf68 z;C|uCi6alz$R;0xbSjE-l0?l!RE6xgJ{=~dsQ4-c&}_kcW37}^3@CHU1}akr9TN>5 zo(Zx)4UuplQM{}lNK$BC-4m~Jzl&6Im{y&EWTtPu){^?P=B#aRyzh6>f_wP`E-}}b<{l@G21&3!)`9F33PX?F2-*4OuB!9p2{GHn@^!_%jvkq#9 zFpzr2o6Jl1n2NO@WuW+V>k22~dn22aLgnbust145q9{eI+F2zF-b{Wqze{O(f6Afi zyTYJrsfN|ws(N@g*w1T~QA0Mk7Mt*#lk%n_DlA7KnvZYxN$M3)X*Yj92$uS4J(OmH zwtg&P$N;ZC?XU`CB%G7>qJzatWbfo5yY(QP(1uF!gtcHEqQzFS)4U#f?E#76JM4f1 z+uDiV*%KNU3Red0v}j~Mhp0T7U`V)@!Ns=F7$raT+{z@!q;OPeRdH4-B=w9D$VTNp~Ggo*y$4M^p!w$XzHFhmjP zqb-AD$ry;XXa?msq*bOdMtwT-mmwxt0v6dv*2dZ~5w$_%nN?)U*v`Jt_A1+y%L`Jj zFZd1xp?S-cYbLnX4%j(94?d&!2HlQ_n_rcD&$~Z7qve>qQ;yj5TaH&gWx9ys3FiStq%!)H8stuRMot$KV)j{P`h5-yJJ2*jM@11)p z-@OfkY{@WJ*maLw+8y{B6B2vtItCtc354?CTSsTI2kK%arqa&;bNo5}9RJ(FxPT8n*L+Ph0JO3wI@T1|hqIDd(E-UDvKQKn~&}`fHw<#rK>&)$U@loinI#Q@6ZHpW$A(E>;_z z>g@}1QJr~Zc`>t)`JS@OTT7+fVjaC5Llmu_n?)v{{Wp(lcEXxpl6C;B#-1|CpBgwJJ7Os0;XG4ewr9)vH#ej$ms6Jr2mgp$KJw~I8q z_I{%sL}+7PHbqAQ1`Cy95?KHol_Hyaa&TrF-z%f26i_1ebe@D^7GO*L4{AWSl78k* zw?v#Q*K3{W^RNchsdyt(|1pJWFrNim-pFM%9E<=vF`yWC*j-_Gxg!J1o`wtwfeNU+ zUj3qD#44B^1R*P=xaXi6|JLnMgC|~({;X0?)7W+@eYrZ-xw5AfQB9pi2EWtDt9@Sb z{k07Da@dtbbukU=3$ND~%rN#g+Bf&F&Y8kYJFd9s3@Gnf6}7Qa4z4z2J~-5rKAV!P z$%0fRbY$m;PB-z`RB)}7Wc^MdP>=T*)sX46++!cC#}PR8z4IL_;kg%qx3Z3EkKmPj zYn-8h$ZFXhZy7{gWOCBupfYpNRpG4EJieoy8p5cEbShv=povjB1h98MKCwd@XU&6h z2D&kr8l9!#MEIn`Vn)y%*q1NMRYHU+)6{5YK~eSJ=#AWNkZdFTFb_r7aC_);JY z#q(u=yZ4s-&a9QkT!Fu6@UIQCI|IuG+#B5dswZgd;yqv}+0m=j)!v7?>7pH1$h~H6z$2jiUT@s*3)mqyG@$&t!l@N|?k&9EZ+vI! zZ1mavoCrFi}JZPQ}w3)PymtjI;+NKgx}dw;u0~d!Ly5 z$Ae>rkw$TE!2HHumKk80f0w=lG<`kV&?~S?4+H?S((?g}S))|!Q+HP9V@+auK!Es? z-@LOLd|)zDMd6zPtgBF!FrG-;(v;FOzl%MD zaj&}c1L{vG$lG$;o{epRJ9&=CORXKk}c(Bf=6$ zo5UN}#YCB}ei*;O>SZt=0cErE@gKSLq-kvf+bOX4SQ0r3py}nR48HY)Je{4Nzm4|g zooS2N$AI#Z70Nm}UhTP4Z_#$O49;I3d-gpnw8jxmSrVH{dv{Kep_D5ASPnEttc++p z?5tmCJw@B__^V*`IZ1v9AB=ZA+cD+Pp(BVsM$b<0VzrS%$Ebj^g5ON%yQBF zDrwIdUu*K8kjXu9(-Att9%D^EPQc!2OVVkKU4F6X#|27G%GGTKTf0VY`pGc2BpM4a z}6Ba4&Yc=_#~|z z+D-9^`*ly@Z9)esn8nTjWp20Se&4viBd|R6$cYVu6$ucB3}RhH^Gcgiymz|NKgXZr z&+*@nmQ@B3AmDajj-E_yNZ!#31)BPrX30UA#RKVUa*u%W%78LgDr<4ioh?Mm8Ztt3 z)qIZE+X3I4JOkKHKO0o9-}QYv#2-l?>y$A(#>6a3|D&sKF#VBft8+0`jxhSUoktYfe+gD%@o~g%>-Fl;x35adUK7)!-77{l)7%g$M zVP6casTNq)0qRK-P<{rLSGS4phz6szKX6Ly9Bp2W(X(mk`@zszCO(KQ$n?-#d?D0B zfpR@oXw|#LV>}d=xj3j|LdMDYB3iA_qo7iwM8mUwz*ge^?rb}7uL+~}D3Aa$Im^9J zDX2+|26+{g%y6`FMGs8rt*yY?Iq1^iIZJ^E zM1^wKKy26;4H_2V7*cae+My$+gCc#TVzxZeEU$EwF7&KGBi0T~P@rddsn{WnpfH$3 zq294%y_B#jfN;!!a-QLx`q+vi@E!@8@aVpT64%cx%k4@J_E~U8pH~4{`wpuRRbB<*h2)QPGTfy7#M@<9;0$yMvBx0g zrHqd}ulVx$@W0Ucuq!s&hVH$&2YduB zzjH;b0}|L5XG?neOS$2Auh_)Ev(Kv(x{xU`z8km=$Q{-UoMq^>j|(yt&kQekOJLG9 zp>=3Cr31;3(V2Z^n(*2;`MP?02r8?@KU;-U(6R8KeBT?lJN(YQqs{RC4*XgMTxNK! zg|F9z_Z=&e?|oyv4ZS+cu=kE&a;Clh&wu`|>q>K-snYKI9oL$cNLS+*&qXObI^(VB zC2@c-*F(wRvo7>OS5i%of=!v2JeaRgI4h z{N~PjsU{u z1ANpNRq>cr)tJw&n22(Yt>`0a>>0^N-$6Jz3tuNLH9Ck)CoNP9yaMye8yob*d*0*d z_&_ICW^nnbd5J(1MSBHDul@%evTsiw6V8pECB74i4xcOq!s}ZoG~ei+ozivMSoQ%M zk9x)-dREy#HhwI;s7@%OJ9u@q(2jAal4Q69Hz!S;d_17Q^mU!aPA4TFziC?f3MNbX zYoRFup8=-HOT8ON93M03R|k_*2KGHAzK`yM2Ljvp?(*)T6PTVwfboYK^yFxsxSlkb z41LJXmOt_52evwn4ho_0Pw!9M)fW$uTm3b`)#lTbi!SB)@mhT;J%Lqa3EersE0i*; z6dYx3F820c4@RaVe?~CW)D&70N?cw@6eu5-ukJ(d`OECT``M_{;v&lp;ONA zDK6g)(}fUIzD2q22Cr4i3v)oc_Rnn{kFpzZUOTXt!*1#qj_r5xu&7riYG)f9!DEQ$ z%21m>JtsHJF}3c`F>0+QDMWDev3XasETQ#+-MmK{GAJo2ck65F37vr|1F7EhIL4^n zo6Rl#MzayC#htM#8f>7V>p;M#i@@5gnYsY_*d z#1?038@5($(t8ZHxXY?$)lE1e((dx@)k@_z?(e(W(6~W6e^lG8NEj<`8xv+Gi;#Q% zXQlF=;nu9k2vz@A;9x_kB^wb$CXX@OrZd*PdOx+G#UA8kX^0A?L^XDuw>@N4116>V7-G8!(p;iKCJ1y3EzDVO=e%HVSL z-(%V38N(Bh6%@HZRk;Q$7T0$Frn(Kw+9|`$@Z!FQ{M{wRKoM~E$k3^(7gnv{8wx|w z|DiqTQHn-lgl? zzy5+{Q2G77aAjcm{a#r1PDAW2T->07jMxjxJD}V_<(2(ppAU(qCm~J_V&R=I4}L04 zZ4ulil@>cwCAtJ&E7B%))wmQsPKWLYz9_m8H&PI5U%ERcH>~Qhqw{SXH1m9LFz-Io zkOrA7Ho_q+1kau-xoLUTv^MxAqtn@2yWbZM*nt<65I+1EgZU9}xnH{);EQ`%Ie_IQ zJRIZ+0we2HEN#>u`zl)-@Wl&3yiLVN4adA*wy?0UZKtqxl^zpsPn`ZR){n!_FO8oM zew%#F3r`Y8254|ud6WvM+GkEuiExJ6O@lShup53KLX6{C&WAgL$=~??{>DH5#MyyJ zqkrY<-RlA_VA(YmwTzV{Ik^3EE2WXR_FWJz=rTiuGo@L$-}WJCjqN@<6G0BEK%)7Q z@k%^ffuGm4@OoYNdc7*6!?mulUuf?eYPt19F_H05C>v^R53ca z=K$_dz^Y&lhEdka06|t%nswTv4+U3HX|y_Yoe-%Z8A2CI*=J&~0H|OG<;5u4k8_ypv>KiG?oCF?DIo;-KxM^wz^)&bcMRsP&C zalU?<%gQpT$MpV5#IIq3wDdY;qUweN7EWxiNAQuYVKCV}(h!Bd!pU*^PvCe4y|b-4 zVUGI9=&OG|H&Icqtd72`YnELMxaAs-ze$dG+68m$IL6B3A56w#v0nv~UF7JvvKm7@*n5$(4S&7gs6P1J zxp?!*AFYA)BVlI;+=~|vmsG*UW)ZIN>%I*zHK*;WlPQ~aF=U7r|GY6`R)VH z1WWTGFXjGz>ionHAa;+bF~Cy>Q$E(YbR6-45C&x+9e^G--M`Ux>s*v)i#)*= z_6k1-JoHB6mIA6bNm=#x7%)GssNZn1)X73c%%MlRk=~V|LWWqH(#}4vLQb6+-fw!p zzjA*$mFX39%xrY-o(#Q9p*JWBiC(z)UxE+Q5$a6#l>K0!@J{$TfD74NjJ=#W_L}+{ zolf)Oeten1NYhHv-YUD`$G~JvL|&O(DE9HYUQ^HLaggz#Uh7=x75w{9 zFNVzX`HRTsgI@{c^qR6Y*bmzsa<92n_o>BKj`zTD^%GF%=)m$x#}F6?S!m#x_|e-1 zYUYCKDA2OUv_C@savc-4(r=iNsab}s|hU#MjQJRK0 z`J92C;Vb|e`q{Sae}c+?jz7o$ddxkQdu1~0Sfv~a1r>{bDGw-)Yq51_qy!Dw<7>x) zt_&zspJ>@N%V-@W`Nd!uUgk%e2+c>^|I=17gLf?_9NX92Q(5f?@x8I7;smUx?{jViMYOrm%U1EDFhezw}!>4MT zg4Z&R=Q=5}Yz#D>xoS11p3i%j_eV@Z-W4Tp%log}-!SA}a32hvgh2sE_Y=KpmaZb) zY-Aac)eagmtwgos;BXO^7pw`-vJ2c}I93!h*b!@rYjtql>p>2_DylK*F`r-HO63eF zGX=J}!u2@@G%jc`8_Gzloa{B$aj}c>A236Y!}fioO-w>BIIH5>MBUdhW5&qfa}U^6 z?d;Xh=d1=24q&lA3RetpCS$H3q+VOIQLcr3U*QyYpjIkhFZ}xY!e4*=g|#Bp>UCXs zzc1MPyV}0@4c~@sH@F=u{p%V`Hs8&R$VA1kXr;U9QFy8V+-i92z?TP2JFzew#6mNl zg35&=WHwc|3a@7V$}EO+nI-L}hGyAlOjjAZ$80}w6xDbQ$I-Duxl)le4=i^q z`7LpRserd*=PKn$ceN`*Kt(4ev*-{_I`1D8sZWGH!17Q==9k9`3(`K=uO4)DTnKg- z{xHb9uHFlHT?_9_lWg}JFoVC7w^g64N+TQ?u>Agh@+omnn+ZUqqgR$Hao+4oGbwh)13*7NK8!uQt;@BLMK)-c^DCzZYL8^8bljlX~Y z#_!*G{`)t6|MO3!C1=J8?sHi|u;RN7uLsW%y@U#NDL~~|nU3HwiUs#B-_DeJ)$f%oeF~c_mF*F+CB!P?>66RxF2+nr%veR9uvL1Z9FPgq5~x>kk~Tndhdb zL1+Z-P>8bW;w&8UPx#&A9bd_FEVfk1ee^n*C6$(w0KK}dK-R1O6_gqPuxeAE8%8_# z@bm67y8lfD8QiCCeva&ktmGMtG7BGybd>58u&M=us}ttkH_BF%t8&5!1nQ3A5lkL! zM*->M?+3+(4Z#$k`WaMqcdUg>3+aOopXc*>1fm{&HwwPVPvpABwOEuLMMT?oX7gF~o~QF{_jtQM z{1HDNrw-ub9#_*(eM;nIDg%o8BlF3@yR|<-qtgl8q|c$`q@yFWH~kd(n=2|=H=x?0vLA9^q?4!wrC%iow~Sl-MqZvy`MsO%d@NIVZ?&!Y z)dSbjI`XN%lc6@pNxlsT`lcEf4=h@xG~Iob19eW&EuZWIr8j(w~*e ze~v%L|9oir>&~~}b*&cGw6;DSqNj>HSS#e)u2rUcZv9cZtZG}&qD#*@WK=9@Ykzt1 zqw)p!MC4)rfvVBrUv=5{xOl1Rce!#l`4jglwv<#YGcfdDWGh2{F*9Fe>x(Rc7w#1S z<@>&{?>km1L+!){ELncuZZUtTNT%i5DLhie;yYo>cd1ol|_C zU1ilwJq|zy+c$1sz`i3}@DAB$)O$aHrTQ>BhM{2FN?laO%6qFy_x8^S0YbAIe6o@H zxxOyFh>a6#`yAo^LO!Z#(Tjy8BE6BFpF6eTT(`NGWUYgcHAj1NdJTl z5GfyUEx<0AG01b_dd(T$4q0*d&p)|B6tJ~q<>Gs+e%||r&g$IW6A=Di_TFY!lO?wn zTa4UC0(>P8G!X7UBO9<@LV(^;ucfI5wg&d30ZEMuNWQ{_a8B(EdO$96F*0|Z?v`Nz zR%O+~Pp1KJIGe)7XLg@Z-po#CiCiK5CY`#_8^WXCF& zr!qfFEnnp`6>eMbGMdUCbv$c7*L8jUbdw=*RTY6W4945e1GDXpMSy6zr=?F`@j71i|iH7&(2wQ(H^Q( zk>%EzqfIvyFEk4tIC$D>N|Udc*5`8oMwqB#JlY)6CIdRF2c!3apWA`Q#wgMWXso6o z*Olb#;e}O>sx26~ETR{j6*nbFiE{H=$vV_Fc*pEXI-#UqoInd9;bBmAKB9X^-bQlN z_xg7{<(fyALr5rZ9ra6_rBTr&3D3@G$amNeQ&-iBj7L7`fvTgt1A)c%3>H}fA9|AD zf?LQb_jo@)^!g6{sdlcGr`XM<(9=m-aqzwnX2={n zJ)-jJC+bCr%}{Nk=lNYLkp~xKk+@xP(4o{HqxH6 zixto7Ue2g{R6A#DHcwLarR>%fnsO|kc+I*WlY?M!ub7ehjSrnXsBD}Ms-dJDzCYMdxl^U(*D%&FYgXcIQ!cqHcSv!4# zUhg)Ywxx}4UvT>YaGI!A4eB3ex%F)87|0CZVx5@UiZ z{R48{5!`(iK`K97BIKQcryYT)&SvcZEB%vu;7V9L`YpEFU&7lEzE}T7@wQ@qttyuDqmL0!HK3ZX1s&}yIypJ#_HrmRPIlgON^cSGh6F%+K_Y}UKi60U&!7Z48Cfc zoA~r(ma!Unt$41sqFF+BQ8bqHCSN^TD zm)Rv}Z<9yH_fBd=g=0G|ZpE}v0gr}nK_;-%a{P&%_=AhuMDa2LbTF1qo1&5s zO8e}o@wzRuoY3^qQHA2O%O|vVk3p8b!m^;F;V4bMG>OZe){ppgNJGnUhP`3|Lf-4- zjWUdiH_Ud7b%+dDpbfyieb$IU@YE0U3w+=*U;cBElYk zPEN55okoxvq*XAM=3pR?-*>VvQW>1LYV>)eIdFL?N6vaFYZOip7 z5pQ)c_-JcQt1V5CYuhPaexL-s6b5(fd&erpbbj6Uu01>F=gtA--FtiM z;AR@x!}zZrh#0oyiz1hcb~}w*lRk>$bO>s62zD-FVA=BBHoPD0UNb5h;0YoS!977= zO5J5*=97&kkPKP#w^PnUPkPn4l@DKUz7Sb>Xq?HLW42DVK{-psAgO$ls(lZjg3V_l z`K;gM$0hRw7g+75eGi*Gl%fvRnxuJXmy4niZ{h zEzSZH73V!*`%vTu4VphjMrT8bOi%9mD%qO(D2^9}Kz7pkn{p;Q+&gDW!{^GQF~C9t zhveU$+nNz;+PH{4hdH z-pYH(#0cBzbSj9k>`jZ(U8JtSY7Z*s?+##Z+-nIxwfq2>gU4ISA@qef=9d}1{qhTb z)&b?*ABObX-fa|H9o+c#?HBwap~rv8P~;CMz<+ToLj#1LGA4K!Q_FFnd;dwtlW!jy?OB@tv_y`TB3wj zI&Zm>O81eB%-S$&zLQL8gB5L7%9B^d6yYKj)y?P}w>CAL*U1MR=Nvs_J_mZ>z}|2R zy}CB-iEblh^Clmyf7+eRX@k6qW3HSqW$To+wu@}ktA2fJEQ&>uxXbve^NXncGj+Ef zY<88Wj02U~_VzeNEIhIt6Ig^M=LD<`ACVZ&&*}Jtu8F+>03ZNKL_t*VwCSj(!^Vs6 ztG3o<9HL*c(i4ffhw|B<95k)FYr z&$dzw6zgKh>-=87``r(f1IiqE!(i4rV&DL(xn|vc5Uh2r@X0g#UP(~$Deve6Lx#9s z>w4LdFkCke4H`;PZdoGB9IpRwh_c^)LN#;;b+FEl+ek+Q`WTpRmF zHsh(;<-&1ZJh1EevX++L8wzx5ONf>WDjNqQy@h_-BWWKWgZD(@)Mc78+qpu7FAa$u z^6V=k*d0WtKdJhQS1b~^*2eZL?C+%x;cGaDbs%}}<89TyEKKvzp~^!cS2@|?izaT{ zAT@@|kD_BydRP5p>KF1k*MOsuV^X5O%^PIp(9h|CPx#4-Z(!P9zJ zY>PKI`f>a?ejM*dz?a%5)Db9%>~0!;Z6gJC_xR?E45pl`Z?}FCd_T_*6F{`49}qC| zoW@Mspj|2L7i_mNn7r@j;PQEGECdvr#5U8?B=&0PW?u__T~&^-8INcoj`p=Xzz9%8 zO3Q&*h8Kq)?Q^t)!KS@%%|YeaXKs65C!lM8+R?p7h=1Y@gdfc#?a~|(1Io)i(%O+e z7hm-vtYek3KiP-c-!1kDUS`M@9AA`5^bAB7i=Ei{w4UK4F!v=<0ay8VTSc?nM`6p3 zJnBQT@@Citvc0g-I`%IIa#ZMqYGHX!*(w}h)H`2?^H`D?V6^pGDMt! z;bY&sObj|=o(57Ftvlj7fwo4>ev6Pwz>08?B*bbEIyTV=!Gn;{3bqa(ZTSfRYO)vRVb zfS?)I23^EB)+`jX%>f2X%z3 z`}PU<2xf#fU; zfobTsl+BC$n(yrU#^<+hfTO}#>)`)=-}v_ViEp3Z@cHd?Tu*1bmBXSiYZ!0%{-Js? zDP@xl#hB&UQ6gUlm=V9kv(KqlKN~4F(^Yy26tE8>=Nt|ZknrEI>_-@M% zTQb9<<`yl%%8pvuLRBDW_6U$>%K1U-Pm~ywbgrPe%|}8fZOF2nySv2SuvR5)8Mcs-@J zWTUWH39B}4>J1KfG2N1fX0mFk7mMv^3%vY-@oUNAn+}ke!>4ZKdG<%M!QX$Zfbr&(T{kJEdkBCpN zAH{0X^)Gd3=26a{_X5vNlEc=Xy4H^m(vCpd+8f&!uy*GmbgsWA<7%;wn^%sqJ4zNo)WdF;XO;7!l|xTl>Se#ya4S%K~&p?ox^*M=Hc&YLP& zMbLBY`*p}wrFZeG91~$J?GsdJR&GxR6JJ*iFmp1s0A}5D9)!i}F_Ozr6XY#*x{7@_ zmNgeB7_&{YZ!D-NoWth?*VK-kO*v8^-uyUz96yfV@1Q)LDwKjxyHoq-4}5*IKobw! zHrDQ)rLD)93SRJeX0y)MlT?3tzp$>WYxWOaau$^-AB~Z`@U*G`M!6RsR`hR zP9UaLrNw`EVO@?Z@y%ehC4~tvI=E69qhXk)A^qGdBFS3zmAYutN;24Eg)#@1OK)1+ z?o+R(K12KK3Zt2!n^JeQVKbQW!OU}vnYqhEeesdYKq-^02(l~&>u$|&?YHq)lX#9B z#YBUOc4gjMhL*l#TlZixa`eHB{OKc)1uGuiz=k=s~IW5&V&+}Zi&^{9-{%-FhhQl~-ly$Z$@2o$4qBMX6C zo|Vo(GxBWI{+$lUxzB(d@0>gkgUC6sylr$qrFKFMfkLqY$IJm~_!87yalqY(AI7NC zJUgvpV0ZT*%CZDD?I2y2D+sSxt$g*Ma_mPP0~X&r_UK&evp`PisSF%#qO(onY=!_) ztlo6kG2drte^GhbIVTw&in=Jyi=?Ax*rmr&S}_4a9oGDGnE6~efJego1NFo()RgV@ph^_L+gn%agZA;ybxITfYK zbSbNGmhxb>L6=i_O$U}~XqeTtnM-;tYh$eo39%Mzduiyy^+s(w>nl83*G`R=&kyY( zj3W(y8V@Q7di}%-rey8Z;P=Y34tR#n41D(-v~<~5N_es4Rupmh?1G*#L+}DV#Q3s) zAMPEA*vUbCHd_^Lj$!CH!>|V!BCll~NU!my<$#05!!f>hu58{|Zsk}sM;k5&&ILF+ zsFD*bvDS@!=S=jWvxL673MN-??d+7>Bv0wxUueMPfbr+IZ}{b>UjX;4%Po5`=f0Kq zXY8+)TNHeLekP8q11tvou~_Cq2MsTR>TS`Ge*Dh6hm6a+XBr0%*M55{_pdSqH0HE^ zaCi@%82P?&eR~W}#DaW>uUH9LMqqs$Z%%Y#O*y>CXQjhXyfz1zJ@OQkcl}qGFpt9L zp>r_!|CR8ATK)w#p=5acrs-&z$8(yNw1LfE+llzqAv#b#TyMzjA)Pn?U$QxS7qcv#m&%7W<=4{xl1QCfcc?x8l5>>3sWSZ)6u{s0%cJ_dpluPf4)Y}?dZh3xU{ z&;^xEG2X`{Lt20GvK*6xw9>b36M zo_*?pHxgdma=+v`;WZ~RsTJul;( zjk5_Jc^>Mk+i7^9+DAytdBfoX{paMKAupLlU_U1vb8vZyPv_kaG$zD5%f&KT@=zmT z`^I!4Nv}X^Y+#;UKb7q^Jy+5{>chSxvL2}VaHUs%M90TObHs}JE0R z70M2{=RLKlX>B2V!*Lt>}*4~>Q3 zLw$^J_L#QRS2jiz=2d#xXSXa%$-3A)@!&IYmNT~N%H6 z)XtOsui*7sfUnqJ{#qAs(L~u}P`N1Q-VxWtE@e|iWXtq-TTXg=Rjh4~ugeZQq4y`I ztw35=pOQKjqRUdZR|IH$fxR!bmQ`D}t;15XAMX0D^x=k2Oq8~x-)qua*}7`-s>)-X zIbOxk)wUywU`&W~Fv2o0J(=y*I9F!R!LAD>_3Tt5zp}T)Cg}N6);a1AgBQ$1P<}5>^MRKWnl5 z*me#4Ygx=-NCSDTkJ_7v4lLgr8y+3uYHzgh367PRIZ9J`zChFD zWii%^Ha7YD)j06MYkhJ#(@H18laGt-`+3(pbrO&UttC!HrxP}w3@8d*VCwcDxKi{8Z3jS!#ckwcX@{xeA^R|cwNyo zEex^xIP*sSd@jOW3T;&vrnVlJmTh9Fc-gjM-mLDG<__!RvN8!xqaFVy;_i>&S0y`BIDv5NX@qFlaDj@2X32l!Sz(}-YBpR@x zJMsNpDb}%!f7GxR`lk}9PSTq~QAG5U7T=e-cP`<~cvBv+;1WnsTROxeIxh*QD?Q*VC z6rGz^PVM$(x$2MjIW$9Fs~%>rM)0WKGs%y3HtV3*`Ibkz+D&{;;*d^UwozacB}7*; zVH}hrdwgp}BbB+fH_|SegzGe296WwHa&m%F4J6;=`_?yM1=8NKan8I4{~vmec3AZ( z4qK*pjqfaJk5eENXPPeLDV@w4kLPNBGjIY?Ir)t$t?!$9&uh|De|y4>w4wm(;2YM# zgtxT*2Y=LegS!9Tqf6i7)bh^n2QH5~rOcB4#LZwj&>ouk*n?HRg2m>U+|xE4gwwXl zHyk~|OD9s$uHsoH`RSGv8$ zkFN}!$$@1^dc(G7zl859f1BFxt@q6=BSkUf{oTGNDa>ilx1TLJQtb(;^HSnwe-!0s zcn&IasNPte{Iv&^;|E>zLXvtk>k}A3UDn^ZH1=Fn{M1gvP*r@BpY*(0mc4Bb_#3?q_eb)p*jL%h zw)o20roK3R&poJ2y`9gOE@eMw+8-u#-dWzKf5YdQ36mzwM=~{eo`0#+r@pf@GjL2r zsnGr@zK4-7RdV=#H*9bCzT^9hERG#fkmp3;82{mbaxClw+!WRBdFn^}~KX1X~8NzmmQ7jm7l?8;ory^%7gO+7Lkb9Nfe1EggRP z-n(uzKt8ZH7+j!ulfe64zp`?HOKWLS92mPVBMS{FX97+AjTh9UN~^?SF%;K?U% z!=XOGoSpi@X8C*i?a&gI=Mht11>X8(EiYpL5)eV|!gPn9`6xXIqxlW_V_Fd-|1q;9 zUvep@%GhwONwrP~)O3Zj!rEE6`GNxAc#Fhn9hKN2ow3V>m33*UCj9}1&hKD+A_o%#jj z{_R{5+jd5i%z@Ewf&syuJl?u6Cdj z<0EWPJN&7e7`~JP^&cM}_$g@#PaWSCt4C43l~XewwhuY<-!2ij=hj`PnwmJ34Fx_sO2jr6y&Ot(}?T=klyo67$o?? z!tga%PQKV9x{ErlN|p{uA5K%HqIEgCZVc&xw*lo`yEthnk5C8HM4X}Ynh({P78BvJ zzUFX`i3(5|uQvC}m!ZYVZa7JLP_*i$b)rszRGiO=om6#Y`&tpCi3MdPwdXa{ERl&M zl0?Oq{oVMWubE_9O=%xd2Nf1!c7F2BryX%549O57bJ`zxPL7#4p?xLo)ATtWfFL-t)^wIV~sO`xSO66b3` zd8mcMRwTyDBZZ3U6Hpwe{KJmF*42Z`dt+PJJlNWw_PH|g5$-zWfD;^LYm#x^;&pjx z;F{Q)eGp$pHQJE*8tb~$Z9!Mr+ssk#52esF%Jj$N&P<>7kl1tGk40Q(K@WKRnzhuf z3ZVDEZTO-?<+g=&uTmll$FMdZ1K(AKTPCWqutVFYW*U1)zR)Jggx+_j}ptP9a~eKeuSTY{uzJrCx3z;$B*O3@p6>B zo;J_vEzKw7c^=g1l*>7}k~)9(8|u!F$PwcS!H6<`X|F=$9PYrrIHWXap!QVW z$3&^QeMVrq;H{WYv`MDVEzaRn0%~zWwND?ew!%@ak*=0>z43w+sHmMacDV-aTPvO1 zV+v^>SlcLVCwMJ}*)%Ss4(Ehn)@yGrWbMVWT}K;qWSSWyyKV7P(!6=PPNy%FTR1W= z){p!H)GGJZloF{$=yje;m)5&ciMBOpYbig>_ADChER*lB=9bI9HW zrvM1^u;$efzm^ORi&IC9UGJ4}_vekzJB;sjFWj}K(5ibCWEE`xFswWF_O4L6+auBy zqhqM?yM-?t;D9j0L)Tz~4k*i{0at#-OglPM>~4u-pE|iOOja@|&>py|EZX@zKUE&d zs9F5QJyq<`j42qNM>lj} zPmYo6+LsT_EA`TJHoktewv@5zOiqXPSx%Z3$q+KfdDu9dPu7WS}Q?1WGk9ikeD;RZ|tSR@`=yQ+lJg1 zdhOU(%T}ze$ozZM`qm=eu*ZS}FL4@u6&KeNn*KJUI2o z;Dg6E`K=%Da6b3~cV!lc936AEW>R8D6o1Mp`IEwy|z&DCZKS_;QNurJZgFg+NzNE4*fU2`;IQr zW9mH@y^GmUe!K*Vk<3z6-NM4O(tNxxiAc@U-h_$oRyA)vz|L1yU+4^CRlFDF^0AG_ zM(~&&GNgJeDznqk7(B#5&tdoCDH~cgusnQ3u?U@3S5v3Gi5*Yj$@-LGE@xe-IcP24 z+1vNp4D#%3p0=wRUJu<*9Lhwe59MGG;yu5zayz^hCP~wlP6}>yw)eWL?P+sI=A&8-nF24YdWG7X zVqbXm`y5|Qusv-u{}sM_O&fxLCq|yK1nR8dS(7^AY}azmtT7-PzHq^s+f zW^efRY2KZ73aA4FhPm`EGoN&*!>gvSvp`ii!5K0!jnxRZdH;jgv)oy;74SAdXDZ z+IBUL%5TXUdaAZ|Of?2fB=+JkbnP45g408lvPLS9lYwAfgZ;UF zzI{G^B=PKrl^E!DR8usL{%Fe-`2+R4X602ctrF?quzmcVcY8T*S)Bay&p&^sL4F)O z{^1|~0e|y1fAb$*j4zLW`?r7lk1oRhPmi|$3Qw!sao|YFtL0BmrCj}#!ez-rvDx%F zMzWd%UPCX)Q96AI1jd<9)cqCKlpSo(-M4u6{uxvJoWVl5aq6d_TqWq`5z3Lwn z4OXpc9T6+LIvD6yI?-m^*sSBA$Q%7@(wRhXvT5gw`W=3v?!V>t&WU7RkxHT+G99rg zX=AFbehx%deWiLXS2bB#bkZ&Z{USL403ZNKL_t*D;~8m@R{z|OIyp}GP}?FER@!MX z|10{stG-*%eV#ekJZzBaL(KjFI6>JN4<;qWo6im12G4#d+m}3>echZq=tBEB0Pgc$ z$txx+lcr+VsClCjIn6SR#8^ffucVZ*opxQj)M5BlGM^9F&e@2`oGt&O!qurC*KB?zbbvcZ)F_!AjsTj zp*ucQyqEBjNvW-5c=R6zYq=QPoi2c}f5LE@7TVkFgjFgHv~fwt`Ewu5Y4nq!C3tl0 zykYV7$GcW6)T&%5i9ux9>bJA(dLgFPpoNkp zo*A5*;TEar8`$k=7c1fjz4CDo=PC)WHUqlNS~kv7p)hj@lZhmo(3olZq+RoZt_m>3 z6vDXQD?*^`ZLkc7vi8QkZLAtF$-D79Ryu=pOvOj6ViAek9#bR%cyY*D78shL3KKMe zu;f2f%TmW@d%H_I1T4|lA=EzD;P>(StXH0XL(p@nOJJTF;gKl=Tc za#^dNkF@pK>HIm4H`x6yt~T%buRWM)4W|hG3UPBhEErn$@g$_Jc0>iN2J;$iJNAQe zb)owP_5uePeQo%Ts6)eM$-bkT9h2*xlOV=`F$amqRwRjuBCE6&Vbn<{t^LV!f>)mX z3=S*@IlTBV9EA|!&TBNSn#N6kb$(Uf`=wncFH?8O4`Nr8KZ(!VTU%#UYla2e`&W)) zk+>-PMT(=6+#o#Vz0*7LrJZHmgSnO|1+9I<*EvBT=zm>ntazU*m7laMN3l4G84}ua z-{hVhUu^V_O`=1~E>;pG?-)eIRh7N?nz&S3`qZBHd0o8J`n1$JF;M&voS;ZOM1&i* zQt1=vha<}D%c;l?pW!n?AJBRCV66D>l2FgS>d7alw0c#P*h{5{$mgQyb=D9jeJXvf zG9zz@9F)!URfdYbA};^BAf&nkt>HGfQfST>4P6re{!g;DGf`Gitpa+NW4 zQ9k?DAIKC%?_wGnZ(R<-y~lOx3)(F6#naB>CCerH^3**q@A2j&L22N!y}EMU?rB$N zL20Y#I79i%hps#IZ0GMn|DnSrf@S2Pkqz6oxUzuG{?!tFU$En>Xx#w4LP=^eA<U$@Ri?mn=KaZkHZ|+S0$#?`i+i{xAnVI?uD&UwV~sWxtUC_JbMV>9f$xZ<7{=HJh~?8{-owB( zm>97nFlt7z$>#wuv*h_g&Vtb?B6j>vXCe%6hwT-NZ3gFn@``eCFxlNBs7IJ(RSs`hsd)^kR9*}Da@76Ip{YAUL3uz&DIY6 z9)|KL&){Wr$Gq|fFdT;{MN%%Np_Cw@2ko5{!mKsFc(B~ei{DHqGM!C)=2RjQUbPr- z0zUiA(E%5yf$3>zvG?mye7!otOi&(9xH=v*X*eLJDl``876hkZFkWbDz3Z`3>gDmC-3M{)ixockFdv1eLJZ+aD{D41}xVoMg_9eF83 zhKl|%by>=UTzfg3Xq=xJ>G3(XHT%pXZ6E7OW=wLOfczLfQW zN);X&FBsO(+qkRa1#Z84c$X>B==XZJN|iFIWz#a(3t6YIq{xF}^BxWz9az>u=)UR%iZC3U|A2O^$N}V?$yp?JCcWAK>DBj916IaDt!|{SLv4xvWniC%Yf^> zz@2o&tw?8uM@_Io`D{|Y{TGI1CsUkl+yYhH&9CSeBCiV1iX=?T98NmL+*fpy^84u5 z7Vc+8RVS#<1N5;XzLkoSdeINz;4GNf=IGpSxv`5O`>%D40G&W$zwNyy)tl%Bl%Ipx z$Fjzl-iB}MJ)FfRYjj5w;p8vscSqR3w0ofSjf?FO<;J$dZ1t7}n-M68^U9+>*qicK z^^omd)P7;J8rqRxi#Zr3`s9^-p7zilvKRM&_Svl3uw~3=xrOguxlDWk^k@@+7y+hNART2nkY9gp6Txd$9w)(6JAU?-cAWfP{Hqh;xvEcBU6+q` zFwSu}TBdjsx>?idai0wPZ$JMWlNJE{_P4*qU;gD^ev#LYD;7IK!a^QL*t+s$}`qI<*y8@vw!daArzzToS7aXO=D3A^B|RrrS-7JM$=UJ z4KSSHmSy$zF>BJd=VsusHpI7QxXEG9W5krJLY;siD8NyH6&(kLC2%!?|eNV?`5Ow`{8hYItd3=eK4UVVR0vlf17-1L0Imk==7Ys_nf;LG7t_ zWydZJZyLFw6T$&d#xH9^!Kt=B+g6;lMFtXLb_kj}rKN`2Z17ed5DHU{FzxY5$&dRg zKfzy=+Nn_+TR=b+4?(tpW6X>{l9;!wi$8u>2U1Y5l~#gzv6+eT zKPomIoK&QImrURo1Ico4IKpqeF>AGdXEoYa9ihbtA z@k|wc%Aw{<9Z|+-J}^53vZ{OVl~q~JfzDCu{M~W>?oSdO*RvyW-T^h3a_G4ihLl!q zT=Dv&=f1A)jpFimI-Ja?R$w{h3?ZvuDg@$Dv;(gpIZ+?8Q(UcFfBY!Y#Hy~ZmYsMz z(Y3D|1MMg;ZIZGjC1e=4Euj(5u-H2cM@c6Jmf>FE7JIT5RUhjq{3aS>lJMCkAx~`9 z)Z(7H@HFz>Wzbt_$Nol&wb5o~eic?8bLBF@cECXkJGm?vCc=WVe-pf5Q%6Cx5{aMk z3+iFYG5Lr1$z0zIE3?u5ac-SF^eWRz-PZ%j9&>%^9r1pa_XG1D2l2paK*6x1(_S>q z{)S0C#z2qFv>tjNvWHo(Y`Y~)s!Bff=j9x{ZKn;X#6g8IIPo^f0HAH1LFg~Y(ei8O zzmf$@Tp@Nn6$)iXH9^yE%2e^`q5CRTv5~4%|DbRmPmR=h*uWuq&Yo|7d3?!B{JBe~ z69s>lLv7w69V+)yh539xR)fo`l5CaJOTz;iES)2po_ic#7s@?#q{x z>uP4c*!I86XRv5hgm{CJ&j+==E}I4OrYB=1Uz&vjE@qx%TSm`*ZwwNyeL3=P*aUJv zcu4rFdA?<#`Z+e<>b7*sQ{(Q)O6c}hd>vaSecFyJk5%(iv6|SIg~f_={XDl8q3>X? zRc%kb*G@IDpU3AZwM{Dw+j&IG+8(|pv*HcKNm|wlQD(>qSrdbRhV>p;W}?kjHTlwV zTZ8)(pE9_7-*@(HyNy)kvp-OE&k40`UiG0UZj%ngejQw&Xb}nZYT8{BI(F`@UN|n8 zUdOAyhxI>uv%*J|5fe!<>+5{BWb$aINYkpame^zI=iI6H*`EE@#O$Y4F=fP+E;69} z`S~f^P#jwu9U;X#RKIuo4%$tjwByFAZ70STpO-I@6Oy|>^wY-&e*X3i0Ps)$^iTM4 z{5XCbUmPV%7*Q@b00&Udo-g{_Nz}Q#Te_3_DF@CHtDSx=19Ypdi4oMZqNrc%b3C7V zsB9azk;G{)l}qknf6a$f4~6dT$%57Iu$a{xQNK8C|Aknk#HqvD#(31ttm!k1q7&2m zcG^wW>A(IiY2=9$pNAQl^)I!3u2S~*@{dJV>bC-$+vTjZpW_$+WYw$apB$KCTL*6k z-|W;GJMAz}LV8UY^X?CuVfpH|<6yEmBIry!xYpHv%#0|ruIsP^^eW1;MfZ{?%C1}- zRK9Rs8+%{a>&Bh-R!kDS;rqrmOItl!<1t~P8d4|nx1CWDPqkr-3$l+;a@R*$_SJpp zF#@K=$dspz)onHhmmP><5c6EA+#*v3Ee)yJ;igg3%v6T3R4fRGa*}Wl(~#dLn|3}R z`7KxtvPOWyg_;SId6n~x1X3V7gzfG4YSxVoD0kf=wY@j&&Q)K?@QJ&f3}e9ctpN3d zAYwWAfLhs>9K=o!S1P;p4A~w~&d^I65egs&lx^kS0eQd8bK`9RQz4l_fwB}w1HIp(s?pKD5x;fK#hw!^!!c%GZUK{(`T8v5X)jYg%PZ|y) zGOaDK{I_&^f-t0yd}j?cCAb6KC$MW$1r9C%+nGSWDQB=aFqOXDKqWw}ydd9sonA@w zcJTeDi|Yw{^Tk>BlF{RJtsl}k)!+Gi_vl_Jz&?`pw>Q{P7Q9KC#H!Wjz?gOwM}?@6 zvvXtx-U#-EHWN4TZ%Q+lNZVv6vO<}*m{7;-Xj-^Ec%fHva2%hRgUI)%V~(q5V* zAb6ZZVJdiJhl;-!KD1m~w&iwCprKr~9&USX>eZh0n~a;CS_dcYSk^4gjTNE{>LfPt z!2@D=m#o%6vf~(7Zv3P!{0<8}+R-^#Ds>!re6CWqu6yZ@iFD*cwmb3UZI$x9Z@_Ul zx)v4=Tq|M;rj34B_r)MsHO@tVC*N5!#b^`SUV^X7rOMUlOWfTX4Yan%|}|TzTnqahrc}Z!dEW;;{O-Sr==D3WbJizr2@75tX17Hfz1q;Yb*-TvP9&t|1D* zRpf*q2ah#0R~n)spaoS~Fa0h}pyq`nUxi*+z!iPICHU7e9Br%vW*xz2m6KoTW`j-` z^k1Z#hu?<%q(Ks{I<6XoowL?TjCVBFMTT0>w$I~z?c>pDssqZO_h$`G)_&lri|QJ= z_}~(M4IG!kDQ20Gbzd+UtXREG5<6jIq^O?WehQ@rm#sWkz)G)FUtcmJ&(9^at*$ z{oB8^v^Q5(^`&eacy}Ncgsfd#W3xR@4DURiNBPEbh0&e;-x^rf0p+nD{1Jgc_JCKv zS6xfy)NMqi!YoqAF!)<1Esw1&Qda)>kN&6zm4EY_-@MfF$MNI%aR6walnyA%LWnvZ zVBsaIK&#@oIW7~zhK^dybH$dM@LCuUuTUYhK~sGR+AQ{hVP(J$O@$u&LFhKx)Hso` z{V*0K|*t%-maoO z(`|#Wemfh-djElrtKPHY>Byi1#8PnfzHyrYcQ+3EK%*m5LaxT;EU$KwT^?!L_Pf=+ zlMb!BXMFfv(VPRw+dO85a<#I>>grh6l&eNIN?0mv!2!Gk=QbMcXtP<>uMTpIb`WC5 z36iF}IxFA4ed6=;6CWQfL+KVHzJpxm4oxFAS3SJH` z0-WEUe`m2OYm@T$@qE?0Rh(BnlOCeD7bc0-@a%^(BG3-`XU*d|&%lN)uCOD;e=#NAe| z?uyWWmMaew3o-acKe1BTcAMH>Z5F!Ck%rI7w4YJ7wi9njWF5Lq~lHCZ}pV<*>lBaYWnb zk>2@5mZKdVj$VD(9hB2fFiHPaF8SLilXhxEmt?D_J1lwM=DGOz<+@g3q00LRn=SMo zW%GF*dah?2>grN;ELORTcF%+DjLN^hY{wiU))XDBo#TT)P&7Joi>7Z(JI~!}Ls=K! zO>7xYfML5Ms({r%TZI6fPzmLX0ntp{>NGessel4y2ueeGXT;1;vI4K*felS$Hav4~T*X6g;Z4G7vuj?%Kn&_O;ag-O{?|H8ZU*}H{ zA|Ho|OsGgF{*~@Z8DQw#CWKl0@ZSPNGga@x--?8EF| zpzizkhtvPyEpnLEeR0CL;@@#aKFfT}cy1gS>DlvId5*$NZK6lp)+$sgM=Hb5hMgEO zb=BmBub4xgJ;mrXov)KAd;NQdp=Q((Qu8x!e3z*4(GC#RO z;H(RA(qm=vzW12$yzg6dq{uLFj(up6l?P>%Zz2J3H3@Czq0ffCf~0BW_WG4_v(h$P z2Gm{%lp$%0vVThMAa)lD2Rp~6nH~51veM&Mb*=Ggx?Vcbr}W}f<4xZeS`Mcf`)t;7 zu3fp-2VKrKBbt&1FW4tWo$b*L(``)pJ;Rc&`hA>9AM&;JeWmRldjhhWf)}p$nIQE= zIa=9y_SSjSyYMDExE&c)jUxsjKVv{S29-bY`FWRfu~mQ5<+?7c^w(UM19l0fv(}@? z)iyn#EL$L$Jyx~`M)zvlJ_|-GHVVc~@r^IG=#|ERliM;q?|aoI)MM^y!^&TM&kVH% zPOemTM1(E3odls>(N@JPR!Vz8%3gG-mCE;2>Sar`GgLYCS;uw<~; zP7xG1qkmP!b(sBXwOZJCml+p*MHXV-zd&30e?l=vKc$jX zebV+A^^s2YYmfxHTa9r!n9QvJsDojsRj#v-p#_w)$rsiivw0tiPHm@WZ?S1O?JfFP z&LrnE`A5q$w9)*gb8^9O$xm`Q2bJ5VoOY^W4BQvhL3LOTfe`evEO0ac;J5DW;n+6|_`YxMS}Rs2dxZ9Mo@tbO z(d&Uh1%RhKR_Dd~1{k3}%R#=b!@=>`vw4)Z76aAg=w_Kt@X|NNZQ|*46geEu zLFI3s`1b7^zJ2?Ky+hDHK6V+pR0`Mf2xGP#`(D^<<6gIfwO!qrTQSQBhEz!Gs9d?v zQSM7^j`n_A8R(>MD=lvxD-mcA)4D#;Ui6;yN4t{)A8oGnj4)eD001BWNklYby2NM;yjjJI#UzaVWlKY$tsLPt-7KUbO=z8|NODpwzw%+g_n!7Atto zcC6%Gmiqy3amLN_7{F!@yf%aFT=B9kLnX!*1PrYh|LMFEB``hw|Xxy zZGG|$;hVH8VyeP9ospHu^WLmisAW1U_q8{(+zaFbA3uHIS{Ll|Y@2j+4JU5mt~ger zZ8?~8=t9L$Dc~Hq?t0R$D5g6-?&IE$^gRhv3_FvD(1LV5YFtvJS-aBQ72cwABD9)V znQ|w+jmJQ8@f)3M@t(`z{mMOU=Ag6Ah-@Cx0W0%Z#Y#iaJpz6fWymw7uUvnel%rj! zv!ipc&$o^DeUpB=Dme%mcJIj)FZ@z05d8E$m~m>%XEccC(_M~LyDb`UUz!|XXtCMJ(WZDJarUfBB<@Im8k(m)!#*=cCt5m z3U(G1=6w&a06Ne@%&!4s(#%A@TRf{CuH_ zJ=$D*)z4pCCCq2S3666VZyhmr*~m0lIL?k`7>6!etv#d6pGeW^L*ux zW*^aik|-)q`n7D+Z^McIV}gjBOQv52mXDMl?Np*!M8gR8tPb(k0sU;t3v;l_iFStK zp~`UK6>a^+ecus%O4}F%DXqM|JY4>e6Xt%($DYdjZhx$IzkMwu= zsTpZl(*avYZS}+Y?(I!bK9zox(xb+$Mo<~KoLQqM?CFYS@uSmksDsOBkI2VbhS*m) z3Zr`nw@YjAUK#6TJpE0d?3D%C5G(emac~tF8$typD=Q#Z;Ym*i5*t^@6=gbmBFXUL zQEEe)VRPa;?erQt94despx6W&3fE|+r%)?Pu(@6S67qTw?-_Uw<2``<;wn$p&N@!u z$jhi1+aiT{xx@E*gzXA?={c^K4#bba_*86c4Wa4jfP&;hUSi0AYc1@nI}U6(4m`sh zp84(V@x10=Gq&1kwbrUoK-ax-U3Y|b%~0aI0IdhGS+ zX7px`*3Ieym;f;RV@$)kDCC9aky61`^iwt;%AI!x9LzU5N#8xlDkj%jhg2}|<}cu+ z@)P_{l0E4)--aPNYda^euv+4f>T~m(S%*m1*;rLIcX&<8X#T8m0)p(A$wLaZakDpo z6@mSO_n5ZUZdfO6bQqP0IKt}Oj;lV$dm3VD>|^vw)TZr;31c9^^hLoaEfb%ofx=F8 z87Z@zI@n3Jqa8UMVsdg87C3l^4kW9`XTVbLm1)LV=AEWGgh&tvayAYPYD75c90aVr zs&Z0+cGz31B4^n-U|dZYgNCZKU+lgv9chtx&v1v;3Ums3)8ygoN_~H=D={Q+@ZLAp zXBgI>_gw~eHzm`+^H7*8d2Bp1p~5+V>+6FV_od3jq>hbCy>BOHoNL>TGpqAUsng?m z{lS}Wh8X+#Li6Luv*a!>HZr_E5OXtg@WhD$lxd*$N$pY5I-fEDXqe{(i(PmF*_LsO zcrYAN7xbYR9)peXE{&6eeg<&Q>e7!dhw6Z^!6G9L-qoJh;s~Q7l~`NG#qV%THgTbu zrH&Wutrwce&ODDgXe-ZGUN93RkR=wSgOd8Epwz0shL}van1e-QaKNS=q{u;Osn;tZ z1asN;b+o?Gj(REdexg3w$p_zM>In;hU&PTYdk*9>ggIxzdsi{5d)n$@0}gs4o?C9% zmJ>8=w$`w*goO9df%#W9$#43X>Vye9!Fvf+o`1d zbWkKOHcw*c^=dw*=aqcPeMOw15WF1%7D>gUyoP5bY+QvyKq~go5s%lZq&3CC+i}8Q z6;-_c{J(fqaqU@Nn>VU5##U1DDU_^=s2!N;sP3-stmL21u|a0f0& z%n-^wyR&T0`sDlJp#6x!!jD`9^l^RHzAqghNLB|s;j6TRlSkTI=n$1-U^)F~ ztp^QPZgb>6rPi}g7#7U##L^pU-OoOwajT7U|X z{_p<|0QkGV`#b#AU;WjWyz}Guas1~!PCXKeq$Y0r@$8$~@(1VkHJdT8gSazIs z)HJ~18AXR}*0yWgkEQc?U)A&4m-yr}mr9=#&x#k)pf3M}ymb0F)|;Q7okFER@OxD4u4jC79zYW)Alj1SDlwQ)7L@7YP=(*{(WWg&WqB#F1N zeF1yd0Kpt|O5t#f0p)5J!H3$TQjPmjS{F*DkuL_n(pvxO!92aZxz@>5yZ znW0xOLpmA21Yh27oS?jNR+I_`XNOM^j&%0EI~Kd_T`<8t1@Vzy^FYj6ysHzmw}%q2 z9#CFOXKis->&Cir06N2D-}l0GZE#Te955@PmFmnvss2MlK7}XuZ|-;0;WQ!ssXi3n z{pGtiKBxg@G#+u4hDb4%Bt26LpP5ZzPyH)!`wXvi53?229|B<5au81 zyUP3JFBOasgD%lha=d081#5eNIevGoj}VoLrRq17O-X>1q}x*mpWRXT&}Gb)5JTqe zpwGkxrc$;h#e{X&EG}p_>6YBTtCbAW>m}mDgMU7Jer!_ulxtKP!azoj6{SEA7`x`_R{w ze*;wMQfb2zC%-&0y%V=Otfojv8Nl;gGEd%+q5?>;dtX9htPJR`cYb$BSMmdoMFzwqoXFmMG}-Vp>iAqmu^&n>F zQH06?(G=^c2SWn7RtR`$~1nB!IJ|QgfT=7YfbX%MDtNJU{k+t4tv{isl8JNql*Bw*dhgJPqwe^@KeWnv5TDM#zjNtvwYya(tEI{2c^ zk-;+tah$b6%W^&69Ss0$01`vK%`ERWKHfY_{d*llcS&7eb?|(Vy14C5?d!`x((pMH zh;}xbR|0{i&F_0qQDqGBQ4KZq@~|^)XJ1n4B7Om6+T&1M#EFp@wlnSRQ^Z|w4!S=M zKW}W5iM9ukcQ%jE*Z7>l7T~^e!Xp~LG0QuhiV$0V>Zd+yeTakFsCd8D+mjxW9xV^` zNfj>E!^E1nT2*cr`cE8Y76#h)O}clU=BM-Ly-qZ$b{=hL-HhnEwProk!gy8mSRJCP zw@cj|5e3i>c}QAHdo5qM+!6a0T=nE$Yl4gv>e9jF>#D8HN|%$5>W|3nDEc8sCh}yh z12!^r^HCq9UF=1k$#2S6w43H+Fw(5s0pq^^4PNYKe6&gTu@DnCR)pe*qry$=hnQi$ zD~x43>Zhr|>-M+Jy^||sIp}M<@bMwK1q+7H#N`FE&srURrQSaMb8li>2=F2k9rAik zG&}dT{EPqkUu#hLAOG3Q@3x1?ff5P6Y1#NcbC%TA{9%OYhfQ&Y4 zu;tmmP#Yriv9l&t<@%~kMr)Q`JUSWj#IPdYs-16oy(d;%CeC)Xp>M_{LYwrX|5ID5 z-%Gva-Gv!8@0ZNjKb*Er_%^!KWmO&NZs{X#=`&{Y)PrGbsFmxZaP39L3ErP}st19c%{p>L+4#FR9xSyH*fY50hNSdJ#gEHi+z zC>P+nS)IhC=!K2Fb6GoA@JPpi3-GDRfFAQUoHN2FRaUPuZ8R0SZm>4cZ9MXWcK3Q0zpxF z&36r6ZOnV~3uWx=C|PB32umL&;;vlsWQB(h$}t9%o(hD>!g@5H4Bi27iV>Lh?_z}r zaA_UeNzdmFMK8tZ;fYojm6X~BUzA53lYtH5__e;KT*qofhQFT9uBWq*G6G=$QBJRp z;%=7>U^yZ*vqdP47$B*nW<6L|X{&OCyjOlY=hRs@_CnE6lSJJr7TRu~2VyC&oLGA!?b!&p_|SRUsNflZE*c9OE1(#8>dus1fW zZ7_z+IbpDnTJ#SnL0$jeEBS?9MI&vQ{@oxy-B6b`+j>1I1s7%)CpFsAkM+0JFW$Ep-BY%|2Hb3>1$GQZYNdqu>VnxXEodYL#e z2b9}U8O2C!Mz3ls{Iy=;*+fm-*V_(OoXQlT`k*^|eQ&UA3u1c;wXt)`>kR9Zwq%f{ zSJ%Sm1c-3_L9*P#UfHSS-{9;+P^U^iq}93?Z121tJb@D@vW?^^N6Ugbd5T`N7svx@ zQyiITUKL3Rv;|tH!d|+hR6p)@mV(r|OYg-SOkhBp$SZZmiiQx(Z9F&WO!ow)o;2EN z4UIACz?-QDXP=_zdm4rbP=z1TcvI`=SpcG3B#*wOmh)vrd-C^$wXSCu%@T{fvGO|# zr?HP!p!^8zk*|c63a7tL`uj)Wnc7JF$*S+6?O(dt#O^$8Pqn$FFQ+wr(AGL19e_T0 zl2}i^5fe^`(mF+|38YVCrCbFMhaGgbcH1Hc#>(f61lBxDXGqK4Y$boaoY+JCD~vt( z`*_-d*UfoNow#~}yPvc_+QF38lm6XTIoe|YnUv{dN)-c%O7dF!Q_o7DXRsnlU7&nB zmf^yaY=n@){fUX} zhi|}eEh>+zQVwmZs)M}e5J0fKDK4otEVJDF< z&;G%r*6e$9K4I$^n*T*ho_Z$JQG6UtYtQ#L`*E%->!uT&ViSM1B&HEbXgXM>%RQ2B zS*^_Q;AY`#1Fq!dg_VQIW_Mj*E28>+)Z~DVx>lS$NlF<5+O{&7Oe*UY%M2CD(4*IN z;ZOhMPw@Bu=l_LYzI_7#{O#ZV?TT|IBp=nm(HV)Nc5~>ZoxD3LPBrnvvbkh9$P!u z%BX|vPrHvCh>63rJLxrSJ^CEGO}h2@d`{^*WZGUYK5e-npM-LJ zT$VG;#Te^`Kdmq2neHeG4T1o}ptmv(mzRw;jEzcnyjSK@b-2x9pXKJ$uAs|T$WB0+ zVC*<{Z@diKokk#?2?Zw5Yiojg6Tax$6mMJ?@MV^S#uS&wUk%&saD8#mq`opjPR|^O zQ7go(nKH=pOaiqmLNWvek{gNdoqLjI+H-}nhO}GtE)(C$Y#NSdbf}#&8iO3Z7M44% z>kws82HQ5)Ub%0o?8lbQiYc_*k8)w%8@3kiW3R3502BQBnoi0FPU)UEYPA5aR3zT% zmeQ(_P3TV9+Y#EMdF$EqqKP4cM;Lt4O)FZwl=M^*@^jYxCF|eE3Sm7TT1=IMK0Y4| zeVfvA(4%Dh%&@A^>Ra2+^eoSsA@@vYc)kU8vbv+&OH zMm?a3ajyEL0@sdOgZijy<5d|$eLX@C(!pEUEx1$Ow@F#0bjz@Iz^VA9_}QzUcRG7~Wu3Wi2rR?FG;LI;G+xd~6SY4y5L*QxL#0`_ zbvr_v&rs+0qh2CqW*!*A&ylXu)b!$c92cIt790c6jxOK$K5EVtZJM0j!#FR!Fs>bbomoO|xU zNMfHRh*GR*Wk|WD)uYi5rd}P1ys?gaM_Gj*Wlp*@4sFKn25R?UCAH2NPF(3sii1iQ zsl00yP93T8Kj_F$h`|-=dgb*?C@^9y z?b%|}Dk4EcVUNyRiNt`;HxW;Co@#fo53Of52N;HpZy9N)BS++Q(y=DakwxL~t=_;&{7`O-%lIopl#U>Xq%xN5UJ3tA8*-d zp=}J+jyrn3bD1MzbN2vS>&ou1d(~ld>S>h>JECcBh8isHiJn#ig^+j3HmTA|`Ujbm zj$R+(bx--Ja_S4E^pbvFFg);wHVRiUi!N$CLt#FE;Gqp(%B~n($%+k9<(sC{_YVn| z*P>Rq<$c@u+_m__57V-@4r}Zadq#Bcn8;X@8*@Ti4aoHjeh|~pvNPmPc}V*(Jc=gX zG9WynQz%L=3U5@Ol=^l#d`F1HFI)WJ$vLRJWumtCMD3XI-cfFZA3KN15H@%n(gsp0 zwc!mgo#;%aAeUF0vK?oBE`LYO?ThO?+sk)B+&AhSxf~)>eET3rmR#yHCfcaXTGpS~ zpHD3_?2+cQb*nA~9sm}~S;&=}*IK1R%6t+T2>Trw(s{=gNZ$3Hyo5jO81|K5_n!4S z2WZcJ3_p%ldVy+Y?TE%CnGRcO4klk%c{V^+kYSYNq1%9m{7^2>zSWyuv!razKsNY9 z*>Xl&XuFI$5H9^>!H#o~0+2qW(j^N~%|kuB9Xst+;zO-e79Mo!r%H=jQ?zG-$F6ib zoc3I)9CW{ZM*X#186yJKNqLX_dZEyGnGT z`nh$YU^VWr4IuY?z8BTC^<@WrO>Fhy_JQO(@{UW-i0P!<4F4a6m7?Xy z=!6}$YuHq1pN`6Mn`$i|mgQnvAFK8u8q}W!=q^Vl+~I@TQRw-KRVHQhXK88~wiL>s z1}%T<9^U9)Inb-F(6Tw?zi-ygL1THYj3A%~Fwo8%byANRlu8~2gTo+p(D)tZXVtlx zyPIuXpN{3(C?g*-ikm9|!qdp83`zHB9KQTiFx`xfeQ(^i;odjwHr(+;4%8a1JXdI( z+zT|qDoKX`x<^cRD$0RH8e67SfcVl`;NCN~vt7hl1&fu_*5UPf@VIA%(2>zACbW}j zQkHBdTRAvi(i&5nIdfL(N-ip_b}A<8<|_rDhktp?i4cDLT;ps!AH32$8sD&!RSKMZ zG0x2g28+EsSu3ts``$72SHeL{L3cXnNX_y+Sn(lkL$OMV@`hXt1trQ5hw>~LU$R=dVCpd-2y`B_*Ulf*`06pOv7q>QMl!ZtKw)oK8*pwhneglX3b|xlafbohrp&|3I^LZIFRhIp>7Pa zSsF9$a{`f&OruRO^jPbZEN?nHq(WUYQM_}|@(mw~O5K#`_E5*ez=5ZFs<-&)kqU~p z*uEuOiUn<*%xg@Yu2q`qy{_d=Jj8{tM(nrqq36+l;Cvz5(14i0LcPaNuHbhMr;s^f zwV=07)djAUKNj*3pTLCuIU)|vXtUIrfOvqB1U(LUcHsq~{bDZlVb_22HR5(V`ZIr!(=5Mof z`kLcn*+=Ip2bHazv%@~CkLWL<+_>RGWsjw!5?(sJ^QS%ZNGmSS;n{q{cGk!+=*w~qPAVDlR)zzbsx`TyOrvH85YB$+p6npU0Lq` zhrPG!*{#dUgsw5y{XF{xNq`O%m^MZ^G;ODvh7OHq3IE_aR(WNPUA7B4k;$f{nYuFW2iAUM!DDG z)eie_H%9z&EoIX(Y#`?Nb?~y~sSj^#eQ@tEnW;>UCeP?rFPZP=JKBb$j~Wnx zCoVus{s@yEh8zy@&y6TVK9(038}UrGQ)VPqA#yd{Q-&&xMc*)%xjRPGV?8r% z4PeaoqyPqM9m#4B0INY2#3S0#DK)6Tc*iL-g7q-J{H6l|oRuMuXe@wjnfy_+;VD6V zz|y*#e5z&JtbmIy+U-R$fz)xj!=`pBONa90U^Y*FSnqYMlO+!&=A`kyr}C_A=Y|dV z;Ssl6OO_8iu{xDkhw`wsr}FFT3tnGe@b2X`&R_BR@{-$KG^kU>ZL-4aJ*ZwC%m*Hi z)tUVAc;NAP;2(VPi}=Go`d?Q0&lA7%JHLZp_=R7%A?nBD0myKKni6yH5pbDj}jID^g++F46Mt}d92L7c7KXN!MSQTAA4 z@kQ}Y`oira{L<=GcZiLKVXY)M4Y08$dVHu;S!hwMzDW@A1~Hu6r@z-wbT*$=`-pqs za&0i!skmpyT&*aXUec0j7?Q_)#-7Ta1dl>$YOW{BOns)Zx(Tdj@oj);?_fAZb_c~O zaLzO%h$hIT-nI5>Ugh4xRt{BGQh`ucI4oEDE5efCF@xB>64Hk9dxNF|NQ03r-pq!- zsFmwsa$*Ap0eb^Zme6N(C<`c*eOBf?F;UAD?Hi--N?)rm zLLxo7KXdPDDhiGW3N6t`BZpEtqb%pZ9M!q3ORH6fn#^uC?u*2wtPE>IOT!qRAYLg) z#qM6tz+fBF(4j(8DjRyXf|Qh>QfH{)p)|$6GC6ma@79UX^1Y=Vqgpl*~PqV`i9Fi)V4u zpdm&}K}Jng8LkZ|oZ#MOGLnz?`d2#%|E96H`&MI3{k(Nx8ss;2DvLv&-_kB3bAYuy zgSk(|oTJHg*WUs@LT^(4z#VfgXP5z(D*zPGlBuVri|5ra>w9I0vcA;A1b^<(W$xCj zWlg(il%46+mtFDh4S1=;i1ub6et{}!M^rme_nqg7X%iD?boPoqjSl4zoy^=Bw~Vm1 zfu#;SM(t SIElQ0|~*0>Ol3&}?h%E(2_1r9#?F`4*s3$y~cvtbHu`h~;ak3cn)c zT0vWK&#s2Bi7rj^ren0aH)?BBQ+9K2=TyzN0ORTyF1x3?jmuuUBWzW4_R!8zuOyAP z3fNky0fy+f_Tob?iqUkq&?@U*Y$CL{rAlPk+u8cX?f25B9O$L(cDS4X9&IsS<-gg;p zl=i7~%{^w?)#z#WzH-cLG$XyMq(mo`E=LZZO?R|&;_a>Gq#Kv^u9YYZ9MqF|#C+>t zX$WcYuaD63mP@y@qpoB;1Tg{BnH|>{h2%^k9vPyS46xo>E}DHHo25^;cbWvYp1f{Qg$jFNA0!AL2dwa z|1;&^48X|uYTD?8;r3LG|8b>dt3;$FcxU~;zVyUIMSe0kQp!&JcG|hp&dDvctws7- zIe=F??7NL+eti%Z8@M=`f18f-(z#R{MmnZ0!3UYdKkGb%>>0^*{toei*0iboUb{Nz zKlNK_e|cVM(BDJ;KEloxjYP+^QdWJ6?P}TUV+=fwM_{gcNY!%6i32mXdeF3mI5Osc zyANDxs;WiP6u$+=xD0M)a8;axn4U3_6-0{lUu>;{G{=g=G+9US&WQbTN60cen3su} zn6_4&<187@LMH92_{i&nOvFv;@f@4jPpr9&Oc8+bS zi_N&Sk-(v>=JZ&>b=l>dVW<0oN?R7bQ|&_`UYTfT^8SgI%KaXRSLgJaq0CO@@rL6# z@b29!K6&?wcdzg8$-8%P{Vp-DK$yfaDRtJV(U$=|WI61Cm&Z$XD8D>j@VCC}yYTn_ z?%&0K|NFlW0Ql-xzltCI(I3V4ec$(8>HG2cczlZ<7b}~F^K)6zf0+z#OYk9hRj&zF z%uD+9YC8$U?ot)37RSZc+4fadJ*s+G6_iPSJH}nvdFZ*m62v*ruqS8upB6D0t{8Tc z=f$pj)?=?*D?{98Y;b*-Ju08B&bv=h`7SI|tzvOa|I@{`!z`b#cuAcz?LHY{k4yLM zC3Wk)whbTFWX5^E;XDm?o{@)R=4!i)&77F$xndcpHlNu};`8fA2K1``cxTgvJKA}+ zq6zIRCiO7JFz@3G8E?pe*GXn=?T$%VO2Gmi_}`JPxL|p3QM+n?_P;9L3e1-jXg+p5 zYgYcM_Bn8+ven)WABG3Uz!2wzh3XvyPicKQ&;7%U@XiRwpTEjNk(NmaR+aU;{=3(c zeZ+odxRq_?<&uGRqHRj~5+p0-m!(uicX_HGIsySn|IXH~31})?Icf>0-LaGRL>ZKj z6!1;~p>m#<%V7?~Iv-@#>XaIrur?-cEx?44icbkB|{9swK_r#$Z9#GNy6Tpsyh}Q6kTV)6#9GW!Za8 z6cAjW&#Q1wvLjUY3Ns0IcZ=S$%CFJ~+CsciiFTAStj=3CohH`urW((xyUCv{x|j9U zT2RSBmPOCD4y~EbN*PKC(&l(VAD8#-3LczgH_^lC)Ems#4B@q3u_aJ+Ek%~;14e*Lf3*j zbLQLHvf8)K4Dt$;Px)ypC~bO81G5=`&iY8+w>SgM_Ez2PYWTPF+$->+XL(U~$RZh3 zk3tO}vc$2$qH}eOv25N5yEg{7-!B7)t>Y72Ci0nKu&H}oR=E@o-InGLUfp*)sJk#;rmcA05P1abrv$c;#jUFH<7rDI!GNaI^SzHP|PNpOZSO&iVl#NALFvGA^$mXd6d01n#cf{Kxe8bKF$wml6S!V&T6Yc(RR9UZ!8&!LCtWx`o8fP-qH6i>%rSf;pufpOC4k65pf(K+e?3>y0_P_UV3|w`Vq)%1kK;+Eyg9(xB>;Z;S7q z%L3nYz&RW+4)_FyO<*iPWrV+S+FUESnZeEz!-v=~1Z~$j^YO{m+jN#La;ADW=24GL zKB<=qA5-qswpRXz)hP`3TF1pKJ7Grzd~={H^BT6c8ymm~TWhm)A0%&(-m*8k#)-@j zLz7tU0mItPECb?O8%?jke6t6v!}g(MQXNltZiuq%4ui~|G6+N8cUWfj*3zadSL4Dg zSJ^BEdA-T6Z*l^kP1dNdrtkVo?VsAiNUbTKRrj{!wd<1FL4B?WlRAQfnpO<#@mO>j z$H3#^c=!5>ckf>D?)4R~?_SqT<<}RCailpe3{$eMMdQIOF|IR~b%M%RDO;oC5$q9v_cy?L#eOTwj2oTHDWq`}g0k&(kJeIvJa; z+@OQCuUbt)u_c|k-N{Z{Dj13%XMTnEI8ZuQoT5IJ8kyvwu&-f0QL?lZjV=wN(O04l zUt6_8d~G;nAYAtRL{jBXUXPpgtD?}&hv1WnA@nuDL2Bn|hn9UPETI@}*;T(~rPi69 zRbZTfgnix@7&XcKvz6`k(a$)HB^4e`6eU=y_*WgyTyawYhX_@bO?}?AlAOgP*1`QP z2ez#Sxn=BQVGE1N13~kJ8D_WnN#y$K9lqp2B$lx756ZU7vvOSn3k4UaZ!P&m2E8eo zqzLE*9>81yeQWQ3=21XSGpWiX(78l(uZ}=Fk5?{5hzv`4EN88jUe?Nm5V-e-feK4^ zeCl;oTuM5s?Y%BH4Gd%>P%oL}*9EUC*y?QP2yN4`t-*j6dyo(x*e+G}?!vhjO-z}K z1Wy1SSyB+iVT#ML3Gy#%wkdzV5m>GIa%u*Sr~w70(y2BnL7~GqF$aLDovc>vusPWb zXb|)s^vUBE+gny)k6E(0Bm4DJ8ul`9Fgl7Tv*n%F_G>Mo1M+u4Mv?e z^Qs1Mi0qiq(y#>;lzC()%HT_#DW3ewAco|hAizaQr*V=xMm@6HmnF3||B|bwy2Oz3 zseUd?q7Ug*mLNYln+C(VPZ|vm1EL6nu^FR?=H!ysJ_{6(pYuFGMoV9foH*or%i^*> zp)L~w2_$p_nLwc#KD{q_fQHE1cViw@deyD~Fk*yzIH|^EtGD)ShgJ20Cgd-(75FeS z_%QgioA)c_8kEDP^d>hg*VOsfzhGL~StAD3JpzOy_v-AU{I`1^c1L&(KrW%{u{`gZ zQ9M&0QTNQoBT6&TQDN`K7#MTn7z5`xFy@J494i_ z9>D`a$h=NKimdCL3qUsjoGU$ujWN0Achk+Ji|~qfDFFnY<_O01;4OYYFiHUlEUhQD z39O5-hgbQ^R$8U=p+n%ouEZ}+LgU9?=Dux*Cruq5oy9T0W7cXMeILudpXDX!pY1PB zs;vPw7$px3WNYaN23-!!6j1Ok%ZS5jJs_)|j*ca2ZqJtHXR< zF9&kEV}y>@rLXgwOH|VLpkI-;ezONj1Jh#H5&V$OEA$&zy%G0(h-1D_J=N*=K?|42 zKS9u>Ou2d}zFNzM5KxAOpFj3@v z2`HIhP4ZCscqLu;whKKiz9Sf+=Y_RZ1M!2YY^?!pI-^pTly1aRno%xw|M=N?svG*9 zq>XbLt-4jaZxYH#!+0#pq)+N+M#Xa%hwZQ&*rsrz)e$Bvl+;?+zgcw){I=QNDd zaGvMtL_E)!A#>IWYtg~K_SZI;FWh{h^ieL;cpTlg7JLPumVVwku??}DbP~?e9+u1u z*qAi~3Niync&cQz)7{K4n(c~sM(|SK2BP%3#ZrH{QVNhhpSGadGcym|P&hnw_(jhu z*N_d0-;ps0q>ZXHTT!3$?~Dtk{d%89aX zQh)XR)Gtk5SG^n8We>&9`Ie`UsmkMxrppJT{^)yB0$j;qOC;e(&}JAO_P`^E^YZe7 z*OwQ(yu9SL7P~&Y4v4^Qp{X*s5|(3P29%jNamq|8pN>ynU-1ur@CWfvzw#9Tz@Pld zpWt_Y_jmClKk_5^czis*@z|`g?JL|oO}M8N`iP!|4pN(H^O$9%(IY|f zfW(n%r9b@^oR@EzC~m8J0I)5Az3)rE`)QRZ?I$k_sHnXF&<5pa-DSJ6sx{Ki!k5jS z(=mqRf3b0UMBu(nT+1l*$@pxnut;T_D!KmDYl#^OuQY-u+j!UpDPbsbkJl8}IBieT zI|gRWg(VihEIN!r2U$adH9Xu`0Tn)sxYk%fjD?HD-g2UBVliy($CwxvW+77X9E6IF zbJX`td>30tjw9CRMl9=sS}9moVH(E+$K!#=`G)C_?h{9TUPIuDGKy=w{0;dz?4}bI z1CAI_EA82^g!mn?@|^bh=+Qfu3lTMKLVh#H1JW1pc@&PzvwBY}xPBM#9|fz?A^rL0 zn6LsSL;yi$2unOOeI`iadi=T;t_Dgyv$3totm9KreztvU$q5AW+N-4LAO%;~bcD?&EFc&`2HS+xM6pRC6RtbE$zr!GVfK-3?_+l zB19-95PQFjRVSO;@j)R(Ho3ORT7EzF9~C96PAAK^f7VRpiDOP2hvV@mu+Le2MPDV8 zDz1TH2gW#7r*b+cGxpP+SerG^uKcNyg!dP(h=C^kQFJPPuxuCd2C~hpw;9`8__ds* zP^{=qOVxABBei!Ky$Tdp`CL1gti~J>xsNh=GWJ1zxwC^kgs{}uhpdRT(-pi~oyc51!>7SNG#WXGn}pXQOxg~knvy2Uk*hc$Q ze!aa)^3<-58Zf7{iZ`_FSMC#-BXmaWbs9^O=16z>p<|uVQQ-fBW3cd7q)Xyc@?_7jcXEaF zv#OKN(|0hlH5hON1+Bh-N{8FqjNwwR>xyxJzF$qrd>L%5K^nLWFkrA524D;fpD|93 zzI4cw`b%lY_8G>W{0gA8qFKod6+QXdC6Br%{1p6?1E;Ji&F77_YuEf6d(U}BNAlS^ zlo!5f#glK*CSt?8e0F)Q0UNvBEOaX{u^qmj(5A8+q3iYmDm%~Z-^)&OfBX2vzSv_N zc#-{%AIGCQm1Pj7;tyk)tjY+b1M4@}48k#DtC6f<$jNzN9w+|c-~W5~+8_N9zWy)% z1pwg7U;Z+F;0JyHU--fo@bUO~d@CPoQ0!fttOkO{<@$2k+|n+0$da~l&kZOw%PkT{ zEb}|X&Qcdv%u4+dW+GULzOLHy9b8dwLiFe6b0k*W25(h-Q0i;M3dBgv);dv~#og>< ztm0aO$l~GDwG(awK4$nhL%!8E2CbVty=f1Yjb+at>)!B3nAz$ZORSE>RAz>Go@;BZZE#cp&B8oBM)of$R!|OZz6glr z)(X5W_Dz2@t4zN*i2~Z|#WQ2Y$7J3CyLQZhF?CPn*xn~PmJgTsLdKF~FhXlr!8cV1 zX|B#Q$wCtn4<&!~I@O-l;%lko@BEmUr|XdYk;iR;8Z$gUlE$r?I+xc1*dY~$b}Z>z zb#8shLMcDNN($curm+*ws$*Hy;zlOP(5^)XBoHc!aI5qVkjvREfy%}crB&lF0P5MLXB1`Bh3HEdCSiJ=Mu-CO4HNgs!aF>?- zDo4tt;t=)3y2jt^mfFBa@*3;j_;2r29?9!#w&=X@4~<*$t8}*IEGX;e=^V9wDRtPE zk-+o?DzM7d1K`{XGU~11f}FJNJ27Mr25sBo;cGkBwOzg;^u2U0tBy?6WIz51=m_*6ZH+zXnT0Dh|~8%)Jg}up83Kbeci76POIV z^r7HfdWRR&Vktczm^Le*<6~{B9kOm?N=HepqCnn1=EOOxL-{x-=CL}F-_8@K4d6VZ z{3oW*6)>>UPi^KZw5pwfbnuV&ll5fd-Cgg}_LV783r!u79A?|nJbdv653H=MZZ($K zd6Na*1Xpd{ZtKB87f9Z^drFJdbfgw#yXQi{!W_QUP4qH(Uf)&zHp=SKHLW8#XD5wZ z(r~rCujg{AjM_8AcpS^6H<#LW=&~qRiuL+-m=W*pGMRzJjZUHit|YAAgnFIqM)58u z+giFN{oJIzw4~|vwUU`viifvCbQPuzE9Zu!=d~l*ZfFvJWV?apaH7r~==>pV=&d-B z&Tz~*Vph|{9KM#P^obb`a&>;l3__8k-pP!1ANh^qSFN7uMVttcRrm0Uc0U8*0QS8$ zyK}peJ+W_xvVN!zEL_g-JCgfM%H~+7ZnK7wn$ed!t#-x6L$Qj8&@j~+`P(j(l)Sf} z23co>R5)@pdhVr+-e%xT<-$gmSj3vXSGq)vnzPSq+|a9hr;SdTIQ2B8MC6xvFmcL8 zgBzty`=fH(d_~6^oZ*GkR3%V!Q|H;%c&y3RJWXHGw%okg)KR^@rBz#)RH1v`TK^oK zj~*oBHYvoIoUy#`h1LsUJ>7igiYr`Rsj{U;PF=CSRJJxgQTl|;q-jzA)l08kup~B` zkWUmtUkcl8+p~2zs!pCS4M(n`;kG*IeALpkE`gmXfzUZ;J)487yN|2YQ+%%-gVpKS zx5lY}==Ob!a60W=xbVjyuu%sFBX*LQva&wDW`JPTPkp;v}wjH#31`z zd-|T|dbB!{qfC*9H?vuy{>fp_;32n(IH9)=3tHETwTro=qG+0|LK?hbGSP`{q$4( z`mg^we&ttw1)qHK2|gYlk8j<>OBe0;+acD|uFrXUvaW4Yazr*dplNWRoG^DWt^fca z07*naROqm#rv&e!={=sunFcd^(WMTwPlm2*POk0wK0-k0uy3j$js!yf^hlk>`boD<9MurRObZd3C;v}_tl}E zaS5#0pv3mF7+wgRmX<^y9dhP2$Yb&i9eA-}HT9Q7za;M8)r*tPtAFK@yN0MBJvl_i z3UBU53l^zX(Kib0Le8qt-A$qMk08zj_jwBi6VVmtCXkX<357R@qvx!}-@#p+E5gf$ zs|ErYG8<+lu~iW}EN3v^<=^tWlno&l1xz()xYLZ_nxp9(9ZiYT#4+mwGOI=UseCQv zGaWY*8?qYO9m4AU zU|@|76h|2iTaFdyex=CBhXJDc$TI!1$2Mmmn~er2N;aw-ng7sWE-tjVCH!{mI)k*~ z`Q`aSd`RhzbGA>@_0JCPPh1>uF{(L|@6LGT`^Begq;(HoR9&mS3gA+2=dvg3%Symf zXLg!d+Nkp!IkG(~7o9R7ua#g0->+e8(}3LL-7VU54K2%#UaU^$BjtKv931Rmu{3{l zEYH=UJmra8eMqco~CX>KgssOcz)&8)ESo&c8t}^wul79nN`9k`j~%>ypv?j z+LWrKO)lPb$N-M0mt~UHa!T}3;nFANKKwfpq zvhtOXxg7MBx)w?E&Y;S$rNdp5jDcXfgm&t56aHnpXjc=6H%%88(W?U6Xd%p3x+Cfx1`S#9T zLi>GVLLWFxi)MAhq&;=uVg?M`;7V1?0>KC=RNLpc+T9mRc8A0MLD5k3|_OKE$wJxD&O-eO>d@mcb~8xP2D_K z4sSvKGnTNpL{4MmOud>YP;H)0XV}90{*>*2+3yNQ}ePxha z+7R(I$_|(4F{)KWRXbUOu=Y4p;+1s)M|HGvP{F!B3*Wfl0k5*Qqd^U=PucBmr!4jF zqD9$JleT!O)|0lhrD&bTHBd$L=&a?v=Q(q^Zw@j(9uL?Ucsw3>efNrY?>@l*{juef z4vht%Bbrw3EPPc&tpFUDfp5%_1Mr89wX%bqILFH8d%pX-@b~`CzlGoX>em1OU;p~o z@ue?)3BUM@zjzzB_;`Fgz7-DoR66D!C~TE*Gy#G&pSZu#55=TS35Bg&vqU-LlX!1W zbK43HXxf5$`~t%IgIB#t``Ew+0xP1e{K74a(*JDf3-%cZ-9}?mL|AkzJy=VASIoJ} zAwIL)TA6UnH8^{ykqjCMrcG3ogUq)^xp*Oj@q!ohTH2((yFT{xfdn%%t0Zic75$$2 zh@42}%Mhi_DY#rV(O@g~1jit5AH^@6^*kkxPC6A|t~Mi<-dJIy&Qd%t z--dhXBXvxa@f;lt05;l2H^ww{N%V|`dSd$r-*2t2FDD;^z{<+V^r@gZd3hqAfh4U<(%ULO!NNTNEjLZ;+vzfr8R<5x<}B z07b#Z)#xIdT{9Blm-noisRP*UC>$lS3M8*NfeVE$%fFQ`zzk_-vj-omI@Zpa$KDCT z!X!N+ljNyguLbuGdjn^i)9|ePm#|wUUHKXSn;EQP+QX5Zz-y%gU|_<**~?bE_*f&x zIiuz9zAxW+dlR5*5o}m>(;4r%%zMsq7+ZZuw3|!dVhhsfa^H?v<{vO8kE)V@%v6@a zO{&!>HNeca1^;n|ryq;udHV29yw4=^7ym}ybk-pj?mv`)1PD=S($zhrI^ zZZ(&bm2FwZueWcjJHK>LTG4+WNu9-Ep>dPuo_;PLD>$s#=B6ESoz-_MX^A`4C zXH<{6WOgnmPDEh~bY8vh zd{v4(Ip1v(n^WXJ<@pw$Kn;$-3eTU4lX?r4ZSsCC^!(7kMyE^d$?Q$#5A*lmAgCSr$NO-O8_csRkQvdp+(ECq^A(hJVaZ^7Pe z2CCQ=R=CxITNCVTA$*d4Rr**+oUC=vuZAy#ree2A;8YW;wyexh>sa3Ny2)Bq)GqV4 zX5-0^chGIuH`bn0+`OH0KUJBHUf)OJgqE>wX+n5|yl=`2{kz#^AeVl-*B!fFiMA0x zt$0_7QT$X_2o-4Zn+D{Rdi^hr96LWV!v=MF8YW;+>sy`5vibvqp}OzxJTsV?c3*WA zix7=%aZwn3Ya5hpw;~2gglc*H`=jHec9iPLaGo{I8H zYb`(XFO9p}U=8wJ0$ZK;)uGHzX1K#g%}mZ9y2)CHd)2M`-Fn{rC6>?D9AJx?O+lv- z&t<2!8K#X0V0y#u<)_9fTEt4S0@G4=Pvq#PHvOC}y%e4K+IG%jbt&4}#mg%uGj8N| z%{t!HE>L(xWJ3d>Z7FS*O(2_RV!<&HyJ`cxGA;)xPnAz)ZI?&YH@$<}u(9G+j}1d4 z4aH6+LzLgA27;=n)mgDjVvyQHRO`_9RrcKf`#j(9_Ws1%+ljZgHymRvagEo-TaU+q zckkZe+rI6SjxVAdWUbVkYHyW^jBVKUd>T|IY+1bJ6RpE$h%Wx&fBWy?|M7`zDaS`Ahq>x8#jV3~CUP1T`j5o-zUsZ} zCvNjWjNz!&uGZ4XWoy=KAU7i*F zkjp;t2j7}VPLvH4NNI)Gc~Z*HOSn*TJ+&4uO(;M zBvJrNUPz;yM!ue-g5%PR4vwZPWz>QOj@EX)HTL>yFl_&>_R_Yq80nvNM*$3eXUoX) z*V&|+oHjpK0BC}|2^eQR+knY3#O|KUqmVy2GhyXBa(^S9lh(Wb zN|4gNyUaG)KKntaUl|Y#THY$&yUU~N*_6Q`zOxO)_yIuEKj^1S&3!E)d0{mJ) zOBf?H?sZM1ZYzc_}gcR@)^L_Oqwy)Y~*OGF-F`wa2yKa*D){_|G~3;*cNm+!a5}_jd&vDAnV~m z5aL6&NASa?ezy(0JGJ=9!NjR&pXeRa6sSOL=`@}%jOQsBw1rs`&$Fb$C5R+=OeyBz3LU!N0|eRm#q;xA+p0`k zvtk*{Y{4Ip4%*%!15%FGsZ2hgd}xt{7$0^G)J9%^O7^wwhb(oRyGMKoB}f4*9MXxr zDA{5qE&q)z)59k3S9Sone6zu-< zet7zhBW5bUygcyk-Mdxy{iC=yZLQEjL>W;RXM2;!Q;)8TNp>KQF=^@=1Ag*{e+d8C zfAx>?=im4X0Ko75?(gC|zT-Ra6F>12SDyWNd_4Y!9~s|nQ1OZ?EoK&GXJx~Vbln&J1lzsi zfb@-sZv^kOI4aXp{NAuUdg;>_92l`yw7JI?Akx3LcX&=&u@%JVE6SsNT+J>st*qCM zl{-vz1i-*uhqB8#-yLDCQ60(?t6sd#`2DQUF(0wY*8^jE<-6RGJg35>-mK;;LxgyKz%gYi{t67N<$~;d z!{BmWsl;dQ*~5{eYb;DK@unni%;4TSVaQh7Q)<)HSSdg%juILXtXMWX??)lJGUeWJ z;&f-dBcEyTwV`+Li?vxQbY{*uV`i9cVlT6x_jWhK5&Hq-hT*ML!%k;&W-C;qvx};N#P@jK*;b4QVeL%YQX*TWSM{WqdOL z;X=nvhqR!L0HlHq3K{xL<-Ma5h8lRlPX2g)&I3NrT#lN{7iPe`5O~@J+<>8_(yjK? zK4PTXV|6OOfByz}1P9)~;r-hi-rnBh?L0GZFy{&Q(q}M>Ah9m*9v#dn2LeW;m7UBZ z^4dBz_-XR0X^1Qv!I&utn%=5|ZKZbU-p+sK(PSVllHa0kq4$=}UQXD$l#zOlTq-E_ zl+|xd2rn|Hg}FHj*JZKzblN$&9zf0%@tkeD^vb0z6M#pHHn1K85wP|UoM9h_TMXKu znv;N5;<>7gVwnZzUuj(lI`|{`Zht-q7-LfEX}R zmo6Iyq`OYuP#(i_AootAt)_RxYh+a(gl8@{Y4r2vzoT$RV;9%7>bJbBbV>YJ^_e=N zS}-t(wJE`B#D%|A@Y6St0LH=sefB74KC5=H%XQJy`Uxw|%5@#P(}O&vfr_+U^}VWM z`F{GU$q&>W%8&>jW2A0X+*dda`T~Ide)p&3`ciKp|LviYc#)*K=UppM#@Wx(=&vn9 zpW>1vdBNmb6pY%M-FJ+EqYFDqd(u}H-=GhSm8~$gC9sJNl`Z6!6?wvUIl6||9XceJ~a=NJZ`0~irh zn~Qr+k$BTci@IE5&zTj@qM zw_U%n{rZQ$7v`XjS(V)fWuAi9l^-&06BSdvMtNJIiE-&r)~#l`tQn3-eCdU?C5bf| z*cHY4@VDF!%+K7LmMVs7j}*QC7;ALX_*d$$daeQWBioam?i1rUV8@7o-6P7%JZQ)7 z%hDP(?TN=Al$krqwGLlwv#y>jCBK5yNV;C__lAp~m31>5S9wD%Cn~&B^zGHGw#1r@ zhcoJ4`f*0NYwyE-rdjJEPYn9<-Z=|z{~3k9>HGHpfG>af%P=$i_>ccMJ{}*BZ?%J%b6+thUBrB--f75mv-KKx$ZrSSV%xnz@E|eM zurhZsRKT`Oq_9^@z8zJV78A1pi`XB`V=#{8^LuAxE`cp^U{2bgztZwdCBK80UZ!gR zwfx1U_ie=%HlwlQ9wU)2bg+)0q)r|v+re!FLdV*gNYMHN#1|qq$eFVIZZ@^_eBm3_ z{beH?X5Z{l_bVHPV3^=YqP60z$L!o{zp8J=?oFFUQq_2h^5Mm2i1efADJ&{STgsJR zq$3CwFvRqsvG5Q(lw&L2;S(cP8q%53Omg+vjg0dioDj$D3!}lZ?oULeVJIb0&?RGq zY%_i*ExcV9&kFrEI$rZyWqfS6m3r6L9pKF5%J`W=VBs~GpiCvmfeNr`K`mLIVNlGf zWxrN79TYkS>3CRsQ=e|03cp2B`>eUL!0O*zz-!Hj^qA2>hdKlHOh?Z_C!mBst1lfb z*S73o#7twv$O*Gp%CKr}a%RD$^wk@Bo>A^$A?OCorn12f&ef^BI@6zxgqV{Ch!Nj$PVHktUYpgwY+p@3*nIVWYENeC?3d9I0RQnbsbtIx) zzeMlA?pu)3rY&A`1Z+<>=yZdzwnUBlrfEiwX7oDq$mLe&WxHd>oK+y^dBwI_wJda| zS~acOZ@dwWv#^_C_|Z_un#DZf^N>JBfufGsOXsJIi^8YvpnR|8Q`RzN$8oHE-`sJY z6Q90+k551S6yN;xn*k2d>Ss1^#Qw}}`D>Y-+~uukXf93Q)RpzAns~iK8OmUp%Czuz zo`WxC3wThVaDyE8xMpweu7i}1lu>UWO!A2WG0E8Ez1{yP2Z7hx`EZX@fK3-rNxVPDzgq{-^$0ry*&j zc5=X;dK8|i?5L0;vn9ldWc#l4y`A^6i{4cpjZ-3& zOsA#WD^~m=_vBXLA9?C>UvQ!7o+pkovG84hI@>`5EU?VD)g#V#dqWv6eZ2;U=72Lv z89kSh#rV4ijLHFEU2kZq?X?9dRMs0#^R%u6#pz)C!sxrMIuv4zz!y{gr=F z{C%ru?RuJ(f1<r6D^<;^eYOYT!&qp{6yQ|?U%tsZH(^soB|Y|;m+dO5@v zZfd%IkaM=ry_M^dsda4nXq}dulp?1*#lrYn9ccDJ;JS2h)ur34j1>q*S-z)in{@P! z<>TPY;yuC?>@>w&Pff!&M z_TU(fIoGycFE1}Rjw6Vn{LIg!PXBm(JpQI1o;szlsLJJs zjwU7vA$ROu!Z<)bvB!1N{wiK!I@XKf1kbXUOqIhb=L=9OJ5!$#9o(x!RqY&InY0nI zk^^JGW>}NMvOmju#+&kQ^bstn`evzp(fI5a0i#)xgCXL*+HioVLh%m(PC)ARZpNg} zwS^wcYM=Vprb7qCWeuzbC+grN#pH=|$YZQW&}Hvz-UoJN9AU>!@olySQNm|p_`$Td zEgM#U$n3=14#sAK3481@?LKK)?d!sl&A+&0(QRO2t4MDE3;%?PGmja(QwQy8EDo)l z8JEqKNX5@nzpDUyJAC_&2u=5Et^(QGP}{n2Zg1~+N(2qkri+#jaR0XkV4odJu~dmV z(9NfuZP}SHl_2XEjd9U%+nH~9Gy=M}XY(Sdt8)cJ>De_WlRxb@q0gi&=w}ixEk(zA z8&fg}GxhJw?+G}ibEvqf0vbw#NN>R42f{h62g(7nGCHmNWIxZc0KWECjP-_wFWOOM z&UwNNcmry-AgY}UC{OIOl)Jtz3%HLGZze7j@9I>Q4n#U%UU`DX1fu+SPo440_d0L; z60|bu$Y4>(vKLlcM(bRi%2W{P?B&QRoT;LBl)O1^4!PNn&Q$YB#F3fa!o$E(+qUznv&mIoB6fiKue^I0Du@3T)FwK zbTbgs9rct51xS}Rr{QLuxF1IG7-sQa)@%D-ZC3BDWz~GsYpYTwvM{!?-5vKW!}Qu! zrkmG>CS#@#g0|OM3hwu9a%u3pLfPKVe2==va>==?M}L=?2I4lJy2(HJFTPqhMSi1} zBYyzhZUC|0FLyLBgV~Jz(=~_@du7CaURNWs}PulQAk;;ZdGWHs|o)xAHHsTx^iQ;r?t$d}dgQowA_0SeG`j#urStFrEoLtG!C5#a?8ZeT$$EXw9uiTGzlhDSxH+ZZ*e^{+Ay#m;9B3 zZD=fbWEhrA2R+6{Pj=x6vZ~*=hA#biK{2S=%7I$UVCFG6 z#bTxnXDUbgd8MLtsxruL$wTYcb57)7M;Pvx#|Nq-*|&4HJ(34NI!xaNJ1RCn-g|a1 zZex_;zMog+X9)i`+8wk2g(GI_%=J0Xh$+Q4uX`&Y1!KK))+c$}v#dw({_M~2^FROdczu0+-dG=xkH_DX!`s)=Cjw3Ku#P$v6Q%tWp}YI*9y3Xu zGh&&rU{HyXzkiZ zTYpOXm0u!gChHo1C_kS8e3b_VR~E4m^;j!4oyUpe-263^=bkay7re5VPfPQYNO=i`(+Q)JbP!r88MiHy~6k(e8`9#f;@B!7vU?L`

FTQzQ%fAV-HIxG~w~@`K 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

$=bic_t}K+RuUR4F%TFvFw{FwLTh~5?Xv1!HQf_ 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