Tactical commit before moving to Fletcher

I *think* I'm on the right lines with this, but illuminator is too slow
and the REPL server is timing out...
This commit is contained in:
Simon Brooke 2018-07-21 16:47:52 +01:00
parent d53c633527
commit 5ad384745f
10 changed files with 1034 additions and 939 deletions

View file

@ -54,7 +54,7 @@
:main ^:skip-aot youyesyet.core
:migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")}
:plugins [[lein-adl ["0.1.1"]]
:plugins [;;[lein-adl ["0.1.1"]]
[lein-cljsbuild "1.1.4"]
[lein-codox "0.10.3"]
[lein-cprop "1.0.1"]

View file

@ -5,7 +5,7 @@
--
-- auto-generated by [Application Description Language framework]
--
-- (https://github.com/simon-brooke/adl) at 20180721T111020.637Z
-- (https://github.com/simon-brooke/adl) at 20180721T133846.002Z
--
-- A web-app intended to be used by canvassers
-- campaigning for a 'Yes' vote in the second independence
@ -72,7 +72,7 @@ CREATE TABLE addresses
(
id SERIAL NOT NULL PRIMARY KEY,
address VARCHAR(256) NOT NULL,
postcode VARCHAR(16) CONSTRAINT pattern_7470 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})$'),
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,
@ -985,31 +985,33 @@ ALTER TABLE ln_roles_canvassers_roles ADD CONSTRAINT ri_ln_roles_canvassers_role
ON DELETE NO ACTION ;
------------------------------------------------------------------------
-- link table joining roles with canvassers
-- link table joining events with teams
------------------------------------------------------------------------
CREATE TABLE ln_members_roles_canvassers
CREATE TABLE ln_teams_events_teams
(
role_id INTEGER,
canvasser_id INTEGER
event_id INTEGER,
team_id INTEGER
);
GRANT SELECT ON ln_members_roles_canvassers TO admin,
GRANT SELECT ON ln_teams_events_teams TO admin,
analysts,
canvassers,
issueeditors,
issueexperts,
teamorganisers ;
GRANT INSERT ON ln_members_roles_canvassers TO admin ;
GRANT UPDATE ON ln_members_roles_canvassers TO admin ;
GRANT DELETE ON ln_members_roles_canvassers TO admin ;
GRANT INSERT ON ln_teams_events_teams TO admin,
teamorganisers ;
GRANT UPDATE ON ln_teams_events_teams TO admin,
teamorganisers ;
GRANT DELETE ON ln_teams_events_teams TO admin ;
ALTER TABLE ln_members_roles_canvassers ADD CONSTRAINT ri_ln_members_roles_canvassers_canvassers_canvasser_id
FOREIGN KEY( canvasser_id )
REFERENCES canvassers(id)
ALTER TABLE ln_teams_events_teams ADD CONSTRAINT ri_ln_teams_events_teams_events_event_id
FOREIGN KEY( event_id )
REFERENCES events(id)
ON DELETE NO ACTION ;
ALTER TABLE ln_members_roles_canvassers ADD CONSTRAINT ri_ln_members_roles_canvassers_roles_role_id
FOREIGN KEY( role_id )
REFERENCES roles(id)
ALTER TABLE ln_teams_events_teams ADD CONSTRAINT ri_ln_teams_events_teams_teams_team_id
FOREIGN KEY( team_id )
REFERENCES teams(id)
ON DELETE NO ACTION ;
------------------------------------------------------------------------

View file

@ -63,6 +63,9 @@
<!-- content: put your main page content into this block -->
{% endblock %}
</div>
<p>
User: {{user}}
</p>
<br clear="both"/>
</div>
{% block foot %}

View file

@ -39,13 +39,18 @@
(declare ^:dynamic *app-context*)
(def ^:dynamic *user* nil)
(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)]))
(tags/add-tags)
(defn raw-get-user-roles [user]
"Return, as a set, the names of the roles of which this user is a member."
;; role assignments change only rarely.
(def get-user-roles
"Return, as a set, the names of the roles of which this `user` is a member."
(memoize
(fn [user]
(if
user
(do
@ -53,14 +58,10 @@
(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))))
roles))))))
;; role assignments change only rarely.
(def get-user-roles (memoize raw-get-user-roles))
(defn render
(defn render-with-session
"renders the HTML `template` located relative to resources/templates in
the context of this session and with these parameters."
;; TODO: I'm passing `session` through into render. The default luminus
@ -68,7 +69,7 @@
;; than me so there's almost certainly a reason it doesn't.
[template session & [params]]
(let [user (:user session)]
(log/debug (str "layout/render: template: '" template "'"))
(log/debug (str "layout/render-with-session: template: '" template "'"))
(content-type
(ok
(parser/render-file
@ -83,6 +84,26 @@
"text/html; charset=utf-8")))
(defn render
"renders the HTML `template` located relative to resources/templates in
the context of this session and with these parameters."
[template & [params]]
(log/debug (str "layout/render: template: '" template "'"))
(content-type
(ok
(parser/render-file
template
(merge 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
"error-details should be a map containing the following keys:
:status - error status

View file

@ -8,7 +8,7 @@
[ring-ttl-session.core :refer [ttl-memory-store]]
[youyesyet.env :refer [defaults]]
[youyesyet.config :refer [env]]
[youyesyet.layout :refer [*app-context* error-page]])
[youyesyet.layout :refer [*app-context* *user* error-page]])
(:import [javax.servlet ServletContext]))
@ -63,6 +63,17 @@
((if (:websocket? request) handler wrapped) request))))
(defn wrap-user
"Dynamically bind *user* to the user in the session, if any, so that it
is available in layout/render, q.v."
[handler]
(fn [request]
(if-let [user (-> request :session :user)]
(binding [*user* user]
(handler request))
(handler request))))
(defn wrap-base [handler]
(-> ((:middleware defaults) handler)
wrap-webjars
@ -71,4 +82,6 @@
(assoc-in [:security :anti-forgery] false)
(assoc-in [:session :store] (ttl-memory-store (* 60 30)))))
wrap-context
wrap-internal-error))
wrap-internal-error
wrap-user))

