Compare commits

..

61 commits

Author SHA1 Message Date
Simon Brooke ef786024d7 Merge tag 'youyesyet-0.2.2' 2019-05-02 16:35:01 +01:00
Simon Brooke f5282a686b lein-release plugin: preparing 0.2.2 release 2019-05-02 16:29:31 +01:00
Simon Brooke 10bb2416af Ran lein ancient; updated most libraries
Deliberately haven't updated Clojure to 1.10
With leaflet 2.0.1-0 selecting pins doesn't work - so haven't updated
2019-05-02 16:28:58 +01:00
Simon Brooke ed2dc5a7fb Mainly documentation (and related) fixes. 2019-05-02 14:29:56 +01:00
Simon Brooke ae0c383365 Intentionally added documentation to repository
So that it can be automatically deployed to github.io
2019-05-02 11:16:38 +01:00
Simon Brooke 71d8174869 Capitalise names of icons, since that seems to be the decision we've made. 2019-03-13 15:08:26 +00:00
Simon Brooke 3f0491d4c5 Work on auxlists 2018-10-04 22:52:51 +01:00
Simon Brooke 316c57745b Multi-select menus now working
Major change, there may be regressions.
2018-10-04 19:16:24 +01:00
Simon Brooke e411eb38b2 Fixed the 'duplicate canvassers role' issue...
And the cause wasn't at all what I thought.
2018-10-04 10:59:11 +01:00
Simon Brooke 918a8205e9 Updated adl-support dependency to current develop 2018-10-02 18:36:57 +01:00
Simon Brooke 254ee9854a Merge branch 'develop' of github.com:simon-brooke/youyesyet into develop 2018-10-02 15:51:42 +01:00
Simon Brooke b9f685b528 Brought README up to date with current develop
It was badly out of date and actively misleading.
2018-10-02 15:01:57 +01:00
Simon Brooke b21c275157 Ignore new auto-generated file 'cache.clj' 2018-09-30 14:48:20 +01:00
Simon Brooke dcba862b27 Merge branch 'develop' of github.com:simon-brooke/youyesyet into develop 2018-09-23 22:37:19 +01:00
Simon Brooke b94fa5596d Added the wee 'about cookies' feature from Smeagol 2018-09-23 19:17:00 +01:00
Simon Brooke 35920a256b Got the library serving on the right URL! 2018-09-23 18:40:01 +01:00
Simon Brooke e7432e62a4 Upversioned to 0.2.2-SNAPSHOT; fixed map menu 2018-09-23 17:33:59 +01:00
Simon Brooke 23591952b8 Auto-redirect from app to login on 403 2018-09-23 14:19:32 +01:00
Simon Brooke 388bb3fb7d Fixed the pin-image regression 2018-09-23 13:27:56 +01:00
Simon Brooke c575b52d01 Merge remote-tracking branch 'origin/master' into develop 2018-09-23 11:58:22 +01:00
Simon Brooke 57182eb9a6 Much better error page 2018-09-23 11:22:54 +01:00
Simon Brooke c1dcd33d82 Correct the logout error 2018-09-23 10:43:49 +01:00
Simon Brooke af7a8f7c25 Added custom 502 page and restart script
Temporary fix for the crash problem.
2018-09-23 08:54:14 +01:00
Simon Brooke bee88f502d Merge tag 'youyesyet-0.2.1' 2018-09-21 16:29:50 +01:00
Simon Brooke dd471015eb lein-release plugin: bumped version from 0.2.1 to 0.2.2-SNAPSHOT for next development cycle 2018-09-21 16:28:37 +01:00
Simon Brooke 28e00b476b lein-release plugin: preparing 0.2.1 release 2018-09-21 16:27:01 +01:00
Simon Brooke 9076e29eb5 Updated Changelog for 0.2.1 release 2018-09-21 16:26:44 +01:00
Simon Brooke a12330b01e Added codox documentation to repository 2018-09-21 16:18:24 +01:00
Simon Brooke 7380fc51cf Added license clause to project.clj 2018-09-21 15:55:33 +01:00
Simon Brooke 93bd4dbd99 Removed "adl" and "npm" "install" from release-tasks
Since both are now done by "uberjar", which is included in release tasks.
2018-09-21 15:43:39 +01:00
Simon Brooke 2fddb28ae8 Map regression fixed, at least in development builds 2018-09-21 15:34:54 +01:00
Simon Brooke 33686d0db5 Merge remote-tracking branch 'origin/master' 2018-09-20 14:04:29 +01:00
Simon Brooke 84a0f07d5f Merge branch 'release/0.2.0' into develop 2018-09-20 13:57:51 +01:00
Simon Brooke 17baf7e800 lein-release plugin: bumped version from 0.2.0 to 0.2.1-SNAPSHOT for next development cycle 2018-09-20 13:56:57 +01:00
Simon Brooke 7d3d4b5d47 lein-release plugin: preparing 0.2.0 release 2018-09-20 13:55:34 +01:00
Simon Brooke 237a44ff40 Downgraded lein-release to 1.0.5, which I can make work. 2018-09-20 13:55:04 +01:00
Simon Brooke 3f337a4d02 lein adl now works! 2018-09-20 13:53:50 +01:00
Simon Brooke 7a791f50f4 Minor fixes discovered while working on the record loading problem 2018-09-19 15:25:30 +01:00
Simon Brooke 7ce041ad0c Merge branch 'feature/15' into develop 2018-09-17 15:14:52 +01:00
Simon Brooke adcdacc6c1 #15: issue experts workflow now very slick 2018-09-17 15:14:41 +01:00
Simon Brooke 2d4f7a897d Fixed database saving issues. 2018-09-16 16:54:26 +01:00
Simon Brooke dc1195bcbe Fixed the Safari map issue!
Hopefully this fixes it for iOS too!
2018-09-15 09:36:03 +01:00
Simon Brooke 5b78192190 Wee tweek to the method detail prompt 2018-09-15 09:00:50 +01:00
Simon Brooke c22c34ff16 Closer, but still not recording requests. 2018-09-14 22:20:28 +01:00
Simon Brooke 046aa2660f Merge remote-tracking branch 'origin/develop' into develop 2018-09-14 21:22:10 +01:00
Simon Brooke cb9e38cdc8 Some improvement to the followuprequest form
Allowing handling of 'other', to capture electors concerns other than the ones we've selected.
2018-09-14 16:30:35 +01:00
Simon Brooke 863a7e3c0e OK, I'm now happy with the issue experts' workflow. 2018-09-06 16:08:51 +01:00
Simon Brooke 3a8b5be15a Considerable progress on the issue experts workflow, not perfect yet. 2018-09-05 17:05:35 +01:00
Simon Brooke 54ad57349c Considerable progress on the issue experts workflow, not perfect yet. 2018-09-03 12:45:48 +01:00
Simon Brooke 3df314ecfc Merge remote-tracking branch 'origin/develop' into develop 2018-08-29 15:41:34 +01:00
Simon Brooke 53122b69b1 Merge branch 'feature/reflow-app' into develop 2018-08-29 15:41:20 +01:00
Simon Brooke 872fc2b6a8 Merge branch 'feature/reflow-app' into develop 2018-08-29 13:55:57 +01:00
Simon Brooke 439f5a2546 I'm almost satisfied; map not scrolling to device location on startup 2018-08-29 13:55:48 +01:00
Simon Brooke b7c745a0a4 Right, that's enough for today. I understand what's wrong. 2018-08-28 18:44:26 +01:00
Simon Brooke 7724a2514a Stuck with an on-click handler that isn't happening 2018-08-28 14:27:44 +01:00
Simon Brooke 028022fd75 Minor fixes to issue-experts' screens. 2018-08-17 09:52:50 +01:00
Simon Brooke 33cdd67154 Couple of wee fixes to get 0.1.0 to actually deploy correctly 2018-08-11 11:16:51 +01:00
Simon Brooke b275000632 0.1.0 First public alpha 2018-07-18 23:31:28 +01:00
simon 72184d2c3c Merge branch 'release/0.2.0' 2017-07-21 17:47:31 +01:00
simon eab2a35cda Upversion to 0.2.0 2017-07-21 17:47:12 +01:00
simon 02b120fb63 Real screenshots in the userspec. 2017-07-21 17:44:56 +01:00
132 changed files with 4071 additions and 582 deletions

