diff --git a/doc/specification/database.md b/doc/specification/database.md new file mode 100644 index 0000000..6239008 --- /dev/null +++ b/doc/specification/database.md @@ -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 + ); diff --git a/doc/specification/entity-relationship-diagram.svg b/doc/specification/entity-relationship-diagram.svg index b20898a..e68f1bf 100644 --- a/doc/specification/entity-relationship-diagram.svg +++ b/doc/specification/entity-relationship-diagram.svg @@ -620,7 +620,7 @@ y="413.67694" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;font-family:Arial;-inkscape-font-specification:Arial">Version: 0.3Version: 0.4 - - - - - - - - - You Yes Yet? - - - +{% extends "base-authenticated.html" %} +{% block whole-page %}
@@ -33,21 +22,16 @@ You must enable JavaScript to use the You Yes Yet app.

- - - - - - - - - - {% script "/js/app.js" %} - - +{% endblock %} +{% block extra-script %} + + + + + + +{% script "/js/app.js" %} +{% endblock %} diff --git a/resources/templates/base-authenticated.html b/resources/templates/base-authenticated.html index 7b2d418..7da389b 100644 --- a/resources/templates/base-authenticated.html +++ b/resources/templates/base-authenticated.html @@ -3,12 +3,14 @@ - + + {{title}} + {% block whole-page %}
@@ -28,12 +34,40 @@
+ {% block big-links %} + {% endblock %} +
- {{content}} + {% block content %} + {% endblock %} +
+
+ + {% endblock %} + + {% block extra-script %} + {% endblock %} diff --git a/resources/templates/base-unauthenticated.html b/resources/templates/base-unauthenticated.html index 7eb5ef5..4e41c8f 100644 --- a/resources/templates/base-unauthenticated.html +++ b/resources/templates/base-unauthenticated.html @@ -6,7 +6,7 @@ - {{title}} + {% block title %}{% endblock %}{{title}}
diff --git a/resources/templates/canvasser.html b/resources/templates/canvasser.html new file mode 100644 index 0000000..b223313 --- /dev/null +++ b/resources/templates/canvasser.html @@ -0,0 +1,60 @@ +{% extends "base-authenticated.html" %} +{% block title %} +{% endblock %} +{% block content %} +
+ {% if canvasser %} + + {% endif %} +

+ + +

+

