diff --git a/CHANGELOG.md.md b/CHANGELOG.md
similarity index 80%
rename from CHANGELOG.md.md
rename to CHANGELOG.md
index 9a553a8..482db6b 100644
--- a/CHANGELOG.md.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
# Change Log
All notable changes to this project will be documented in this file. This change log is intended to follow the conventions of [keepachangelog.com](http://keepachangelog.com/).
+# 0.2.1, 2018-09-21
+
+(Hopefully) fixed map regression in 0.2.0
+
# 0.2.0, 2018-09-20
Second public alpha, most features complete.
diff --git a/doc/specification/userspec.md b/doc/specification/userspec.md
index efa9f59..60a83e0 100644
--- a/doc/specification/userspec.md
+++ b/doc/specification/userspec.md
@@ -75,18 +75,26 @@ Note that:
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
-1. 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*;
+1. On buildings with multiple flats, the *Building View*;
2. 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.
@@ -112,9 +120,18 @@ The *Issues View* is a simple list of issues:
| 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
@@ -132,6 +149,8 @@ The *Followup Request* form is a simple form which allows the canvasser to recor
| |
+------------------------------------------------+
+
+
## How Street Canvassers will use the system
Street Canvassers will typically use the system by
diff --git a/documentation/authorisation.html b/documentation/authorisation.html
new file mode 100644
index 0000000..0c83e18
--- /dev/null
+++ b/documentation/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/documentation/competitors.html b/documentation/competitors.html
new file mode 100644
index 0000000..040c485
--- /dev/null
+++ b/documentation/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/documentation/index.html b/documentation/index.html
new file mode 100644
index 0000000..662dc9d
--- /dev/null
+++ b/documentation/index.html
@@ -0,0 +1,3 @@
+
+Youyesyet 0.2.1-SNAPSHOT
User interface routes for Youyesyet auto-generated by [Application Description Language framework](https://github.com/simon-brooke/adl) at 20180920T131125.604Z
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/documentation/userspec.html b/documentation/userspec.html
new file mode 100644
index 0000000..e2f9a92
--- /dev/null
+++ b/documentation/userspec.html
@@ -0,0 +1,142 @@
+
+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, 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 only one dwelling, 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 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
+
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/documentation/youyesyet.authorisation.html b/documentation/youyesyet.authorisation.html
new file mode 100644
index 0000000..4b154a7
--- /dev/null
+++ b/documentation/youyesyet.authorisation.html
@@ -0,0 +1,4 @@
+
+youyesyet.authorisation documentation
\ No newline at end of file
diff --git a/documentation/youyesyet.config.html b/documentation/youyesyet.config.html
new file mode 100644
index 0000000..ecf03f0
--- /dev/null
+++ b/documentation/youyesyet.config.html
@@ -0,0 +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
new file mode 100644
index 0000000..50de3d4
--- /dev/null
+++ b/documentation/youyesyet.db.core.html
@@ -0,0 +1,269 @@
+
+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
+
create-authority!-sqlvec
creates a new authority record (sqlvec)
+
create-canvasser!
creates a new canvasser record
+
create-canvasser!-sqlvec
creates a new canvasser record (sqlvec)
+
create-district!
creates a new district record
+
create-district!-sqlvec
creates a new district record (sqlvec)
+
create-dwelling!
creates a new dwelling record
+
create-dwelling!-sqlvec
creates a new dwelling record (sqlvec)
+
create-elector!
creates a new elector record
+
create-elector!-sqlvec
creates a new elector record (sqlvec)
+
create-event!
creates a new event record
+
create-event!-sqlvec
creates a new event record (sqlvec)
+
create-followupaction!
creates a new followupaction record
+
create-followupaction!-sqlvec
creates a new followupaction record (sqlvec)
+
create-followupmethod!
creates a new followupmethod record
+
create-followupmethod!-sqlvec
creates a new followupmethod record (sqlvec)
+
create-followuprequest!
creates a new followuprequest record
+
create-followuprequest!-sqlvec
creates a new followuprequest record (sqlvec)
+
create-gender!
creates a new gender record
+
create-gender!-sqlvec
creates a new gender record (sqlvec)
+
create-intention!
creates a new intention record
+
create-intention!-sqlvec
creates a new intention record (sqlvec)
+
create-issue!
creates a new issue record
+
create-issue!-sqlvec
creates a new issue record (sqlvec)
+
create-option!
creates a new option record
+
create-option!-sqlvec
creates a new option record (sqlvec)
+
create-role!
creates a new role record
+
create-role!-sqlvec
creates a new role record (sqlvec)
+
create-team!
creates a new team record
+
create-team!-sqlvec
creates a new team record (sqlvec)
+
create-visit!
creates a new visit record
+
create-visit!-sqlvec
creates a new visit record (sqlvec)
+
delete-address!
updates an existing address record
+
delete-address!-sqlvec
updates an existing address record (sqlvec)
+
delete-authority!
updates an existing authority record
+
delete-authority!-sqlvec
updates an existing authority record (sqlvec)
+
delete-canvasser!
updates an existing canvasser record
+
delete-canvasser!-sqlvec
updates an existing canvasser record (sqlvec)
+
delete-district!
updates an existing district record
+
delete-district!-sqlvec
updates an existing district record (sqlvec)
+
delete-dwelling!
updates an existing dwelling record
+
delete-dwelling!-sqlvec
updates an existing dwelling record (sqlvec)
+
delete-elector!
updates an existing elector record
+
delete-elector!-sqlvec
updates an existing elector record (sqlvec)
+
delete-event!
updates an existing event record
+
delete-event!-sqlvec
updates an existing event record (sqlvec)
+
delete-followupaction!
updates an existing followupaction record
+
delete-followupaction!-sqlvec
updates an existing followupaction record (sqlvec)
+
delete-followupmethod!
updates an existing followupmethod record
+
delete-followupmethod!-sqlvec
updates an existing followupmethod record (sqlvec)
+
delete-followuprequest!
updates an existing followuprequest record
+
delete-followuprequest!-sqlvec
updates an existing followuprequest record (sqlvec)
+
delete-gender!
updates an existing gender record
+
delete-gender!-sqlvec
updates an existing gender record (sqlvec)
+
delete-intention!
updates an existing intention record
+
delete-intention!-sqlvec
updates an existing intention record (sqlvec)
+
delete-issue!
updates an existing issue record
+
delete-issue!-sqlvec
updates an existing issue record (sqlvec)
+
delete-option!
updates an existing option record
+
delete-option!-sqlvec
updates an existing option record (sqlvec)
+
delete-role!
updates an existing role record
+
delete-role!-sqlvec
updates an existing role record (sqlvec)
+
delete-team!
updates an existing team record
+
delete-team!-sqlvec
updates an existing team record (sqlvec)
+
delete-visit!
updates an existing visit record
+
delete-visit!-sqlvec
updates an existing visit record (sqlvec)
+
get-address
selects an existing address record
+
get-address-sqlvec
selects an existing address record (sqlvec)
+
get-authority
selects an existing authority record
+
get-authority-sqlvec
selects an existing authority record (sqlvec)
+
get-canvasser
selects an existing canvasser record
+
get-canvasser-by-username
selects an existing canvasser record
+
get-canvasser-by-username-sqlvec
selects an existing canvasser record (sqlvec)
+
get-canvasser-sqlvec
selects an existing canvasser record (sqlvec)
+
get-district
selects an existing district record
+
get-district-sqlvec
selects an existing district record (sqlvec)
+
get-dwelling
selects an existing dwelling record
+
get-dwelling-sqlvec
selects an existing dwelling record (sqlvec)
+
get-elector
selects an existing elector record
+
get-elector-sqlvec
selects an existing elector record (sqlvec)
+
get-event
selects an existing event record
+
get-event-sqlvec
selects an existing event record (sqlvec)
+
get-followupaction
selects an existing followupaction record
+
get-followupaction-sqlvec
selects an existing followupaction record (sqlvec)
+
get-followupmethod
selects an existing followupmethod record
+
get-followupmethod-sqlvec
selects an existing followupmethod record (sqlvec)
+
get-followuprequest
selects an existing followuprequest record
+
get-followuprequest-sqlvec
selects an existing followuprequest record (sqlvec)
+
get-gender
selects an existing gender record
+
get-gender-sqlvec
selects an existing gender record (sqlvec)
+
get-intention
selects an existing intention record
+
get-intention-sqlvec
selects an existing intention record (sqlvec)
+
get-issue
selects an existing issue record
+
get-issue-sqlvec
selects an existing issue record (sqlvec)
+
get-last-visit-by-canvasser
returns the most recent visit record of the canvasser with the specified `:id`
+
get-locality-for-visit
returns the locality of the address of this visit
+
get-option
selects an existing option record
+
get-option-sqlvec
selects an existing option record (sqlvec)
+
get-role
selects an existing role record
+
get-role-by-name
selects an existing role record
+
get-role-by-name-sqlvec
selects an existing role record (sqlvec)
+
get-role-sqlvec
selects an existing role record (sqlvec)
+
get-team
selects an existing team record
+
get-team-sqlvec
selects an existing team record (sqlvec)
+
get-visit
selects an existing visit record
+
get-visit-sqlvec
selects an existing visit record (sqlvec)
+
list-addresses
lists all existing address records
+
list-addresses-by-district
lists all existing district records related to a given address
+
list-addresses-by-district-sqlvec
lists all existing district records related to a given address (sqlvec)
+
list-addresses-by-locality
lists all existing address records in a given locality
+
list-addresses-sqlvec
lists all existing address records (sqlvec)
+
list-authorities
lists all existing authority records
+
list-authorities-sqlvec
lists all existing authority records (sqlvec)
+
list-canvassers
lists all existing canvasser records
+
list-canvassers-by-address
lists all existing address records related to a given canvasser
+
list-canvassers-by-address-sqlvec
lists all existing address records related to a given canvasser (sqlvec)
+
list-canvassers-by-authority
lists all existing authority records related to a given canvasser
+
list-canvassers-by-authority-sqlvec
lists all existing authority records related to a given canvasser (sqlvec)
+
list-canvassers-by-elector
lists all existing elector records related to a given canvasser
+
list-canvassers-by-elector-sqlvec
lists all existing elector records related to a given canvasser (sqlvec)
+
list-canvassers-by-issues
links all existing issue records related to a given canvasser
+
list-canvassers-by-issues-sqlvec
links all existing issue records related to a given canvasser (sqlvec)
+
list-canvassers-by-roles
links all existing canvasser records related to a given role
+
list-canvassers-by-roles-sqlvec
links all existing canvasser records related to a given role (sqlvec)
+
list-canvassers-sqlvec
lists all existing canvasser records (sqlvec)
+
list-districts
lists all existing district records
+
list-districts-sqlvec
lists all existing district records (sqlvec)
+
list-dwellings
lists all existing dwelling records
+
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-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)
+
list-electors-by-gender
lists all existing gender records related to a given elector
+
list-electors-by-gender-sqlvec
lists all existing gender records related to a given elector (sqlvec)
+
list-electors-sqlvec
lists all existing elector records (sqlvec)
+
list-events
lists all existing event records
+
list-events-by-teams
links all existing team records related to a given event
+
list-events-by-teams-sqlvec
links all existing team records related to a given event (sqlvec)
+
list-events-sqlvec
lists all existing event records (sqlvec)
+
list-followupactions
lists all existing followupaction records
+
list-followupactions-by-canvasser
lists all existing canvasser records related to a given followupaction
+
list-followupactions-by-canvasser-sqlvec
lists all existing canvasser records related to a given followupaction (sqlvec)
+
list-followupactions-by-followuprequest
lists all existing followuprequest records related to a given followupaction
+
list-followupactions-by-followuprequest-sqlvec
lists all existing followuprequest records related to a given followupaction (sqlvec)
+
list-followupactions-sqlvec
lists all existing followupaction records (sqlvec)
+
list-followupmethods
lists all existing followupmethod records
+
list-followupmethods-sqlvec
lists all existing followupmethod records (sqlvec)
+
list-followuprequests
lists all existing followuprequest records
+
list-followuprequests-by-canvasser
lists all existing canvasser records related to a given followuprequest
+
list-followuprequests-by-canvasser-sqlvec
lists all existing canvasser records related to a given followuprequest (sqlvec)
+
list-followuprequests-by-elector
lists all existing elector records related to a given followuprequest
+
list-followuprequests-by-elector-sqlvec
lists all existing elector records related to a given followuprequest (sqlvec)
+
list-followuprequests-by-followupmethod
lists all existing followupmethod records related to a given followuprequest
+
list-followuprequests-by-followupmethod-sqlvec
lists all existing followupmethod records related to a given followuprequest (sqlvec)
+
list-followuprequests-by-issue
lists all existing issue records related to a given followuprequest
+
list-followuprequests-by-issue-sqlvec
lists all existing issue records related to a given followuprequest (sqlvec)
+
list-followuprequests-by-visit
lists all existing visit records related to a given followuprequest
+
list-followuprequests-by-visit-sqlvec
lists all existing visit records related to a given followuprequest (sqlvec)
+
list-followuprequests-sqlvec
lists all existing followuprequest records (sqlvec)
+
list-genders
lists all existing gender records
+
list-genders-sqlvec
lists all existing gender records (sqlvec)
+
list-intentions
lists all existing intention records
+
list-intentions-by-elector
lists all existing elector records related to a given intention
+
list-intentions-by-elector-sqlvec
lists all existing elector records related to a given intention (sqlvec)
+
list-intentions-by-option
lists all existing option records related to a given intention
+
list-intentions-by-option-sqlvec
lists all existing option records related to a given intention (sqlvec)
+
list-intentions-by-visit
lists all existing visit records related to a given intention
+
list-intentions-by-visit-sqlvec
lists all existing visit records related to a given intention (sqlvec)
+
list-intentions-sqlvec
lists all existing intention records (sqlvec)
+
list-issues
lists all existing issue records
+
list-issues-sqlvec
lists all existing issue records (sqlvec)
+
list-members-by-team
links all existing canvasser records related to a given team
+
list-members-by-team-sqlvec
links all existing canvasser records related to a given team (sqlvec)
+
list-open-requests
lists all existing followuprequest records which have not been closed and which the :expert has expertise to answer.
+
list-options
lists all existing option records
+
list-options-sqlvec
lists all existing option records (sqlvec)
+
list-organisers-by-team
links all existing canvasser records related to a given team
+
list-organisers-by-team-sqlvec
links all existing canvasser records related to a given team (sqlvec)
+
list-roles
lists all existing role records
+
list-roles-by-canvasser
links all existing canvasser records related to a given role
+
list-roles-sqlvec
lists all existing role records (sqlvec)
+
list-teams
lists all existing team records
+
list-teams-by-canvasser
links all existing team records related to a given canvasser
+
list-teams-by-canvasser-sqlvec
links all existing team records related to a given canvasser (sqlvec)
+
list-teams-by-district
lists all existing district records related to a given team
+
list-teams-by-district-sqlvec
lists all existing district records related to a given team (sqlvec)
+
list-teams-sqlvec
lists all existing team records (sqlvec)
+
list-visits
lists all existing visit records
+
list-visits-by-address
lists all existing address records related to a given visit
+
list-visits-by-address-sqlvec
lists all existing address records related to a given visit (sqlvec)
+
list-visits-by-canvasser
lists all existing canvasser records related to a given visit
+
list-visits-by-canvasser-sqlvec
lists all existing canvasser records related to a given visit (sqlvec)
+
list-visits-sqlvec
lists all existing visit records (sqlvec)
+
search-strings-addresses
selects existing address records having any string field matching the parameter of the same name by substring match
+
search-strings-addresses-sqlvec
selects existing address records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-authorities
selects existing authority records having any string field matching the parameter of the same name by substring match
+
search-strings-authorities-sqlvec
selects existing authority records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-canvassers
selects existing canvasser records having any string field matching the parameter of the same name by substring match
+
search-strings-canvassers-sqlvec
selects existing canvasser records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-districts
selects existing district records having any string field matching the parameter of the same name by substring match
+
search-strings-districts-sqlvec
selects existing district records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-dwellings
selects existing dwelling records having any string field matching the parameter of the same name by substring match
+
search-strings-dwellings-sqlvec
selects existing dwelling records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-electors
selects existing elector records having any string field matching the parameter of the same name by substring match
+
search-strings-electors-sqlvec
selects existing elector records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-events
selects existing event records having any string field matching the parameter of the same name by substring match
+
search-strings-events-sqlvec
selects existing event records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-followupactions
selects existing followupaction records having any string field matching the parameter of the same name by substring match
+
search-strings-followupactions-sqlvec
selects existing followupaction records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-followupmethods
selects existing followupmethod records having any string field matching the parameter of the same name by substring match
+
search-strings-followupmethods-sqlvec
selects existing followupmethod records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-followuprequests
selects existing followuprequest records having any string field matching the parameter of the same name by substring match
+
search-strings-followuprequests-sqlvec
selects existing followuprequest records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-genders
selects existing gender records having any string field matching the parameter of the same name by substring match
+
search-strings-genders-sqlvec
selects existing gender records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-intentions
selects existing intention records having any string field matching the parameter of the same name by substring match
+
search-strings-intentions-sqlvec
selects existing intention records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-issues
selects existing issue records having any string field matching the parameter of the same name by substring match
+
search-strings-issues-sqlvec
selects existing issue records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-options
selects existing option records having any string field matching the parameter of the same name by substring match
+
search-strings-options-sqlvec
selects existing option records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-roles
selects existing role records having any string field matching the parameter of the same name by substring match
+
search-strings-roles-sqlvec
selects existing role records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-teams
selects existing team records having any string field matching the parameter of the same name by substring match
+
search-strings-teams-sqlvec
selects existing team records having any string field matching the parameter of the same name by substring match (sqlvec)
+
search-strings-visits
selects existing visit records having any string field matching the parameter of the same name by substring match
+
search-strings-visits-sqlvec
selects existing visit records having any string field matching the parameter of the same name by substring match (sqlvec)
+
to-date
(to-date sql-date)
FIXME: write docs
to-pg-json
(to-pg-json value)
FIXME: write docs
update-address!
updates an existing address record
+
update-address!-sqlvec
updates an existing address record (sqlvec)
+
update-authority!
updates an existing authority record
+
update-authority!-sqlvec
updates an existing authority record (sqlvec)
+
update-canvasser!
updates an existing canvasser record
+
update-canvasser!-sqlvec
updates an existing canvasser record (sqlvec)
+
update-district!
updates an existing district record
+
update-district!-sqlvec
updates an existing district record (sqlvec)
+
update-dwelling!
updates an existing dwelling record
+
update-dwelling!-sqlvec
updates an existing dwelling record (sqlvec)
+
update-elector!
updates an existing elector record
+
update-elector!-sqlvec
updates an existing elector record (sqlvec)
+
update-event!
updates an existing event record
+
update-event!-sqlvec
updates an existing event record (sqlvec)
+
update-followupaction!
updates an existing followupaction record
+
update-followupaction!-sqlvec
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/documentation/youyesyet.handler.html b/documentation/youyesyet.handler.html
new file mode 100644
index 0000000..ba0b8df
--- /dev/null
+++ b/documentation/youyesyet.handler.html
@@ -0,0 +1,8 @@
+
+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
+an app server such as Tomcat
+put any initialization code here
init-app
FIXME: write docs
\ No newline at end of file
diff --git a/documentation/youyesyet.layout.html b/documentation/youyesyet.layout.html
new file mode 100644
index 0000000..46cdc96
--- /dev/null
+++ b/documentation/youyesyet.layout.html
@@ -0,0 +1,12 @@
+
+youyesyet.layout documentation
Render web pages using Selmer templating markup.
+
*app-context*
dynamic
FIXME: write docs
*user*
dynamic
FIXME: write docs
error-page
(error-page error-details)
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
get-user-roles
Return, as a set, the names of the roles of which this `user` is a member.
+
render
(render template & [params])
renders the HTML `template` located relative to resources/templates in
+the context of this session and with these parameters.
render-with-session
(render-with-session template session & [params])
renders the HTML `template` located relative to resources/templates in
+the context of this session and with these parameters.
\ No newline at end of file
diff --git a/documentation/youyesyet.locality.html b/documentation/youyesyet.locality.html
new file mode 100644
index 0000000..4791272
--- /dev/null
+++ b/documentation/youyesyet.locality.html
@@ -0,0 +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
new file mode 100644
index 0000000..b34fe75
--- /dev/null
+++ b/documentation/youyesyet.middleware.html
@@ -0,0 +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
new file mode 100644
index 0000000..586c8bc
--- /dev/null
+++ b/documentation/youyesyet.oauth.html
@@ -0,0 +1,8 @@
+
+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.
+
oauth-callback-uri
(oauth-callback-uri {:keys [headers]})
Generates the oauth request callback URI.
+
\ No newline at end of file
diff --git a/documentation/youyesyet.outqueue.html b/documentation/youyesyet.outqueue.html
new file mode 100644
index 0000000..d5f18fb
--- /dev/null
+++ b/documentation/youyesyet.outqueue.html
@@ -0,0 +1,12 @@
+
+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
+item in the queue, if the queue is not currently locked; return the value
+returned by process.
new-queue
(new-queue)(new-queue items)
Create a new queue
+
peek
(peek q)
Look at the next item which could be removed from the queue.
+
queue?
(queue? x)
True if x is a queue, else false.
+
take!
(take! q)
Return the first item from the queue, rebind the queue to the remaining
+items. If the queue is empty return nil.
unlock!
(unlock! q)(unlock! q value)
FIXME: write docs
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.auto-json.html b/documentation/youyesyet.routes.auto-json.html
new file mode 100644
index 0000000..96f050a
--- /dev/null
+++ b/documentation/youyesyet.routes.auto-json.html
@@ -0,0 +1,61 @@
+
+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
+
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.
+
create-district!
(create-district! request)
Auto-generated method to insert one record to the `districts` table. Expects the following key(s) to be present in `params`: `(:name)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-dwelling!
(create-dwelling! request)
Auto-generated method to insert one record to the `dwellings` table. Expects the following key(s) to be present in `params`: `(:address_id :sub-address)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-elector!
(create-elector! request)
Auto-generated method to insert one record to the `electors` table. Expects the following key(s) to be present in `params`: `(:name :dwelling_id :phone :email :gender :signature)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-event!
(create-event! request)
Auto-generated method 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.
+
create-followupaction!
(create-followupaction! request)
Auto-generated method to insert one record to the `followupactions` table. Expects the following key(s) to be present in `params`: `(:request_id :actor :date :notes :closed)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-followupmethod!
(create-followupmethod! request)
Auto-generated method to insert one record to the `followupmethods` table. Expects the following key(s) to be present in `params`: `(:id)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-followuprequest!
(create-followuprequest! request)
Auto-generated method to insert one record to the `followuprequests` table. Expects the following key(s) to be present in `params`: `(:elector_id :visit_id :issue_id :issue_detail :method_id :method_detail :locked_by :locked)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-gender!
(create-gender! request)
Auto-generated method to insert one record to the `genders` table. Expects the following key(s) to be present in `params`: `(:id)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-intention!
(create-intention! request)
Auto-generated method to insert one record to the `intentions` table. Expects the following key(s) to be present in `params`: `(:visit_id :elector_id :option_id :locality)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-issue!
(create-issue! request)
Auto-generated method 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.
+
create-option!
(create-option! request)
Auto-generated method to insert one record to the `options` table. Expects the following key(s) to be present in `params`: `(:id)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-role!
(create-role! request)
Auto-generated method 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.
+
create-team!
(create-team! request)
Auto-generated method to insert one record to the `teams` table. Expects the following key(s) to be present in `params`: `(:name :district_id :latitude :longitude)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
create-visit!
(create-visit! request)
Auto-generated method to insert one record to the `visits` table. Expects the following key(s) to be present in `params`: `(:address_id :canvasser_id :date)`. Returns a map containing the keys `#{"id"}` identifying the record created.
+
delete-address!
(delete-address! request)
Auto-generated method to delete one record from the `addresses` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-authority!
(delete-authority! request)
Auto-generated method to delete one record from the `authorities` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-canvasser!
(delete-canvasser! request)
Auto-generated method to delete one record from the `canvassers` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-district!
(delete-district! request)
Auto-generated method to delete one record from the `districts` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-dwelling!
(delete-dwelling! request)
Auto-generated method to delete one record from the `dwellings` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-elector!
(delete-elector! request)
Auto-generated method to delete one record from the `electors` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-event!
(delete-event! request)
Auto-generated method to delete one record from the `events` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-followupaction!
(delete-followupaction! request)
Auto-generated method to delete one record from the `followupactions` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-followupmethod!
(delete-followupmethod! request)
Auto-generated method to delete one record from the `followupmethods` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-followuprequest!
(delete-followuprequest! request)
Auto-generated method to delete one record from the `followuprequests` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-gender!
(delete-gender! request)
Auto-generated method to delete one record from the `genders` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-intention!
(delete-intention! request)
Auto-generated method to delete one record from the `intentions` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-issue!
(delete-issue! request)
Auto-generated method to delete one record from the `issues` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-option!
(delete-option! request)
Auto-generated method to delete one record from the `options` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-role!
(delete-role! request)
Auto-generated method to delete one record from the `roles` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-team!
(delete-team! request)
Auto-generated method to delete one record from the `teams` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
delete-visit!
(delete-visit! request)
Auto-generated method to delete one record from the `visits` table. Expects the following key(s) to be present in `params`: `#{"id"}`.
+
get-address
FIXME: write docs
get-authority
FIXME: write docs
get-canvasser
FIXME: write docs
get-canvasser-by-username
FIXME: write docs
get-district
FIXME: write docs
get-dwelling
FIXME: write docs
get-elector
FIXME: write docs
get-event
(get-event request)
Auto-generated method 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`.
+
get-followupaction
(get-followupaction request)
Auto-generated method to select one record from the `followupactions` table. Expects the following key(s) to be present in `params`: `#{"id"}`. Returns a map containing the following keys: `clojure.lang.LazySeq@6b32af0e`.
+
get-followupmethod
FIXME: write docs
get-followuprequest
FIXME: write docs
get-gender
FIXME: write docs
get-intention
FIXME: write docs
get-issue
FIXME: write docs
get-option
FIXME: write docs
get-role
FIXME: write docs
get-role-by-name
FIXME: write docs
get-team
FIXME: write docs
get-visit
FIXME: write docs
list-addresses
FIXME: write docs
list-addresses-by-district
(list-addresses-by-district {:keys [params]})
FIXME: write docs
list-authorities
FIXME: write docs
list-canvassers
FIXME: write docs
list-canvassers-by-address
(list-canvassers-by-address {:keys [params]})
FIXME: write docs
list-canvassers-by-authority
(list-canvassers-by-authority {:keys [params]})
FIXME: write docs
list-canvassers-by-elector
(list-canvassers-by-elector {:keys [params]})
FIXME: write docs
list-canvassers-by-issues
(list-canvassers-by-issues {:keys [params]})
FIXME: write docs
list-canvassers-by-roles
(list-canvassers-by-roles {:keys [params]})
FIXME: write docs
list-districts
FIXME: write docs
list-dwellings
FIXME: write docs
list-dwellings-by-address
(list-dwellings-by-address {:keys [params]})
FIXME: write docs
list-electors
FIXME: write docs
list-electors-by-dwelling
(list-electors-by-dwelling {:keys [params]})
FIXME: write docs
list-electors-by-gender
(list-electors-by-gender {:keys [params]})
FIXME: write docs
list-events
(list-events request)
Auto-generated method 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)`.
+
list-events-by-teams
(list-events-by-teams {:keys [params]})
FIXME: write docs
list-followupactions
(list-followupactions request)
Auto-generated method to select all records from the `followupactions` table. If the keys `(:limit :offset)` are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: `(:request_id :actor :date :notes :closed :id)`.
+
Auto-generated method 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)`.
+
search-strings-followupactions
(search-strings-followupactions request)
Auto-generated method to select all records from the `followupactions` table with any text field matching the value of the key `:pattern` which should be in the request. If the keys `(:limit :offset)` are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: `(:request_id :actor :date :notes :closed :id)`.
+
search-strings-followupmethods
FIXME: write docs
search-strings-followuprequests
FIXME: write docs
search-strings-genders
FIXME: write docs
search-strings-intentions
FIXME: write docs
search-strings-issues
FIXME: write docs
search-strings-options
FIXME: write docs
search-strings-roles
FIXME: write docs
search-strings-teams
FIXME: write docs
search-strings-visits
FIXME: write docs
update-address!
(update-address! request)
Auto-generated method to update one record in the `addresses` table. Expects the following key(s) to be present in `params`: `(:address :district_id :id :latitude :longitude :postcode)`.
+
update-authority!
(update-authority! request)
Auto-generated method to update one record in the `authorities` table. Expects the following key(s) to be present in `params`: `(:access-token-uri :authorize-uri :consumer-key :consumer-secret :id :request-token-uri)`.
+
update-canvasser!
(update-canvasser! request)
Auto-generated method to update one record in the `canvassers` table. Expects the following key(s) to be present in `params`: `(:address_id :authorised :authority_id :avatar :bio :elector_id :email :fullname :id :phone :username)`.
+
update-district!
(update-district! request)
Auto-generated method to update one record in the `districts` table. Expects the following key(s) to be present in `params`: `(:id :name)`.
+
update-dwelling!
(update-dwelling! request)
Auto-generated method to update one record in the `dwellings` table. Expects the following key(s) to be present in `params`: `(:address_id :id :sub-address)`.
+
update-elector!
(update-elector! request)
Auto-generated method to update one record in the `electors` table. Expects the following key(s) to be present in `params`: `(:dwelling_id :email :gender :id :name :phone :signature)`.
+
update-event!
(update-event! request)
Auto-generated method to update one record in the `events` table. Expects the following key(s) to be present in `params`: `(:cancelled :date :decription :id :name :time)`.
+
update-followupaction!
(update-followupaction! request)
Auto-generated method to update one record in the `followupactions` table. Expects the following key(s) to be present in `params`: `(:actor :closed :date :id :notes :request_id)`.
+
update-followupmethod!
(update-followupmethod! request)
Auto-generated method to update one record in the `followupmethods` table. Expects the following key(s) to be present in `params`: `(:id)`.
+
update-followuprequest!
(update-followuprequest! request)
Auto-generated method to update one record in the `followuprequests` table. Expects the following key(s) to be present in `params`: `(:elector_id :id :issue_detail :issue_id :locked :locked_by :method_detail :method_id :visit_id)`.
+
update-gender!
(update-gender! request)
Auto-generated method to update one record in the `genders` table. Expects the following key(s) to be present in `params`: `(:id)`.
+
update-intention!
(update-intention! request)
Auto-generated method 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)`.
+
update-issue!
(update-issue! request)
Auto-generated method to update one record in the `issues` table. Expects the following key(s) to be present in `params`: `(:brief :current :id :url)`.
+
update-option!
(update-option! request)
Auto-generated method to update one record in the `options` table. Expects the following key(s) to be present in `params`: `(:id)`.
+
update-role!
(update-role! request)
Auto-generated method to update one record in the `roles` table. Expects the following key(s) to be present in `params`: `(:id :name)`.
+
update-team!
(update-team! request)
Auto-generated method to update one record in the `teams` table. Expects the following key(s) to be present in `params`: `(:district_id :id :latitude :longitude :name)`.
+
update-visit!
(update-visit! request)
Auto-generated method to update one record in the `visits` table. Expects the following key(s) to be present in `params`: `(:address_id :canvasser_id :date :id)`.
+
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.auto.html b/documentation/youyesyet.routes.auto.html
new file mode 100644
index 0000000..647307f
--- /dev/null
+++ b/documentation/youyesyet.routes.auto.html
@@ -0,0 +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
+
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
new file mode 100644
index 0000000..24cd05f
--- /dev/null
+++ b/documentation/youyesyet.routes.home.html
@@ -0,0 +1,5 @@
+
+youyesyet.routes.home documentation
Routes/pages available to unauthenticated users.
+
about-page
(about-page)
FIXME: write docs
call-me-page
(call-me-page request)
FIXME: write docs
home-page
(home-page)
FIXME: write docs
home-routes
FIXME: write docs
login-page
(login-page request)
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
new file mode 100644
index 0000000..2cceafd
--- /dev/null
+++ b/documentation/youyesyet.routes.issue-experts.html
@@ -0,0 +1,8 @@
+
+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
+the request list on success, the request form on failure.
release-followuprequest!
(release-followuprequest! id user)
Release the lock held on the `followuprequest` record indicated by this
+`id` held by this `user`, if present.
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.manual.html b/documentation/youyesyet.routes.manual.html
new file mode 100644
index 0000000..9417a27
--- /dev/null
+++ b/documentation/youyesyet.routes.manual.html
@@ -0,0 +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
new file mode 100644
index 0000000..db60c55
--- /dev/null
+++ b/documentation/youyesyet.routes.oauth.html
@@ -0,0 +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
new file mode 100644
index 0000000..88bfd58
--- /dev/null
+++ b/documentation/youyesyet.routes.rest.html
@@ -0,0 +1,18 @@
+
+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.
create-request-and-visit!
(create-request-and-visit! request)
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.
current-visit-id
(current-visit-id request)
Return the id of the current visit by the current user, creating it if necessary.
+
get-local-data
(get-local-data request)
Get data local to the user of the canvasser app. Expects arguments `latitude` and
+`longitude`, or `locality`. Returns a block of data for that locality
in-get-local-data
Local data is volatile, because hopefully canvassers are updating it as they
+work. So cache for only 90 seconds.
last-visit-by-current-user
(last-visit-by-current-user request)
Return the most recent visit by the currently logged in user
+
rest-routes
FIXME: write docs
update-elector-signature!
(update-elector-signature! request)
Set the `signature` in the params of this `request` as the signature for
+the elector whose `id` is in the params of this `request`.
\ No newline at end of file
diff --git a/documentation/youyesyet.routes.roles.html b/documentation/youyesyet.routes.roles.html
new file mode 100644
index 0000000..ab04f64
--- /dev/null
+++ b/documentation/youyesyet.routes.roles.html
@@ -0,0 +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
new file mode 100644
index 0000000..29cf074
--- /dev/null
+++ b/documentation/youyesyet.routes.services.html
@@ -0,0 +1,4 @@
+
+youyesyet.routes.services documentation
\ No newline at end of file
diff --git a/documentation/youyesyet.utils.html b/documentation/youyesyet.utils.html
new file mode 100644
index 0000000..2286012
--- /dev/null
+++ b/documentation/youyesyet.utils.html
@@ -0,0 +1,3 @@
+
+youyesyet.utils documentation
\ No newline at end of file
diff --git a/documentation/youyesyet.validation.html b/documentation/youyesyet.validation.html
new file mode 100644
index 0000000..17fb005
--- /dev/null
+++ b/documentation/youyesyet.validation.html
@@ -0,0 +1,3 @@
+
+youyesyet.validation documentation
\ No newline at end of file
diff --git a/dummies/building.png b/dummies/building.png
new file mode 100644
index 0000000..ff8fbc3
Binary files /dev/null and b/dummies/building.png differ
diff --git a/dummies/building.xcf b/dummies/building.xcf
new file mode 100644
index 0000000..4c494bb
Binary files /dev/null and b/dummies/building.xcf differ
diff --git a/dummies/followup.png b/dummies/followup.png
new file mode 100644
index 0000000..768a3d0
Binary files /dev/null and b/dummies/followup.png differ
diff --git a/dummies/followup.xcf b/dummies/followup.xcf
new file mode 100644
index 0000000..36db36e
Binary files /dev/null and b/dummies/followup.xcf differ
diff --git a/dummies/issue.png b/dummies/issue.png
new file mode 100644
index 0000000..7591d5e
Binary files /dev/null and b/dummies/issue.png differ
diff --git a/dummies/issue.xcf b/dummies/issue.xcf
new file mode 100644
index 0000000..0498dd5
Binary files /dev/null and b/dummies/issue.xcf differ
diff --git a/dummies/issues.png b/dummies/issues.png
new file mode 100644
index 0000000..1249700
Binary files /dev/null and b/dummies/issues.png differ
diff --git a/dummies/issues.xcf b/dummies/issues.xcf
new file mode 100644
index 0000000..e6ae96f
Binary files /dev/null and b/dummies/issues.xcf differ
diff --git a/project.clj b/project.clj
index 097ef7d..87b02f2 100644
--- a/project.clj
+++ b/project.clj
@@ -1,4 +1,4 @@
-(defproject youyesyet "0.2.1-SNAPSHOT"
+(defproject youyesyet "0.2.1"
:description "Canvassing tool for referenda"
:license {:name "GNU General Public License,version 2.0 or (at your option) any later version"
@@ -79,7 +79,8 @@
:codox {:metadata {:doc "FIXME: write docs"}
:languages [:clojure :clojurescript]
- :source-paths ["src/clj" "src/cljc" "src/cljs"]}
+ :source-paths ["src/clj" "src/cljc" "src/cljs"]
+ :output-path "documentation"}
:npm {:dependencies [[datatables.net "1.10.19"]
[datatables.net-dt "1.10.19"]
@@ -90,6 +91,9 @@
[simplemde "1.11.2"]]
:root "resources/public/js/lib"}
+ ;; `lein release` doesn't play nice with `git flow release`. Run `lein release` in the
+ ;; `develop` branch, then reset the `master` branch to the release tag.
+
:release-tasks [["vcs" "assert-committed"]
["change" "version" "leiningen.release/bump-version" "release"]
["vcs" "commit"]
diff --git a/resources/public/css/yyy-common.css b/resources/public/css/yyy-common.css
index cbfb6b6..288243a 100644
--- a/resources/public/css/yyy-common.css
+++ b/resources/public/css/yyy-common.css
@@ -399,6 +399,10 @@ th {
padding-bottom: 2em;
}
+ #issue-text {
+ font-size: 200%;
+ }
+
#main-container {
width: 100%;
margin: 0;
diff --git a/resources/public/error/502.html b/resources/public/error/502.html
new file mode 100644
index 0000000..d0f149f
--- /dev/null
+++ b/resources/public/error/502.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Project Hope: 502 Bad Gateway
+
+
+
+
+
+
+
+
+
+
+
+
Sorry, we have a problem
+
+
+
+
+
+
+
+
+
+
+
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.
+