6
.gitignore vendored
View file

@ -43,3 +43,9 @@ resources/sql/youyesyet\.postgres\.sql
/dumps/
youyesyet\.canonical\.adl\.xml
youyesyet\.dump\.20180816
*.tar
src/clj/youyesyet/cache\.clj

10
CHANGELOG.md Normal file
View file

@ -0,0 +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.

View file

@ -43,6 +43,30 @@ If you're thinking of joining in development on this I'd strongly recommend you
You should also read the [User-Oriented Specification](doc/specification/userspec.md) and any other documentation which appears under the *doc/specification* hierarchy.
## Building this
This application is built using [Application Description Language](https://github.com/simon-brooke/adl/). The `adl` pre-processor is run as a prep task to building the `uberjar`, which in turn is preparatory to building the `uberwar`.
This will generate a large number of the source files required by YouYesYet, **including** the database initialisation scripts. These generated source files are not, as a matter of policy, held in the repository.
### What is auto-generated, and how to override it
The following files are generated from the master file `youyesyet.adl.xml`:
* `resources/sql/queries.auto.sql` - [HugSQL](https://www.hugsql.org/) queries for selection, insertion, modification and deletion of records of all entities described in the ADL file.
* `resources/sql/[application-name].postgres.sql` - [Postgres](https://www.postgresql.org/) database initialisation script including tables for all entities, convenience views for all entities, all necessary link tables and referential integrity constraints.
* `resources/templates/auto/*.html` - [Selmer](https://github.com/yogthos/Selmer) templates for each form or list list specified in the ADL file (pages are not yet handled).
* `src/clj/[application-name]/routes/auto.clj` - [Compojure]() routes for each form or list list specified in the ADL file (pages are not yet handled).
* `src/clj/[application-name]/routes/auto-json.clj` - [Compojure]() routes returning JSON responses for each query generated in `resources/sql/queries.auto.sql`.
*You are strongly advised never to edit any of these files*.
* To override any query, add that query to a file `resources/sql/queries.sql`
* To add additional material (for example reference data) to the database initialisation, add it to a separate file or a family of separate files.
* To override any template, copy the template file from `resources/templates/auto/` to `resources/templates/` and edit it there.
* To override any route, write a function of the same name in the namespace `[application-name].routes.manual`.
## Getting the database up
You'll need a file *profiles.clj*, with content similar to the following; it's not in the repository because it contains passwords.
@ -59,11 +83,19 @@ Do get the database initialised, run
createdb youyesyet_dev
I'm no longer using Migratus as I'm using [Application Description Language]()
I'm no longer using Migratus as I'm using [Application Description Language](https://github.com/simon-brooke/adl/)
to generate the majority of the application, and, as changes are made to the application
description, new database schemas are generated. The database initialisation script will
be found at `resources/sql/youyesyet.postgres.sql`. Reference data initialisation scripts
will in due course be stored in the same directory.
be found at `resources/sql/youyesyet.postgres.sql`. Manually maintained overrides are found in
`resources/sql/youyesyet.postgres.overrides.sql`. So to initialise the database, invoke
psql youyesyet_dev < resources/sql/youyesyet.postgres.sql
followed by
psql youyesyet_dev < resources/sql/youyesyet.postgres.overrides.sql
Reference data initialisation scripts will in due course be stored in the same directory.
Once we have a more or less finished application it may be worth going back to
[Migratus](https://github.com/yogthos/migratus); I might have a go at generating migrations from
@ -75,23 +107,33 @@ To run in a dev environment, checkout the *develop* branch
To download and install Javascript delendencies, run
cd youyesyet
lein npm install
To start a development web server for the application, run:
lein run
Then
If you're wanting to work on cljs development, you need two terminal sessions. In one run
lein repl
lein run
Wait for the clojure `user=>` prompt to appear, and enter
as above; in the other, run
(mount/start)
This will get the application running for development; ideally, open a new terminal and invoke
lein figwheel
which will aid in work on the ClojureScript components.
## Running in a production environment
Doesn't really work yet; if you want to try it, see [Bug #36](https://github.com/simon-brooke/youyesyet/issues/36) and check out the associated feature branch.
Either
1. run `lein uberjar` and execute the resulting jar file directly; or
2. run `lein uberwar` and serve the resulting war file from a servlet container.
The [beta production server](https://www.projecthope.scot/) currently runs an uberwar build in Tomcat behind Nginx.
## Working on this project
@ -122,7 +164,7 @@ Note that all tools recommended in this document are free for non-commercial use
### Editors/IDEs
I (Simon) use, like and recommend [LightTable](http://lighttable.com/) as my editor; I used to use Emacs, and there is excellent Clojure tooling for Emacs, but these days Emacs ways of working seem just too far from everything else to be comfortable to me. [NightCode](https://sekao.net/nightcode/) is a lighter-weight Clojure IDE which you may like. There's also [Cursive](https://cursive-ide.com/) but it isn't free and I haven't tried it.
I (Simon) use, like and recommend [LightTable](http://lighttable.com/) as my editor; I used to use Emacs, and there is excellent Clojure tooling for Emacs, but these days Emacs ways of working seem just too far from everything else to be comfortable to me. [NightCode](https://sekao.net/nightcode/) is a lighter-weight Clojure IDE which you may like. There's also [Cursive](https://cursive-ide.com/) but it isn't free and I have so far found it more annoying than helpful; or [Counterclockwise](https://github.com/ccw-ide/ccw) which I don't have recent experience of.
### Git

View file

@ -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:
![Map View](https://raw.githubusercontent.com/simon-brooke/youyesyet/master/dummies/mapview_800.png)
![Map View](https://raw.githubusercontent.com/simon-brooke/youyesyet/master/dummies/mapview.png)
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.
![Building View](https://raw.githubusercontent.com/simon-brooke/youyesyet/master/dummies/building.png)
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:
![Electors View](https://raw.githubusercontent.com/simon-brooke/youyesyet/master/dummies/occupants_800.png)
![Electors View](https://raw.githubusercontent.com/simon-brooke/youyesyet/master/dummies/occupants.png)
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 |
+------------------------------------------------+
![Issues View](https://raw.githubusercontent.com/simon-brooke/youyesyet/master/dummies/issues.png)
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*.
![Issue View](https://raw.githubusercontent.com/simon-brooke/youyesyet/master/dummies/issue.png)
## Followup Request form
@ -132,6 +149,8 @@ The *Followup Request* form is a simple form which allows the canvasser to recor
| |
+------------------------------------------------+
![Followup Request Form](https://raw.githubusercontent.com/simon-brooke/youyesyet/master/dummies/followup.png)
## How Street Canvassers will use the system
Street Canvassers will typically use the system by

19
docs/authorisation.html Normal file

File diff suppressed because one or more lines are too long

14
docs/competitors.html Normal file

File diff suppressed because one or more lines are too long

551
docs/css/default.css Normal file
View file

@ -0,0 +1,551 @@
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 15px;
}
pre, code {
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
font-size: 9pt;
margin: 15px 0;
}
h1 {
font-weight: normal;
font-size: 29px;
margin: 10px 0 2px 0;
padding: 0;
}
h2 {
font-weight: normal;
font-size: 25px;
}
h5.license {
margin: 9px 0 22px 0;
color: #555;
font-weight: normal;
font-size: 12px;
font-style: italic;
}
.document h1, .namespace-index h1 {
font-size: 32px;
margin-top: 12px;
}
#header, #content, .sidebar {
position: fixed;
}
#header {
top: 0;
left: 0;
right: 0;
height: 22px;
color: #f5f5f5;
padding: 5px 7px;
}
#content {
top: 32px;
right: 0;
bottom: 0;
overflow: auto;
background: #fff;
color: #333;
padding: 0 18px;
}
.sidebar {
position: fixed;
top: 32px;
bottom: 0;
overflow: auto;
}
.sidebar.primary {
background: #e2e2e2;
border-right: solid 1px #cccccc;
left: 0;
width: 250px;
}
.sidebar.secondary {
background: #f2f2f2;
border-right: solid 1px #d7d7d7;
left: 251px;
width: 200px;
}
#content.namespace-index, #content.document {
left: 251px;
}
#content.namespace-docs {
left: 452px;
}
#content.document {
padding-bottom: 10%;
}
#header {
background: #3f3f3f;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
z-index: 100;
}
#header h1 {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: lighter;
text-shadow: -1px -1px 0px #333;
}
#header h1 .project-version {
font-weight: normal;
}
.project-version {
padding-left: 0.15em;
}
#header a, .sidebar a {
display: block;
text-decoration: none;
}
#header a {
color: #f5f5f5;
}
.sidebar a {
color: #333;
}
#header h2 {
float: right;
font-size: 9pt;
font-weight: normal;
margin: 4px 3px;
padding: 0;
color: #bbb;
}
#header h2 a {
display: inline;
}
.sidebar h3 {
margin: 0;
padding: 10px 13px 0 13px;
font-size: 19px;
font-weight: lighter;
}
.sidebar h3 a {
color: #444;
}
.sidebar h3.no-link {
color: #636363;
}
.sidebar ul {
padding: 7px 0 6px 0;
margin: 0;
}
.sidebar ul.index-link {
padding-bottom: 4px;
}
.sidebar li {
display: block;
vertical-align: middle;
}
.sidebar li a, .sidebar li .no-link {
border-left: 3px solid transparent;
padding: 0 10px;
white-space: nowrap;
}
.sidebar li .no-link {
display: block;
color: #777;
font-style: italic;
}
.sidebar li .inner {
display: inline-block;
padding-top: 7px;
height: 24px;
}
.sidebar li a, .sidebar li .tree {
height: 31px;
}
.depth-1 .inner { padding-left: 2px; }
.depth-2 .inner { padding-left: 6px; }
.depth-3 .inner { padding-left: 20px; }
.depth-4 .inner { padding-left: 34px; }
.depth-5 .inner { padding-left: 48px; }
.depth-6 .inner { padding-left: 62px; }
.sidebar li .tree {
display: block;
float: left;
position: relative;
top: -10px;
margin: 0 4px 0 0;
padding: 0;
}
.sidebar li.depth-1 .tree {
display: none;
}
.sidebar li .tree .top, .sidebar li .tree .bottom {
display: block;
margin: 0;
padding: 0;
width: 7px;
}
.sidebar li .tree .top {
border-left: 1px solid #aaa;
border-bottom: 1px solid #aaa;
height: 19px;
}
.sidebar li .tree .bottom {
height: 22px;
}
.sidebar li.branch .tree .bottom {
border-left: 1px solid #aaa;
}
.sidebar.primary li.current a {
border-left: 3px solid #a33;
color: #a33;
}
.sidebar.secondary li.current a {
border-left: 3px solid #33a;
color: #33a;
}
.namespace-index h2 {
margin: 30px 0 0 0;
}
.namespace-index h3 {
font-size: 16px;
font-weight: bold;
margin-bottom: 0;
}
.namespace-index .topics {
padding-left: 30px;
margin: 11px 0 0 0;
}
.namespace-index .topics li {
padding: 5px 0;
}
.namespace-docs h3 {
font-size: 18px;
font-weight: bold;
}
.public h3 {
margin: 0;
float: left;
}
.usage {
clear: both;
}
.public {
margin: 0;
border-top: 1px solid #e0e0e0;
padding-top: 14px;
padding-bottom: 6px;
}
.public:last-child {
margin-bottom: 20%;
}
.members .public:last-child {
margin-bottom: 0;
}
.members {
margin: 15px 0;
}
.members h4 {
color: #555;
font-weight: normal;
font-variant: small-caps;
margin: 0 0 5px 0;
}
.members .inner {
padding-top: 5px;
padding-left: 12px;
margin-top: 2px;
margin-left: 7px;
border-left: 1px solid #bbb;
}
#content .members .inner h3 {
font-size: 12pt;
}
.members .public {
border-top: none;
margin-top: 0;
padding-top: 6px;
padding-bottom: 0;
}
.members .public:first-child {
padding-top: 0;
}
h4.type,
h4.dynamic,
h4.added,
h4.deprecated {
float: left;
margin: 3px 10px 15px 0;
font-size: 15px;
font-weight: bold;
font-variant: small-caps;
}
.public h4.type,
.public h4.dynamic,
.public h4.added,
.public h4.deprecated {
font-size: 13px;
font-weight: bold;
margin: 3px 0 0 10px;
}
.members h4.type,
.members h4.added,
.members h4.deprecated {
margin-top: 1px;
}
h4.type {
color: #717171;
}
h4.dynamic {
color: #9933aa;
}
h4.added {
color: #508820;
}
h4.deprecated {
color: #880000;
}
.namespace {
margin-bottom: 30px;
}
.namespace:last-child {
margin-bottom: 10%;
}
.index {
padding: 0;
font-size: 80%;
margin: 15px 0;
line-height: 16px;
}
.index * {
display: inline;
}
.index p {
padding-right: 3px;
}
.index li {
padding-right: 5px;
}
.index ul {
padding-left: 0;
}
.type-sig {
clear: both;
color: #088;
}
.type-sig pre {
padding-top: 10px;
margin: 0;
}
.usage code {
display: block;
color: #008;
margin: 2px 0;
}
.usage code:first-child {
padding-top: 10px;
}
p {
margin: 15px 0;
}
.public p:first-child, .public pre.plaintext {
margin-top: 12px;
}
.doc {
margin: 0 0 26px 0;
clear: both;
}
.public .doc {
margin: 0;
}
.namespace-index .doc {
margin-bottom: 20px;
}
.namespace-index .namespace .doc {
margin-bottom: 10px;
}
.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td {
line-height: 22px;
}
.markdown li {
padding: 2px 0;
}
.markdown h2 {
font-weight: normal;
font-size: 25px;
margin: 30px 0 10px 0;
}
.markdown h3 {
font-weight: normal;
font-size: 20px;
margin: 30px 0 0 0;
}
.markdown h4 {
font-size: 15px;
margin: 22px 0 -4px 0;
}
.doc, .public, .namespace .index {
max-width: 680px;
overflow-x: visible;
}
.markdown pre > code {
display: block;
padding: 10px;
}
.markdown pre > code, .src-link a {
border: 1px solid #e4e4e4;
border-radius: 2px;
}
.markdown code:not(.hljs), .src-link a {
background: #f6f6f6;
}
pre.deps {
display: inline-block;
margin: 0 10px;
border: 1px solid #e4e4e4;
border-radius: 2px;
padding: 10px;
background-color: #f6f6f6;
}
.markdown hr {
border-style: solid;
border-top: none;
color: #ccc;
}
.doc ul, .doc ol {
padding-left: 30px;
}
.doc table {
border-collapse: collapse;
margin: 0 10px;
}
.doc table td, .doc table th {
border: 1px solid #dddddd;
padding: 4px 6px;
}
.doc table th {
background: #f2f2f2;
}
.doc dl {
margin: 0 10px 20px 10px;
}
.doc dl dt {
font-weight: bold;
margin: 0;
padding: 3px 0;
border-bottom: 1px solid #ddd;
}
.doc dl dd {
padding: 5px 0;
margin: 0 0 5px 10px;
}
.doc abbr {
border-bottom: 1px dotted #333;
font-variant: none;
cursor: help;
}
.src-link {
margin-bottom: 15px;
}
.src-link a {
font-size: 70%;
padding: 1px 4px;
text-decoration: none;
color: #5555bb;
}

97
docs/css/highlight.css Normal file
View file

@ -0,0 +1,97 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}
.hljs-comment,
.hljs-quote {
color: #998;
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #333;
font-weight: bold;
}
.hljs-number,
.hljs-literal,
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: #008080;
}
.hljs-string,
.hljs-doctag {
color: #d14;
}
.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #900;
font-weight: bold;
}
.hljs-subst {
font-weight: normal;
}
.hljs-type,
.hljs-class .hljs-title {
color: #458;
font-weight: bold;
}
.hljs-tag,
.hljs-name,
.hljs-attribute {
color: #000080;
font-weight: normal;
}
.hljs-regexp,
.hljs-link {
color: #009926;
}
.hljs-symbol,
.hljs-bullet {
color: #990073;
}
.hljs-built_in,
.hljs-builtin-name {
color: #0086b3;
}
.hljs-meta {
color: #999;
font-weight: bold;
}
.hljs-deletion {
background: #fdd;
}
.hljs-addition {
background: #dfd;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

173
docs/database.html Normal file

File diff suppressed because one or more lines are too long

3
docs/index.html Normal file

File diff suppressed because one or more lines are too long

2
docs/js/highlight.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4
docs/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

112
docs/js/page_effects.js Normal file
View file

@ -0,0 +1,112 @@
function visibleInParent(element) {
var position = $(element).position().top
return position > -50 && position < ($(element).offsetParent().height() - 50)
}
function hasFragment(link, fragment) {
return $(link).attr("href").indexOf("#" + fragment) != -1
}
function findLinkByFragment(elements, fragment) {
return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first()
}
function scrollToCurrentVarLink(elements) {
var elements = $(elements);
var parent = elements.offsetParent();
if (elements.length == 0) return;
var top = elements.first().position().top;
var bottom = elements.last().position().top + elements.last().height();
if (top >= 0 && bottom <= parent.height()) return;
if (top < 0) {
parent.scrollTop(parent.scrollTop() + top);
}
else if (bottom > parent.height()) {
parent.scrollTop(parent.scrollTop() + bottom - parent.height());
}
}
function setCurrentVarLink() {
$('.secondary a').parent().removeClass('current')
$('.anchor').
filter(function(index) { return visibleInParent(this) }).
each(function(index, element) {
findLinkByFragment(".secondary a", element.id).
parent().
addClass('current')
});
scrollToCurrentVarLink('.secondary .current');
}
var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }())
function scrollPositionId(element) {
var directory = window.location.href.replace(/[^\/]+\.html$/, '')
return 'scroll::' + $(element).attr('id') + '::' + directory
}
function storeScrollPosition(element) {
if (!hasStorage) return;
localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft())
localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop())
}
function recallScrollPosition(element) {
if (!hasStorage) return;
$(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x"))
$(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y"))
}
function persistScrollPosition(element) {
recallScrollPosition(element)
$(element).scroll(function() { storeScrollPosition(element) })
}
function sidebarContentWidth(element) {
var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() })
return Math.max.apply(Math, widths)
}
function calculateSize(width, snap, margin, minimum) {
if (width == 0) {
return 0
}
else {
return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2))
}
}
function resizeSidebars() {
var primaryWidth = sidebarContentWidth('.primary')
var secondaryWidth = 0
if ($('.secondary').length != 0) {
secondaryWidth = sidebarContentWidth('.secondary')
}
// snap to grid
primaryWidth = calculateSize(primaryWidth, 32, 13, 160)
secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160)
$('.primary').css('width', primaryWidth)
$('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1)
if (secondaryWidth > 0) {
$('#content').css('left', primaryWidth + secondaryWidth + 2)
}
else {
$('#content').css('left', primaryWidth + 1)
}
}
$(window).ready(resizeSidebars)
$(window).ready(setCurrentVarLink)
$(window).ready(function() { persistScrollPosition('.primary')})
$(window).ready(function() {
$('#content').scroll(setCurrentVarLink)
$(window).resize(setCurrentVarLink)
})