+ (TODO: Not absolutely sure what I'm going to do for an elector id widget yet.) +

+

+ + {% if address.id %} + + + {{address.address}} + + {% else %} + (TODO: Some sort of address lookup widget goes here.) + {% endif %} +

+

+ + +

+

+ + +

+

+ + +

+ +

+ 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, + introduced_by int references canvassers(id), + authorised boolean + +
+ +{% endblock %} diff --git a/resources/templates/login.html b/resources/templates/login.html index f3e8731..5ee3706 100644 --- a/resources/templates/login.html +++ b/resources/templates/login.html @@ -1,9 +1,4 @@ {% extends "base-unauthenticated.html" %} -{% block big-links %} - -{% endblock %} {% block content %}

We're not going to do login in the long term; we're going to use oauth. diff --git a/resources/templates/roles.html b/resources/templates/roles.html new file mode 100644 index 0000000..6b49d2e --- /dev/null +++ b/resources/templates/roles.html @@ -0,0 +1,16 @@ +{% extends "base-authenticated.html" %} +{% block title %} + {{ user }} +{% endblock %} +{% block big-links %} +

+ {% for role in roles %} + + {% endfor %} +{% endblock %} +{% block content %} +{% endblock %} diff --git a/src/clj/youyesyet/config.clj b/src/clj/youyesyet/config.clj index d3f80e0..2de50fa 100644 --- a/src/clj/youyesyet/config.clj +++ b/src/clj/youyesyet/config.clj @@ -1,4 +1,6 @@ -(ns youyesyet.config +(ns ^{:doc "Read configuration." + :author "Simon Brooke"} + youyesyet.config (:require [cprop.core :refer [load-config]] [cprop.source :as source] [mount.core :refer [args defstate]])) diff --git a/src/clj/youyesyet/db/core.clj b/src/clj/youyesyet/db/core.clj index e514d08..a57fa50 100644 --- a/src/clj/youyesyet/db/core.clj +++ b/src/clj/youyesyet/db/core.clj @@ -1,4 +1,6 @@ -(ns youyesyet.db.core +(ns ^{:doc "Database access functions." + :author "Simon Brooke"} + youyesyet.db.core (:require [cheshire.core :refer [generate-string parse-string]] [clojure.java.jdbc :as jdbc] @@ -15,12 +17,10 @@ Timestamp PreparedStatement])) -(def ^:dynamic *db* {:name "java:comp/env/jdbc/EmployeeDB"}) -;; (defstate ^:dynamic *db* -;; :start (conman/connect! {:jdbc-url-env (env :database-url) -;; :jdbc-url "jdbc:postgresql://127.0.0.1/youyesyet_dev?user=youyesyet&password=thisisnotsecure" -;; :driver-class-name "org.postgresql.Driver"}) -;; :stop (conman/disconnect! *db*)) +(defstate ^:dynamic *db* + :start (conman/connect! {:jdbc-url (env :database-url) + :driver-class-name "org.postgresql.Driver"}) + :stop (conman/disconnect! *db*)) (conman/bind-connection *db* "sql/queries.sql") diff --git a/src/clj/youyesyet/db/schema.clj b/src/clj/youyesyet/db/schema.clj index 7a6c45b..7571251 100644 --- a/src/clj/youyesyet/db/schema.clj +++ b/src/clj/youyesyet/db/schema.clj @@ -1,4 +1,5 @@ -(ns youyesyet.db.schema +(ns ^{:doc "Korma-flavour database setup, now obsolete but retained for documentation." + :author "Simon Brooke"} youyesyet.db.schema (:require [clojure.java.jdbc :as sql] [korma.core :as kc] [youyesyet.db.core :as yyydb])) diff --git a/src/clj/youyesyet/handler.clj b/src/clj/youyesyet/handler.clj index f898fe5..173497e 100644 --- a/src/clj/youyesyet/handler.clj +++ b/src/clj/youyesyet/handler.clj @@ -1,6 +1,9 @@ -(ns youyesyet.handler +(ns ^{:doc "Handlers for starting and stopping the webapp." + :author "Simon Brooke"} + youyesyet.handler (:require [compojure.core :refer [routes wrap-routes]] [youyesyet.layout :refer [error-page]] + [youyesyet.routes.authenticated :refer [authenticated-routes]] [youyesyet.routes.home :refer [home-routes]] [youyesyet.routes.oauth :refer [oauth-routes]] [compojure.route :as route] @@ -10,6 +13,29 @@ [clojure.tools.logging :as log] [youyesyet.config :refer [env]])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.handler: handlers for starting and stopping the webapp. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (mount/defstate init-app :start ((or (:init defaults) identity)) :stop ((or (:stop defaults) identity))) @@ -38,6 +64,7 @@ (wrap-routes middleware/wrap-csrf) (wrap-routes middleware/wrap-formats)) #'oauth-routes + #'authenticated-routes (route/not-found (:body (error-page {:status 404 diff --git a/src/clj/youyesyet/layout.clj b/src/clj/youyesyet/layout.clj index aaec434..d131cb7 100644 --- a/src/clj/youyesyet/layout.clj +++ b/src/clj/youyesyet/layout.clj @@ -1,4 +1,6 @@ -(ns youyesyet.layout +(ns^{:doc "Render web pages using Selmer tamplating markup." + :author "Simon Brooke"} + youyesyet.layout (:require [selmer.parser :as parser] [selmer.filters :as filters] [markdown.core :refer [md-to-html-string]] @@ -12,6 +14,7 @@ (parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) (filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)])) + (defn render "renders the HTML template located relative to resources/templates" [template & [params]] @@ -26,6 +29,7 @@ :version (System/getProperty "youyesyet.version")))) "text/html; charset=utf-8")) + (defn error-page "error-details should be a map containing the following keys: :status - error status diff --git a/src/clj/youyesyet/middleware.clj b/src/clj/youyesyet/middleware.clj index 1775cb9..2288720 100644 --- a/src/clj/youyesyet/middleware.clj +++ b/src/clj/youyesyet/middleware.clj @@ -1,4 +1,6 @@ -(ns youyesyet.middleware +(ns ^{:doc "Plumbing, mainly boilerplate from Luminus." + :author "Simon Brooke"} + youyesyet.middleware (:require [youyesyet.env :refer [defaults]] [clojure.tools.logging :as log] [youyesyet.layout :refer [*app-context* error-page]] diff --git a/src/clj/youyesyet/routes/administrator.clj b/src/clj/youyesyet/routes/administrator.clj new file mode 100644 index 0000000..7a08b36 --- /dev/null +++ b/src/clj/youyesyet/routes/administrator.clj @@ -0,0 +1,11 @@ +(ns ^{:doc "Routes/pages available to administrators, only." + :author "Simon Brooke"} + youyesyet.routes.administrator + (:require [clojure.java.io :as io] + [clojure.walk :refer [keywordize-keys]] + [compojure.core :refer [defroutes GET POST]] + [noir.response :as nresponse] + [noir.util.route :as route] + [ring.util.http-response :as response] + [youyesyet.layout :as layout] + [youyesyet.db.core :as db])) diff --git a/src/clj/youyesyet/routes/authenticated.clj b/src/clj/youyesyet/routes/authenticated.clj new file mode 100644 index 0000000..b02c512 --- /dev/null +++ b/src/clj/youyesyet/routes/authenticated.clj @@ -0,0 +1,75 @@ +(ns ^{:doc "Routes/pages available to all authenticated users." + :author "Simon Brooke"} + youyesyet.routes.authenticated + (:require [clojure.java.io :as io] + [clojure.walk :refer [keywordize-keys]] + [compojure.core :refer [defroutes GET POST]] + [noir.response :as nresponse] + [noir.util.route :as route] + [ring.util.http-response :as response] + [youyesyet.layout :as layout] + [youyesyet.db.core :as db])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.routes.authenticated: routes and pages for authenticated users. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; This code adapted from http://www.luminusweb.net/docs#accessing_the_database + +(defn post? + "Return true if the argument is a ring request which is a post request" + [request] + true) + +(defn canvasser-page + "Process this canvasser request, and render the canvasser page" + [request] + (let [canvasser (if + (:params request) + (let [params (:params request)] + (if (:id params) + (if (post? request) + (db/update-canvasser! params) + (db/create-canvasser! params)) + (db/get-canvasser (:id params))) + ))] + (layout/render + "canvasser.html" + {:title (if canvasser + (str + "Edit canvasser " + (:fullname canvasser) + " " + (:email canvasser)) + "Add new canvasser") + :canvasser canvasser + :address (if (:address_id canvasser) (db/get-address (:address_id canvasser)))}))) + +(defn routing-page + "Render the routing page, which offers routes according to the user's roles" + [] + (layout/render "routing.html")) + +(defroutes authenticated-routes + (GET "/edit-canvasser" request (canvasser-page request)) + (POST "/edit-canvasser" request (canvasser-page request)) + (GET "/routing" [] (routing-page))) diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj index b44a289..9fbc094 100644 --- a/src/clj/youyesyet/routes/home.clj +++ b/src/clj/youyesyet/routes/home.clj @@ -1,4 +1,5 @@ -(ns youyesyet.routes.home +(ns ^{:doc "Routes/pages available to unauthenticated users." + :author "Simon Brooke"} youyesyet.routes.home (:require [clojure.walk :refer [keywordize-keys]] [noir.response :as nresponse] [noir.util.route :as route] @@ -8,12 +9,37 @@ [ring.util.http-response :as response] [clojure.java.io :as io])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.routes.home: routes and pages for unauthenticated users. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defn app-page [] (layout/render "app.html")) + (defn about-page [] (layout/render "about.html")) + (defn call-me-page [request] (if request @@ -25,28 +51,53 @@ ;; TODO: Issues need to be fetched from the database :concerns nil}))) + +(defn roles-page [request] + (let + [session (:session request) + username (:user session) + user (if username (db-core/get-canvasser-by-username db-core/*db* {:username username})) + roles (if user (db-core/get-roles-by-canvasser db-core/*db* {:canvasser (:id user)}))] + (cond + roles (layout/render "roles.html" + {:title (str "Welcome " (:fullname user) ", what do you want to do?") + :user user + :roles roles}) + (empty? roles)(response/found "/app") + true (assoc (response/found "/login") :session (dissoc session :user)) + ))) + + (defn home-page [] (layout/render "home.html" {:title "You Yes Yet?"})) + (defn login-page "This is very temporary. We're going to do authentication by oauth." [request] (let [params (keywordize-keys (:form-params request)) session (:session request) username (:username params) + user (if username (db-core/get-canvasser-by-username db-core/*db* {:username username})) password (:password params) - redirect-to (or (:redirect-to params) "app")] - (if - (and (= username "test") (= password "test")) - (do - (assoc (response/found redirect-to) :session (assoc session :user username))) - (layout/render "login.html" {:title "Please log in" :redirect-to redirect-to})))) + redirect-to (or (:redirect-to params) "roles")] + (cond + ;; this is obviously, ABSURDLY, insecure. I don't want to put just-about-good-enough, + ;; it-will-do-for-now security in place; instead, I want this to be test code only + ;; until we have o-auth properly working. + (and user (= username password)) + (assoc (response/found redirect-to) :session (assoc session :user username)) + user + (layout/render "login.html" {:title (str "User " username " is unknown") :redirect-to redirect-to}) + true + (layout/render "login.html" {:title "Please log in" :redirect-to redirect-to})))) (defroutes home-routes (GET "/" [] (home-page)) (GET "/home" [] (home-page)) (GET "/about" [] (about-page)) + (GET "/roles" request (route/restricted (roles-page request))) (GET "/app" [] (route/restricted (app-page))) (GET "/call-me" [] (call-me-page nil)) (POST "/call-me" request (call-me-page request)) diff --git a/src/clj/youyesyet/routes/oauth.clj b/src/clj/youyesyet/routes/oauth.clj index d916e10..a7c7e46 100644 --- a/src/clj/youyesyet/routes/oauth.clj +++ b/src/clj/youyesyet/routes/oauth.clj @@ -1,4 +1,5 @@ -(ns youyesyet.routes.oauth +(ns ^{:doc "OAuth authentication routes - not finished, does not work yet." + :author "Simon Brooke"} youyesyet.routes.oauth (:require [compojure.core :refer [defroutes GET]] [ring.util.http-response :refer [ok found]] [clojure.java.io :as io] diff --git a/src/clj/youyesyet/routes/services.clj b/src/clj/youyesyet/routes/services.clj index c0de2b5..43347c7 100644 --- a/src/clj/youyesyet/routes/services.clj +++ b/src/clj/youyesyet/routes/services.clj @@ -1,4 +1,5 @@ -(ns youyesyet.routes.services +(ns ^{:doc "REST API." + :author "Simon Brooke"} youyesyet.routes.services (:require [clj-http.client :as client] [ring.util.http-response :refer :all] [compojure.api.sweet :refer :all] diff --git a/src/cljc/youyesyet/outqueue.cljc b/src/cljc/youyesyet/outqueue.cljc index f2f0df3..8386d4a 100644 --- a/src/cljc/youyesyet/outqueue.cljc +++ b/src/cljc/youyesyet/outqueue.cljc @@ -1,4 +1,6 @@ -(ns youyesyet.outqueue +(ns ^{:doc "Queue of messages waiting to be sent to the server." + :author "Simon Brooke"} + youyesyet.outqueue (:require #?(:clj [clojure.core] :cljs [reagent.core :refer [atom]]))) @@ -41,6 +43,7 @@ (reverse items) (list items))}))) + (defn add! "Add this item to the queue." [q item] @@ -49,7 +52,9 @@ (assoc a :items (cons item (:items a)))))) -(defn q? + +(defn queue? + "True if x is a queue, else false." [x] (try (let [q (deref x) @@ -61,21 +66,25 @@ #?(:clj (print (.getMessage any)) :cljs (js/console.log (str any)))))) + (defn peek "Look at the next item which could be removed from the queue." [q] (last (:items (deref q)))) + (defn locked? [q] (:locked (deref q))) + (defn unlock! ([q ] (unlock! q true)) ([q value] (swap! q (fn [a] (assoc a :locked (not (true? value))))))) + (defn lock! [q] (unlock! q false)) @@ -86,6 +95,7 @@ [q] (count (deref q))) + (defn take! "Return the first item from the queue, rebind the queue to the remaining items. If the queue is empty return nil." @@ -97,12 +107,13 @@ (assoc (assoc a :items new-queue) :v item)))) (:v (deref q))) + (defn maybe-process-next "Apply this process, assumed to be a function of one argument, to the next item in the queue, if the queue is not currently locked; return the value returned by process." [q process] - (if (and (q? q)(not (locked? q))) + (if (and (queue? q)(not (locked? q))) (try (lock! q) (let [v (apply process (list (peek q)))] diff --git a/src/cljc/youyesyet/validation.cljc b/src/cljc/youyesyet/validation.cljc index fd01d8f..7c293dd 100644 --- a/src/cljc/youyesyet/validation.cljc +++ b/src/cljc/youyesyet/validation.cljc @@ -1,3 +1,26 @@ (ns youyesyet.validation (:require [bouncer.core :as b] [bouncer.validators :as v])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.validation: +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/cljs/youyesyet/ajax.cljs b/src/cljs/youyesyet/ajax.cljs deleted file mode 100644 index 07cf00d..0000000 --- a/src/cljs/youyesyet/ajax.cljs +++ /dev/null @@ -1,20 +0,0 @@ -(ns youyesyet.ajax - (:require [ajax.core :as ajax])) - -(defn local-uri? [{:keys [uri]}] - (not (re-find #"^\w+?://" uri))) - -(defn default-headers [request] - (if (local-uri? request) - (-> request - (update :uri #(str js/context %)) - (update :headers #(merge {"x-csrf-token" js/csrfToken} %))) - request)) - -(defn load-interceptors! [] - (swap! ajax/default-interceptors - conj - (ajax/to-interceptor {:name "default headers" - :request default-headers}))) - - diff --git a/src/cljs/youyesyet/canvasser_app/ajax.cljs b/src/cljs/youyesyet/canvasser_app/ajax.cljs new file mode 100644 index 0000000..3db48fc --- /dev/null +++ b/src/cljs/youyesyet/canvasser_app/ajax.cljs @@ -0,0 +1,46 @@ +(ns ^{:doc "Canvasser app transciever for ajax packets." + :author "Simon Brooke"} + youyesyet.canvasser-app.ajax + (:require [ajax.core :as ajax])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; youyesyet.canvasser-app.ajax: transciever for ajax packets. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2016 Simon Brooke for Radical Independence Campaign +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defn local-uri? [{:keys [uri]}] + (not (re-find #"^\w+?://" uri))) + +(defn default-headers [request] + (if (local-uri? request) + (-> request + (update :uri #(str js/context %)) + (update :headers #(merge {"x-csrf-token" js/csrfToken} %))) + request)) + +(defn load-interceptors! [] + (swap! ajax/default-interceptors + conj + (ajax/to-interceptor {:name "default headers" + :request default-headers}))) + + diff --git a/src/cljs/youyesyet/core.cljs b/src/cljs/youyesyet/canvasser_app/core.cljs similarity index 84% rename from src/cljs/youyesyet/core.cljs rename to src/cljs/youyesyet/canvasser_app/core.cljs index 27e831c..971261e 100644 --- a/src/cljs/youyesyet/core.cljs +++ b/src/cljs/youyesyet/canvasser_app/core.cljs @@ -1,4 +1,6 @@ -(ns youyesyet.core +(ns ^{:doc "Canvasser app navigation and routing." + :author "Simon Brooke"} + youyesyet.canvasser-app.core (:require cljsjs.react-leaflet [ajax.core :refer [GET POST]] [goog.events :as events] @@ -7,21 +9,21 @@ [reagent.core :as r] [re-frame.core :as rf] [secretary.core :as secretary] - [youyesyet.ajax :refer [load-interceptors!]] - [youyesyet.handlers] - [youyesyet.subscriptions] - [youyesyet.ui-utils :as ui] - [youyesyet.views.about :as about] - [youyesyet.views.electors :as electors] - [youyesyet.views.followup :as followup] - [youyesyet.views.issue :as issue] - [youyesyet.views.issues :as issues] - [youyesyet.views.map :as maps]) + [youyesyet.canvasser-app.ajax :refer [load-interceptors!]] + [youyesyet.canvasser-app.handlers] + [youyesyet.canvasser-app.subscriptions] + [youyesyet.canvasser-app.ui-utils :as ui] + [youyesyet.canvasser-app.views.about :as about] + [youyesyet.canvasser-app.views.electors :as electors] + [youyesyet.canvasser-app.views.followup :as followup] + [youyesyet.canvasser-app.views.issue :as issue] + [youyesyet.canvasser-app.views.issues :as issues] + [youyesyet.canvasser-app.views.map :as maps]) (:import goog.History)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.core: core of the app. +;;;; youyesyet.canvasser-app.core: core of the app. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/handlers.cljs b/src/cljs/youyesyet/canvasser_app/handlers.cljs similarity index 96% rename from src/cljs/youyesyet/handlers.cljs rename to src/cljs/youyesyet/canvasser_app/handlers.cljs index deb11f4..e94d1b2 100644 --- a/src/cljs/youyesyet/handlers.cljs +++ b/src/cljs/youyesyet/canvasser_app/handlers.cljs @@ -1,7 +1,9 @@ -(ns youyesyet.handlers +(ns ^{:doc "Canvasser app event handlers." + :author "Simon Brooke"} + youyesyet.canvasser-app.handlers (:require [cljs.reader :refer [read-string]] [re-frame.core :refer [dispatch reg-event-db]] - [youyesyet.db :as db] + [youyesyet.canvasser-app.state :as db] )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/cljs/youyesyet/db.cljs b/src/cljs/youyesyet/canvasser_app/state.cljs similarity index 96% rename from src/cljs/youyesyet/db.cljs rename to src/cljs/youyesyet/canvasser_app/state.cljs index 9659c44..711413e 100644 --- a/src/cljs/youyesyet/db.cljs +++ b/src/cljs/youyesyet/canvasser_app/state.cljs @@ -1,8 +1,10 @@ -(ns youyesyet.db) +(ns ^{:doc "Canvasser app client state." + :author "Simon Brooke"} + youyesyet.canvasser-app.state) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.db: the state of the app. +;;;; youyesyet.canvasser-app.state: the state of the app. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/subscriptions.cljs b/src/cljs/youyesyet/canvasser_app/subscriptions.cljs similarity index 93% rename from src/cljs/youyesyet/subscriptions.cljs rename to src/cljs/youyesyet/canvasser_app/subscriptions.cljs index b1aa09c..c3d18e5 100644 --- a/src/cljs/youyesyet/subscriptions.cljs +++ b/src/cljs/youyesyet/canvasser_app/subscriptions.cljs @@ -1,4 +1,6 @@ -(ns youyesyet.subscriptions +(ns ^{:doc "Canvasser app event subscriptions." + :author "Simon Brooke"} + youyesyet.canvasser-app.subscriptions (:require [re-frame.core :refer [reg-sub]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/cljs/youyesyet/ui_utils.cljs b/src/cljs/youyesyet/canvasser_app/ui_utils.cljs similarity index 95% rename from src/cljs/youyesyet/ui_utils.cljs rename to src/cljs/youyesyet/canvasser_app/ui_utils.cljs index 719090b..068bad6 100644 --- a/src/cljs/youyesyet/ui_utils.cljs +++ b/src/cljs/youyesyet/canvasser_app/ui_utils.cljs @@ -1,4 +1,6 @@ -(ns youyesyet.ui-utils +(ns ^{:doc "Canvasser app user interface widgets." + :author "Simon Brooke"} + youyesyet.canvasser-app.ui-utils (:require [reagent.core :as r] [re-frame.core :as rf])) diff --git a/src/cljs/youyesyet/views/about.cljs b/src/cljs/youyesyet/canvasser_app/views/about.cljs similarity index 91% rename from src/cljs/youyesyet/views/about.cljs rename to src/cljs/youyesyet/canvasser_app/views/about.cljs index 4ca4a4a..6b4d9ca 100644 --- a/src/cljs/youyesyet/views/about.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/about.cljs @@ -1,11 +1,13 @@ -(ns youyesyet.views.about +(ns ^{:doc "Canvasser app about panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.about (:require [re-frame.core :refer [reg-sub subscribe]] [markdown.core :refer [md->html]] - [youyesyet.ui-utils :as ui])) + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.about: about/credits view for youyesyet. +;;;; youyesyet.canvasser-app.views.about: about/credits view for youyesyet. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/views/building.cljs b/src/cljs/youyesyet/canvasser_app/views/building.cljs similarity index 87% rename from src/cljs/youyesyet/views/building.cljs rename to src/cljs/youyesyet/canvasser_app/views/building.cljs index 01d3dd6..40e5d0e 100644 --- a/src/cljs/youyesyet/views/building.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/building.cljs @@ -1,9 +1,11 @@ -(ns youyesyet.views.building +(ns ^{:doc "Canvasser app households in building panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.building (:require [re-frame.core :refer [reg-sub]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.building: building view for youyesyet. +;;;; youyesyet.canvasser-app.views.building: building view for youyesyet. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/views/electors.cljs b/src/cljs/youyesyet/canvasser_app/views/electors.cljs similarity index 94% rename from src/cljs/youyesyet/views/electors.cljs rename to src/cljs/youyesyet/canvasser_app/views/electors.cljs index 05f7af4..aee2d4d 100644 --- a/src/cljs/youyesyet/views/electors.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/electors.cljs @@ -1,11 +1,13 @@ -(ns youyesyet.views.electors +(ns ^{:doc "Canvasser app electors in household panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.electors (:require [reagent.core :refer [atom]] [re-frame.core :refer [reg-sub subscribe dispatch]] - [youyesyet.ui-utils :as ui])) + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.electors: electors view for youyesyet. +;;;; youyesyet.canvasser-app.views.electors: electors view for youyesyet. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/views/followup.cljs b/src/cljs/youyesyet/canvasser_app/views/followup.cljs similarity index 92% rename from src/cljs/youyesyet/views/followup.cljs rename to src/cljs/youyesyet/canvasser_app/views/followup.cljs index 78c0c04..b81ff58 100644 --- a/src/cljs/youyesyet/views/followup.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/followup.cljs @@ -1,10 +1,12 @@ -(ns youyesyet.views.followup +(ns ^{:doc "Canvasser followup request form panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.followup (:require [re-frame.core :refer [reg-sub subscribe dispatch]] - [youyesyet.ui-utils :as ui])) + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.followup-request: followup-request view for youyesyet. +;;;; youyesyet.canvasser-app.views.followup-request: followup-request view for youyesyet. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/views/issue.cljs b/src/cljs/youyesyet/canvasser_app/views/issue.cljs similarity index 88% rename from src/cljs/youyesyet/views/issue.cljs rename to src/cljs/youyesyet/canvasser_app/views/issue.cljs index 20620d6..34afe10 100644 --- a/src/cljs/youyesyet/views/issue.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issue.cljs @@ -1,8 +1,10 @@ -(ns youyesyet.views.issue +(ns ^{:doc "Canvasser app current issue detail panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.issue (:require [re-frame.core :refer [reg-sub subscribe]] [markdown.core :refer [md->html]] - [youyesyet.ui-utils :as ui] - [youyesyet.views.issues :as issues])) + [youyesyet.canvasser-app.ui-utils :as ui] + [youyesyet.canvasser-app.views.issues :as issues])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; diff --git a/src/cljs/youyesyet/views/issues.cljs b/src/cljs/youyesyet/canvasser_app/views/issues.cljs similarity index 88% rename from src/cljs/youyesyet/views/issues.cljs rename to src/cljs/youyesyet/canvasser_app/views/issues.cljs index ea8f383..8aa1744 100644 --- a/src/cljs/youyesyet/views/issues.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/issues.cljs @@ -1,10 +1,12 @@ -(ns youyesyet.views.issues +(ns ^{:doc "Canvasser app current issues list panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.issues (:require [re-frame.core :refer [reg-sub subscribe]] - [youyesyet.ui-utils :as ui])) + [youyesyet.canvasser-app.ui-utils :as ui])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.issues: issues view for youyesyet. +;;;; youyesyet.canvasser-app.views.issues: issues view for youyesyet. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/src/cljs/youyesyet/views/map.cljs b/src/cljs/youyesyet/canvasser_app/views/map.cljs similarity index 96% rename from src/cljs/youyesyet/views/map.cljs rename to src/cljs/youyesyet/canvasser_app/views/map.cljs index c725531..6df73dd 100644 --- a/src/cljs/youyesyet/views/map.cljs +++ b/src/cljs/youyesyet/canvasser_app/views/map.cljs @@ -1,10 +1,12 @@ -(ns youyesyet.views.map +(ns ^{:doc "Canvasser app map view panel." + :author "Simon Brooke"} + youyesyet.canvasser-app.views.map (:require [re-frame.core :refer [reg-sub subscribe dispatch]] [reagent.core :as reagent])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; -;;;; youyesyet.views.map: map view for youyesyet. +;;;; youyesyet.canvasser-app.views.map: map view for youyesyet. ;;;; ;;;; This program is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU General Public License diff --git a/test/cljs/youyesyet/core_test.cljs b/test/cljs/youyesyet/canvasser_app/core_test.cljs similarity index 65% rename from test/cljs/youyesyet/core_test.cljs rename to test/cljs/youyesyet/canvasser_app/core_test.cljs index 7de65c8..d0f5e40 100644 --- a/test/cljs/youyesyet/core_test.cljs +++ b/test/cljs/youyesyet/canvasser_app/core_test.cljs @@ -1,7 +1,7 @@ -(ns youyesyet.core-test +(ns youyesyet.canvasser-app.core-test (:require [cljs.test :refer-macros [is are deftest testing use-fixtures]] [reagent.core :as reagent :refer [atom]] - [youyesyet.core :as rc])) + [youyesyet.canvasser-app.core :as rc])) (deftest test-home (is (= true true))) diff --git a/test/cljs/youyesyet/canvasser_app/doo_runner.cljs b/test/cljs/youyesyet/canvasser_app/doo_runner.cljs new file mode 100644 index 0000000..f6c89dd --- /dev/null +++ b/test/cljs/youyesyet/canvasser_app/doo_runner.cljs @@ -0,0 +1,6 @@ +(ns youyesyet.canvasser-app.doo-runner + (:require [doo.runner :refer-macros [doo-tests]] + [youyesyet.canvasser-app.core-test])) + +(doo-tests 'youyesyet.canvasser-app.canvasser-app.core-test) + diff --git a/test/cljs/youyesyet/doo_runner.cljs b/test/cljs/youyesyet/doo_runner.cljs deleted file mode 100644 index 486a282..0000000 --- a/test/cljs/youyesyet/doo_runner.cljs +++ /dev/null @@ -1,6 +0,0 @@ -(ns youyesyet.doo-runner - (:require [doo.runner :refer-macros [doo-tests]] - [youyesyet.core-test])) - -(doo-tests 'youyesyet.core-test) -