Compare commits

..

235 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 039b0eef3c Attempt to get telephone auto-dialing to work 2018-08-07 14:51:44 +01:00
Simon Brooke f0788c6415 Fixes on Engraver 2018-08-06 12:41:49 +01:00
Simon Brooke 1069984849 Work on linking 2018-08-05 10:16:33 +01:00
Simon Brooke 42d8f69165 Fixes related to ADL issue #5 2018-07-29 01:20:46 +01:00
Simon Brooke 3a8b3dbbfa Downgraded to Clojure 1.8; and, some minor refactoring
Clojure 1.9 was causing some problems for my tooling.
2018-07-28 16:22:42 +01:00
Simon Brooke 9433f8cc67 Merge remote-tracking branch 'origin/develop' into develop 2018-07-27 18:22:46 +01:00
Simon Brooke 1098db3b47 Fix to the :fetch-later issue
Thanks to @mdallastella@functional.cafe
2018-07-27 18:22:36 +01:00
Simon Brooke 17ebcbc782 Working! 2018-07-27 18:21:20 +01:00
Simon Brooke 495b07ded8 Working! 2018-07-27 16:36:28 +01:00
Simon Brooke 2d106ee582 Finally, it works. Now lots more to do! 2018-07-27 13:04:10 +01:00
Simon Brooke 2c4c61c458 Have now (finally) inserting correctly, but not returning success(?) 2018-07-27 12:46:35 +01:00
Simon Brooke cdd3723c7b Very close to successful transmit, but not yet working. 2018-07-27 09:08:19 +01:00
Simon Brooke 58958ceec7 Merge branch 'feature/ancient' into develop 2018-07-26 17:47:40 +01:00
Simon Brooke 9564c76ae6 Almost at the point of transmitting intentions. 2018-07-26 17:47:31 +01:00
Simon Brooke 981a4d0d34 Fixed major regression, still some problems but progress. 2018-07-26 11:12:50 +01:00
Simon Brooke 18a267fd8c WARNING: this is a REGRESSION, and does not work. 2018-07-26 08:41:59 +01:00
Simon Brooke 913beffb61 Merge remote-tracking branch 'origin/develop' into develop 2018-07-24 17:53:08 +01:00
Simon Brooke 0e7a74a28b Working on transmitting visits, intentions, requests 2018-07-24 17:52:23 +01:00
Simon Brooke f3f46b8e2f Very small fixes found on Knave
The good news: the dev environment does work on Windows. Bad news: the broadband in the office is so slow it's unusable.
2018-07-24 14:54:00 +01:00
Simon Brooke f7c76115fd Minor fixes re: pins on map 2018-07-24 11:38:10 +01:00
Simon Brooke de5d4de763 Now populating the map with pins as you move across it
Unfortunately, haven't worked out how to remove the pins!
2018-07-22 20:28:39 +01:00
Simon Brooke a256e492dc Merge remote-tracking branch 'origin/develop' into develop 2018-07-22 18:54:59 +01:00
Simon Brooke 67853186e2 Now generating the right URI for fetch-local-data 2018-07-22 18:53:48 +01:00
Simon Brooke bccf53cedd Now generating the right URI for fetch-local-data 2018-07-22 18:10:34 +01:00
Simon Brooke 90a8dd5d6a I 2018-07-22 17:49:38 +01:00
Simon Brooke ea89e37d77 Fixes related to servlet-context 2018-07-22 09:44:58 +01:00
Simon Brooke b7ddc0a443 Servlet-context fixes 2018-07-22 09:24:37 +01:00
Simon Brooke 3463292e3c Finished work on backing out passing session to layout. 2018-07-22 09:00:14 +01:00
Simon Brooke 5ad384745f Tactical commit before moving to Fletcher
I *think* I'm on the right lines with this, but illuminator is too slow
and the REPL server is timing out...
2018-07-21 16:47:52 +01:00
Simon Brooke d53c633527 Probably a bad merge I'll regret 2018-07-21 12:21:10 +01:00
Simon Brooke 912d417cb6 Working towards lein-adl 2018-07-21 11:50:38 +01:00
Simon Brooke 3b11b678a1 Version 0.2.0-SNAPSHOT 2018-07-19 17:59:34 +01:00
Simon Brooke 2bf43396f0 Final tweeks for public alpha 2018-07-19 11:36:36 +01:00
Simon Brooke 8f26534b3b Upversion to 0.2.0-SNAPSHOT 2018-07-18 23:32:44 +01:00
Simon Brooke b275000632 0.1.0 First public alpha 2018-07-18 23:31:28 +01:00
Simon Brooke ec259c0f7a Merge branch 'release/0.1.0' into develop 2018-07-18 23:25:48 +01:00
Simon Brooke 42f02b6de0 Upversion to 0.1.0 2018-07-18 23:25:23 +01:00
Simon Brooke 542ce9f56d I think I'm ready for public alpha. 2018-07-18 23:01:52 +01:00
Simon Brooke 325ad7ed67 Generally, removing files auto-generated from ADL from the repository. 2018-07-17 22:37:34 +01:00
Simon Brooke 0bf1a2d7f6 Generally, ignore all files automatically generated from ADL. 2018-07-17 22:34:24 +01:00
Simon Brooke 55135d7d2e Most of the followup action workflow done. 2018-07-17 22:31:06 +01:00
Simon Brooke e53361a3a5 Minor changes to ADL and to logging. 2018-07-17 09:04:15 +01:00
Simon Brooke a610eca59d Issues flow in app now working again (now with real data) 2018-07-16 11:42:02 +01:00
Simon Brooke ab568365ef User profile page working. 2018-07-16 10:17:02 +01:00
Simon Brooke b65223198c Minor fix to ADL - wrong capitalisation
Plus regenerated files.
2018-07-15 12:42:32 +01:00
Simon Brooke 5356f65ca3 Much UI work, significant improvement. 2018-07-10 16:26:41 +01:00
Simon Brooke 9c3af4c936 #17: progress, not complete. 2018-07-09 22:00:39 +01:00
Simon Brooke 0026218993 Tactical commit; much progress but not ready for release. 2018-07-07 10:21:09 +01:00
Simon Brooke fc2bd86cff Extremely close to alpha release 2018-07-05 11:16:48 +01:00
Simon Brooke ff5c78d030 Fixed the regression with the map, and two issues in autogenerated code 2018-07-04 00:14:28 +01:00
Simon Brooke 3a49e50a51 Now actually pulling data from server, but some regressions.
The map isn't rendering, and there's something wrong with options. But a lot is working.
2018-07-03 20:16:38 +01:00
Simon Brooke d4f0f4aa5c get-local-data now working 2018-07-03 12:49:14 +01:00
Simon Brooke 6b44b70330 #31, #44, #45: GDPR workflow complete; started on locality data 2018-07-03 10:04:59 +01:00
Simon Brooke b42a593e34 Signature widget working in app. 2018-07-02 21:55:28 +01:00
Simon Brooke f70d8ee2ff Trying to get the GDPR consent form working (fail) 2018-07-02 19:12:20 +01:00
Simon Brooke c7b6484597 Got rid of obsolete stuff
Still trying to work out where the obsolete version of the app is hiding!
2018-07-02 18:17:46 +01:00
Simon Brooke 13f454afb5 Updated README database section; reformat of electors
Electors page is STILL rendering an obsolete version which is
nowhere in the code, and I cannot see how it possibly can.
2018-07-02 17:42:15 +01:00
Simon Brooke 8cf28e9ca9 Merge branch 'develop' of /home/simon/workspace/youyesyet into develop 2018-07-02 17:33:29 +01:00
Simon Brooke e19e01d9be Trying to track down why we're seeing a version of the electors panel
which should no longer be there. Failing.
2018-07-02 17:29:01 +01:00
Simon Brooke 7aac888912 Tactical commit while I try to understand why the app doesn't rebuild 2018-07-02 16:35:34 +01:00
Simon Brooke 16af040537 Tactical commit while I try to understand why the app doesn't rebuild 2018-07-02 16:22:38 +01:00
Simon Brooke f5afa67eed More work on UI, but this commit has a regression
The user is no longer correctly stored in the session, and I'm not sure why not.
2018-07-02 10:51:42 +01:00
Simon Brooke e6d6109575 Tactical commit during work on user interface 2018-06-30 19:19:01 +01:00
Simon Brooke 23a3e71464 Yet more progress! 2018-06-30 12:51:52 +01:00
Simon Brooke 78365f8c8b More detail work. An awful lot is working now. 2018-06-29 23:33:23 +01:00
Simon Brooke 635d1830d3 Much progress 2018-06-29 18:39:08 +01:00
Simon Brooke 4e296537c4 Progress on #2, #5; oauth isn't working but not far off
Using the Noir session doesn't seem to be the right thing to do. I should be using the Ring session, but I'm having a hard time getting my head round it. @yogthos is too bloody clever!
2018-06-29 17:14:55 +01:00
Simon Brooke 88468461fd Massive progress, following sorting the data import problem 2018-06-29 11:11:50 +01:00
Simon Brooke acfaf985fa Much progress, nothing yet works. 2018-06-20 09:17:53 +01:00
Simon Brooke 284509fa7b Work on getting forms to go. Not yet complete. 2018-06-19 08:23:30 +01:00
Simon Brooke 6f09c0841c List views now work 2018-06-15 10:50:29 +01:00
Simon Brooke 14abdb0dce Merge remote-tracking branch 'origin/develop' into develop 2018-06-14 21:37:12 +01:00
Simon Brooke bfb5a67266 Further work on auto-generation of components 2018-06-14 21:33:20 +01:00
Simon Brooke eb1c35489b Minor changes to documentation strings 2018-06-14 16:12:36 +01:00
Simon Brooke a9fd6617d2 A number of small changes to the ADL specification 2018-06-14 00:14:06 +01:00
Simon Brooke 706e42f483 More work, some progress made. 2018-06-11 21:49:16 +01:00
Simon Brooke bbf6daf6ca #47, #48, #49, #50: MY MONSTER IT LIVES
Most of the forms of the admin user interface render and sort-of work. It needs polish - but it's a huge leap forward!
2018-06-11 19:07:38 +01:00
Simon Brooke 98695e41dc #46, #50: Further improvements on list generation 2018-06-11 10:10:23 +01:00
Simon Brooke 88b693acc5 Merge remote-tracking branch 'origin/develop' into develop 2018-06-10 22:46:51 +01:00
Simon Brooke a00da8079c #47, #48, #49, #50: Auto-generated admin system largely done
This work is closely coupled with work on [ADL](https://github.com/simon-brooke/adl), q.v. At present ADL largely supports this project.
2018-06-10 22:45:58 +01:00
Simon Brooke 85b54fbd60 #47, #48, #49, #50: Auto-generated admin system largely done
This work is closely coupled with work on [ADL](https://github.com/simon-brooke/adl), q.v. At present ADL largely supports this project.
2018-06-10 22:31:12 +01:00
Simon Brooke 79bf3ed7eb More work on ADL. 2018-06-05 09:26:36 +01:00
Simon Brooke 5b72ffac78 Merge branch 'feature/28' into develop 2018-06-03 08:57:28 +01:00
Simon Brooke ad8563d668 Merge remote-tracking branch 'origin/feature/28' into feature/28 2018-06-03 08:54:50 +01:00
Simon Brooke f86c5cffe6 Mainly, added the dwellings table, implied by the revised ERD.
Some other tidy up.
2018-05-26 19:23:21 +01:00
Simon Brooke 162274c23f Getting serious autogenerated files now! 2018-05-13 18:36:17 +01:00
Simon Brooke 0eac2f5581 Added migration for reference data, canonical ADL. 2018-05-12 14:26:22 +01:00
Simon Brooke cf1eb70748 Merge remote-tracking branch 'origin/feature/28' into feature/28 2018-03-17 21:40:48 +00:00
Simon Brooke de4b76c5f1 Merge remote-tracking branch 'origin/feature/28' into feature/28 2018-03-17 18:43:03 +00:00
Simon Brooke f23e52a65b Squirrel-parse generated routes and queries are now working
Routes and queries auto-generated from migrations by [squirrel-parse](https://github.com/simon-brooke/squirrel-parse) are now actually working!
2018-03-17 18:41:52 +00:00
Simon Brooke 604e7f485d Squirrel-parse generated routes and queries are now working
Routes and queries auto-generated from migrations by [squirrel-parse]() are now actually working!
2018-03-17 18:40:31 +00:00
simon 9157871cc1 Added a note on locality indexing. 2017-07-24 19:46:52 +01:00
Simon Brooke 62ab37de47 Merge remote-tracking branch 'refs/remotes/origin/develop'
Conflicts:
	src/clj/youyesyet/routes/services.clj
2017-07-22 15:57:50 +01:00
simon e300d0c4c5 #28 Changed the way I'm doing REST, for simplicity 2017-07-22 11:04:59 +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
simon 89745e3a83 Minor UI fixes 2017-07-21 16:39:59 +01:00
simon 3a18cacf0f #43 Zoom and lat/long now captured in state. 2017-07-21 15:51:16 +01:00
simon 4dc7ad396a Merge branch 'feature/41' into develop 2017-07-21 14:18:37 +01:00
simon 2e81c7b664 #41 Updated client to cope with dwelling changs 2017-07-21 14:18:22 +01:00
simon 88c5c74c52 Merge branch 'feature/5' into feature/41 2017-07-21 12:40:57 +01:00
simon fc1b17b7a5 #41 Updated ERD; added and tested migrations. 2017-07-21 12:18:42 +01:00
simon c87efe9a66 Documentation changes associated with introdoction of codox 2017-07-21 08:46:50 +01:00
simon e77d5d0393 Documentation and disambiguation
Separate package name hierarchies for clj and cljs parts of the system, so that it's unambiguous what package (e.g. 'youyesyet.core' is.
2017-07-16 13:03:24 +01:00
simon 2ce44cc0b8 Progress... 2017-07-15 19:36:37 +01:00
simon 4213f6159c #28: only the very beginning of the beginning
Don't even know if this is worth doing - this specification language doesn't yet mean much to me.
2017-07-15 17:51:28 +01:00
simon be324e9d06 Beginnings of a working role-routing page
(also the first actual database reads)
2017-07-15 17:49:32 +01:00
simon d534302fd8 #5: Progress, but nothing working yet 2017-04-15 11:57:17 +01:00
simon 95507002c7 Very rough beginning of making the database stuff work. 2017-04-05 09:56:27 +01:00
simon 19e7b24933 Added lots of queries (not yet complete). 2017-04-04 09:33:24 +01:00
simon 3795f4e0fe #5: Sorting out merge horror 2017-04-01 15:24:30 +01:00
simon e425c56ec2 #5: Started work on roles switching 2017-04-01 15:11:50 +01:00
Simon Brooke 888205e069 Merge branch 'feature/36' into develop 2017-04-01 07:56:35 +01:00
Simon Brooke 32e9d9b9f5 #36: Fixed 2017-04-01 07:56:21 +01:00
Simon Brooke a8c0f48aa5 Stopping transient bower artefacts cluttering the repository 2017-04-01 07:17:54 +01:00
Simon Brooke 0ad6a3e81b Minor look-and-feel stuff. 2017-03-31 10:03:01 +01:00
Simon Brooke 0cb097a64d Merge branch 'feature/36' into develop 2017-03-31 07:31:15 +01:00
Simon Brooke c1a120daf2 #36 App user interface now fully working for pre-alpha release
... but app.js is still not being deployed.
2017-03-31 07:30:55 +01:00
Simon Brooke c75bdb9e5e #36: Followup request doesn't actually send, everything else works 2017-03-30 22:15:03 +01:00
Simon Brooke 4be0b9ce08 Merge branch 'feature/13' into feature/36 2017-03-30 13:22:00 +01:00
Simon Brooke bc34ca16ff Merge branch 'develop' into feature/36 2017-03-30 07:03:24 +01:00
Simon Brooke 7d4bf2a2c2 Merge branch 'feature/33' into develop 2017-03-30 07:03:06 +01:00
Simon Brooke ca9807dc1c #33: Fix
Easy.
2017-03-30 07:02:52 +01:00
Simon Brooke 2863badef7 Updated README. 2017-03-29 11:29:13 +01:00
Simon Brooke ba27df1750 Don't know what happened there!
Moving favicon.ico back into place.
2017-03-29 10:23:02 +01:00
Simon Brooke 6483b5ae9d #36: Still not working, but good enough for now
The compiled clojurescript is still not correctly deployed in the war; however, it is now compiled correctly and does work, and can be moved into place manually after deployment.
2017-03-29 10:22:13 +01:00
Simon Brooke 63a0ac0aca Fixed image path for issues.png which was absolute not relative. 2017-03-29 07:20:37 +01:00
Simon Brooke 14ec599fd2 Merge branch 'develop' into feature/36 2017-03-29 06:40:49 +01:00
Simon Brooke c1b9e97849 Merge branch 'feature/37' into develop 2017-03-29 06:40:19 +01:00
Simon Brooke 72b4f058ed #37: Well, THAT was easy!
Protective wrapper around :page rendering so that the initial, transient null value doesn't get rendered!
2017-03-29 06:40:04 +01:00
Simon Brooke b1956def91 #37: never set :page to nil 2017-03-29 06:14:15 +01:00
Simon Brooke 36d8fa1273 #13: Beginning work on outbound queue 2017-03-29 06:10:07 +01:00
Simon Brooke 00c7cb9b84 #37: Preliminary work 2017-03-29 06:05:20 +01:00
Simon Brooke 92ad75da92 Merge branch 'master' into develop 2017-03-26 20:43:13 +01:00
Simon Brooke ff92725cf6 Merge develop into master 2017-03-26 18:54:13 +01:00
Simon Brooke e5299bd2cf Quick fix on select menus 2017-03-26 18:47:45 +01:00
Simon Brooke a589afb6ab Merge remote-tracking branch 'origin/develop' into develop 2017-03-26 18:42:15 +01:00
Simon Brooke 86285e01b9 Merge branch 'feature/30' into develop 2017-03-26 18:40:11 +01:00
Simon Brooke 28f5a3f10c #30: Done
The concerns of the server and client side are now separated; there's plenty of work still to be done on both but it's clear what belongs where.
2017-03-26 18:40:01 +01:00
simon 560938e6ae #30: Still not quite complete 2017-03-26 15:58:43 +01:00
Simon Brooke 7621040c8f Now with the right pin image; pin click still not working. 2017-03-26 12:03:25 +01:00
Simon Brooke 6c50592543 Map pins on map working! 2017-03-26 11:30:41 +01:00
Simon Brooke b95fa9919e Woohoo! Not broken, looking good 2017-03-26 10:17:41 +01:00
Simon Brooke 65b69939b3 UI mostly working
There's no target yet for the followup form, but it otherwise good.
2017-03-26 09:12:15 +01:00
simon 013ebea4db #30: Very close to good on client look and feell 2017-03-25 14:43:47 +00:00
simon d36c58b44c #30: Tremendous progress, now understand reg-sub model
Although there's no actual collection of data from the back end, the user interface is now mostly working. And, I flatter myself, looking good.
2017-03-24 10:32:31 +00:00
Simon Brooke 9392091428 Simplified geographic sharding 2017-03-23 10:02:20 +00:00
simon 9d83c585dd #30: Another removed panel. 2017-03-22 21:01:26 +00:00
simon e782560d40 #30: More progress, not finished. 2017-03-22 20:58:50 +00:00
simon 5b35b5bc18 #30: Roughly working on Tomcat.
Not perfect yet by a long way but good enough for alpha.
2017-03-22 18:58:14 +00:00
Simon Brooke d31ed67b98 Competitor analysis, more on scaling, a dummy login page 2017-03-22 18:56:56 +00:00
Simon Brooke 0a01978036 #30: Extremely small fix to last night's commit 2017-03-20 08:19:38 +00:00
Simon Brooke e5d399b86d Significant progress, but a long way from finished. 2017-03-19 23:28:27 +00:00
Simon Brooke 7439e5ae15 #30: Starting to work.
We can serve both conventional pages, and the app.
2017-03-19 17:54:37 +00:00
simon 68bb22bd31 Merge remote-tracking branch 'origin/develop' into develop 2017-03-19 16:59:40 +00:00
Simon Brooke db8c3ed22c Merge branch 'feature/9' into develop 2017-03-19 16:34:19 +00:00
Simon Brooke ce9520fc2c Added luminus logo to credits 2017-03-19 16:32:14 +00:00
simon d2d98b13b9 Merge branch 'feature/21' into develop 2017-03-19 16:01:46 +00:00
Simon Brooke b581f8c06e Minor edits 2017-03-19 15:14:54 +00:00
Simon Brooke 89beabfc87 Merge branch 'develop' into feature/9 2017-03-18 09:09:07 +00:00
Simon Brooke a3fe0db2d1 Ooops! Home was broken in last commit 2017-03-18 09:02:29 +00:00
Simon Brooke dd85254607 Merge branch 'feature/clientframework' into develop 2017-03-18 08:56:28 +00:00
Simon Brooke 9ff8235d3e /clientframework: Restructured a bit 2017-03-18 08:56:15 +00:00
Simon Brooke 94465273a0 Sorted a bit of merge mess. 2017-03-18 07:37:05 +00:00
Simon Brooke 250e0e6887 Merge branch 'feature/9' into feature/clientframework 2017-03-18 07:29:46 +00:00
Simon Brooke 95436e955b Fixes to get the whole lot to build as a war 2017-03-17 23:04:47 +00:00
Simon Brooke 933e5ac2a4 #9: Woot woot woot!
Map now working with OSM tiles
2017-03-17 21:45:11 +00:00
Simon Brooke ee9654aa02 Merge branch 'feature/22' into develop 2017-03-16 21:19:14 +00:00
Simon Brooke 84c2bb19c4 Added credits to the spinner CSS 2017-03-16 21:16:41 +00:00
Simon Brooke 59378b5a5f Very pleased with this
All the basic bits work; startup spinner is very neat.
2017-03-16 21:16:07 +00:00
Simon Brooke b4618a4311 Look-and-feel of dynamic pages now very good 2017-03-16 19:39:27 +00:00
Simon Brooke 6b9e315b2f Dynamic site now looks very like the dummy...
But the phone menu isn't working nicely yet.
2017-03-16 19:27:08 +00:00
Simon Brooke 9540ae0575 Added collaboration stuff to README 2017-03-16 09:00:39 +00:00
Simon Brooke a46f55e50e #22: This isn't even nearly working...
But it's starting to work!
2017-03-16 08:22:07 +00:00
Simon Brooke 036c84972a Merge branch 'feature/21' into develop 2017-03-16 06:02:39 +00:00
Simon Brooke 1f18be5771 Added 'profiles.clj' to gitignore 2017-03-16 06:02:11 +00:00
Simon Brooke ff4565a30d Added an 'about' file to the dummy 2017-03-15 21:27:36 +00:00
277 changed files with 12181 additions and 8434 deletions

30
.gitignore vendored
View file

@ -9,6 +9,8 @@ pom.xml.asc
/checkouts/ /checkouts/
/resources/public/content/.git /resources/public/content/.git
/resources/public/vendor /resources/public/vendor
/bower_components/
/resources/public/js/lib/
.lein-deps-sum .lein-deps-sum
.lein-repl-history .lein-repl-history
.lein-plugins/ .lein-plugins/
@ -19,3 +21,31 @@ pom.xml.asc
.DS_Store .DS_Store
*-init.clj *-init.clj
profiles\.clj profiles\.clj
.bowerrc
bower.json
.idea
src/clj/youyesyet/scratchpad\.clj
*.iml
resources/sql/queries\.auto\.sql
resources/templates/auto/
src/clj/youyesyet/routes/auto\.clj
src/clj/youyesyet/routes/auto_json\.clj
resources/sql/youyesyet\.postgres\.sql
\.rebel_readline_history
/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.

133
README.md
View file

@ -10,16 +10,12 @@ generated using Luminus version "2.9.11.05"
## Status ## Status
**Nothing works yet**. This is very early development code, a long way pre-alpha. Very early pre-alpha; user interface mostly works (enough to demonstrate), back end is hardly started.
## What is it supposed to do? ## What is it supposed to do?
To understand what I'm aiming for, read [this essay](http://blog.journeyman.cc/2016/10/preparing-for-next-independence.html). Design documentation, such as there is of it yet, is in the *dummy* sub-directory. Also look at [src/clj/youyesyet/db/schema.clj](https://github.com/simon-brooke/youyesyet/blob/master/src/clj/youyesyet/db/schema.clj). To understand what I'm aiming for, read [this essay](http://blog.journeyman.cc/2016/10/preparing-for-next-independence.html). Design documentation, such as there is of it yet, is in the *dummy* sub-directory. Also look at [src/clj/youyesyet/db/schema.clj](https://github.com/simon-brooke/youyesyet/blob/master/src/clj/youyesyet/db/schema.clj).
### Dummy website
I've put a [dummy site](http://www.journeyman.cc/~simon/tmp/yyy-dummy/index.html) up for people to play with. Obviously it's intended to work with mobile phones.
## Prerequisites ## Prerequisites
You will need [Leiningen][1] 2.0 or above installed. The database required must be [Postgres][2] 9.3 or above. You will need [Leiningen][1] 2.0 or above installed. The database required must be [Postgres][2] 9.3 or above.
@ -47,32 +43,143 @@ 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. 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 ## 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.
;; WARNING
;; The profiles.clj file is used for local environment variables, such as database credentials.
;; This file is listed in .gitignore and will be excluded from version control by Git.
{:profiles/dev {:env {:database-url "jdbc:postgresql://127.0.0.1/youyesyet_dev?user=youyesyet&password=yourverysecurepassword"}}
:profiles/test {:env {:database-url "jdbc:postgresql://127.0.0.1/youyesyet_test?user=youyesyet&password=yourverysecurepassword"}}}
Do get the database initialised, run Do get the database initialised, run
createdb youyesyet_dev createdb youyesyet_dev
I'm no longer using Migratus as I'm using [Application Description Language](https://github.com/simon-brooke/adl/)
to generate the majority of the application, and, as changes are made to the application
description, new database schemas are generated. The database initialisation script will
be found at `resources/sql/youyesyet.postgres.sql`. Manually maintained overrides are found in
`resources/sql/youyesyet.postgres.overrides.sql`. So to initialise the database, invoke
psql youyesyet_dev < resources/sql/youyesyet.postgres.sql
followed by followed by
lein migratus migrate psql youyesyet_dev < resources/sql/youyesyet.postgres.overrides.sql
**NOTE THAT** in the namespace *youyesyet.db.schema*, there are a series of functions *create-xxx-table!*. These are a snare and a delusion; they are how I originally bootstrapped the database, but are no longer used (and should probably be deleted). The database is now constructed using [migratus](https://github.com/yogthos/migratus). Reference data initialisation scripts will in due course be stored in the same directory.
## Running 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
diffs between successive versions of the application description.
To start a web server for the application, run: ## Running in a dev environment
lein run To run in a dev environment, checkout the *develop* branch
If you're wanting to work on cljs development, you need two terminal sessions. In one run To download and install Javascript delendencies, run
lein run cd youyesyet
lein npm install
as above; in the other, run To start a development web server for the application, run:
Then
lein repl
Wait for the clojure `user=>` prompt to appear, and enter
(mount/start)
This will get the application running for development; ideally, open a new terminal and invoke
lein figwheel lein figwheel
which will aid in work on the ClojureScript components.
## Running in a production environment
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
You should get the [ZenHub](https://github.com/integrations/zenhub) plug-in for your favourite browser; I use ZenHub for project management, and you (and I) will find collaborating much easier if you do. To join in
1. Clone the project from [my repository](https://github.com/simon-brooke/youyesyet);
2. Visit the [kanban board](https://github.com/simon-brooke/youyesyet#boards?repos=70809242) and choose an issue you'd like to work on;
3. Assign that issue to yourself and move it into the 'in progress' column;
4. Use gitflow: start a new feature;
5. Work on the issue;
6. When you are satisfied with your work, commit it to your repository in your feature branch;
7. Submit a pull request and move the issue to the 'QA/Review' column on the kanban board;
8. If your work works, I'll integrate it into the develop branch, which you can then pull;
9. Rinse and repeat!
Obviously I'd appreciate it if you'd [mail me](mailto:simon@journeyman.cc) to introduce yourself, but there's no need to do so.
Finally, if you take a ticket and are not able to finish it, please
1. mail me to say so;
2. move the issue back to the 'backlog' column on the kanban board.
And *many thanks*!
## More about tooling
Note that all tools recommended in this document are free for non-commercial use; not all are open source.
### 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 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
I'm currently using [GitKraken](https://www.gitkraken.com/), on Linux, Windows, and Mac. Like so many things it works best on Linux, but it's good on both other platforms. However, I confess that although I like the ability to easily view the whole structure of the git repository, I tend to drop back to the command line if I'm doing anything tricky with Git.
### Database
The database is [Postgres](https://www.postgresql.org/). I have no plans even to support anything else; I don't feel we have time to mess about. However, if someone wants to fork the code and implement a different database, that's up to them. I'm afraid I do all database stuff from the command line; [there are graphical tools for Postgres](https://wiki.postgresql.org/wiki/Community_Guide_to_PostgreSQL_GUI_Tools) but personally I've never found graphical tools for databases much use.
## Further Reading
If you're thinking of joining in development on this I'd strongly recommend you get hold of a copy of [Dmitry Sotnikov](https://github.com/yogthos)'s [Web Development with Clojure, Second Edition](https://pragprog.com/book/dswdcloj2/web-development-with-clojure-second-edition).
You should also read the [User-Oriented Specification](doc/specification/userspec.md) and any other documentation which appears under the *doc/specification* hierarchy.
## License ## License
Copyright © 2016 Simon Brooke for the Radical Independence Campaign. Copyright © 2016 Simon Brooke for the Radical Independence Campaign.

View file

@ -0,0 +1,23 @@
#%RAML 0.8
---
title: YouYesYet API
baseUri: https://api.yyy.scot/{version}
version: v1
/canvassers:
get:
put:
/electors:
get:
/{id}:
get:
/{address_id}:
get:
# Location isn't a real entity in the database, but it is a means of searching for
# addresses and electors.
/location:
/{lat}/{long}/{radius}:
get:

View file

@ -0,0 +1,17 @@
# Security and authorisation
Essentially we have six levels of authorisation, at essentially increasing levels of sensitivity.
1. *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.
2. *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.
3. *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.
4. *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.
5. *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.
6. *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.

View file

@ -0,0 +1,23 @@
# 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](http://www.voteleavetakecontrol.org/)'s [Vics](https://dominiccummings.wordpress.com/2016/10/29/on-the-referendum-20-the-campaign-physics-and-data-science-vote-leaves-voter-intention-collection-system-vics-now-available-for-all/) 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](https://github.com/celestial-winter/vics) 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](https://github.com/xetorthio/jedis), a Java port of [Redis](https://redis.io/), 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](https://bower.io/), 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.
So for tonight I've failed. I shall try again.

View file

@ -0,0 +1,235 @@
# Database Specification
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
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

View file

@ -14,7 +14,7 @@
viewBox="0 0 1052.3622 744.09448" viewBox="0 0 1052.3622 744.09448"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.91 r13725" inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="entity-relationship-diagram.svg"> sodipodi:docname="entity-relationship-diagram.svg">
<defs <defs
id="defs4" /> id="defs4" />
@ -25,16 +25,16 @@
borderopacity="1.0" borderopacity="1.0"
inkscape:pageopacity="0.0" inkscape:pageopacity="0.0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="1.979899" inkscape:zoom="1.4"
inkscape:cx="833.70674" inkscape:cx="630.17912"
inkscape:cy="324.89697" inkscape:cy="325.79533"
inkscape:document-units="px" inkscape:document-units="px"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="true" showgrid="true"
inkscape:window-width="1920" inkscape:window-width="1920"
inkscape:window-height="1058" inkscape:window-height="1016"
inkscape:window-x="1920" inkscape:window-x="0"
inkscape:window-y="0" inkscape:window-y="27"
inkscape:window-maximized="1"> inkscape:window-maximized="1">
<inkscape:grid <inkscape:grid
type="xygrid" type="xygrid"
@ -62,50 +62,48 @@
id="rect4428" id="rect4428"
width="1030" width="1030"
height="730" height="730"
x="232.23355" x="16.060907"
y="376.0018" /> y="312.36218" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="36.365494" x="36.365494"
y="335.1539" y="335.1539"
id="text4136" id="text4136"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4138" id="tspan4138"
x="36.365494" x="36.365494"
y="335.1539" /></text> y="335.1539"
style="font-size:20px;line-height:1.25"> </tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="117.1777" x="117.1777"
y="360.40771" y="360.40771"
id="text4140" id="text4140"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4142" id="tspan4142"
x="117.1777" x="117.1777"
y="360.40771" y="360.40771"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'">YouYesYet: Entity Relationship Diagram</tspan></text> style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold'">YouYesYet: Entity Relationship Diagram</tspan></text>
<rect <rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4146" id="rect4146"
width="100" width="100"
height="60" height="60"
x="220.18532" x="52.499996"
y="411.65665" /> y="410.48724" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="230.18532" x="62.499996"
y="441.65665" y="440.48724"
id="text4148" id="text4148"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4150" id="tspan4150"
x="230.18532" x="62.499996"
y="441.65665" y="440.48724"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">District</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">District</tspan></text>
<rect <rect
y="533.88507" y="533.88507"
x="220.18532" x="220.18532"
@ -114,17 +112,16 @@
id="rect4152" id="rect4152"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text <text
sodipodi:linespacing="125%"
id="text4154" id="text4154"
y="563.88513" y="563.88513"
x="230.18532" x="230.18532"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
y="563.88513" y="563.88513"
x="230.18532" x="230.18532"
id="tspan4156" id="tspan4156"
sodipodi:role="line">Addresss</tspan></text> sodipodi:role="line">Dwelling</tspan></text>
<rect <rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4158" id="rect4158"
@ -134,16 +131,15 @@
y="653.08313" /> y="653.08313" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="230.18532" x="230.18532"
y="683.08313" y="683.08313"
id="text4160" id="text4160"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4162" id="tspan4162"
x="230.18532" x="230.18532"
y="683.08313" y="683.08313"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">Elector</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Elector</tspan></text>
<rect <rect
y="653.08313" y="653.08313"
x="732.33264" x="732.33264"
@ -152,13 +148,12 @@
id="rect4164" id="rect4164"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text <text
sodipodi:linespacing="125%"
id="text4166" id="text4166"
y="683.08313" y="683.08313"
x="742.33264" x="742.33264"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
y="683.08313" y="683.08313"
x="742.33264" x="742.33264"
id="tspan4168" id="tspan4168"
@ -166,14 +161,15 @@
<flowRoot <flowRoot
xml:space="preserve" xml:space="preserve"
id="flowRoot4170" id="flowRoot4170"
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:URW Chancery L;font-style:italic;font-weight:bold;font-size:20px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:URW Chancery L Bold Italic;font-stretch:normal;font-variant:normal"><flowRegion style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0.01%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"><flowRegion
id="flowRegion4172"><rect id="flowRegion4172"><rect
id="rect4174" id="rect4174"
width="150" width="150"
height="100" height="100"
x="20" x="20"
y="174.09448" /></flowRegion><flowPara y="174.09448" /></flowRegion><flowPara
id="flowPara4176" /></flowRoot> <rect id="flowPara4176"
style="font-size:20px;line-height:1.25"> </flowPara></flowRoot> <rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4178" id="rect4178"
width="100" width="100"
@ -182,16 +178,15 @@
y="533.88507" /> y="533.88507" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="742.33264" x="742.33264"
y="563.88513" y="563.88513"
id="text4180" id="text4180"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4182" id="tspan4182"
x="742.33264" x="742.33264"
y="563.88513" y="563.88513"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">Authority</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Authority</tspan></text>
<rect <rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4184" id="rect4184"
@ -201,16 +196,15 @@
y="533.88507" /> y="533.88507" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="400.90109" x="400.90109"
y="563.88513" y="563.88513"
id="text4186" id="text4186"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4188" id="tspan4188"
x="400.90109" x="400.90109"
y="563.88513" y="563.88513"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">Visit</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Visit</tspan></text>
<rect <rect
y="653.08313" y="653.08313"
x="390.90109" x="390.90109"
@ -219,13 +213,12 @@
id="rect4190" id="rect4190"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text <text
sodipodi:linespacing="125%"
id="text4192" id="text4192"
y="683.08313" y="683.08313"
x="400.90109" x="400.90109"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
y="683.08313" y="683.08313"
x="400.90109" x="400.90109"
id="tspan4194" id="tspan4194"
@ -239,20 +232,19 @@
y="775.31158" /> y="775.31158" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="742.33264" x="742.33264"
y="805.31158" y="805.31158"
id="text4198" id="text4198"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4200" id="tspan4200"
x="742.33264" x="742.33264"
y="805.31158" y="805.31158"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">Issue</tspan><tspan style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Issue</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="742.33264" x="742.33264"
y="824.06158" y="824.06158"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan4208">Expertise</tspan></text> id="tspan4208">Expertise</tspan></text>
<rect <rect
y="898.55017" y="898.55017"
@ -262,13 +254,12 @@
id="rect4202" id="rect4202"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text <text
sodipodi:linespacing="125%"
id="text4204" id="text4204"
y="928.55017" y="928.55017"
x="742.33264" x="742.33264"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
y="928.55017" y="928.55017"
x="742.33264" x="742.33264"
id="tspan4206" id="tspan4206"
@ -281,18 +272,17 @@
id="rect4221" id="rect4221"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text <text
sodipodi:linespacing="125%"
id="text4223" id="text4223"
y="803.29126" y="803.29126"
x="230.18532" x="230.18532"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
y="803.29126" y="803.29126"
x="230.18532" x="230.18532"
id="tspan4225" id="tspan4225"
sodipodi:role="line">Followup</tspan><tspan sodipodi:role="line">Followup</tspan><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
y="822.04126" y="822.04126"
x="230.18532" x="230.18532"
sodipodi:role="line" sodipodi:role="line"
@ -306,33 +296,32 @@
y="896.52985" /> y="896.52985" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="230.18532" x="230.18532"
y="926.52985" y="926.52985"
id="text4231" id="text4231"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4233" id="tspan4233"
x="230.18532" x="230.18532"
y="926.52985" y="926.52985"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">Followup</tspan><tspan style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Followup</tspan><tspan
id="tspan4235" id="tspan4235"
sodipodi:role="line" sodipodi:role="line"
x="230.18532" x="230.18532"
y="945.27985" y="945.27985"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">Action</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Action</tspan></text>
<g <g
id="g4243" id="g4243"
transform="translate(170.18532,29.294424)"> transform="translate(2.4999994,28.125007)">
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
id="path4237" id="path4237"
d="m 100,442.3622 0,60" d="m 100,442.3622 v 60"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
id="path4239" id="path4239"
d="m 90,452.3622 20,0" d="m 90,452.3622 h 20"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
@ -528,13 +517,12 @@
id="rect4323" id="rect4323"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text <text
sodipodi:linespacing="125%"
id="text4325" id="text4325"
y="683.08313" y="683.08313"
x="570.60675" x="570.60675"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
y="683.08313" y="683.08313"
x="570.60675" x="570.60675"
id="tspan4327" id="tspan4327"
@ -553,8 +541,8 @@
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" /> sodipodi:nodetypes="ccc" />
<path <path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.12152243px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 440,534.15662 0,-152.5 -320,0 0,410 100,0" d="m 440,534.15662 v -152.5 H 37.5 v 410 h 182.8125"
id="path4343" id="path4343"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" /> sodipodi:nodetypes="ccccc" />
@ -602,37 +590,36 @@
sodipodi:nodetypes="ccc" /> sodipodi:nodetypes="ccc" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="472.0152" x="472.0152"
y="394.48404" y="394.48404"
id="text4361" id="text4361"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4363" id="tspan4363"
x="472.0152" x="472.0152"
y="394.48404" y="394.48404"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial"><tspan style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'"
id="tspan4371">Version: </tspan>0.2</tspan><tspan id="tspan4371">Version: </tspan>0.4</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="472.0152" x="472.0152"
y="413.23404" y="413.23404"
id="tspan4365" id="tspan4365"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial"><tspan style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'"
id="tspan4373">Date: </tspan>20170315</tspan><tspan id="tspan4373">Date: </tspan>20180719</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="472.0152" x="472.0152"
y="431.98404" y="431.98404"
id="tspan4367" id="tspan4367"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial"><tspan style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'"
id="tspan4375">Author: </tspan>Simon Brooke</tspan><tspan id="tspan4375">Author: </tspan>Simon Brooke</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="472.0152" x="472.0152"
y="450.73404" y="450.73404"
id="tspan4369" id="tspan4369"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial"><tspan style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:'Arial Bold'"
id="tspan4377">Copyright:</tspan> (c) 2016 Simon Brooke for Radical Independence Campaign</tspan></text> id="tspan4377">Copyright:</tspan> (c) 2016 Simon Brooke for Radical Independence Campaign</tspan></text>
<path <path
@ -653,11 +640,10 @@
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="670" x="670"
y="762.36218" y="762.36218"
id="text4385" id="text4385"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4387" id="tspan4387"
x="670" x="670"
@ -665,11 +651,10 @@
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Introduced</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Introduced</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="530" x="530"
y="498.31903" y="498.31903"
id="text4389" id="text4389"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4391" id="tspan4391"
x="530" x="530"
@ -677,70 +662,64 @@
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Visited</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Visited</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;text-anchor:start;text-align:start;writing-mode:lr;" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="450" x="450"
y="632.36218" y="632.36218"
id="text4393" id="text4393"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4395" id="tspan4395"
x="450" x="450"
y="632.36218" y="632.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%;">Recorded</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Recorded</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="130" x="130"
y="392.36221" y="392.36221"
id="text4397" id="text4397"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4399" id="tspan4399"
x="130" x="130"
y="392.36221" y="392.36221"
style="-inkscape-font-specification:'Arial, Bold Italic';font-family:Arial;font-weight:bold;font-style:italic;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Raised</tspan></text> style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Bold Italic';text-align:start;writing-mode:lr-tb;text-anchor:start">Raised</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="790" x="790"
y="622.36218" y="622.36218"
id="text4401" id="text4401"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4403" id="tspan4403"
x="790" x="790"
y="622.36218" y="622.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Authenticates</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Authenticates</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="790" x="790"
y="742.36218" y="742.36218"
id="text4405" id="text4405"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4407" id="tspan4407"
x="790" x="790"
y="742.36218" y="742.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Has</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Has</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="790" x="790"
y="862.36218" y="862.36218"
id="text4409" id="text4409"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4411" id="tspan4411"
x="790" x="790"
y="862.36218" y="862.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">About</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">About</tspan></text>
<text <text
sodipodi:linespacing="125%"
id="text4413" id="text4413"
y="780.53986" y="780.53986"
x="337.45166" x="337.45166"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start"
y="780.53986" y="780.53986"
@ -749,88 +728,81 @@
sodipodi:role="line">About</tspan></text> sodipodi:role="line">About</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="280" x="280"
y="872.36218" y="872.36218"
id="text4417" id="text4417"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4419" id="tspan4419"
x="280" x="280"
y="872.36218" y="872.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Responded to</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Responded to</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="330" x="330"
y="702.36218" y="702.36218"
id="text4421" id="text4421"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4423" id="tspan4423"
x="330" x="330"
y="702.36218" y="702.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Expressed</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Expressed</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="280" x="112.31468"
y="512.36218" y="511.19278"
id="text4425" id="text4425"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4427" id="tspan4427"
x="280" x="112.31468"
y="512.36218" y="511.19278"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Contains</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Contains</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="280" x="280"
y="632.36218" y="632.36218"
id="text4429" id="text4429"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4431" id="tspan4431"
x="280" x="280"
y="632.36218" y="632.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Resides at</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Resides at</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="280" x="280"
y="742.36218" y="742.36218"
id="text4433" id="text4433"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4435" id="tspan4435"
x="280" x="280"
y="742.36218" y="742.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Requested</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Requested</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="280" x="280"
y="1002.3622" y="1002.3622"
id="text4441" id="text4441"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4443" id="tspan4443"
x="280" x="280"
y="1002.3622" y="1002.3622"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">Performed</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Performed</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="340" x="340"
y="552.36218" y="552.36218"
id="text4445" id="text4445"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4447" id="tspan4447"
x="340" x="340"
y="552.36218" y="552.36218"
style="-inkscape-font-specification:'Arial, Normal';font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:10px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%">To</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">To</tspan></text>
<path <path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 810,722.36221 20,0" d="m 810,722.36221 20,0"
@ -845,11 +817,10 @@
y="653.08313" /> y="653.08313" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="908.88831" x="908.88831"
y="682.36218" y="682.36218"
id="text4285" id="text4285"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4287" id="tspan4287"
x="908.88831" x="908.88831"
@ -871,47 +842,47 @@
y="773.29126" /> y="773.29126" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="920" x="920"
y="562.36218" y="562.36218"
id="text4302" id="text4302"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4304" id="tspan4304"
x="920" x="920"
y="562.36218" /></text> y="562.36218"
style="font-size:20px;line-height:1.25"> </tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:15px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="908.88831" x="908.88831"
y="802.36218" y="802.36218"
id="text4306" id="text4306"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4308" id="tspan4308"
x="908.88831" x="908.88831"
y="802.36218" y="802.36218"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">Organiser-</tspan><tspan style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Organiser-</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="908.88831" x="908.88831"
y="821.11218" y="821.11218"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan4310">ship</tspan></text> id="tspan4310">ship</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="908.88831" x="908.88831"
y="562.36218" y="562.36218"
id="text4312" id="text4312"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4314" id="tspan4314"
x="908.88831" x="908.88831"
y="562.36218">Team</tspan><tspan y="562.36218"
style="font-size:15px;line-height:1.25">Team</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="908.88831" x="908.88831"
y="581.11218" y="581.11218"
id="tspan4385">Membership</tspan></text> id="tspan4385"
style="font-size:15px;line-height:1.25">Membership</tspan></text>
<path <path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 830,672.36221 40,0 0,-110 30,0" d="m 830,672.36221 40,0 0,-110 30,0"
@ -963,11 +934,10 @@
id="path4338" id="path4338"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
<text <text
sodipodi:linespacing="125%"
id="text4340" id="text4340"
y="743.37231" y="743.37231"
x="879.90356" x="879.90356"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start"
y="743.37231" y="743.37231"
@ -976,11 +946,10 @@
sodipodi:role="line">Has</tspan></text> sodipodi:role="line">Has</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;line-height:125%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="879.90356" x="879.90356"
y="621.14386" y="621.14386"
id="text4344" id="text4344"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4346" id="tspan4346"
x="879.90356" x="879.90356"
@ -988,28 +957,26 @@
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Has</tspan></text> style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">Has</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="960" x="960"
y="622.36218" y="622.36218"
id="text4348" id="text4348"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4350" id="tspan4350"
x="960" x="960"
y="622.36218" y="622.36218"
style="font-size:10px">of</tspan></text> style="font-size:10px;line-height:1.25">of</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="960" x="960"
y="742.36218" y="742.36218"
id="text4352" id="text4352"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4354" id="tspan4354"
x="960" x="960"
y="742.36218" y="742.36218"
style="font-size:10px">of</tspan></text> style="font-size:10px;line-height:1.25">of</tspan></text>
<g <g
id="g4375" id="g4375"
transform="matrix(1,0,0,-1,171.72593,1606.7279)"> transform="matrix(1,0,0,-1,171.72593,1606.7279)">
@ -1035,16 +1002,15 @@
</g> </g>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="450" x="450"
y="742.36218" y="742.36218"
id="text4386" id="text4386"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4388" id="tspan4388"
x="450" x="450"
y="742.36218" y="742.36218"
style="font-size:10px">For</tspan></text> style="font-size:10px;line-height:1.25">For</tspan></text>
<rect <rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4390" id="rect4390"
@ -1054,19 +1020,20 @@
y="653.08313" /> y="653.08313" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="570" x="570"
y="682.36218" y="682.36218"
id="text4394" id="text4394"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4396" id="tspan4396"
x="570" x="570"
y="682.36218">Role</tspan><tspan y="682.36218"
style="font-size:15px;line-height:1.25">Role</tspan><tspan
sodipodi:role="line" sodipodi:role="line"
x="570" x="570"
y="701.11218" y="701.11218"
id="tspan4383">Membership</tspan></text> id="tspan4383"
style="font-size:15px;line-height:1.25">Membership</tspan></text>
<g <g
id="g4354" id="g4354"
transform="translate(0,-241.93154)"> transform="translate(0,-241.93154)">
@ -1078,16 +1045,16 @@
x="561.61688" x="561.61688"
y="774.30139" /> y="774.30139" />
<text <text
sodipodi:linespacing="125%"
id="text4398" id="text4398"
y="802.36218" y="802.36218"
x="570" x="570"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan xml:space="preserve"><tspan
y="802.36218" y="802.36218"
x="570" x="570"
id="tspan4400" id="tspan4400"
sodipodi:role="line">Role</tspan></text> sodipodi:role="line"
style="font-size:15px;line-height:1.25">Role</tspan></text>
</g> </g>
<g <g
id="g4338"> id="g4338">
@ -1114,28 +1081,26 @@
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="690" x="690"
y="677.36218" y="677.36218"
id="text4346" id="text4346"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4348" id="tspan4348"
x="690" x="690"
y="677.36218" y="677.36218"
style="font-size:10px">Is</tspan></text> style="font-size:10px;line-height:1.25">Is</tspan></text>
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="619.09393" x="619.09393"
y="631.24536" y="631.24536"
id="text4350" id="text4350"><tspan
sodipodi:linespacing="125%"><tspan
sodipodi:role="line" sodipodi:role="line"
id="tspan4352" id="tspan4352"
x="619.09393" x="619.09393"
y="631.24536" y="631.24536"
style="font-size:10px">Includes</tspan></text> style="font-size:10px;line-height:1.25">Includes</tspan></text>
<g <g
id="g4359" id="g4359"
transform="translate(341.43156,-241.93151)"> transform="translate(341.43156,-241.93151)">
@ -1159,5 +1124,53 @@
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
</g> </g>
</g> </g>
<rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect1017"
width="100"
height="60"
x="50.000015"
y="533.88507" />
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="60.000015"
y="563.88513"
id="text1021"><tspan
sodipodi:role="line"
id="tspan1019"
x="60.000015"
y="563.88513"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:1.25;font-family:Arial;-inkscape-font-specification:Arial">Address</tspan></text>
<g
transform="translate(1.6497108e-5,29.294424)"
id="g1029">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 150,532.3622 h 70"
id="path1023"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 160,522.3622 v 20 0 0 0"
id="path1025"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 220,522.3622 -10,10 10,10"
id="path1027"
inkscape:connector-curvature="0" />
</g>
<text
id="text1033"
y="552.36218"
x="169.8147"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:'URW Chancery L';-inkscape-font-specification:'URW Chancery L Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start"
y="552.36218"
x="169.8147"
id="tspan1031"
sodipodi:role="line">Contains</tspan></text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -22,7 +22,7 @@ Database reads are probably more infrequent. Each client will obviously need to
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. 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 ### 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. 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.
@ -40,7 +40,53 @@ This means that the maximum number of transactions per second across Scotland is
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? 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 ## 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](https://github.com/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 ### Geographic sharding
@ -56,6 +102,28 @@ The geographic sharding strategy is scalable. We could start with a single serve
But having considerable numbers of database servers will have cost implications. 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.
We can then set up in DNS four addresses:
+----------------------+-----------+-----------+
| Address | longitude | latitude |
+----------------------+-----------+-----------+
| nw.data.yyy.scot | < -4 | >= 56 |
+----------------------+-----------+-----------+
| ne.data.yyy.scot | >= -4 | >= 56 |
+----------------------+-----------+-----------+
| sw.data.yyy.scot | < -4 | < 56 |
+----------------------+-----------+-----------+
| se.data.yyy.scot | >= -4 | < 56 |
+----------------------+-----------+-----------+
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 ### 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. 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.
@ -76,15 +144,13 @@ From the above I think the scaling problem should be addressed as follows:
4. When the initial cluster of three database servers becomes overloaded, shard into two identical groups ('east' and 'west'); 4. When the initial cluster of three database servers becomes overloaded, shard into two identical groups ('east' and 'west');
5. When any shard becomes overloaded, split it into two further shards. 5. 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 and then set geographic polygons to address database requests from clients within a given polygon to the database server for that polygon. If we have prepared for sharding, all that is required is to duplicate the database.
This means that essentially we should set up one polygon for each electoral district from the launch of the app, but initially requests from all of these polygons should be directed to the same database server group. As shards are added, the map of polygons to database server groups should be updated.
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. 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 ## Preparing for sharding
We should prepare a Docker image for the app server and a Docker image for the database server. We should prepare, as part of the app (i.e. not in the database but as a Clojure or Json data file) a datastructure which maps polygons representing each of Scotland's electoral districts to database URLs. For security reasons this datastructure should live server-side and should not be part of the single-page app sent to the client. 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 ## Further reading on optimising Postgres performance

View file

@ -1,4 +1,4 @@
### YouYesYet: User-oriented specification # YouYesYet: User-oriented specification
## Overview ## Overview
@ -24,7 +24,7 @@ It's my hope that we'd have enough Issue Experts sitting in the comfort of their
### Analysts ### Analysts
*Canvassers* are able to see voter canvas 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*. *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 ### Administrators
@ -75,25 +75,35 @@ 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: 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 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*. 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 ## Electors View
The *Electors View* shows a schematic of the registered electors in a dwelling: 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. Below the figure are: 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:
1. One clear 'voting intention' button for each option (e.g., 'Yes', 'No'), greyed unless selected; 1. One clear 'voting intention' button for each option (e.g., 'Yes', 'No'), greyed unless selected;
2. One issues button. 2. One issues button.
Selecting an option icon records that the elector represented by the figure has expressed an opinion that they will vote for that option. Selecting the issues icon brings up and issues view. 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 ## Issues View
@ -110,9 +120,18 @@ The *Issues View* is a simple list of issues:
| Other | | 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. 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 ## Followup Request form
@ -130,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 ## How Street Canvassers will use the system
Street Canvassers will typically use the system by 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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 350 KiB

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