108
docs/scaling.html Normal file

File diff suppressed because one or more lines are too long

151
docs/userspec.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,551 @@
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 15px;
}
pre, code {
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
font-size: 9pt;
margin: 15px 0;
}
h1 {
font-weight: normal;
font-size: 29px;
margin: 10px 0 2px 0;
padding: 0;
}
h2 {
font-weight: normal;
font-size: 25px;
}
h5.license {
margin: 9px 0 22px 0;
color: #555;
font-weight: normal;
font-size: 12px;
font-style: italic;
}
.document h1, .namespace-index h1 {
font-size: 32px;
margin-top: 12px;
}
#header, #content, .sidebar {
position: fixed;
}
#header {
top: 0;
left: 0;
right: 0;
height: 22px;
color: #f5f5f5;
padding: 5px 7px;
}
#content {
top: 32px;
right: 0;
bottom: 0;
overflow: auto;
background: #fff;
color: #333;
padding: 0 18px;
}
.sidebar {
position: fixed;
top: 32px;
bottom: 0;
overflow: auto;
}
.sidebar.primary {
background: #e2e2e2;
border-right: solid 1px #cccccc;
left: 0;
width: 250px;
}
.sidebar.secondary {
background: #f2f2f2;
border-right: solid 1px #d7d7d7;
left: 251px;
width: 200px;
}
#content.namespace-index, #content.document {
left: 251px;
}
#content.namespace-docs {
left: 452px;
}
#content.document {
padding-bottom: 10%;
}
#header {
background: #3f3f3f;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
z-index: 100;
}
#header h1 {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: lighter;
text-shadow: -1px -1px 0px #333;
}
#header h1 .project-version {
font-weight: normal;
}
.project-version {
padding-left: 0.15em;
}
#header a, .sidebar a {
display: block;
text-decoration: none;
}
#header a {
color: #f5f5f5;
}
.sidebar a {
color: #333;
}
#header h2 {
float: right;
font-size: 9pt;
font-weight: normal;
margin: 4px 3px;
padding: 0;
color: #bbb;
}
#header h2 a {
display: inline;
}
.sidebar h3 {
margin: 0;
padding: 10px 13px 0 13px;
font-size: 19px;
font-weight: lighter;
}
.sidebar h3 a {
color: #444;
}
.sidebar h3.no-link {
color: #636363;
}
.sidebar ul {
padding: 7px 0 6px 0;
margin: 0;
}
.sidebar ul.index-link {
padding-bottom: 4px;
}
.sidebar li {
display: block;
vertical-align: middle;
}
.sidebar li a, .sidebar li .no-link {
border-left: 3px solid transparent;
padding: 0 10px;
white-space: nowrap;
}
.sidebar li .no-link {
display: block;
color: #777;
font-style: italic;
}
.sidebar li .inner {
display: inline-block;
padding-top: 7px;
height: 24px;
}
.sidebar li a, .sidebar li .tree {
height: 31px;
}
.depth-1 .inner { padding-left: 2px; }
.depth-2 .inner { padding-left: 6px; }
.depth-3 .inner { padding-left: 20px; }
.depth-4 .inner { padding-left: 34px; }
.depth-5 .inner { padding-left: 48px; }
.depth-6 .inner { padding-left: 62px; }
.sidebar li .tree {
display: block;
float: left;
position: relative;
top: -10px;
margin: 0 4px 0 0;
padding: 0;
}
.sidebar li.depth-1 .tree {
display: none;
}
.sidebar li .tree .top, .sidebar li .tree .bottom {
display: block;
margin: 0;
padding: 0;
width: 7px;
}
.sidebar li .tree .top {
border-left: 1px solid #aaa;
border-bottom: 1px solid #aaa;
height: 19px;
}
.sidebar li .tree .bottom {
height: 22px;
}
.sidebar li.branch .tree .bottom {
border-left: 1px solid #aaa;
}
.sidebar.primary li.current a {
border-left: 3px solid #a33;
color: #a33;
}
.sidebar.secondary li.current a {
border-left: 3px solid #33a;
color: #33a;
}
.namespace-index h2 {
margin: 30px 0 0 0;
}
.namespace-index h3 {
font-size: 16px;
font-weight: bold;
margin-bottom: 0;
}
.namespace-index .topics {
padding-left: 30px;
margin: 11px 0 0 0;
}
.namespace-index .topics li {
padding: 5px 0;
}
.namespace-docs h3 {
font-size: 18px;
font-weight: bold;
}
.public h3 {
margin: 0;
float: left;
}
.usage {
clear: both;
}
.public {
margin: 0;
border-top: 1px solid #e0e0e0;
padding-top: 14px;
padding-bottom: 6px;
}
.public:last-child {
margin-bottom: 20%;
}
.members .public:last-child {
margin-bottom: 0;
}
.members {
margin: 15px 0;
}
.members h4 {
color: #555;
font-weight: normal;
font-variant: small-caps;
margin: 0 0 5px 0;
}
.members .inner {
padding-top: 5px;
padding-left: 12px;
margin-top: 2px;
margin-left: 7px;
border-left: 1px solid #bbb;
}
#content .members .inner h3 {
font-size: 12pt;
}
.members .public {
border-top: none;
margin-top: 0;
padding-top: 6px;
padding-bottom: 0;
}
.members .public:first-child {
padding-top: 0;
}
h4.type,
h4.dynamic,
h4.added,
h4.deprecated {
float: left;
margin: 3px 10px 15px 0;
font-size: 15px;
font-weight: bold;
font-variant: small-caps;
}
.public h4.type,
.public h4.dynamic,
.public h4.added,
.public h4.deprecated {
font-size: 13px;
font-weight: bold;
margin: 3px 0 0 10px;
}
.members h4.type,
.members h4.added,
.members h4.deprecated {
margin-top: 1px;
}
h4.type {
color: #717171;
}
h4.dynamic {
color: #9933aa;
}
h4.added {
color: #508820;
}
h4.deprecated {
color: #880000;
}
.namespace {
margin-bottom: 30px;
}
.namespace:last-child {
margin-bottom: 10%;
}
.index {
padding: 0;
font-size: 80%;
margin: 15px 0;
line-height: 16px;
}
.index * {
display: inline;
}
.index p {
padding-right: 3px;
}
.index li {
padding-right: 5px;
}
.index ul {
padding-left: 0;
}
.type-sig {
clear: both;
color: #088;
}
.type-sig pre {
padding-top: 10px;
margin: 0;
}
.usage code {
display: block;
color: #008;
margin: 2px 0;
}
.usage code:first-child {
padding-top: 10px;
}
p {
margin: 15px 0;
}
.public p:first-child, .public pre.plaintext {
margin-top: 12px;
}
.doc {
margin: 0 0 26px 0;
clear: both;
}
.public .doc {
margin: 0;
}
.namespace-index .doc {
margin-bottom: 20px;
}
.namespace-index .namespace .doc {
margin-bottom: 10px;
}
.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td {
line-height: 22px;
}
.markdown li {
padding: 2px 0;
}
.markdown h2 {
font-weight: normal;
font-size: 25px;
margin: 30px 0 10px 0;
}
.markdown h3 {
font-weight: normal;
font-size: 20px;
margin: 30px 0 0 0;
}
.markdown h4 {
font-size: 15px;
margin: 22px 0 -4px 0;
}
.doc, .public, .namespace .index {
max-width: 680px;
overflow-x: visible;
}
.markdown pre > code {
display: block;
padding: 10px;
}
.markdown pre > code, .src-link a {
border: 1px solid #e4e4e4;
border-radius: 2px;
}
.markdown code:not(.hljs), .src-link a {
background: #f6f6f6;
}
pre.deps {
display: inline-block;
margin: 0 10px;
border: 1px solid #e4e4e4;
border-radius: 2px;
padding: 10px;
background-color: #f6f6f6;
}
.markdown hr {
border-style: solid;
border-top: none;
color: #ccc;
}
.doc ul, .doc ol {
padding-left: 30px;
}
.doc table {
border-collapse: collapse;
margin: 0 10px;
}
.doc table td, .doc table th {
border: 1px solid #dddddd;
padding: 4px 6px;
}
.doc table th {
background: #f2f2f2;
}
.doc dl {
margin: 0 10px 20px 10px;
}
.doc dl dt {
font-weight: bold;
margin: 0;
padding: 3px 0;
border-bottom: 1px solid #ddd;
}
.doc dl dd {
padding: 5px 0;
margin: 0 0 5px 10px;
}
.doc abbr {
border-bottom: 1px dotted #333;
font-variant: none;
cursor: help;
}
.src-link {
margin-bottom: 15px;
}
.src-link a {
font-size: 70%;
padding: 1px 4px;
text-decoration: none;
color: #5555bb;
}

