diff --git a/.gitignore b/.gitignore
index 2096dbc..3d04b99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,3 +47,5 @@ youyesyet\.canonical\.adl\.xml
youyesyet\.dump\.20180816
*.tar
+
+src/clj/youyesyet/cache\.clj
diff --git a/README.md b/README.md
index 74f4f00..714542e 100644
--- a/README.md
+++ b/README.md
@@ -45,17 +45,27 @@ You should also read the [User-Oriented Specification](doc/specification/userspe
## Building this
-This application is built using [Application Description Language](); the intention is that soon Application Description Language will run as a Leiningen plugin, but that does not yet work.
+This application is built using [Application Description Language](https://github.com/simon-brooke/adl/). The `adl` pre-processor is run as a prep task to building the `uberjar`, which in turn is preparatory to building the `uberwar`.
-So first you must check out the Application Description Language repository as well as this repository, ideally within a a common directory;
+This will generate a large number of the source files required by YouYesYet, **including** the database initialisation scripts. These generated source files are not, as a matter of policy, held in the repository.
-then:
+### What is auto-generated, and how to override it
- cd adl
- lein uberjar
- java -jar target/adl-1.4.4-SNAPSHOT-standalone.jar --path ../youyesyet/ ../youyesyet/youyesyet.adl.xml
+The following files are generated from the master file `youyesyet.adl.xml`:
+
+* `resources/sql/queries.auto.sql` - [HugSQL](https://www.hugsql.org/) queries for selection, insertion, modification and deletion of records of all entities described in the ADL file.
+* `resources/sql/[application-name].postgres.sql` - [Postgres](https://www.postgresql.org/) database initialisation script including tables for all entities, convenience views for all entities, all necessary link tables and referential integrity constraints.
+* `resources/templates/auto/*.html` - [Selmer](https://github.com/yogthos/Selmer) templates for each form or list list specified in the ADL file (pages are not yet handled).
+* `src/clj/[application-name]/routes/auto.clj` - [Compojure]() routes for each form or list list specified in the ADL file (pages are not yet handled).
+* `src/clj/[application-name]/routes/auto-json.clj` - [Compojure]() routes returning JSON responses for each query generated in `resources/sql/queries.auto.sql`.
+
+*You are strongly advised never to edit any of these files*.
+
+* To override any query, add that query to a file `resources/sql/queries.sql`
+* To add additional material (for example reference data) to the database initialisation, add it to a separate file or a family of separate files.
+* To override any template, copy the template file from `resources/templates/auto/` to `resources/templates/` and edit it there.
+* To override any route, write a function of the same name in the namespace `[application-name].routes.manual`.
-This will generate a large number of the source files required by YouYesYet, **including** the database initialisation scripts.
## Getting the database up
@@ -73,7 +83,7 @@ Do get the database initialised, run
createdb youyesyet_dev
-I'm no longer using Migratus as I'm using [Application Description Language]()
+I'm no longer using Migratus as I'm using [Application Description Language](https://github.com/simon-brooke/adl/)
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`. Manually maintained overrides are found in
@@ -118,7 +128,12 @@ which will aid in work on the ClojureScript components.
## Running in a production environment
-Doesn't really work yet; if you want to try it, see [Bug #36](https://github.com/simon-brooke/youyesyet/issues/36) and check out the associated feature branch.
+Either
+
+1. run `lein uberjar` and execute the resulting jar file directly; or
+2. run `lein uberwar` and serve the resulting war file from a servlet container.
+
+The [beta production server](https://www.projecthope.scot/) currently runs an uberwar build in Tomcat behind Nginx.
## Working on this project
@@ -149,7 +164,7 @@ Note that all tools recommended in this document are free for non-commercial use
### Editors/IDEs
-I (Simon) use, like and recommend [LightTable](http://lighttable.com/) as my editor; I used to use Emacs, and there is excellent Clojure tooling for Emacs, but these days Emacs ways of working seem just too far from everything else to be comfortable to me. [NightCode](https://sekao.net/nightcode/) is a lighter-weight Clojure IDE which you may like. There's also [Cursive](https://cursive-ide.com/) but it isn't free and I haven't tried it.
+I (Simon) use, like and recommend [LightTable](http://lighttable.com/) as my editor; I used to use Emacs, and there is excellent Clojure tooling for Emacs, but these days Emacs ways of working seem just too far from everything else to be comfortable to me. [NightCode](https://sekao.net/nightcode/) is a lighter-weight Clojure IDE which you may like. There's also [Cursive](https://cursive-ide.com/) but it isn't free and I have so far found it more annoying than helpful; or [Counterclockwise](https://github.com/ccw-ide/ccw) which I don't have recent experience of.
### Git
diff --git a/docs/authorisation.html b/docs/authorisation.html
new file mode 100644
index 0000000..c808f3c
--- /dev/null
+++ b/docs/authorisation.html
@@ -0,0 +1,19 @@
+
+
Essentially we have six levels of authorisation, at essentially increasing levels of sensitivity.
+
+
+
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.
+
+
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.
+
+
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.
+
+
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.
+
+
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.
+
+
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.
\ No newline at end of file
diff --git a/docs/competitors.html b/docs/competitors.html
new file mode 100644
index 0000000..88b37dd
--- /dev/null
+++ b/docs/competitors.html
@@ -0,0 +1,14 @@
+
+Competitor Analysis
Obviously You Yes Yet? is my baby; I’ve put a lot of thought into it. At the time I started working on it I wasn’t aware of any open source competitors; I did to a web search, and I emailed the Bernie Sanders campaign to see whether their widely admired tools were open source. I didn’t find anything.
+
However, I’ve just been pointed to Vote Leave’s Vics tool, and there may well be others.
+
There is no room here for ego. What matters is that the Yes campaign gets the best available tool for the job. So it’s important to do competitor analysis, and not to invest too much work into You Yes Yet? unless there’s a realistic possibility of producing a tool which is better than any of the available alternatives. But it’s also the case that by studying competitors we may find ways to improve the design of You Yes Yet?.
+
Vics
+
Vics, the Voter Intention Collection System, is reputed to have been a significant factor in the successful campaign by Vote Leave to take Britain out of the EU. It has been released as open source under MIT licence, so it is unambiguously available for us to use.
+
The architecture comprises a single-page app built using Angular talking to a server built in Java using the Spring framework. The database engine used is Postgres. Jedis, a Java port of Redis, is used as an in-memory data cache, server side.
+
### Download and initial build
+
I checked out the source from the GitHub repository, and following the instructions in the README created the database and ran a maven install process. Unfortunately, run as a normal user, when this process goes into its test sequence many tests fail unable to contact Jedis. I find it slightly worrying to run such a large and complex build as root, but as root it gets substantially further. The build still doesn’t complete but it seems that it is closer to completion.
+
The ironic point is that it fails because it depends on the JavaScript package manager bower, and bower (very sensibly) refuses to run as root. I therefore made a small modification to the build script to allow it to run bower as root, but unfortunately that didn’t solve the build problem; the jedis service was still not found where it was expected.
+
This is difficult to diagnose; the exception is so deeply nested in framework code that no code from the actual Vics application appears on the stack dump, which makes it very hard to know where to start in debugging.
Note that this is a work in progress. Read it in concert with the Entity-Relationship Diagram.
+
Tables are listed in alphabetical order.
+
Address
+
The postal address of a building which contains at least one dwelling at which electors are registered.
+
CREATE TABLE IF NOT EXISTS addresses (
+ id integer NOT NULL,
+ address character varying(256) NOT NULL,
+ postcode character varying(16),
+ phone character varying(16),
+ district_id integer,
+ latitude real,
+ longitude real
+);
+
+
Authority
+
An oauth authority which authenticates canvassers. Note that there will need to be substantially more in this table but I don’t yet know what.
+
CREATE TABLE IF NOT EXISTS authorities (
+ id character varying(32) NOT NULL
+);
+
+
Canvasser
+
A user of the system.
+
CREATE TABLE IF NOT EXISTS canvassers (
+ id serial,
+ username character varying(32) NOT NULL,
+ fullname character varying(64) NOT NULL,
+ elector_id integer,
+ address_id integer NOT NULL,
+ phone character varying(16),
+ email character varying(128),
+ authority_id character varying(32) NOT NULL,
+ authorised boolean
+);
+
+
District
+
An electoral district.
+
CREATE TABLE IF NOT EXISTS districts (
+ id integer NOT NULL,
+ name character varying(64) NOT NULL
+);
+
+
Dwelling
+
A dwelling at which electors are registered. Most addresses obviously have only one dwelling, but in flatted buildings there will be multiple dwellings. The sub_address field contains information to distinguish the dwelling, e.g. ‘flat 2.1’.
+
CREATE TABLE IF NOT EXISTS dwellings (
+ id serial NOT NULL primary key,
+ address_id integer NOT NULL references addresses(id),
+ sub_address varchar(16)
+);
+
+
Elector
+
Someone entitled to cast a vote in the referendum.
+
CREATE TABLE IF NOT EXISTS electors (
+ id integer NOT NULL,
+ name character varying(64) NOT NULL,
+ dwelling_id integer NOT NULL,
+ phone character varying(16),
+ email character varying(128)
+);
+
+
Followup Action
+
An action performed by an issue expert in response to a followup request.
+
CREATE TABLE IF NOT EXISTS followupactions (
+ id integer NOT NULL,
+ request_id integer NOT NULL,
+ actor integer NOT NULL,
+ date timestamp with time zone DEFAULT now() NOT NULL,
+ notes text,
+ closed boolean
+);
+
+
Followup Method
+
A method for responding to a followup request; reference data.
+
CREATE TABLE IF NOT EXISTS followupmethods (
+ id character varying(32) NOT NULL
+);
+
+insert into followupmethods values ('Telephone');
+insert into followupmethods values ('eMail');
+insert into followupmethods values ('Post');
+
+
Followup Request
+
A request recorded by a canvasser for an issue expert to contact an elector with regard to a particular issue.
+
CREATE TABLE IF NOT EXISTS followuprequests (
+ id integer NOT NULL,
+ elector_id integer NOT NULL,
+ visit_id integer NOT NULL,
+ issue_id character varying(32) NOT NULL,
+ method_id character varying(32) NOT NULL
+);
+
+
Intention
+
An intention, by an elector, to vote for an option; captured by a canvasser during a visit.
+
CREATE TABLE IF NOT EXISTS intentions (
+ id serial not null,
+ elector integer not null references elector(id),
+ option varchar(32) not null references option(id),
+ visit integer not null references visit(id),
+ date timestamp with time zone DEFAULT now() NOT NULL
+);
+
+
Issue
+
An issue which might affect electors’ decisions regarding their intention.
+
CREATE TABLE IF NOT EXISTS issues (
+ id character varying(32) NOT NULL,
+ url character varying(256),
+ content varchar(1024),
+ current default false
+);
+
+
Issue expertise
+
Expertise of a canvasser able to use a method, in an issue.
+
CREATE TABLE IF NOT EXISTS issueexpertise (
+ canvasser_id integer NOT NULL,
+ issue_id character varying(32) NOT NULL,
+ method_id character varying(32) NOT NULL
+);
+
+
Option
+
An option for which an elector may have an intention to vote.
+
CREATE TABLE IF NOT EXISTS options (
+ id character varying(32) NOT NULL
+);
+
+
Role
+
A role (other than basic Canvasser) that a user may have in the system. Reference data.
+
create table if not exists roles (
+ id serial primary key,
+ name varchar(64) not null
+);
+
+
Role Member
+
Membership of a user (*Canvasser*) of an additional role; link table.
+
create table if not exists rolememberships (
+ role_id integer not null references roles(id),
+ canvasser_id integer not null references canvassers(id)
+);
+
+
Team
+
A team of canvassers in a locality who are known to one another and frequently canvas together.
+
create table if not exists teams (
+ id serial primary key,
+ name varchar(64) not null,
+ district_id integer not null references districts(id),
+ latitude real,
+ longitude real
+);
+
+
Team Member
+
Membership of a user (*Canvasser*) of a particular team. Canvassers may join multiple teams. Link table.
+
create table if not exists teammemberships (
+ team_id integer not null references teams(id),
+ canvasser_id integer not null references canvassers(id)
+);
+
+
Team Organiser
+
A relationship which defines a user (*Canvasser*) as an organiser of a team. A team may have more than one organiser. An organiser (if they also have the role ‘Recruiter’, which they often will have) may recruit additional Canvassers as members of their team, or accept applications by canvassers to join their team. An organiser may promote a member of the team to organiser of the team, and may also exclude a member from the team.
+
create table if not exists teamorganiserships (
+ team_id integer not null references teams(id),
+ canvasser_id integer not null references canvassers(id)
+);
+
+
Visit
+
A visit by a canvasser to an address on a date to solicit intentions from electors.
+
CREATE TABLE IF NOT EXISTS visits (
+ id integer NOT NULL,
+ address_id integer NOT NULL,
+ canvasser_id integer NOT NULL,
+ date timestamp with time zone DEFAULT now() NOT NULL
+);
+
\ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..48b5683
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,3 @@
+
+Youyesyet 0.2.2-SNAPSHOT
Suppose the YouYesYet project works and we have thousands or tens of thousands of volunteers across Scotland all out chapping doors at the same time: how do we ensure the system stays up under load?
+
Sizing the problem
+
There’s no point in building the app if it will break down under load. We need to be persuaded that it is possible to support the maximum predictable load the system might experience.
+
Database load per volunteer
+
A street canvasser visits on average while working not more than one dwelling every two minutes; the average doorknock-to-doorknock time is probably more like five minutes. Each visit results in
+
+
Zero or one visit record being created;
+
Zero to about five intention records;
+
Zero to about five followup request records.
+
+
So in aggregate minimum zero, maximum about eleven records, typical probably one visit, two intentions = three database inserts per street volunteer per visit. Telephone canvassers probably achieve slightly more because they don’t have to walk from door to door. But over all we’re looking at an average of less than one insert per volunteer per minute.
+
Database reads are probably more infrequent. Each client will obviously need to download the data for each dwelling visited, but it will download these in geograhic blocks of probably around 100 dwellings, and will download a new block only when the user goes outside the area of previously downloaded blocks. However, there ideally should be frequent updates so that the canvasser can see which dwellings other members of the team have already visited, in order that the same dwelling is not visited repeatedly. So there’s probably on average one database read per visit.
+
Reliability of network links
+
Mobile phones typically can have intermittent network access. The client must be able to buffer a queue of records to be stored, and must not prevent the user from moving on to the next doorstep just because the data from the last visit has not yet been stored. There should probably be some on-screen indication of when there is unsent buffered data.
+
Pattern of canvassing
+
Canvassing takes place typically between 6:30pm and 9:00pm on a weekday evening. There will be some canvassing outside this period, but not enough to create significant load. Canvassing will be higher on dry nights than on wet ones, and will probably ramp up through the campaign.
+
Total number of volunteers
+
Personally I’ve never worked in a big canvassing team - maximum about forty people. I believe that there were bigger teams in some parts of urban Scotland. I would guess that the maximum number of volunteers canvassing at any one time - across all groups campaigning for ‘Yes’ in the first independence referendum - never exceeded 35,000 and was probably much lower. I’ve asked whether anyone has better figures but until I have a better estimate I’m going to work on the basis of 35,000 maximum concurrent users.
+
Estimated peak transactions per second
+
This means that the maximum number of transactions per second across Scotland is about
+
35,000 * (1 + 0.2)
+------------------ = 700 transactions per second
+ 60
+
+
700 transactions per second is not a very large number. We should be able to support this level of load on a single server. But what if we can’t?
+
Spreading the load
+
Caching and memoizing
+
People typically go out canvassing in teams; each member of the team will need the same elector data.
+
Glasgow has a population density of about 3,260 per Km^2; that means each half kilometer square has a maximum population of not much more than 1,000. Downloading 1,000 elector records at startup time is not infeasible. If we normalise data requests to a 100 metre square grid and serve records in 500 metre square chunks, all the members of the same team will request the same chunk of data. Also, elector data is not volatile. Therefore it makes sense to memoize requests for elector data. The app should only request fresh elector data when the device moves within 100 metres of the edge of the current 500 metre cell.
+
Intention data is volatile: we’ll want to update canvassers with fresh intention data frequently, because the other members of their team will be recording intention data as they work, and it’s by seeing that intention data that the canvassers know which doors are still unchapped. So we don’t want to cache intention data for very long. But nevertheless it still makes sense to deliver it in normalised 500 metre square chunks, because that means we can temporarily cache it server side and do not actually have to hit the database with many requests for the same data.
+
Finally, issue data is not volatile over the course of a canvassing session, although it may change over a period of days. So issue data - all the current issues - should be fetched once at app startup time, and not periodically refreshed during a canvassing session. Also, of course, every canvasser will require exactly the same issue data (unless we start thinking of local or regional issues…?), so it absolutely makes sense to memoise requests for issue data.
+
All this normalisation and memoisation reduces the number of read requests on the database.
+
Note that 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.
+
So we could have one database master for each electoral district (or contiguous group of districts) with no real problems except that volunteers working at the very edge of an electoral district would only be supported to canvas on one side of the boundary. I’d rather find an architectural solution which works for the whole of Scotland, but if we cannot do that it isn’t a crisis.
+
It also should not be terribly difficult to organise for a street canvasser user using the Map View to be connected automatically to right geographic shard server, without any action by the user. The issue for telephone canvasser users is a bit different because they will often - perhaps typically - be canvassing voters in a region distant from where they are physically located, so if the geographic sharding model is adopted there would probably have to be an additional electoral district selection screen in the telephone canvasser’s interface.
+
Data from many ‘front-line’ database servers each serving a restricted geographic area can relatively simply be aggregated into a national server by doing the integration work in the wee sma’ oors, when most volunteers (and voters) are asleep.
+
The geographic sharding strategy is scalable. We could start with a single server, split it into a ‘west server’ and an ‘east server’ when that gets overloaded, and further subdivide as needed through the campaign. But we can only do this effectively if we have prepared and tested the strategy in advance.
+
But having considerable numbers of database servers will have cost implications.
+
Geographic sharding by DNS
+
When I first thought of geographic sharding, I intended sharding by electoral district, but actually that makes no sense because electoral districts are complex polygons, which makes point-within-polygon computationally expensive. 4 degrees west falls just west of Stirling, and divides the country in half north-south. 56 degrees north runs north of Edinburgh and Glasgow, but just south of Falkirk. It divides the country in half east to west. Very few towns (and no large towns) straddle either line. Thus we can divide Scotland neatly into four, and it is computationally extremely cheap to compute which shard each data item should be despatched to.
giving us an incredibly simple dispatch table. Furthermore, initially all four addresses can point to the same server. This is an incredibly simple scheme, and I’m confident it’s good enough.
+
Data that’s inserted from the canvassing app - that is to say, voter intention data and followup request data - should have an additional field ‘shard’ (char(2)) which should hold the digraph representing the shard to which it was dispatched, and that field should form part of the primary key, allowing the data from all servers to be integrated. Data that isn’t from the canvassing app should probably be directed to the ‘nw’ shard (which will be lightest loaded), or to a separate master server, and then all servers should be synced overnight.
+
Read servers and write servers
+
It’s a common practice in architecting busy web systems to have one master database server to which all write operations are directed, surrounded by a ring of slave databases which replicate from the master and serve all read requests. This works because for the majority of web systems there are many more reads than writes.
+
My feeling is that it’s likely that YouYesYet would see more writes than reads. Thus the ‘write to master, read from slaves’ pattern probably isn’t a big win. That isn’t to say that every database master should not have a ‘hot failover’ slave replicating from it which can take over immediately if the master goes down.
+
App servers and database servers
+
The majority of the processing in YouYesYet happens client side; most of what is being sent back to the server is data to be stored directly in the database. So although there will be a small performance win in separating the app server from the database server this isn’t a very big win either.
+
Summary: spreading the load
+
From the above I think the scaling problem should be addressed as follows:
+
+
Start with a pair of database servers (master and hot failover, with replication) and a single app server;
+
Add additional app servers on a load-balancing basis as needed;
+
Add a third database server (‘read server’), also replicating from the master, and direct reads to this;
+
When the initial cluster of three database servers becomes overloaded, shard into two identical groups (‘east’ and ‘west’);
+
When any shard becomes overloaded, split it into two further shards.
+
+
If we have prepared for sharding, all that is required is to duplicate the database.
+
Obviously, once we have split the database into multiple shards, there is a task to integrate the data from the multiple shards in order to create an ‘across Scotland’ overview of the canvas data; however, again if we have prepared for it in advance, merging the databases should not be difficult, and can be done either in the wee sma’ oors or alternatively during the working day, as the system will be relatively lighty loaded during these periods.
+
Preparing for sharding
+
We should prepare a Docker image for the app server and an image or setup script for the database server.
+
## Further reading on optimising Postgres performance
\ No newline at end of file
diff --git a/docs/userspec.html b/docs/userspec.html
new file mode 100644
index 0000000..cc84abe
--- /dev/null
+++ b/docs/userspec.html
@@ -0,0 +1,151 @@
+
+YouYesYet: User-oriented specification
YouYesYet is a proposed web-app intended to simplify the collection of canvas data from voters, specifically for the upcoming Scottish Independence referendum; it is intended that it should later be adaptable for other canvassing campaigns, but that is a much lower priority.
+
General Principles
+
The objective of this is to produce something untrained users can use on a mobile phone on the doorstep on a cold wet night, with potentially intermittent network connectivity.
+
The client side of tha app will be built as a single page app using re-frame. The reason for using a single page, HTML5 app is that additional content does not need to be downloaded on the fly in order to render pages. It should be possible to develop separate Android and iPhone native apps addressing the same back end, but those are not part of the present project. Note that there are still potential issues with intermittent connectivity e.g. for mapping, but elector data should be downloaded in geographical blocks in order that the user doesn’t have to wait on the doorstep of a house for data to download.
+
Classes of Users
+
Canvassers
+
From the point of view of the application, a Canvasser is a user who ‘cold calls’ electors, either by knocking on doors or by telephoning them. All authorised users are deemed to be canvassers.
+
Issue Experts
+
An Issue Expert is a person who is well versed on a particular issue likely to be of concern to electors.
+
It’s my hope that we’d have enough Issue Experts sitting in the comfort of their own homes on a typical canvassing evening that we can get followup phone calls or emails happening within a few minutes of the canvasser calling, so it’s still fresh in the elector’s mind.
+
Analysts
+
Canvassers are able to see voter canvas data for the streets immediately around where they are working. They must be able to do so, because otherwise they cannot effectively canvas. But we must assume that sooner or later a hostile person will join the system as a canvasser, so canvassers should not have access to wider canvas data. Therefore we need a category of trusted user who can view wider maps of where canvas data has been collected, in order to be able to direct teams of canvassers to areas where it has not been collected. For want of a better word I’ll call these Analysts.
+
Administrators
+
An Administrator has access to a full range of back end functions which are not yet fully thought through, but in particular, can
+
+
Lock user accounts
+
Authorise Issue Experts
+
Create new issues
+
+
Creating an account
+
A potential user who does not have a login should be able to create an account. However, not just anybody should be able to create an account, because there is the risk that opponents might register as users and deliberately pollute the database with bad data. Therefore to create an account a user must have an introduction from an existing user - an ‘Invite my friends’ feature of the system should mail out to invitees a token, registered to a particular email address, which allows an account to be created. With that token, creating an account should be simple.
+
My preference is not to store even encrypted passwords of users, but to rely entirely on oauth. This may not be possible but it’s worth a try.
+
So a potential user, who has a token, should be presented with a form in which they are invited to pick a username, supply their real name and phone number, and choose an oauth provider from those we support. They also need to agree to conditions of use. We already have their email address because we know what email address the invitation token was sent to; a confirmation email is sent to this address and the account is not enabled until a link from that email is clicked.
+
Each user record shall contain a field indicating the inviter. Thus, if a user is suspected of acting for the opposition, administrators can see not only who invited that user to join the system, but also which other users have been invited by that user.
+
+------------------------------------------------+
+| Welcome to YouYesYet |
+| |
+| Choose a username: [________________] |
+| Tell us your real name: [________________] |
+| Tell us your phone number: [________________] |
+| Choose an authentication |
+| provider: [Twitter________v] |
+| Agree to the conditions |
+| of use: [I don't agree v] |
+| |
+| [Join us!] |
++------------------------------------------------+
+
+
Logging in
+
The login screen is very simple: all the user needs enter is their username; they are then directed to their chosen oauth provider to be authenticated.
+
After login
+
After successful login, a Canvasser will be presented with the Map View, see below. All other classes of user will be presented with a menu of their user classes, including the Canvasser user class. Selecting the Canvasser user class will direct them to the Map View. Selecting other user classes will direct to menus of the options available to those user classes.
+
Note that:
+
+
Only the views available to canvassers form part of the one-page app, since it’s assumed that when acting as other user classes the user will have reliable network connectivity;
+
For much the same reason, canvasser views will generally be adapted for use on a mobile phone screen; admin and analyst views may not be.
+
+
Map View
+
The map view shows a map of the streets immediately around their current location, overlaid, on dwellings where canvas has already been done, with icons indicating the voting preference expressed, and with the dwellings where canvassing is still required marked with an icon indicating this:
+
+
Selecting a building on the map leads to
+
+
On buildings with multiple flats, the Building View;
+
On buildings with only one dwelling, the Electors View.
+
+
Building View
+
A list of dwellings in a building.
+
+
Selecting a flat from this view leads to the Electors View.
+
Electors View
+
The Electors View shows a schematic of the registered electors in a dwelling:
+
+
One figure is shown for each elector, labelled with their name. In the dummy pages I’ve shown gendered stick figures, because I believe that in many casesthis will help the canvasser identify the person who has answered the door; but this may be seen as excluding electors with non-binary gender, and, in any case, I believe we don’t actually get gender data (other than salutation) in the electoral roll data. So this may have to be reconsidered.
+
Below the figure are:
+
+
One clear ‘voting intention’ button for each option (e.g., ‘Yes’, ‘No’), greyed unless selected;
+
One issues button.
+
+
Selecting an option icon records that the elector represented by the figure has expressed an intention that they will vote for that option. Selecting the issues icon brings up and issues view.
+
Issues View
+
The Issues View is a simple list of issues:
+
+------------------------------------------------+
+| YouYesYet: Issues [<-] |
+| |
+| Currency |
+| EU Membership |
+| Can Scotland afford it? |
+| Keep the Queen? |
+| Defence/NATO |
+| Other |
++------------------------------------------------+
+
+
+
This list will not be hard-coded but will be dynamic; thus, if we find an issue we didn’t predict is regularly coming up on the doorstep an Administrator can add it to the list.
+
Selecting the back button from the Issues View returns to the Electors View. Selecting any option from the Issues view leads to the Issue View.
+
Issue View
+
A single page giving top level points the canvasser can make to the elector on the doorstep, regarding the selected issue; and a link to a Followup Request form. There is also a ‘back’ button allowing the user to return to the Issues View.
+
+
Followup Request form
+
The Followup Request form is a simple form which allows the canvasser to record a followup request. The elector and the issue are already known from the route taken to reach the form, so don’t have to be filled in by the user. In case of followup by post (we mail them out a leaflet on the issue) the address is also known. If the elector chooses followup by telephone or by email, the canvasser will be prompted for the telephone number or email address respectively.
Street Canvassers will typically use the system by
+
+
Arriving in an area, often with a group of other canvassers;
+
Logging into the system;
+
Looking at the map view and choosing an uncanvassed dwelling to canvas;
+
Opening the Electors View for that dwelling;
+
Chapping the door;
+
Asking the person who answers about voting intentions, and recording these be selecting the appropriate option buttons;
+
Addressing any issues the elector raises directly, if possible;
+
If there are more detailed issues the elector wants to address, raising a followup request;
+
Making a polite goodbye;
+
Returning to the map view and choosing the next dwelling to canvas.
+
+
Telephone canvassing
+
We also need a workflow for telephone canvassing, but I have not yet given thought to how that would work.
+
Followup Requests view
+
This view is available only to Issue Experts, and is the first view and Issue Expert sees after aelecting Issue Expert from the roles menu.
+
After a canvasser has spoken with an elector, the canvasser may tag the elector with a followup request on an issue. An issue expert will have access to a screen showing all unclosed followup requests for those issues for which he or she is considered an expert. The expert may pick one from the screen, phone or email the elector to discuss the issue, and then either mark the request as closed or add a note for a further followup later.
+
+-------------------------------------------------------------------------------+
+| YouYesYet: Unclosed Followup Requests on Currency |
+| |
+| | Name | Canvasser | Canvassed | Followup by | Contact Detail | |
+| +-------------+-------------+-------------+-------------+-----------------+ |
+| | Archie Bell | Betty Black | 12-Dec-2016 | Telephone | 01312345678 | |
+| | Carol Craig | Donald Dunn | 12-Dec-2016 | eMail | carol@gmail.com | |
+
+
Picking a Followup Request from this list should result in it being temporarily removed from all other Issue Expert’s views, and open the Followup Action view.
+
Followup Action view
+
The Followup Action view shows the name and contact detail of the elector, with the same voting intention buttons as on the Electors View; a toggle to mark this requst closed; a text area to enter any notes; a ‘back’ button which returns to the Followup Requests view; and a list of any previous followup actions on this request with any notes made on them.
+
Below this on the page is a Wiki section which contains links to resources which may be useful in addressing the elector’s concerns.
+
Wiki
+
As specified above, the Followup Action view contains a section which functions as a Wiki page. This may incorporate links to further Wiki pages, or to resources out on the wider Internet. Issue Experts are entitled to edit Wiki pages within the system.
+
Note that the Issue View in the Canvassers’ user interface is a special Wiki page, which can also be edited by the relevant issue experts.
+
Gamification and ‘Scores’
+
Reading up on Bernie Saunders’ campaign’s canvassing app, it apparently contained gamification features which were seen as significantly motivational for younger canvassers. Canvassers should be able to see a screen which shows
+
+
Total time logged in;
+
Total visits made;
+
Total voting intentions recorded;
+
Number of visits made per hour logged in;
+
Number of voting intentions recorded per hour logged in.
+
+
I’d like a way for local canvassing teams to be able to see one another’s scores, so as to encourage friendly competition, but I haven’t yet defined a mechanism for how that might be done. I think a national high-score table would probably be a bad thing, because it might encourage people to create fictional records without actually talking to the electors.
\ No newline at end of file
diff --git a/docs/youyesyet.cache.html b/docs/youyesyet.cache.html
new file mode 100644
index 0000000..cbe470d
--- /dev/null
+++ b/docs/youyesyet.cache.html
@@ -0,0 +1,3 @@
+
+youyesyet.cache documentation
Auto-generated function 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: clojure.lang.LazySeq@490531d1. Results will be held in cache for 1000000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@352cf516. Results will be held in cache for 10000000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@771caed2. Results will be held in cache for 10000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@771caed2. Results will be held in cache for 10000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@1c48b9f5. Results will be held in cache for 10000000 seconds.
Auto-generated function to select one record from the dwellings table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@f0e6e3d6. Results will be held in cache for 1000000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@c7611432. Results will be held in cache for 100000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@3c72487c. Results will be held in cache for 10000000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@ae10eaf. Results will be held in cache for 100 seconds.
Auto-generated function to select one record from the genders table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@3c72487c. Results will be held in cache for 1000000 seconds.
Auto-generated function 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@27b31781. Results will be held in cache for 100 seconds.
Auto-generated function 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: clojure.lang.LazySeq@606826e4. Results will be held in cache for 1000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@3c72487c. Results will be held in cache for 10000000 seconds.
Auto-generated function to select one record from the roles table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@a81570c6. Results will be held in cache for 10000000 seconds.
Auto-generated function to select one record from the roles table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@a81570c6. Results will be held in cache for 10000000 seconds.
Auto-generated function to select one record from the teams table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@31118d64. Results will be held in cache for 10000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@a7611ce6. Results will be held in cache for 100 seconds.
Auto-generated function 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 :postcode :district_id :latitude :longitude :dwellings :locality :id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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: (:request-token-uri :access-token-uri :authorize-uri :consumer-key :consumer-secret :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:username :fullname :avatar :bio :elector_id :address_id :phone :email :authority_id :authorised :roles :expertise :teams :id). Results will be held in cache for 10000 seconds.
Auto-generated function 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: (:name :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:address_id :sub-address :electors :id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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: (:name :dwelling_id :phone :email :gender :signature :id). Results will be held in cache for 100000 seconds.
Auto-generated function 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). Results will be held in cache for 10000000 seconds.
Auto-generated function 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 :visit_id :issue_id :issue_detail :method_id :method_detail :locked_by :locked :actions :id). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:url :current :brief :experts :id). Results will be held in cache for 1000 seconds.
Auto-generated function 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). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:name :members :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:name :district_id :latitude :longitude :members :organisers :events :id). Results will be held in cache for 10000 seconds.
Auto-generated function 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). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:address :postcode :district_id :latitude :longitude :dwellings :locality :id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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: (:request-token-uri :access-token-uri :authorize-uri :consumer-key :consumer-secret :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:username :fullname :avatar :bio :elector_id :address_id :phone :email :authority_id :authorised :roles :expertise :teams :id). Results will be held in cache for 10000 seconds.
Auto-generated function 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: (:name :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:address_id :sub-address :electors :id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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: (:name :dwelling_id :phone :email :gender :signature :id). Results will be held in cache for 100000 seconds.
Auto-generated function 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: (:id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:elector_id :visit_id :issue_id :issue_detail :method_id :method_detail :locked_by :locked :actions :id). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:url :current :brief :experts :id). Results will be held in cache for 1000 seconds.
Auto-generated function 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: (:id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:name :members :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:name :district_id :latitude :longitude :members :organisers :events :id). Results will be held in cache for 10000 seconds.
Auto-generated function 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: (:address_id :canvasser_id :date :id). Results will be held in cache for 100 seconds.
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.ajax.html b/docs/youyesyet.canvasser-app.ajax.html
new file mode 100644
index 0000000..81fed2e
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.ajax.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.ajax documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.core.html b/docs/youyesyet.canvasser-app.core.html
new file mode 100644
index 0000000..30d3a6f
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.core.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.core documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.gis.html b/docs/youyesyet.canvasser-app.gis.html
new file mode 100644
index 0000000..43e4823
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.gis.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.gis documentation
On clicking on the pin, navigate to the electors at the address. This way of doing it adds an antry in the browser location history, so back links work.
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.handlers.html b/docs/youyesyet.canvasser-app.handlers.html
new file mode 100644
index 0000000..c302f44
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.handlers.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.handlers documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.state.html b/docs/youyesyet.canvasser-app.state.html
new file mode 100644
index 0000000..b103b92
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.state.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.state documentation
The default configuration state of the app, when first loaded. 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.
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.subscriptions.html b/docs/youyesyet.canvasser-app.subscriptions.html
new file mode 100644
index 0000000..83f71f4
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.subscriptions.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.subscriptions documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.ui-utils.html b/docs/youyesyet.canvasser-app.ui-utils.html
new file mode 100644
index 0000000..6a95fda
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.ui-utils.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.ui-utils documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.about.html b/docs/youyesyet.canvasser-app.views.about.html
new file mode 100644
index 0000000..5761770
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.about.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.about documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.building.html b/docs/youyesyet.canvasser-app.views.building.html
new file mode 100644
index 0000000..fe73a4b
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.building.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.building documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.dwelling.html b/docs/youyesyet.canvasser-app.views.dwelling.html
new file mode 100644
index 0000000..d1884a7
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.dwelling.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.dwelling documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.elector.html b/docs/youyesyet.canvasser-app.views.elector.html
new file mode 100644
index 0000000..3ca6f7c
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.elector.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.elector documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.followup.html b/docs/youyesyet.canvasser-app.views.followup.html
new file mode 100644
index 0000000..be239c1
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.followup.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.followup documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.gdpr.html b/docs/youyesyet.canvasser-app.views.gdpr.html
new file mode 100644
index 0000000..b65dc27
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.gdpr.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.gdpr documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.issue.html b/docs/youyesyet.canvasser-app.views.issue.html
new file mode 100644
index 0000000..098e085
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.issue.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.issue documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.issues.html b/docs/youyesyet.canvasser-app.views.issues.html
new file mode 100644
index 0000000..e712085
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.issues.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.issues documentation
\ No newline at end of file
diff --git a/docs/youyesyet.canvasser-app.views.map.html b/docs/youyesyet.canvasser-app.views.map.html
new file mode 100644
index 0000000..5fa9fb5
--- /dev/null
+++ b/docs/youyesyet.canvasser-app.views.map.html
@@ -0,0 +1,3 @@
+
+youyesyet.canvasser-app.views.map documentation
\ No newline at end of file
diff --git a/docs/youyesyet.config.html b/docs/youyesyet.config.html
new file mode 100644
index 0000000..1ae0293
--- /dev/null
+++ b/docs/youyesyet.config.html
@@ -0,0 +1,3 @@
+
+youyesyet.config documentation
\ No newline at end of file
diff --git a/docs/youyesyet.db.core.html b/docs/youyesyet.db.core.html
new file mode 100644
index 0000000..81330c0
--- /dev/null
+++ b/docs/youyesyet.db.core.html
@@ -0,0 +1,3 @@
+
+youyesyet.db.core documentation
updates an existing followupaction record (sqlvec)
update-followupmethod!
updates an existing followupmethod record
update-followupmethod!-sqlvec
updates an existing followupmethod record (sqlvec)
update-followuprequest!
updates an existing followuprequest record
update-followuprequest!-sqlvec
updates an existing followuprequest record (sqlvec)
update-gender!
updates an existing gender record
update-gender!-sqlvec
updates an existing gender record (sqlvec)
update-intention!
updates an existing intention record
update-intention!-sqlvec
updates an existing intention record (sqlvec)
update-issue!
updates an existing issue record
update-issue!-sqlvec
updates an existing issue record (sqlvec)
update-option!
updates an existing option record
update-option!-sqlvec
updates an existing option record (sqlvec)
update-role!
updates an existing role record
update-role!-sqlvec
updates an existing role record (sqlvec)
update-team!
updates an existing team record
update-team!-sqlvec
updates an existing team record (sqlvec)
update-visit!
updates an existing visit record
update-visit!-sqlvec
updates an existing visit record (sqlvec)
\ No newline at end of file
diff --git a/docs/youyesyet.handler.html b/docs/youyesyet.handler.html
new file mode 100644
index 0000000..d47b288
--- /dev/null
+++ b/docs/youyesyet.handler.html
@@ -0,0 +1,3 @@
+
+youyesyet.handler documentation
\ No newline at end of file
diff --git a/docs/youyesyet.layout.html b/docs/youyesyet.layout.html
new file mode 100644
index 0000000..80917ba
--- /dev/null
+++ b/docs/youyesyet.layout.html
@@ -0,0 +1,4 @@
+
+youyesyet.layout documentation
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
\ No newline at end of file
diff --git a/docs/youyesyet.locality.html b/docs/youyesyet.locality.html
new file mode 100644
index 0000000..8e5cbd6
--- /dev/null
+++ b/docs/youyesyet.locality.html
@@ -0,0 +1,3 @@
+
+youyesyet.locality documentation
\ No newline at end of file
diff --git a/docs/youyesyet.middleware.html b/docs/youyesyet.middleware.html
new file mode 100644
index 0000000..f67218a
--- /dev/null
+++ b/docs/youyesyet.middleware.html
@@ -0,0 +1,3 @@
+
+youyesyet.middleware documentation
\ No newline at end of file
diff --git a/docs/youyesyet.oauth.html b/docs/youyesyet.oauth.html
new file mode 100644
index 0000000..6f766b6
--- /dev/null
+++ b/docs/youyesyet.oauth.html
@@ -0,0 +1,3 @@
+
+youyesyet.oauth documentation
\ No newline at end of file
diff --git a/docs/youyesyet.outqueue.html b/docs/youyesyet.outqueue.html
new file mode 100644
index 0000000..b406019
--- /dev/null
+++ b/docs/youyesyet.outqueue.html
@@ -0,0 +1,3 @@
+
+youyesyet.outqueue documentation
Apply this process, assumed to be a function of one argument, to the next item in the queue q, if the queue is not currently locked; return the value returned by process.
\ No newline at end of file
diff --git a/docs/youyesyet.routes.auto-json.html b/docs/youyesyet.routes.auto-json.html
new file mode 100644
index 0000000..6ade069
--- /dev/null
+++ b/docs/youyesyet.routes.auto-json.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.auto-json documentation
Auto-generated function to insert one record to the addresses table. Expects the following key(s) to be present in params: (:address :postcode :district_id :latitude :longitude). Returns a map containing the keys #{"id"} identifying the record created. Results will be held in cache for 1000000 seconds.
Auto-generated function 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. Results will be held in cache for 10000000 seconds.
Auto-generated function to insert one record to the canvassers table. Expects the following key(s) to be present in params: (:username :fullname :avatar :bio :elector_id :address_id :phone :email :authority_id :authorised). Returns a map containing the keys #{"id"} identifying the record created. Results will be held in cache for 10000 seconds.
Auto-generated function 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. Results will be held in cache for 10000000 seconds.
Auto-generated function 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. Results will be held in cache for 1000000 seconds.
Auto-generated function to insert one record to the electors table. Expects the following key(s) to be present in params: (:name :dwelling_id :phone :email :gender :signature). Returns a map containing the keys #{"id"} identifying the record created. Results will be held in cache for 100000 seconds.
Auto-generated function to insert one record to the events table. Expects the following key(s) to be present in params: (:name :date :time :decription :cancelled). Returns a map containing the keys #{"id"} identifying the record created.
Auto-generated function 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.
Auto-generated function 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. Results will be held in cache for 10000000 seconds.
Auto-generated function to insert one record to the followuprequests table. Expects the following key(s) to be present in params: (:elector_id :visit_id :issue_id :issue_detail :method_id :method_detail :locked_by :locked). Returns a map containing the keys #{"id"} identifying the record created. Results will be held in cache for 100 seconds.
Auto-generated function 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. Results will be held in cache for 1000000 seconds.
Auto-generated function 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. Results will be held in cache for 100 seconds.
Auto-generated function to insert one record to the issues table. Expects the following key(s) to be present in params: (:url :current :brief :id). Returns a map containing the keys #{"id"} identifying the record created. Results will be held in cache for 1000 seconds.
Auto-generated function 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. Results will be held in cache for 10000000 seconds.
Auto-generated function to insert one record to the roles table. Expects the following key(s) to be present in params: (:name :id). Returns a map containing the keys #{"id"} identifying the record created. Results will be held in cache for 10000000 seconds.
Auto-generated function 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. Results will be held in cache for 10000 seconds.
Auto-generated function 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. Results will be held in cache for 100 seconds.
Auto-generated function to delete one record from the addresses table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 1000000 seconds.
Auto-generated function to delete one record from the authorities table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 10000000 seconds.
Auto-generated function to delete one record from the canvassers table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 10000 seconds.
Auto-generated function to delete one record from the districts table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 10000000 seconds.
Auto-generated function to delete one record from the dwellings table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 1000000 seconds.
Auto-generated function to delete one record from the electors table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 100000 seconds.
Auto-generated function to delete one record from the followupmethods table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 10000000 seconds.
Auto-generated function to delete one record from the followuprequests table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 100 seconds.
Auto-generated function to delete one record from the genders table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 1000000 seconds.
Auto-generated function to delete one record from the intentions table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 100 seconds.
Auto-generated function to delete one record from the issues table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 1000 seconds.
Auto-generated function to delete one record from the options table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 10000000 seconds.
Auto-generated function to delete one record from the roles table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 10000000 seconds.
Auto-generated function to delete one record from the teams table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 10000 seconds.
Auto-generated function to delete one record from the visits table. Expects the following key(s) to be present in params: #{"id"}. Results will be held in cache for 100 seconds.
Auto-generated function 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: clojure.lang.LazySeq@490531d1. Results will be held in cache for 1000000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@352cf516. Results will be held in cache for 10000000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@771caed2. Results will be held in cache for 10000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@771caed2. Results will be held in cache for 10000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@1c48b9f5. Results will be held in cache for 10000000 seconds.
Auto-generated function to select one record from the dwellings table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@f0e6e3d6. Results will be held in cache for 1000000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@c7611432. Results will be held in cache for 100000 seconds.
Auto-generated function to select one record from the events table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@3ac90901.
Auto-generated function 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.
Auto-generated function 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: clojure.lang.LazySeq@3c72487c. Results will be held in cache for 10000000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@ae10eaf. Results will be held in cache for 100 seconds.
Auto-generated function to select one record from the genders table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@3c72487c. Results will be held in cache for 1000000 seconds.
Auto-generated function 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@27b31781. Results will be held in cache for 100 seconds.
Auto-generated function 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: clojure.lang.LazySeq@606826e4. Results will be held in cache for 1000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@3c72487c. Results will be held in cache for 10000000 seconds.
Auto-generated function to select one record from the roles table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@a81570c6. Results will be held in cache for 10000000 seconds.
Auto-generated function to select one record from the roles table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@a81570c6. Results will be held in cache for 10000000 seconds.
Auto-generated function to select one record from the teams table. Expects the following key(s) to be present in params: #{"id"}. Returns a map containing the following keys: clojure.lang.LazySeq@31118d64. Results will be held in cache for 10000 seconds.
Auto-generated function 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: clojure.lang.LazySeq@a7611ce6. Results will be held in cache for 100 seconds.
Auto-generated function 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 :postcode :district_id :latitude :longitude :dwellings :locality :id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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: (:request-token-uri :access-token-uri :authorize-uri :consumer-key :consumer-secret :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:username :fullname :avatar :bio :elector_id :address_id :phone :email :authority_id :authorised :roles :expertise :teams :id). Results will be held in cache for 10000 seconds.
Auto-generated function 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: (:name :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:address_id :sub-address :electors :id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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: (:name :dwelling_id :phone :email :gender :signature :id). Results will be held in cache for 100000 seconds.
Auto-generated function to select all records from the events 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: (:name :teams :date :time :decription :cancelled :id).
Auto-generated function 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).
Auto-generated function 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). Results will be held in cache for 10000000 seconds.
Auto-generated function 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 :visit_id :issue_id :issue_detail :method_id :method_detail :locked_by :locked :actions :id). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:url :current :brief :experts :id). Results will be held in cache for 1000 seconds.
Auto-generated function 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). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:name :members :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:name :district_id :latitude :longitude :members :organisers :events :id). Results will be held in cache for 10000 seconds.
Auto-generated function 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). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:address :postcode :district_id :latitude :longitude :dwellings :locality :id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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: (:request-token-uri :access-token-uri :authorize-uri :consumer-key :consumer-secret :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:username :fullname :avatar :bio :elector_id :address_id :phone :email :authority_id :authorised :roles :expertise :teams :id). Results will be held in cache for 10000 seconds.
Auto-generated function 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: (:name :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:address_id :sub-address :electors :id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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: (:name :dwelling_id :phone :email :gender :signature :id). Results will be held in cache for 100000 seconds.
Auto-generated function to select all records from the events 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: (:name :teams :date :time :decription :cancelled :id).
Auto-generated function 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).
Auto-generated function 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: (:id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:elector_id :visit_id :issue_id :issue_detail :method_id :method_detail :locked_by :locked :actions :id). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:id). Results will be held in cache for 1000000 seconds.
Auto-generated function 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). Results will be held in cache for 100 seconds.
Auto-generated function 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: (:url :current :brief :experts :id). Results will be held in cache for 1000 seconds.
Auto-generated function 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: (:id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:name :members :id). Results will be held in cache for 10000000 seconds.
Auto-generated function 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: (:name :district_id :latitude :longitude :members :organisers :events :id). Results will be held in cache for 10000 seconds.
Auto-generated function 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: (:address_id :canvasser_id :date :id). Results will be held in cache for 100 seconds.
Auto-generated function to update one record in the addresses table. Expects the following key(s) to be present in params: (:address :district_id :id :latitude :longitude :postcode). Results will be held in cache for 1000000 seconds.
Auto-generated function 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). Results will be held in cache for 10000000 seconds.
Auto-generated function to update one record in the canvassers table. Expects the following key(s) to be present in params: (:address_id :authorised :authority_id :avatar :bio :elector_id :email :fullname :id :phone :username). Results will be held in cache for 10000 seconds.
Auto-generated function to update one record in the districts table. Expects the following key(s) to be present in params: (:id :name). Results will be held in cache for 10000000 seconds.
Auto-generated function to update one record in the dwellings table. Expects the following key(s) to be present in params: (:address_id :id :sub-address). Results will be held in cache for 1000000 seconds.
Auto-generated function 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 :signature). Results will be held in cache for 100000 seconds.
Auto-generated function to update one record in the events table. Expects the following key(s) to be present in params: (:cancelled :date :decription :id :name :time).
Auto-generated function 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).
Auto-generated function to update one record in the followupmethods table. Expects the following key(s) to be present in params: (:id). Results will be held in cache for 10000000 seconds.
Auto-generated function to update one record in the followuprequests table. Expects the following key(s) to be present in params: (:elector_id :id :issue_detail :issue_id :locked :locked_by :method_detail :method_id :visit_id). Results will be held in cache for 100 seconds.
Auto-generated function to update one record in the genders table. Expects the following key(s) to be present in params: (:id). Results will be held in cache for 1000000 seconds.
Auto-generated function to update one record in the intentions table. Expects the following key(s) to be present in params: (:elector_id :id :locality :option_id :visit_id). Results will be held in cache for 100 seconds.
Auto-generated function to update one record in the issues table. Expects the following key(s) to be present in params: (:brief :current :id :url). Results will be held in cache for 1000 seconds.
Auto-generated function to update one record in the options table. Expects the following key(s) to be present in params: (:id). Results will be held in cache for 10000000 seconds.
Auto-generated function to update one record in the roles table. Expects the following key(s) to be present in params: (:id :name). Results will be held in cache for 10000000 seconds.
Auto-generated function to update one record in the teams table. Expects the following key(s) to be present in params: (:district_id :id :latitude :longitude :name). Results will be held in cache for 10000 seconds.
Auto-generated function to update one record in the visits table. Expects the following key(s) to be present in params: (:address_id :canvasser_id :date :id). Results will be held in cache for 100 seconds.
\ No newline at end of file
diff --git a/docs/youyesyet.routes.auto.html b/docs/youyesyet.routes.auto.html
new file mode 100644
index 0000000..a05a1cb
--- /dev/null
+++ b/docs/youyesyet.routes.auto.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.auto documentation
\ No newline at end of file
diff --git a/docs/youyesyet.routes.home.html b/docs/youyesyet.routes.home.html
new file mode 100644
index 0000000..176017f
--- /dev/null
+++ b/docs/youyesyet.routes.home.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.home documentation
\ No newline at end of file
diff --git a/docs/youyesyet.routes.issue-experts.html b/docs/youyesyet.routes.issue-experts.html
new file mode 100644
index 0000000..470cc94
--- /dev/null
+++ b/docs/youyesyet.routes.issue-experts.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.issue-experts documentation
From this request, create a followupaction record, and, if an option_id is present in the params, an intention record; show the request list on success, the request form on failure.
\ No newline at end of file
diff --git a/docs/youyesyet.routes.manual.html b/docs/youyesyet.routes.manual.html
new file mode 100644
index 0000000..857b123
--- /dev/null
+++ b/docs/youyesyet.routes.manual.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.manual documentation
\ No newline at end of file
diff --git a/docs/youyesyet.routes.oauth.html b/docs/youyesyet.routes.oauth.html
new file mode 100644
index 0000000..3affe79
--- /dev/null
+++ b/docs/youyesyet.routes.oauth.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.oauth documentation
\ No newline at end of file
diff --git a/docs/youyesyet.routes.rest.html b/docs/youyesyet.routes.rest.html
new file mode 100644
index 0000000..d335c09
--- /dev/null
+++ b/docs/youyesyet.routes.rest.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.rest documentation
Manually maintained routes which handle data transfer to/from the canvasser app.
create-intention-and-visit!
(create-intention-and-visit! request)
Doing visit creation logic server side; request params are expected to include an option_id, an elector_id and an address_id, or an option and a location. If no address_id is provided, we simply create an intention record from the option_id and the locality; if an address_id is provided, we need to check whether the last visit by the current user was to the same address, if so use that as the visit_id, if not create a new visit record.
Doing visit creation logic server side; request params are expected to include an issue, an elector_id and an address_id (and also a method_id and method_detail). Ye cannae reasonably create a request without having recorded the visit, so let’s not muck about.
\ No newline at end of file
diff --git a/docs/youyesyet.routes.roles.html b/docs/youyesyet.routes.roles.html
new file mode 100644
index 0000000..8fc327c
--- /dev/null
+++ b/docs/youyesyet.routes.roles.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.roles documentation
My expectation is that analysts will do a lot of their work through QGIS or some other geographical information system; so there isn’t a need to put anything sophisticated here.
\ No newline at end of file
diff --git a/docs/youyesyet.routes.services.html b/docs/youyesyet.routes.services.html
new file mode 100644
index 0000000..3e8c2d0
--- /dev/null
+++ b/docs/youyesyet.routes.services.html
@@ -0,0 +1,3 @@
+
+youyesyet.routes.services documentation
\ No newline at end of file
diff --git a/docs/youyesyet.utils.html b/docs/youyesyet.utils.html
new file mode 100644
index 0000000..683730b
--- /dev/null
+++ b/docs/youyesyet.utils.html
@@ -0,0 +1,3 @@
+
+youyesyet.utils documentation
\ No newline at end of file
diff --git a/docs/youyesyet.validation.html b/docs/youyesyet.validation.html
new file mode 100644
index 0000000..31ec683
--- /dev/null
+++ b/docs/youyesyet.validation.html
@@ -0,0 +1,3 @@
+
+youyesyet.validation documentation
\ No newline at end of file
diff --git a/documentation/authorisation.html b/documentation/authorisation.html
index 0c83e18..7a7c786 100644
--- a/documentation/authorisation.html
+++ b/documentation/authorisation.html
@@ -1,6 +1,6 @@
-Security and authorisation
Obviously You Yes Yet? is my baby; I’ve put a lot of thought into it. At the time I started working on it I wasn’t aware of any open source competitors; I did to a web search, and I emailed the Bernie Sanders campaign to see whether their widely admired tools were open source. I didn’t find anything.
However, I’ve just been pointed to Vote Leave’s Vics tool, and there may well be others.
There is no room here for ego. What matters is that the Yes campaign gets the best available tool for the job. So it’s important to do competitor analysis, and not to invest too much work into You Yes Yet? unless there’s a realistic possibility of producing a tool which is better than any of the available alternatives. But it’s also the case that by studying competitors we may find ways to improve the design of You Yes Yet?.
User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180920T131125.604Z
User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180921T143946.559Z
\ No newline at end of file
diff --git a/documentation/scaling.html b/documentation/scaling.html
index 4621445..b9dc764 100644
--- a/documentation/scaling.html
+++ b/documentation/scaling.html
@@ -1,6 +1,6 @@
-YouYesYet: Scaling
Suppose the YouYesYet project works and we have thousands or tens of thousands of volunteers across Scotland all out chapping doors at the same time: how do we ensure the system stays up under load?
Sizing the problem
There’s no point in building the app if it will break down under load. We need to be persuaded that it is possible to support the maximum predictable load the system might experience.
YouYesYet is a proposed web-app intended to simplify the collection of canvas data from voters, specifically for the upcoming Scottish Independence referendum; it is intended that it should later be adaptable for other canvassing campaigns, but that is a much lower priority.
General Principles
@@ -51,15 +51,19 @@
Map View
The map view shows a map of the streets immediately around their current location, overlaid, on dwellings where canvas has already been done, with icons indicating the voting preference expressed, and with the dwellings where canvassing is still required marked with an icon indicating this:
-
+
Selecting a building on the map leads to
-
On buildings with multiple flats, a schematic view of the flats in the building (I haven’t yet really got a good idea how to do this; it may be just a list). Selecting a flat from this view leads to the Electors View;
+
On buildings with multiple flats, the Building View;
On buildings with only one dwelling, the Electors View.
+
Building View
+
A list of dwellings in a building.
+
+
Selecting a flat from this view leads to the Electors View.
Electors View
The Electors View shows a schematic of the registered electors in a dwelling:
-
+
One figure is shown for each elector, labelled with their name. In the dummy pages I’ve shown gendered stick figures, because I believe that in many casesthis will help the canvasser identify the person who has answered the door; but this may be seen as excluding electors with non-binary gender, and, in any case, I believe we don’t actually get gender data (other than salutation) in the electoral roll data. So this may have to be reconsidered.
Below the figure are:
@@ -80,8 +84,12 @@
| Other |
+------------------------------------------------+
+
This list will not be hard-coded but will be dynamic; thus, if we find an issue we didn’t predict is regularly coming up on the doorstep an Administrator can add it to the list.
-
Selecting the back button from the Issues View returns to the Electors View. Selecting any option from the Issues view leads to a single page giving top level points the canvasser can make to the elector on the doorstep, and a link to a Followup Request form. There is also a ‘back’ button allowing the user to return to the Issues View
+
Selecting the back button from the Issues View returns to the Electors View. Selecting any option from the Issues view leads to the Issue View.
+
Issue View
+
A single page giving top level points the canvasser can make to the elector on the doorstep, regarding the selected issue; and a link to a Followup Request form. There is also a ‘back’ button allowing the user to return to the Issues View.
+
Followup Request form
The Followup Request form is a simple form which allows the canvasser to record a followup request. The elector and the issue are already known from the route taken to reach the form, so don’t have to be filled in by the user. In case of followup by post (we mail them out a leaflet on the issue) the address is also known. If the elector chooses followup by telephone or by email, the canvasser will be prompted for the telephone number or email address respectively.
\ No newline at end of file
diff --git a/documentation/youyesyet.config.html b/documentation/youyesyet.config.html
index ecf03f0..d626104 100644
--- a/documentation/youyesyet.config.html
+++ b/documentation/youyesyet.config.html
@@ -1,4 +1,4 @@
-youyesyet.config documentation
\ No newline at end of file
diff --git a/documentation/youyesyet.db.core.html b/documentation/youyesyet.db.core.html
index 50de3d4..fbbe7b3 100644
--- a/documentation/youyesyet.db.core.html
+++ b/documentation/youyesyet.db.core.html
@@ -1,6 +1,6 @@
-youyesyet.db.core documentation
Database access functions, mostly from Luminus template.
*db*
dynamic
FIXME: write docs
create-address!
creates a new address record
create-address!-sqlvec
creates a new address record (sqlvec)
create-authority!
creates a new authority record
@@ -134,6 +134,7 @@
list-dwellings-by-address
lists all existing dwelling records related to a given address
list-dwellings-by-address-sqlvec
lists all existing dwelling records related to a given address (sqlvec)
list-dwellings-sqlvec
lists all existing dwelling records (sqlvec)
+
list-elector-intentions
short form of `list-intentions-by-elector`, returning far less data, for use in `youyesyet.routes.rest/get-local-data`, q.v.
list-electors
lists all existing elector records
list-electors-by-dwelling
lists all existing dwelling records related to a given elector
list-electors-by-dwelling-sqlvec
lists all existing dwelling records related to a given elector (sqlvec)
diff --git a/documentation/youyesyet.handler.html b/documentation/youyesyet.handler.html
index ba0b8df..b38a338 100644
--- a/documentation/youyesyet.handler.html
+++ b/documentation/youyesyet.handler.html
@@ -1,6 +1,6 @@
-youyesyet.handler documentation
destroy will be called when your application
shuts down, put any clean up code here
init
(init)
init will be called once when
app is deployed as a servlet on
diff --git a/documentation/youyesyet.layout.html b/documentation/youyesyet.layout.html
index 46cdc96..0be9065 100644
--- a/documentation/youyesyet.layout.html
+++ b/documentation/youyesyet.layout.html
@@ -1,6 +1,6 @@
-youyesyet.layout documentation
error-details should be a map containing the following keys:
:status - error status
:title - error title (optional)
diff --git a/documentation/youyesyet.locality.html b/documentation/youyesyet.locality.html
index 4791272..dd328e7 100644
--- a/documentation/youyesyet.locality.html
+++ b/documentation/youyesyet.locality.html
@@ -1,6 +1,6 @@
-youyesyet.locality documentation
Compute the locality index for this `latitude`/`longitude` pair.
neighbouring-localities
(neighbouring-localities locality)
Return this locality with the localities immediately
north east, north, north west, east, west, south west,
south and south east of it.
\ No newline at end of file
diff --git a/documentation/youyesyet.middleware.html b/documentation/youyesyet.middleware.html
index b34fe75..de9ddb3 100644
--- a/documentation/youyesyet.middleware.html
+++ b/documentation/youyesyet.middleware.html
@@ -1,5 +1,5 @@
-youyesyet.middleware documentation
Dynamically bind *user* to the user in the session, if any, so that it
is available in layout/render, q.v.
\ No newline at end of file
diff --git a/documentation/youyesyet.oauth.html b/documentation/youyesyet.oauth.html
index 586c8bc..ab9e15f 100644
--- a/documentation/youyesyet.oauth.html
+++ b/documentation/youyesyet.oauth.html
@@ -1,6 +1,6 @@
-youyesyet.oauth documentation
Fetches a request token from the authority implied by this `request`.
get-authorities
(get-authorities _)
Fetch the authorities from the database and return a map of them.
diff --git a/documentation/youyesyet.outqueue.html b/documentation/youyesyet.outqueue.html
index d5f18fb..e3da898 100644
--- a/documentation/youyesyet.outqueue.html
+++ b/documentation/youyesyet.outqueue.html
@@ -1,6 +1,6 @@
-youyesyet.outqueue documentation
Queue of messages waiting to be sent to the server.
add!
(add! q item)
Add this item to the queue.
count
(count q)
Return the count of items currently in the queue.
lock!
(lock! q)
FIXME: write docs
locked?
(locked? q)
FIXME: write docs
maybe-process-next
(maybe-process-next q process)
Apply this process, assumed to be a function of one argument, to the next
diff --git a/documentation/youyesyet.routes.auto-json.html b/documentation/youyesyet.routes.auto-json.html
index 96f050a..a0f6943 100644
--- a/documentation/youyesyet.routes.auto-json.html
+++ b/documentation/youyesyet.routes.auto-json.html
@@ -1,6 +1,6 @@
-youyesyet.routes.auto-json documentation
JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180920T131123.673Z
+youyesyet.routes.auto-json documentation
JSON routes for youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180921T143945.111Z
auto-rest-routes
FIXME: write docs
create-address!
(create-address! request)
Auto-generated method to insert one record to the `addresses` table. Expects the following key(s) to be present in `params`: `(:address :postcode :district_id :latitude :longitude)`. Returns a map containing the keys `#{"id"}` identifying the record created.
create-authority!
(create-authority! request)
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.
create-canvasser!
(create-canvasser! request)
Auto-generated method to insert one record to the `canvassers` table. Expects the following key(s) to be present in `params`: `(:username :fullname :avatar :bio :elector_id :address_id :phone :email :authority_id :authorised)`. Returns a map containing the keys `#{"id"}` identifying the record created.
diff --git a/documentation/youyesyet.routes.auto.html b/documentation/youyesyet.routes.auto.html
index 647307f..5ab54ce 100644
--- a/documentation/youyesyet.routes.auto.html
+++ b/documentation/youyesyet.routes.auto.html
@@ -1,5 +1,5 @@
-youyesyet.routes.auto documentation
User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180920T131125.604Z
+youyesyet.routes.auto documentation
User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180921T143946.559Z
Prefer the manually-written version of the handler with name `n`, if it exists, to the automatically generated one
resolve-handler
FIXME: write docs
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.home.html b/documentation/youyesyet.routes.home.html
index 24cd05f..2e54c1f 100644
--- a/documentation/youyesyet.routes.home.html
+++ b/documentation/youyesyet.routes.home.html
@@ -1,5 +1,5 @@
-youyesyet.routes.home documentation
This is very temporary. We're going to do authentication by oauth.
motd
(motd)
FIXME: write docs
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.issue-experts.html b/documentation/youyesyet.routes.issue-experts.html
index 2cceafd..20f7479 100644
--- a/documentation/youyesyet.routes.issue-experts.html
+++ b/documentation/youyesyet.routes.issue-experts.html
@@ -1,6 +1,6 @@
-youyesyet.routes.issue-experts documentation
Return the `followuprequest` record indicated by this `id`, provided that
it is unlocked. As a side effect, lock it to this `user`.
get-followup-request-page
(get-followup-request-page request)
FIXME: write docs
issue-expert-routes
FIXME: write docs
list-page
(list-page request)
FIXME: write docs
post-followup-action
(post-followup-action request)
From this `request`, create a `followupaction` record, and, if an
`option_id` is present in the params, an `intention` record; show
diff --git a/documentation/youyesyet.routes.manual.html b/documentation/youyesyet.routes.manual.html
index 9417a27..7d1a732 100644
--- a/documentation/youyesyet.routes.manual.html
+++ b/documentation/youyesyet.routes.manual.html
@@ -1,4 +1,4 @@
-youyesyet.routes.manual documentation
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.oauth.html b/documentation/youyesyet.routes.oauth.html
index db60c55..2711a8f 100644
--- a/documentation/youyesyet.routes.oauth.html
+++ b/documentation/youyesyet.routes.oauth.html
@@ -1,6 +1,6 @@
-youyesyet.routes.oauth documentation
OAuth authentication routes - not finished, does not work yet.
oauth-callback
(oauth-callback request_token {:keys [session]})
Handles the callback from the authority.
oauth-init
(oauth-init request)
Initiates the OAuth with the authority implied by this `request`
oauth-routes
FIXME: write docs
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.rest.html b/documentation/youyesyet.routes.rest.html
index 88bfd58..46f599e 100644
--- a/documentation/youyesyet.routes.rest.html
+++ b/documentation/youyesyet.routes.rest.html
@@ -1,6 +1,6 @@
-youyesyet.routes.rest documentation
Manually maintained routes which handle data transfer to/from the canvasser app.
create-intention-and-visit!
(create-intention-and-visit! request)
Doing visit creation logic server side; request params are expected to
include an `option_id`, an `elector_id` and an `address_id`, or an `option` and
a `location`. If no `address_id` is provided, we simply create an
diff --git a/documentation/youyesyet.routes.roles.html b/documentation/youyesyet.routes.roles.html
index ab04f64..5cd69b6 100644
--- a/documentation/youyesyet.routes.roles.html
+++ b/documentation/youyesyet.routes.roles.html
@@ -1,6 +1,6 @@
-youyesyet.routes.roles documentation
Routes/pages available to authenticated users in specific roles.
admins-page
(admins-page request)
FIXME: write docs
analysts-page
(analysts-page request)
My expectation is that analysts will do a lot of their work through QGIS or
some other geographical information system; so there isn't a need to put
anything sophisticated here.
canvassers-page
(canvassers-page request)
FIXME: write docs
roles-page
(roles-page request)
FIXME: write docs
roles-routes
FIXME: write docs
team-organisers-page
(team-organisers-page request)
FIXME: write docs
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.services.html b/documentation/youyesyet.routes.services.html
index 29cf074..87024de 100644
--- a/documentation/youyesyet.routes.services.html
+++ b/documentation/youyesyet.routes.services.html
@@ -1,4 +1,4 @@
-youyesyet.routes.services documentation
\ No newline at end of file
diff --git a/documentation/youyesyet.utils.html b/documentation/youyesyet.utils.html
index 2286012..c2d655d 100644
--- a/documentation/youyesyet.utils.html
+++ b/documentation/youyesyet.utils.html
@@ -1,3 +1,3 @@
-youyesyet.utils documentation
\ No newline at end of file
diff --git a/documentation/youyesyet.validation.html b/documentation/youyesyet.validation.html
index 17fb005..74382ff 100644
--- a/documentation/youyesyet.validation.html
+++ b/documentation/youyesyet.validation.html
@@ -1,3 +1,3 @@
-youyesyet.validation documentation
- We are suffering an intermittent problem causing an occasional crash of
- the Project Hope canvassing application. We apologise for this, and are
- working on a permanent fix.
-
-
- The application will restart automatically
- within five minutes, please take a short break.
-
-
-
-
-
-
-
+
Error: 502 Bad Gateway
+
+ We are suffering an intermittent problem causing an occasional crash of
+ the Project Hope canvassing application. We apologise for this, and are
+ working on a permanent fix.
+
+
+ The application will restart automatically
+ within five minutes, please take a short break.
+
diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql
index 855342f..e5404bf 100644
--- a/resources/sql/queries.sql
+++ b/resources/sql/queries.sql
@@ -75,13 +75,13 @@ FROM addresses, visits
WHERE visits.address_id = addresses.id
AND visits.id = :id
--- I don't know why this next one isn't autogenerating, but it isn't and it's critical.
--- :name list-roles-by-canvasser :? :*
--- :doc links all existing canvasser records related to a given role
-SELECT roles.*
-FROM roles, ln_canvassers_roles
-WHERE roles.id = ln_canvassers_roles.role_id
- AND ln_canvassers_roles.canvasser_id = :id
-ORDER BY roles.name,
- roles.id
+-- :name list-elector-intentions :? :*
+-- :doc short form of `list-intentions-by-elector`, returning far less data, for use in `youyesyet.routes.rest/get-local-data`, q.v.
+-- TODO: should be limited to visits in the past 24 hours, to prevent the app being
+-- used to harrass NO voters. See https://github.com/simon-brooke/youyesyet/issues/58
+SELECT intentions.id, intentions.option_id, visits.date
+FROM intentions, visits
+WHERE intentions.visit_id = visits.id
+AND intentions.elector_id = :id
+ORDER BY visits.date DESC
diff --git a/resources/templates/base.html b/resources/templates/base.html
index 950e3e8..92e2a75 100644
--- a/resources/templates/base.html
+++ b/resources/templates/base.html
@@ -27,7 +27,7 @@
+
+
+ This website stores session information as a 'cookie' on your browser.
+ This helps us show you the content you want to see. This cookie does
+ not identify you, and cannot be read by other websites. It is deleted
+ by your browser as soon as you leave this site. This website does not
+ use any third party cookies, so your visit here cannot be tracked by
+ other websites.
+