View file

@ -49,7 +49,7 @@
(defn about-page []
(layout/render "about.html" {} {:title
(layout/render "about.html" {:title
(str "About " (:site-title env))
:motd (md-to-html-string (slurp (io/resource "about.md")))}))
@ -67,7 +67,7 @@
(defn home-page []
(layout/render "home.html" {} {:title "You yes yet?"
(layout/render "home.html" {:title "You yes yet?"
:motd (md-to-html-string (motd))}))
@ -100,13 +100,12 @@
(assoc
(response/found redirect-to)
:session
(assoc session :user user :roles roles)))
(assoc session :user (assoc 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
:warnings ["Your user name was not recognised or your password did not match"]})
@ -114,7 +113,6 @@
true
(layout/render
"login.html"
session
{:title "Please log in"
:redirect-to redirect-to
:authorities (db-core/list-authorities db-core/*db*)}))))

View file

@ -42,7 +42,7 @@
(defn list-page [request]
(layout/render
"issue-expert/list.html"
(:session request)
;; (:session request)
(let [user (:user (:session request))]
{:title "Open requests"
:user user
@ -69,7 +69,7 @@
db/*db* {:id (:visit_id record)})))]
(layout/render
"issue-expert/request.html"
(:session request)
;; (:session request)
{:title (str "Request from " (:name elector) " at " (:date visit))
:user (:user (:session request))
:visit visit

View file

@ -42,7 +42,6 @@
(defn app-page [request]
(layout/render "app.html"
(:session request)
{:title "Canvasser app"}))
@ -51,7 +50,7 @@
(let [record (-> request :session :user)]
(layout/render
"auto/form-canvassers-Canvasser.html"
(:session request)
;; (:session request)
{:title (str "Profile for " (-> request :session :user :fullname))
:record record
:elector_id

View file

@ -19,14 +19,14 @@
"Render the routing page for the roles the currently logged in user is member of."
(let
[session (:session request)
user (:user session)
user (-> request :session :user)
roles (if
user
(db-core/list-roles-by-canvasser db-core/*db* {:id (:id user)}))]
(log/info (str "Roles routing page; user is " user "; roles are " roles))
(cond
roles (layout/render "roles.html"
(:session request)
;; ;; (:session request)
{:title (str "Welcome " (:fullname user) ", what do you want to do?")
:user user
:roles (map #(assoc % :link (safe-name (:name %) :sql)) roles)})
@ -38,7 +38,7 @@
[request]
(layout/render
(support/resolve-template "application-index.html")
(:session request)
;; (:session request)
{:title "Administrative menu"}))
@ -49,18 +49,24 @@
[request]
(layout/render
(support/resolve-template "application-index.html")
(:session request)
;; (:session request)
{:title "Administrative menu"}))
(defn canvassers-page
[request]
(layout/render "roles/canvasser.html" (:session request) {}))
(layout/render
"roles/canvasser.html"
;; (:session request)
{}))
(defn team-organisers-page
[request]
(layout/render "roles/team-orgenisers.html" request {}))
(layout/render
"roles/team-orgenisers.html"
;; request
{}))
(defroutes roles-routes

View file

@ -14,8 +14,7 @@
* Generated using adl2canonical.xslt 1.10 $
*
***************************************************************************
-->
<documentation>A web-app intended to be used by canvassers
--><documentation>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
@ -26,7 +25,9 @@
set the system up and authorise canvassers, and a 'followup'
interface through which issue-expert specialist canvassers can
address particular electors' queries.</documentation>
<typedef name="postcode" type="string" pattern="^([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})$" size="16">
<typedef name="postcode" type="string"
pattern="^([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})$"
size="16">
<documentation>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</documentation>
@ -72,20 +73,23 @@
</group>
<!--
entity electors already has a key - not generating one
-->
<entity table="electors" name="electors" magnitude="6" volatility="5">
--><entity table="electors" name="electors" magnitude="6" volatility="5">
<documentation>All electors known to the system; electors are
people believed to be entitled to vote in the current
campaign.</documentation>
<key>
<property required="true" type="integer" name="id" column="id" immutable="true" distinct="system">
<property required="true" type="integer" name="id" column="id" immutable="true"
distinct="system">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property size="64" required="true" type="string" name="name" column="name" distinct="user">
<property size="64" required="true" type="string" name="name" column="name"
distinct="user">
<prompt prompt="Name" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" name="dwelling_id" column="dwelling_id" entity="dwellings" farkey="id">
<property required="true" type="entity" name="dwelling_id" column="dwelling_id"
entity="dwellings"
farkey="id">
<prompt prompt="Home" locale="en_GB.UTF-8"/>
</property>
<property size="16" type="string" name="phone" column="phone">
@ -94,7 +98,8 @@
<property size="128" type="string" name="email" column="email">
<prompt prompt="Email" locale="en_GB.UTF-8"/>
</property>
<property name="gender" type="entity" column="gender" entity="genders" farkey="id" default="Unknown">
<property name="gender" type="entity" column="gender" entity="genders" farkey="id"
default="Unknown">
<prompt prompt="Gender" locale="en_GB.UTF-8"/>
</property>
<property name="signature" type="text">
@ -139,8 +144,7 @@
</entity>
<!--
entity genders already has a key - not generating one
-->
<entity table="genders" name="genders" magnitude="1" volatility="6">
--><entity table="genders" name="genders" magnitude="1" volatility="6">
<documentation>All genders which may be assigned to
electors.</documentation>
<key>
@ -167,8 +171,7 @@
</entity>
<!--
entity dwellings already has a key - not generating one
-->
<entity table="dwellings" name="dwellings" magnitude="6" volatility="6">
--><entity table="dwellings" name="dwellings" magnitude="6" volatility="6">
<documentation>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,
@ -176,11 +179,15 @@
what's behind that door. So a tenement or a block of flats
would be one address with many dwellings.</documentation>
<key>
<property required="true" type="integer" name="id" column="id" immutable="true" distinct="system">
<property required="true" type="integer" name="id" column="id" immutable="true"
distinct="system">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property required="true" type="entity" name="address_id" column="address_id" entity="addresses" farkey="id" distinct="user">
<property required="true" type="entity" name="address_id" column="address_id"
entity="addresses"
farkey="id"
distinct="user">
<prompt prompt="Building Address" locale="en_GB.UTF-8"/>
</property>
<property required="false" type="string" size="32" name="sub-address" distinct="user">
@ -215,25 +222,28 @@
</entity>
<!--
entity addresses already has a key - not generating one
-->
<entity table="addresses" name="addresses" magnitude="6" volatility="6">
--><entity table="addresses" name="addresses" magnitude="6" volatility="6">
<documentation>Addresses of all buildings which contain
dwellings.</documentation>
<key>
<property required="true" type="integer" name="id" column="id" immutable="true" distinct="system">
<property required="true" type="integer" name="id" column="id" immutable="true"
distinct="system">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property required="true" type="string" name="address" column="address" size="256" distinct="user">
<property required="true" type="string" name="address" column="address" size="256"
distinct="user">
<prompt prompt="Address" locale="en_GB.UTF-8"/>
</property>
<property type="defined" typedef="postcode" name="postcode" column="postcode" size="16" distinct="user">
<property type="defined" typedef="postcode" name="postcode" column="postcode" size="16"
distinct="user">
<prompt prompt="Postcode" locale="en_GB.UTF-8"/>
</property>
<property type="string" name="phone" column="phone" size="16">
<prompt prompt="Phone" locale="en_GB.UTF-8"/>
</property>
<property type="entity" name="district_id" column="district_id" entity="districts" farkey="id">
<property type="entity" name="district_id" column="district_id" entity="districts"
farkey="id">
<prompt prompt="District" locale="en_GB.UTF-8"/>
</property>
<property type="real" name="latitude" column="latitude">
@ -301,8 +311,7 @@
</entity>
<!--
entity visits already has a key - not generating one
-->
<entity table="visits" name="visits" magnitude="7" volatility="2">
--><entity table="visits" name="visits" magnitude="7" volatility="2">
<documentation>All visits made by canvassers to dwellings in
which opinions were recorded.</documentation>
<key>
@ -311,13 +320,19 @@
</property>
</key>
<!-- TODO: should visit be keyed against dwelling rather than address? -->
<property required="true" type="entity" name="address_id" column="address_id" entity="addresses" farkey="id" distinct="user">
<property required="true" type="entity" name="address_id" column="address_id"
entity="addresses"
farkey="id"
distinct="user">
<prompt prompt="Address" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" name="canvasser_id" column="canvasser_id" entity="canvassers" farkey="id">
<property required="true" type="entity" name="canvasser_id" column="canvasser_id"
entity="canvassers"
farkey="id">
<prompt prompt="Canvasser" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="timestamp" name="date" column="date" default="now()" distinct="user">
<property required="true" type="timestamp" name="date" column="date" default="now()"
distinct="user">
<prompt prompt="Date" locale="en_GB.UTF-8"/>
</property>
<permission group="canvassers" permission="noedit">
@ -364,12 +379,12 @@
</entity>
<!--
entity authorities already has a key - not generating one
-->
<entity table="authorities" name="authorities" magnitude="2" volatility="7">
--><entity table="authorities" name="authorities" magnitude="2" volatility="7">
<documentation>Authorities which may authenticate canvassers to
the system.</documentation>
<key>
<property required="true" type="string" name="id" column="id" size="32" distinct="all" immutable="true">
<property required="true" type="string" name="id" column="id" size="32" distinct="all"
immutable="true">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
@ -408,12 +423,12 @@
</entity>
<!--
entity issues already has a key - not generating one
-->
<entity table="issues" name="issues" magnitude="1" volatility="3">
--><entity table="issues" name="issues" magnitude="1" volatility="3">
<documentation>Issues believed to be of interest to electors,
about which they may have questions.</documentation>
<key>
<property required="true" type="string" name="id" column="id" size="32" immutable="true" distinct="all">
<property required="true" type="string" name="id" column="id" size="32" immutable="true"
distinct="all">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
@ -464,8 +479,7 @@
</entity>
<!--
entity intentions already has a key - not generating one
-->
<entity table="intentions" name="intentions" magnitude="6" volatility="2">
--><entity table="intentions" name="intentions" magnitude="6" volatility="2">
<documentation>Intentions of electors to vote for options
elicited in visits.</documentation>
<key>
@ -473,13 +487,16 @@
<generator action="native"/>
</property>
</key>
<property required="true" type="entity" entity="visits" farkey="id" name="visit_id" column="visit_id">
<property required="true" type="entity" entity="visits" farkey="id" name="visit_id"
column="visit_id">
<prompt prompt="visit_id" locale="en_GB.UTF-8"/>
</property>
<property type="entity" entity="electors" farkey="id" name="elector_id" column="elector_id">
<property type="entity" entity="electors" farkey="id" name="elector_id"
column="elector_id">
<prompt prompt="elector_id" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" entity="options" farkey="id" name="option_id" column="option_id">
<property required="true" type="entity" entity="options" farkey="id" name="option_id"
column="option_id">
<prompt prompt="option_id" locale="en_GB.UTF-8"/>
</property>
<property name="locality" type="integer" required="true">
@ -532,8 +549,7 @@
</entity>
<!--
entity canvassers already has a key - not generating one
-->
<entity table="canvassers" name="canvassers" magnitude="5" volatility="4">
--><entity table="canvassers" name="canvassers" magnitude="5" volatility="4">
<documentation>Primary users of the system: those actually
interviewing electors.</documentation>
<key>
@ -541,10 +557,12 @@
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property required="true" type="string" name="username" column="username" size="32" distinct="all">
<property required="true" type="string" name="username" column="username" size="32"
distinct="all">
<prompt prompt="username" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="string" name="fullname" column="fullname" size="64" distinct="user">
<property required="true" type="string" name="fullname" column="fullname" size="64"
distinct="user">
<prompt prompt="fullname" locale="en_GB.UTF-8"/>
</property>
<property name="avatar" type="image">
@ -556,10 +574,14 @@
<documentation>Information the canvasser supplies about themselves; an introduction.</documentation>
<prompt prompt="Bio" locale="en_GB.UTF-8"/>
</property>
<property type="entity" name="elector_id" column="elector_id" entity="electors" farkey="id">
<property type="entity" name="elector_id" column="elector_id" entity="electors"
farkey="id">
<prompt prompt="Electoral roll entry" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" name="address_id" column="address_id" entity="addresses" farkey="id" distinct="user">
<property required="true" type="entity" name="address_id" column="address_id"
entity="addresses"
farkey="id"
distinct="user">
<prompt prompt="address" locale="en_GB.UTF-8"/>
</property>
<property type="string" name="phone" column="phone" size="16" distinct="user">
@ -568,7 +590,8 @@
<property type="string" name="email" column="email" size="128" distinct="user">
<prompt prompt="email" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" name="authority_id" entity="authorities" farkey="id">
<property required="true" type="entity" name="authority_id" entity="authorities"
farkey="id">
<prompt prompt="authority_id" locale="en_GB.UTF-8"/>
</property>
<property type="boolean" name="authorised" column="authorised">
@ -651,25 +674,34 @@
</entity>
<!--
entity followuprequests already has a key - not generating one
-->
<entity table="followuprequests" name="followuprequests" magnitude="7" volatility="2">
--><entity table="followuprequests" name="followuprequests" magnitude="7" volatility="2">
<documentation>Requests for a followup with an issue
expert</documentation>
<key>
<property required="true" type="integer" name="id" column="id" distinct="system" immutable="true">
<property required="true" type="integer" name="id" column="id" distinct="system"
immutable="true">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property required="true" type="entity" name="elector_id" column="elector_id" entity="electors" farkey="id" distinct="user">
<property required="true" type="entity" name="elector_id" column="elector_id"
entity="electors"
farkey="id"
distinct="user">
<prompt prompt="elector_id" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" name="visit_id" column="visit_id" entity="visits" farkey="id" distinct="user">
<property required="true" type="entity" name="visit_id" column="visit_id" entity="visits"
farkey="id"
distinct="user">
<prompt prompt="visit_id" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" name="issue_id" column="issue_id" entity="issues" farkey="id" distinct="user">
<property required="true" type="entity" name="issue_id" column="issue_id" entity="issues"
farkey="id"
distinct="user">
<prompt prompt="issue_id" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" name="method_id" column="method_id" entity="followupmethods" farkey="id">
<property required="true" type="entity" name="method_id" column="method_id"
entity="followupmethods"
farkey="id">
<prompt prompt="method_id" locale="en_GB.UTF-8"/>
</property>
<list properties="listed" name="Followuprequests">
@ -712,8 +744,7 @@
</entity>
<!--
entity roles already has a key - not generating one
-->
<entity table="roles" name="roles" magnitude="1" volatility="7">
--><entity table="roles" name="roles" magnitude="1" volatility="7">
<documentation>A role (essentially, the same as a group, but
application layer rather than database layer) of which a user
may be a member.</documentation>
@ -722,7 +753,9 @@
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property required="true" type="string" name="name" column="name" size="64" distinct="all" immutable="true">
<property required="true" type="string" name="name" column="name" size="64"
distinct="all"
immutable="true">
<prompt prompt="name" locale="en_GB.UTF-8"/>
</property>
<property type="link" entity="canvassers" name="members">
@ -753,17 +786,18 @@
</entity>
<!--
entity teams already has a key - not generating one
-->
<entity table="teams" name="teams" magnitude="4" volatility="4">
--><entity table="teams" name="teams" magnitude="4" volatility="4">
<key>
<property type="integer" name="id" column="id" distinct="system" immutable="true">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property required="true" type="string" name="name" column="name" size="64" distinct="user">
<property required="true" type="string" name="name" column="name" size="64"
distinct="user">
<prompt prompt="name" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" entity="districts" farkey="id" name="district_id" column="district_id">
<property required="true" type="entity" entity="districts" farkey="id" name="district_id"
column="district_id">
<prompt prompt="district_id" locale="en_GB.UTF-8"/>
</property>
<property type="real" name="latitude" column="latitude">
@ -859,8 +893,7 @@
</entity>
<!--
entity events already has a key - not generating one
-->
<entity volatility="0" name="events" table="events" magnitude="5">
--><entity volatility="0" name="events" table="events" magnitude="5">
<documentation>
An event to which a team or teams are invited. Typically created by the team organiser(s).
May be a training event, a social event or a canvassing session.
@ -870,7 +903,8 @@
<generator action="native"/>
</property>
</key>
<property required="true" type="string" name="name" column="name" size="64" distinct="user">
<property required="true" type="string" name="name" column="name" size="64"
distinct="user">
<prompt prompt="name" locale="en_GB.UTF-8"/>
</property>
<property name="teams" type="link" entity="teams"/>
@ -885,9 +919,23 @@
<permission permission="read" group="analysts"/>
<permission permission="read" group="issueeditors"/>
<permission permission="all" group="admin"/>
<form properties="listed" name="Events"><field property="id"><generator action="native"/></field><field property="name"><prompt prompt="name" locale="en_GB.UTF-8"/></field><field property="teams"/><field property="date"/><field property="time"/><field property="decription"/><field property="cancelled"/></form>
<form properties="listed" name="Events">
<field property="id">
<generator action="native"/>
</field>
<field property="name">
<prompt prompt="name" locale="en_GB.UTF-8"/>
</field>
<field property="teams"/>
<field property="date"/>
<field property="time"/>
<field property="decription"/>
<field property="cancelled"/>
</form>
<list properties="listed" name="Events">
<field property="name"><prompt prompt="name" locale="en_GB.UTF-8"/></field>
<field property="name">
<prompt prompt="name" locale="en_GB.UTF-8"/>
</field>
<field property="date"/>
<field property="time"/>
<field property="cancelled"/>
@ -895,17 +943,18 @@
</entity>
<!--
entity districts already has a key - not generating one
-->
<entity table="districts" name="districts" magnitude="4" volatility="7">
--><entity table="districts" name="districts" magnitude="4" volatility="7">
<documentation>Electoral districts: TODO: Shape (polygon)
information will need to be added, for use in
maps.</documentation>
<key>
<property required="true" type="integer" name="id" column="id" immutable="true" distinct="system">
<property required="true" type="integer" name="id" column="id" immutable="true"
distinct="system">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property required="true" type="string" name="name" column="name" size="64" distinct="user">
<property required="true" type="string" name="name" column="name" size="64"
distinct="user">
<prompt prompt="name" locale="en_GB.UTF-8"/>
</property>
<permission group="public" permission="read"/>
@ -929,22 +978,26 @@
</entity>
<!--
entity followupactions already has a key - not generating one
-->
<entity table="followupactions" name="followupactions" magnitude="7" volatility="0">
--><entity table="followupactions" name="followupactions" magnitude="7" volatility="0">
<documentation>Actions taken on followup
requests.</documentation>
<key>
<property required="true" type="integer" name="id" column="id" distinct="system" immutable="true">
<property required="true" type="integer" name="id" column="id" distinct="system"
immutable="true">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
<property required="true" type="entity" name="request_id" column="request_id" entity="followuprequests" farkey="id">
<property required="true" type="entity" name="request_id" column="request_id"
entity="followuprequests"
farkey="id">
<prompt prompt="request_id" locale="en_GB.UTF-8"/>
</property>
<property required="true" type="entity" name="actor" column="actor" entity="canvassers" farkey="id">
<property required="true" type="entity" name="actor" column="actor" entity="canvassers"
farkey="id">
<prompt prompt="actor" locale="en_GB.UTF-8"/>
</property>
<property required="true" default="now()" type="timestamp" name="date" column="date" distinct="user">
<property required="true" default="now()" type="timestamp" name="date" column="date"
distinct="user">
<prompt prompt="date" locale="en_GB.UTF-8"/>
</property>
<property type="text" name="notes" column="notes" distinct="user">
@ -999,12 +1052,12 @@
</entity>
<!--
entity options already has a key - not generating one
-->
<entity table="options" name="options" magnitude="1" volatility="7">
--><entity table="options" name="options" magnitude="1" volatility="7">
<documentation>Options in the election or referendum being
canvassed on</documentation>
<key>
<property required="true" type="string" name="id" column="id" size="32" distinct="all" immutable="true">
<property required="true" type="string" name="id" column="id" size="32" distinct="all"
immutable="true">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>
@ -1027,11 +1080,11 @@
</entity>
<!--
entity followupmethods already has a key - not generating one
-->
<entity table="followupmethods" name="followupmethods" magnitude="1" volatility="7">
--><entity table="followupmethods" name="followupmethods" magnitude="1" volatility="7">
<documentation>Methods which may be used to follow up a followup request. Reference data.</documentation>
<key>
<property required="true" type="string" size="32" name="id" column="id" immutable="true" distinct="all">
<property required="true" type="string" size="32" name="id" column="id" immutable="true"
distinct="all">
<prompt prompt="id" locale="en_GB.UTF-8"/>
</property>
</key>