View file

@ -0,0 +1,97 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}
.hljs-comment,
.hljs-quote {
color: #998;
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #333;
font-weight: bold;
}
.hljs-number,
.hljs-literal,
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: #008080;
}
.hljs-string,
.hljs-doctag {
color: #d14;
}
.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #900;
font-weight: bold;
}
.hljs-subst {
font-weight: normal;
}
.hljs-type,
.hljs-class .hljs-title {
color: #458;
font-weight: bold;
}
.hljs-tag,
.hljs-name,
.hljs-attribute {
color: #000080;
font-weight: normal;
}
.hljs-regexp,
.hljs-link {
color: #009926;
}
.hljs-symbol,
.hljs-bullet {
color: #990073;
}
.hljs-built_in,
.hljs-builtin-name {
color: #0086b3;
}
.hljs-meta {
color: #999;
font-weight: bold;
}
.hljs-deletion {
background: #fdd;
}
.hljs-addition {
background: #dfd;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

173
documentation/database.html Normal file

File diff suppressed because one or more lines are too long

3
documentation/index.html Normal file

File diff suppressed because one or more lines are too long

2
documentation/js/highlight.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4
documentation/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,112 @@
function visibleInParent(element) {
var position = $(element).position().top
return position > -50 && position < ($(element).offsetParent().height() - 50)
}
function hasFragment(link, fragment) {
return $(link).attr("href").indexOf("#" + fragment) != -1
}
function findLinkByFragment(elements, fragment) {
return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first()
}
function scrollToCurrentVarLink(elements) {
var elements = $(elements);
var parent = elements.offsetParent();
if (elements.length == 0) return;
var top = elements.first().position().top;
var bottom = elements.last().position().top + elements.last().height();
if (top >= 0 && bottom <= parent.height()) return;
if (top < 0) {
parent.scrollTop(parent.scrollTop() + top);
}
else if (bottom > parent.height()) {
parent.scrollTop(parent.scrollTop() + bottom - parent.height());
}
}
function setCurrentVarLink() {
$('.secondary a').parent().removeClass('current')
$('.anchor').
filter(function(index) { return visibleInParent(this) }).
each(function(index, element) {
findLinkByFragment(".secondary a", element.id).
parent().
addClass('current')
});
scrollToCurrentVarLink('.secondary .current');
}
var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }())
function scrollPositionId(element) {
var directory = window.location.href.replace(/[^\/]+\.html$/, '')
return 'scroll::' + $(element).attr('id') + '::' + directory
}
function storeScrollPosition(element) {
if (!hasStorage) return;
localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft())
localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop())
}
function recallScrollPosition(element) {
if (!hasStorage) return;
$(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x"))
$(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y"))
}
function persistScrollPosition(element) {
recallScrollPosition(element)
$(element).scroll(function() { storeScrollPosition(element) })
}
function sidebarContentWidth(element) {
var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() })
return Math.max.apply(Math, widths)
}
function calculateSize(width, snap, margin, minimum) {
if (width == 0) {
return 0
}
else {
return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2))
}
}
function resizeSidebars() {
var primaryWidth = sidebarContentWidth('.primary')
var secondaryWidth = 0
if ($('.secondary').length != 0) {
secondaryWidth = sidebarContentWidth('.secondary')
}
// snap to grid
primaryWidth = calculateSize(primaryWidth, 32, 13, 160)
secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160)
$('.primary').css('width', primaryWidth)
$('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1)
if (secondaryWidth > 0) {
$('#content').css('left', primaryWidth + secondaryWidth + 2)
}
else {
$('#content').css('left', primaryWidth + 1)
}
}
$(window).ready(resizeSidebars)
$(window).ready(setCurrentVarLink)
$(window).ready(function() { persistScrollPosition('.primary')})
$(window).ready(function() {
$('#content').scroll(setCurrentVarLink)
$(window).resize(setCurrentVarLink)
})

