diff --git a/README.md b/README.md index e047a46..c4ce356 100644 --- a/README.md +++ b/README.md @@ -57,3 +57,7 @@ as above; in the other, run Copyright © 2016 Simon Brooke for the Radical Independence Campaign. Licensed under the GNU General Public License, version 2.0 or (at your option) any later version. + +**NOTE THAT** files which are directly created by the Luminus template do not currently have a GPL header +at the top; files which are new in this project or which have been substantially modified for this project +do have a GPL header at the top. diff --git a/src/clj/youyesyet/db/schema.clj b/src/clj/youyesyet/db/schema.clj index 3e187cb..1cc6caf 100644 --- a/src/clj/youyesyet/db/schema.clj +++ b/src/clj/youyesyet/db/schema.clj @@ -27,6 +27,12 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; 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. + + (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 @@ -38,14 +44,14 @@ 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. - ]))) + ;; 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 @@ -62,18 +68,18 @@ 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]]))) + [: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 @@ -84,16 +90,16 @@ (kc/has-one district)) -(defn create-authority-table! +(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 - :authority - [[:id "varchar(32) not null primary key"] - ;; more stuff here when I understand more - ]))) + :authorities + [:id "varchar(32) not null primary key"] + ;; more stuff here when I understand more + ))) (kc/defentity authority @@ -110,15 +116,15 @@ 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)"]]))) + ;; 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 @@ -136,21 +142,21 @@ yyydb/*db* (sql/create-table-ddl :canvassers - [;; id is the kc/kc/entity-fields. - [:id "varchar(32) not null primary key"] - [: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 authority(id) on delete no action"] - ;; true if the canvasser is authorised to use the app; else false. This allows us to - ;; block canvassers we suspect of misbehaving. - [:authorised :boolean]]))) + ;; id is the username the canvasser logs in as. + [:id "varchar(32) not null primary key"] + [: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"] + ;; 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 @@ -163,17 +169,17 @@ (kc/has-one authority)) -(defn create-visit-table! +(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 address(id)"] - [:canvasser_id "varchar(32) references canvassers(id) not null"] - [:date "timestamp with timezone not null default now()"]]))) + [:id "serial not null primary key"] + [:address_id "integer not null references addresses(id)"] + [:canvasser_id "varchar(32) references canvassers(id) not null"] + [:date "timestamp with time zone not null default now()"]))) (kc/defentity visit @@ -185,20 +191,20 @@ (kc/has-one canvasser)) -(defn create-option-table! +(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." + 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. - ]))) + ;; 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 @@ -210,15 +216,15 @@ (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'." + 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)"]]))) + [: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. @@ -232,12 +238,12 @@ yyydb/*db* (sql/create-table-ddl :opinions - [[:id "serial primary key"] - ;; the elector who gave this opinion - [: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)"]]))) + [:id "serial primary key"] + ;; the elector who gave this opinion + [: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 opinion @@ -259,10 +265,10 @@ 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)"]]))) + ;; 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 @@ -272,20 +278,20 @@ (kc/entity-fields :id :url)) -(defn create-followup-method-table! +(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 - :followupmethod - [[;; the method, e.g. 'telephone', 'email', 'post' - :id "varchar(32) not null primary key"]]))) + :followupmethods + [;; the method, e.g. 'telephone', 'email', 'post' + :id "varchar(32) not null primary key"]))) (kc/defentity followup-method (kc/pk :id) - (kc/table :followupmethod) + (kc/table :followupmethods) (kc/database yyydb/*db*) (kc/entity-fields :id)) @@ -298,12 +304,12 @@ yyydb/*db* (sql/create-table-ddl :issueexpertise - [;; the expert canvasser - [:canvasser-id "varchar(32) not null references canvasser(id)"] - ;; the issue they have expertise in - [:issue_id "varchar(32) not null references issues(issue)"] - ;; the method by which this expert can respond to electors on this issue - [:method_id "varchar 32 not null references followupmethod(method)"]]))) + ;; the expert canvasser + [:canvasser_id "varchar(32) 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 @@ -315,25 +321,25 @@ (kc/has-one followup-method)) -(defn create-followup-request-table! +(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 - :followuprequest - [[: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(issue)"] - ;; 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 followupmethod(method)"]]))) + :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 :followuprequest) + (kc/table :followuprequests) (kc/database yyydb/*db*) (kc/entity-fields :id) (kc/has-one elector) @@ -342,7 +348,7 @@ (kc/has-one followup-method)) -(defn create-followup-action-table! +(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." @@ -350,19 +356,36 @@ (sql/db-do-commands yyydb/*db* (sql/create-table-ddl - :followupaction - [[:id "serial primary key"] - [:request_id "integer not null references followuprequest(id)"] - [:actor "varchar(32) not null references canvassers(id)"] - [:date "timestamp with timezone not null default now()"] - [:notes "text"] - ;; true if this action closes the request - [:closed :boolean]]))) + :followupactions + [:id "serial primary key"] + [:request_id "integer not null references followuprequests(id)"] + [:actor "varchar(32) 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 :followupaction) + (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 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!) + )