108
documentation/scaling.html Normal file

File diff suppressed because one or more lines are too long

151
documentation/userspec.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
dummies/building.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
dummies/building.xcf Normal file

Binary file not shown.

BIN
dummies/followup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
dummies/followup.xcf Normal file

Binary file not shown.

BIN
dummies/issue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
dummies/issue.xcf Normal file

Binary file not shown.

BIN
dummies/issues.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
dummies/issues.xcf Normal file

Binary file not shown.

View file

@ -1,50 +1,58 @@
(defproject youyesyet "0.2.0-SNAPSHOT"
(defproject youyesyet "0.2.2"
:description "Canvassing tool for referenda"
:license {:name "GNU General Public License,version 2.0 or (at your option) any later version"
:url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"}
:url "https://github.com/simon-brooke/youyesyet"
:dependencies [[adl-support "0.1.4-SNAPSHOT"]
:dependencies [[adl-support "0.1.6"]
[bouncer "1.0.1"]
[ch.qos.logback/logback-classic "1.2.3"]
[clj-oauth "1.5.5"]
[cljsjs/react-leaflet "1.6.5-0"]
[cljs-ajax "0.7.4"]
;; [cljsjs/react-leaflet "2.0.1-0"] is available but doesn't seem to work fully
[cljs-ajax "0.8.0"]
[com.andrewmcveigh/cljs-time "0.5.2"]
[clojure.java-time "0.3.2"]
[com.cemerick/url "0.1.1"]
[compojure "1.6.1"]
[conman "0.8.2"]
[cprop "0.1.11"]
[conman "0.8.3"]
[cprop "0.1.13"]
[day8.re-frame/http-fx "0.1.6"]
[korma "0.4.3"]
[lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]]
[luminus/ring-ttl-session "0.3.2"]
[luminus-nrepl "0.1.4"]
[luminus-migrations "0.5.2"]
[luminus-immutant "0.2.4"]
[markdown-clj "1.0.2"]
[luminus-nrepl "0.1.6"]
[luminus-migrations "0.6.5"]
[luminus-immutant "0.2.5"]
[markdown-clj "1.0.8"]
[metosin/compojure-api "1.1.12"]
[metosin/ring-http-response "0.9.0"]
[migratus "1.0.8"]
[mount "0.1.12"]
[metosin/ring-http-response "0.9.1"]
[migratus "1.2.3"]
[mount "0.1.16"]
[org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.10.339" :scope "provided"]
[org.clojure/clojurescript "1.10.520" :scope "provided"]
[org.clojure/core.memoize "0.7.1"]
;;[org.clojure/spec.alpha "0.2.168"]
[org.clojure/tools.cli "0.3.7"]
[org.clojure/tools.cli "0.4.2"]
[org.clojure/tools.logging "0.4.1"]
[org.postgresql/postgresql "42.2.4"]
[org.webjars/bootstrap "4.1.2"]
[org.webjars/font-awesome "5.1.0"]
[org.postgresql/postgresql "42.2.5"]
[org.webjars/bootstrap "4.3.1"]
[org.webjars/font-awesome "5.8.1"]
[org.webjars.bower/tether "1.4.4"]
[re-frame "0.10.5"]
[postgre-types "0.0.4"]
[re-frame "0.10.6"]
[reagent "0.8.1"]
[reagent-utils "0.3.1"]
[ring-middleware-format "0.7.2"]
[reagent-utils "0.3.2"]
[ring-middleware-format "0.7.4"]
[ring/ring-defaults "0.3.2"]
[ring/ring-servlet "1.6.3"]
[ring/ring-servlet "1.7.1"]
[ring-webjars "0.2.0"]
[secretary "1.2.3"]
[selmer "1.11.8"]]
[selmer "1.12.12"]]
:deploy-repositories [["releases" :clojars]
["snapshots" :clojars]]
:min-lein-version "2.0.0"
@ -56,23 +64,27 @@
:main ^:skip-aot youyesyet.core
:migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")}
:plugins [;;[lein-adl ["0.1.2"]]
:plugins [[lein-adl "0.1.7"]
[lein-cljsbuild "1.1.7"]
[lein-codox "0.10.4"]
[lein-codox "0.10.7-multilang"]
[lein-cprop "1.0.3"]
[lein-kibit "0.1.6"]
[lein-less "1.7.5"]
[lein-npm "0.6.2"]
[lein-release "1.1.3"]
[lein-release "1.0.5"]
[lein-uberwar "0.2.0"]
[migratus-lein "0.5.9"]
[org.clojars.punkisdead/lein-cucumber "1.0.7"]]
:cucumber-feature-paths ["test/clj/features"]
:codox {:metadata {:doc "FIXME: write docs"}
:codox {:metadata {:doc "**TODO**: write docs"
:doc/format :markdown}
:languages [:clojure :clojurescript]
:source-paths ["src/clj" "src/cljc" "src/cljs"]}
:source-paths ["src/clj" "src/cljc" "src/cljs"]
:source-uri "https://github.com/simon-brooke/youyesyet/blob/master/{filepath}#L{line}"
:output-path "docs"}
:npm {:dependencies [[datatables.net "1.10.19"]
[datatables.net-dt "1.10.19"]
@ -83,15 +95,15 @@
[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"]
["adl"]
["vcs" "commit"]
;; ["vcs" "tag"] -- not working, problems with secret key
["clean"]
["npm" "install"]
["uberjar"]
[uberwar]
["uberwar"]
["docker" "build"]
["docker" "push"]
["change" "version" "leiningen.release/bump-version"]
@ -112,9 +124,10 @@
:profiles {:uberjar {:omit-source true
:prep-tasks [;;"adl"
"compile"
["cljsbuild" "once" "min"]]
:prep-tasks ["adl"
"compile"
["npm" "install"]
["cljsbuild" "once" "min"]]
:cljsbuild
{:builds
{:min
@ -135,21 +148,21 @@
:test [:project/dev :project/test :profiles/test]
:project/dev {:dependencies [[prone "1.1.4"]
[ring/ring-mock "0.3.2"]
[ring/ring-devel "1.6.3"]
:project/dev {:dependencies [[prone "1.6.3"]
[ring/ring-mock "0.4.0"]
[ring/ring-devel "1.7.1"]
[org.webjars/webjars-locator-jboss-vfs "0.1.0"]
[luminus-immutant "0.2.4"]
[pjstadig/humane-test-output "0.8.3"]
[luminus-immutant "0.2.5"]
[pjstadig/humane-test-output "0.9.0"]
[binaryage/devtools "0.9.10"]
[com.cemerick/piggieback "0.2.2"]
[directory-naming/naming-java "0.8"]
[doo "0.1.10"]
[figwheel-sidecar "0.5.16"]]
[doo "0.1.11"]
[figwheel-sidecar "0.5.18"]]
:plugins [[com.jakemccrary/lein-test-refresh "0.23.0"]
[lein-doo "0.1.10"]
[lein-figwheel "0.5.16"]
[org.clojure/clojurescript "1.9.495"]]
[org.clojure/clojurescript "1.10.520"]]
:cljsbuild {:builds
{:app
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]

View file

@ -45,7 +45,6 @@ h1 {
width: 100%;
_position: absolute;
_top: expression(document.documentElement.scrollTop);
z-index: 149;
background:rgba(7,27,51,0.8);
}
@ -57,6 +56,7 @@ h1 {
#nav-menu {
margin: 0;
padding: 0;
z-index: 02110-1301;
}
#nav menu li {
@ -103,6 +103,7 @@ h1 {
#nav:hover #nav-menu {
display: block;
list-style-type: none;
z-index: 148;
width: 100%;
}
@ -141,7 +142,7 @@ h1 {
margin: 0;
padding: 0;
position: fixed;
z-index: 149;
z-index: 600;
color: silver;
background:rgba(40,40,40,0.9);
}
@ -149,6 +150,7 @@ h1 {
#nav:hover #nav-menu {
display: block;
list-style-type: none;
z-index: 500;
width: 100%;
}

View file

@ -212,7 +212,7 @@ th {
width: 30%;
float: right;
position: fixed;
bottom: 3.5em;
bottom: 2em;
right: 0;
z-index: 175;
background: transparent;
@ -362,7 +362,6 @@ th {
/* content of the current page in the Wiki - editable, provided by users. Within main-container */
#content {
border: thin solid silver;
width: 80%;
float: right;
padding-bottom: 5em;
@ -396,11 +395,14 @@ th {
/* content of the current in the Wiki - editable, provided by users. Within main-container */
#content {
border: thin solid silver;
width: 100%;
padding-bottom: 2em;
}
#issue-text {
font-size: 200%;
}
#main-container {
width: 100%;
margin: 0;

View file

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html>
<head>
<!-- a page which can be served as static (by nginx) if Tomcat has crashed -->
<!-- head: if you want entire custom head content, override this block. -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="referrer" content="same-origin">
<link href="../css/yyy-common.css" rel="stylesheet" type="text/css" />
<link href="../css/yyy-site.css" rel="stylesheet" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Archivo+Black|Archivo+Narrow" rel="stylesheet"/>
<title>Project Hope: 502 Bad Gateway</title>
<!-- extra-head: put any additional markup to go into the head of your document into this block -->
<!-- e.g. script tags, link tags -->
</head>
<body>
<!-- whole-page: if you want an entire custom page layout, override this block. -->
<header>
<h1>Sorry, we have a problem</h1>
</header>
<div id="main-container" class="container">
<h2><span class="text-danger">Error: 502 Bad Gateway</span></h2>
<p>
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.
</p>
<p>
The application will restart automatically
within five minutes, please take a short break.
</p>
</div>
<br clear="both"/>
</div>
<!-- foot: override this block if you don't want the standard footer -->
<footer>
<div id="credits">
<div>
<img height="16" width="16" alt="Clojure" src="/img/credits/luminus-logo.png"/>Built with <a href="http://www.luminusweb.net/">LuminusWeb</a> ||
<img height="16" width="16" alt="Clojure" src="/img/credits/clojure-icon.gif"/> Powered by <a href="http://clojure.org">Clojure</a> ||
<img height="16" width="16" alt="GitHub" src="/img/credits/github-logo-transparent.png"/>Find me/fork me on <a href="https://github.com/simon-brooke/youyesyet">Github</a> ||
<img height="16" width="16" alt="Free Software Foundation" src="/img/credits/gnu.small.png"/>Licensed under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU General Public License version 2.0</a>
</div>
</div>
</footer>
</body>
</html>

View file

@ -43,7 +43,7 @@ SELECT DISTINCT request.*,
addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS visit_id_expanded,
request.issue_id as issue_id_expanded,
request.method_id AS method_id_expanded,
visits.date
visits.date AS raised
FROM followuprequests as request,
ln_experts_issues_canvassers as expertise,
canvassers as experts,
@ -53,12 +53,13 @@ FROM followuprequests as request,
where not exists (select * from followupactions as action
where action.request_id = request.id
and action.closed = true)
and request.locked_by is null
and request.elector_id = electors.id
and request.visit_id = visits.id
and visits.address_id = addresses.id
and request.issue_id = expertise.issue_id
and expertise.canvasser_id = :expert
ORDER BY visits.date desc
ORDER BY raised
--:name get-last-visit-by-canvasser :? :1
--:doc returns the most recent visit record of the canvasser with the specified `:id`
@ -67,13 +68,20 @@ WHERE canvasser_id = :id
ORDER BY date desc
LIMIT 1
-- I don't know why this next one isn't autogenerating, but it isn't and it's critical.
--:name get-locality-for-visit :? :1
--:doc returns the locality of the address of this visit
SELECT addresses.locality
FROM addresses, visits
WHERE visits.address_id = addresses.id
AND visits.id = :id
-- :name list-roles-by-canvasser :? :*
-- :doc links all existing canvasser records related to a given role
SELECT roles.*
FROM roles, ln_canvassers_roles
WHERE roles.id = ln_canvassers_roles.role_id
AND ln_canvassers_roles.canvasser_id = :id
ORDER BY roles.name,
roles.id
-- :name list-elector-intentions :? :*
-- :doc short form of `list-intentions-by-elector`, returning far less data, for use in `youyesyet.routes.rest/get-local-data`, q.v.
-- TODO: should be limited to visits in the past 24 hours, to prevent the app being
-- used to harrass NO voters. See https://github.com/simon-brooke/youyesyet/issues/58
SELECT intentions.id, intentions.option_id, visits.date
FROM intentions, visits
WHERE intentions.visit_id = visits.id
AND intentions.elector_id = :id
ORDER BY visits.date DESC

View file

@ -0,0 +1,53 @@
------------------------------------------------------------------------
-- User `youyesyet` (the app, and less secure parts of the site)
-- must have the permissions of `canvassers`.
------------------------------------------------------------------------
DO
$do$
BEGIN
IF NOT EXISTS (
SELECT -- SELECT list can stay empty for this
FROM pg_catalog.pg_roles
WHERE rolname = 'youyesyet') THEN
CREATE ROLE youyesyet LOGIN PASSWORD 'thisisnotsecure';
END IF;
END
$do$;
grant canvassers to youyesyet;
------------------------------------------------------------------------
-- convenience view lv_followupactions of entity followupactions for
-- lists, et cetera
-- ADL is not yet correctly chaining tables when generating convenience
-- views, so the auto-generated convenience view is a horrible
-- cross-product join
------------------------------------------------------------------------
DROP VIEW lv_followupactions;
CREATE VIEW lv_followupactions AS
SELECT electors.name ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| visits.date ||', '|| issues.id AS request_id_expanded,
followupactions.request_id,
canvassers.username ||', '|| canvassers.fullname ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| canvassers.phone ||', '|| canvassers.email AS actor_expanded,
followupactions.actor,
canvassers.fullname AS actor_name,
followupactions.date,
followupactions.notes,
followupactions.closed,
followupactions.id
FROM followuprequests, visits, canvassers, addresses, followupactions, issues, electors
WHERE followupactions.request_id = followuprequests.id
AND followuprequests.elector_id = electors.id
AND followuprequests.visit_id = visits.id
AND followuprequests.issue_id = issues.id
AND visits.address_id = addresses.id
AND followupactions.actor = canvassers.id
;
GRANT SELECT ON lv_followupactions TO canvassers, issueexperts;
------------------------------------------------------------------------
-- request locking
------------------------------------------------------------------------
ALTER TABLE followuprequests ADD COLUMN locked_by INTEGER REFERENCES canvassers(id) ON DELETE SET NULL;
ALTER TABLE followuprequests ADD COLUMN locked TIMESTAMP;

View file

@ -5,6 +5,7 @@
<!-- head: if you want entire custom head content, override this block. -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="referrer" content="same-origin">
{% style "/css/yyy-common.css" %}
{% style "/css/yyy-site.css" %}
{% style "/css/spinner.css" %}
@ -26,10 +27,14 @@
<img id="nav-icon" src="{{servlet-context}}/img/threelines.png" alt="Menu"/>
<menu id="nav-menu" class="nav">
<li class=""><a href="{{servlet-context}}/home">Home</a></li>
<li class=""><a href="{{servlet-context}}/library">Library</a></li>
<li class=""><a href="https://library.projecthope.scot/">Library</a></li>
{% if user %}
<li class=""><a href="{{servlet-context}}/roles">Roles</a></li>
<li class=""><a href="{{servlet-context}}/logout">Logout</a></li>
{% else %}
<li class=""><a href="{{servlet-context}}/register">Register</a></li>
<li class="">{% if user %}<a href="{{servlet-context}}/logout">Logout</a>
{% else %}<a href="{{servlet-context}}/login">Login</a>{% endif %}</li>
<li class=""><a href="{{servlet-context}}/login">Login</a></li>
{% endif %}
<li class=""><a href="{{servlet-context}}/about">About</a></li>
{% if user %}
<li id="user"><a href="{{servlet-context}}/profile">Logged in as {{user.username}}</a></li>
@ -63,18 +68,31 @@
<!-- content: put your main page content into this block -->
{% endblock %}
</div>
<div id="cookies">
<div id="more-about-cookies">
This website stores session information as a 'cookie' on your browser.
This helps us show you the content you want to see. This cookie does
not identify you, and cannot be read by other websites. It is deleted
by your browser as soon as you leave this site. This website does not
use any third party cookies, so your visit here cannot be tracked by
other websites.
</div>
<div id="about-cookies">
About cookies
</div>
</div>
<br clear="both"/>
</div>
{% block foot %}
<!-- foot: override this block if you don't want the standard footer -->
<footer>
<div id="credits">
<div>
<!-- div>
<img src="{{servlet-context}}/img/credits/ric-logo.png" width="24" height="24"/>
A project of the
<a href="https://radical.scot/">Radical Independence Campaign</a> ||
Version {{version}}
</div>
</div -->
<div>
<img height="16" width="16" alt="Clojure" src="{{servlet-context}}/img/credits/luminus-logo.png"/>Built with <a href="http://www.luminusweb.net/">LuminusWeb</a> ||
<img height="16" width="16" alt="Clojure" src="{{servlet-context}}/img/credits/clojure-icon.gif"/> Powered by <a href="http://clojure.org">Clojure</a> ||

View file

@ -2,23 +2,12 @@
{% block big-links %}
{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row-fluid">
<div class="col-lg-12">
<div class="centering text-center">
<div class="text-center">
<h1><span class="text-danger">Error: {{status}}</span></h1>
<hr>
{% if title %}
<h2 class="without-margin">{{title}}</h2>
{% endif %}
{% if message %}
<h4 class="text-danger">{{message}}</h4>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% if status %}
<h2>Error: {{status}}</h2>
{% endif %}
<hr>
{% if error %}
<p>{{error}}</p>
{% endif %}
{% endblock %}

View file

@ -1,5 +1,9 @@
{% extends "base.html" %}
{% block extra-head %}
<meta http-equiv="refresh" content="60">
{% endblock %}
{% block back-links %}
<div>
<div class='back-link-container'>
@ -36,19 +40,16 @@ Add a new Followuprequest
<thead>
<tr>
<th>
Id
Elector
</th>
<th>
Elector_id
Raised
</th>
<th>
Visit_id
Issue
</th>
<th>
Issue_id
</th>
<th>
Method_id
Method
</th>
<th>
&nbsp;
@ -56,13 +57,10 @@ Method_id
</tr>
<tr>
<th>
<input id='id' type='text' name='id' value='{{ params.id }}'/>
</th>
<th>
<input id='elector_id' type='text' name='elector_id' value='{{ params.elector_id }}'/>
</th>
<th>
<input id='visit_id' type='text' name='visit_id' value='{{ params.visit_id }}'/>
<input id='raised' type='text' name='raised' value='{{ params.raised }}'/>
</th>
<th>
<input id='issue_id' type='text' name='issue_id' value='{{ params.issue_id }}'/>
@ -79,22 +77,21 @@ Method_id
{% for record in records %}
<tr>
<td>
{{ record.id }}
</td>
<td>
<a href='{{servlet-context}}/form-electors-Elector?id={{ record.elector_id }}'>
{{ record.elector_id_expanded }}
</a>
</td>
<td>
<a href='{{servlet-context}}/form-visits-Visit?id={{ record.visit_id }}'>
{{ record.visit_id_expanded }}
</a>
{{ record.raised }}
</td>
<td>
<a href='{{servlet-context}}/form-issues-Issue?id={{ record.issue_id }}'>
{{ record.issue_id_expanded }}
</a>
{% ifequal record.issue_id "Other" %}
{{ record.issue_detail }}
{% else %}
<a href='{{servlet-context}}/form-issues-Issue?id={{ record.issue_id }}'>
{{ record.issue_id }}
</a>
{% endifequal %}
</td>
<td>
<a href='{{servlet-context}}/form-followupmethods-Followupmethod?id={{ record.method_id }}'>

View file

@ -13,16 +13,19 @@
<form action='{{servlet-context}}/issue-expert/followup-action' method='POST'>
{% csrf-field %}
<input id='id' name='id' type='hidden' value='{{record.id}}'/>
<input id='request_id' name='request_id' type='hidden' value='{{record.id}}'/>
<input id='elector_id' name='elector_id' type='hidden' value='{{record.elector_id}}'/>
<input id='visit_id' name='visit_id' type='hidden' value='{{record.visit_id}}'/>
<p class='widget'>
<label for='elector_id'>
Elector
</label>
{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %}
<span id='elector_id' name='elector_id' class='pseudo-widget disabled'>
<span id='elector' name='elector' class='pseudo-widget disabled'>
{{elector.name}} ({{elector.gender}})
</span>
{% else %}
<span id='elector_id' name='elector_id' class='pseudo-widget not-authorised'>
<span id='elector' name='elector' class='pseudo-widget not-authorised'>
You are not permitted to view elector of followuprequests
</span>
{% endifmemberof %}
@ -33,7 +36,7 @@
</label>
{% ifmemberof issueexperts analysts issueeditors admin %}
<span id='visit' name='visit' class='pseudo-widget disabled'>
by {{visit.canvasser_id_expanded}} at {{visit.date}}
by {{visit.canvasser_id_expanded}} on {{visit.date}}
</span>
{% else %}
<span id='visit_id' name='visit_id' class='pseudo-widget not-authorised'>
@ -43,8 +46,17 @@
</p>
<p class='widget'>
<label for='issue_id'>
{{issue.id}}
Issue
</label>
{% ifmemberof issueexperts analysts issueeditors admin %}
<span id='issue_id' name='issue_id' class='pseudo-widget disabled'>
{{issue.id}} <em>{{record.issue_detail}}</em>
</span>
{% else %}
<span id='issue_id' name='issue_id' class='pseudo-widget not-authorised'>
You are not permitted to view issue of followuprequests
</span>
{% endifmemberof %}
{% ifmemberof issueexperts admin %}
<div id="issue-brief">
{{issue.brief|safe}}
@ -92,7 +104,7 @@
<tbody>
{% for action in actions %}
<tr>
<td>{{action.actor}}</td>
<td><a href="{{servlet-context}}/form-canvassers-Canvasser?id={{action.actor}}">{{action.actor_name}}</a></td>
<td>{{action.date}}</td>
<td>{{action.closed}}</td>
</tr>
@ -104,13 +116,13 @@
</table>
</p>
{% endif %}
{% if not closed %}
<p class='widget'>
<label for='notes'>
Your notes
</label>
{% ifmemberof admin issueexperts %}
<textarea rows='8' cols='60' id='notes' name='notes'>
</textarea>
<textarea rows='8' cols='60' id='notes' name='notes'></textarea>
{% endifmemberof %}
</p>
<p class='widget'>
@ -121,7 +133,21 @@
<input id='closed' name='closed' type='checkbox' maxlength='' size='16'/>
{% endifmemberof %}
</p>
<p class='widget'>
<label for='intention'>
What is the elector's voting intention now?
</label>
{% ifmemberof admin issueexperts %}
<select id='option_id' name='option_id'>
<option value="">Not stated</option>
{% for option in options %}
<img src="{{servlet-context}}/img/option/{{option.id}}-{% ifequal record.option_id option.id %}selected{% else %}unselected{% endifequal %}.png" alt="{{option.id}}"/>
<option value='{{option.id}}' {% ifequal record.option_id option.id%}selected='selected'{% endifequal %} style="background-image: url('{{servlet-context}}/img/option/{{option.id}}-{% ifequal record.option_id option.id %}selected{% else %}unselected{% endifequal %}.png')">
{{option.id}}</option>
{% endfor %}
</select>
{% endifmemberof %}
</p>
{% ifmemberof admin issueexperts %}
<p class='widget action-safe'>
<label for='save-button' class='action-safe'>
@ -130,6 +156,7 @@
<input id='save-button' name='save-button' class='action-safe' type='submit' value='Save!'/>
</p>
{% endifmemberof %}
{% endif %}
</form>
</div>

Some files were not shown because too many files have changed in this diff Show more