Merge branch 'feature/5' into feature/41
This commit is contained in:
commit
88c5c74c52
235
doc/specification/database.md
Normal file
235
doc/specification/database.md
Normal 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
|
||||||
|
);
|
|
@ -620,7 +620,7 @@
|
||||||
y="413.67694"
|
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"><tspan
|
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: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.3</tspan><tspan
|
id="tspan4371">Version: </tspan>0.4</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
x="472.0152"
|
x="472.0152"
|
||||||
y="432.42694"
|
y="432.42694"
|
||||||
|
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
@ -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,7 @@ 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
|
### Caching and memoizing
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
### YouYesYet: User-oriented specification
|
# YouYesYet: User-oriented specification
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
|
4
env/dev/cljs/youyesyet/dev.cljs
vendored
4
env/dev/cljs/youyesyet/dev.cljs
vendored
|
@ -1,5 +1,5 @@
|
||||||
(ns ^:figwheel-no-load youyesyet.app
|
(ns ^:figwheel-no-load youyesyet.canvasser-app.app
|
||||||
(:require [youyesyet.core :as core]
|
(:require [youyesyet.canvasser-app.core :as core]
|
||||||
[devtools.core :as devtools]
|
[devtools.core :as devtools]
|
||||||
[figwheel.client :as figwheel :include-macros true]))
|
[figwheel.client :as figwheel :include-macros true]))
|
||||||
|
|
||||||
|
|
2
env/prod/cljs/youyesyet/prod.cljs
vendored
2
env/prod/cljs/youyesyet/prod.cljs
vendored
|
@ -1,5 +1,5 @@
|
||||||
(ns youyesyet.app
|
(ns youyesyet.app
|
||||||
(:require [youyesyet.core :as core]))
|
(:require [youyesyet.canvasser-app.core :as core]))
|
||||||
|
|
||||||
;;ignore println statements in prod
|
;;ignore println statements in prod
|
||||||
(set! *print-fn* (fn [& _]))
|
(set! *print-fn* (fn [& _]))
|
||||||
|
|
|
@ -55,12 +55,17 @@
|
||||||
[lein-cljsbuild "1.1.4"]
|
[lein-cljsbuild "1.1.4"]
|
||||||
[lein-uberwar "0.2.0"]
|
[lein-uberwar "0.2.0"]
|
||||||
[lein-bower "0.5.1"]
|
[lein-bower "0.5.1"]
|
||||||
[lein-less "1.7.5"]]
|
[lein-less "1.7.5"]
|
||||||
|
[lein-codox "0.10.3"]]
|
||||||
|
|
||||||
:bower-dependencies [[leaflet "0.7.3"]]
|
:bower-dependencies [[leaflet "0.7.3"]]
|
||||||
|
|
||||||
:cucumber-feature-paths ["test/clj/features"]
|
:cucumber-feature-paths ["test/clj/features"]
|
||||||
|
|
||||||
|
:codox {:metadata {:doc "FIXME: write docs"}
|
||||||
|
:languages [:clojure :clojurescript]
|
||||||
|
:source-paths ["src/clj" "src/cljc" "src/cljs"]}
|
||||||
|
|
||||||
:uberwar
|
:uberwar
|
||||||
{:handler youyesyet.handler/app
|
{:handler youyesyet.handler/app
|
||||||
:init youyesyet.handler/init
|
:init youyesyet.handler/init
|
||||||
|
@ -121,7 +126,7 @@
|
||||||
{:app
|
{:app
|
||||||
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
|
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
|
||||||
:compiler
|
:compiler
|
||||||
{:main "youyesyet.app"
|
{:main "youyesyet.canvasser-app.app"
|
||||||
:asset-path "/js/out"
|
:asset-path "/js/out"
|
||||||
:output-to "target/cljsbuild/public/js/app.js"
|
:output-to "target/cljsbuild/public/js/app.js"
|
||||||
:output-dir "target/cljsbuild/public/js/out"
|
:output-dir "target/cljsbuild/public/js/out"
|
||||||
|
|
|
@ -118,15 +118,16 @@ ALTER TABLE public.authorities OWNER TO youyesyet;
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS canvassers (
|
CREATE TABLE IF NOT EXISTS canvassers (
|
||||||
id serial,
|
id serial,
|
||||||
username character varying(32) NOT NULL,
|
username character varying(32) NOT NULL,
|
||||||
fullname character varying(64) NOT NULL,
|
fullname character varying(64) NOT NULL,
|
||||||
elector_id integer,
|
elector_id integer,
|
||||||
address_id integer NOT NULL,
|
address_id integer NOT NULL,
|
||||||
phone character varying(16),
|
phone character varying(16),
|
||||||
email character varying(128),
|
email character varying(128),
|
||||||
authority_id character varying(32) NOT NULL,
|
authority_id character varying(32) NOT NULL,
|
||||||
authorised boolean
|
introduced_by int references canvassers(id),
|
||||||
|
authorised boolean
|
||||||
);
|
);
|
||||||
--;;
|
--;;
|
||||||
|
|
||||||
|
@ -534,6 +535,8 @@ ALTER TABLE ONLY canvassers
|
||||||
ADD CONSTRAINT canvassers_elector_id_fkey FOREIGN KEY (elector_id) REFERENCES electors(id);
|
ADD CONSTRAINT canvassers_elector_id_fkey FOREIGN KEY (elector_id) REFERENCES electors(id);
|
||||||
--;;
|
--;;
|
||||||
|
|
||||||
|
create unique index canvassers_username_ix on canvassers (username);
|
||||||
|
create unique index canvassers_email_ix on canvassers(email);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: electors_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet
|
-- Name: electors_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: youyesyet
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
-- this is just a teardown of everything set up in the corresponding .up.sql file
|
||||||
|
|
||||||
|
delete from roles where name = 'Expert';
|
||||||
|
delete from roles where name = 'Administrator';
|
||||||
|
delete from roles where name = 'Recruiter';
|
||||||
|
delete from roles where name = 'Organiser';
|
||||||
|
delete from roles where name = 'Editor';
|
||||||
|
|
||||||
|
alter table issues drop column content;
|
||||||
|
alter table issues drop column current;
|
||||||
|
|
||||||
|
delete from issues where id = 'Currency';
|
||||||
|
delete from issues where id = 'Monarchy';
|
||||||
|
delete from issues where id = 'Defence';
|
||||||
|
|
||||||
|
delete from options where id = 'Yes';
|
||||||
|
delete from options where id = 'No';
|
|
@ -0,0 +1,58 @@
|
||||||
|
|
||||||
|
-- We don't explicitly instantiate the 'Canvasser' role since every user is
|
||||||
|
-- deemed to be a canvasser.
|
||||||
|
|
||||||
|
|
||||||
|
-- an 'Expert' is someone with expertise in one or more issues, who is
|
||||||
|
-- trusted to discuss those issues in detail with electors.
|
||||||
|
insert into roles (name) values ('Expert');
|
||||||
|
|
||||||
|
|
||||||
|
-- an 'Administrator' is someone entitled to broadly alter reference data
|
||||||
|
-- throughout the system.
|
||||||
|
insert into roles (name) values ('Administrator');
|
||||||
|
|
||||||
|
|
||||||
|
-- a 'Recruiter' is someone entitled to invite other people to become users
|
||||||
|
-- ('Canvassers'). A Recruiter is entitled to lock the account of anyone they
|
||||||
|
-- have recruited, recursively.
|
||||||
|
insert into roles (name) values ('Recruiter');
|
||||||
|
|
||||||
|
|
||||||
|
-- an 'Organiser' is someone who organises one or more local teams. An Organiser
|
||||||
|
-- is entitled to exclude any Canvasser from any team they organise.
|
||||||
|
insert into roles (name) values ('Organiser');
|
||||||
|
|
||||||
|
|
||||||
|
-- an 'Editor' is someone entitled to add and edit issues.
|
||||||
|
insert into roles (name) values ('Editor');
|
||||||
|
|
||||||
|
-- issue text is local; there may still in addition be a further link to more
|
||||||
|
-- information, but the basic issue text should be part of the issue record.
|
||||||
|
-- The text should fit on a phone screen without scrolling, so is reasonably
|
||||||
|
-- short.
|
||||||
|
alter table issues add column content varchar(1024);
|
||||||
|
|
||||||
|
-- an issue may be current or not current; when not current it is not deleted
|
||||||
|
-- from the system but kept because it may become current again later. Only
|
||||||
|
-- current issues are shown in the app. Typically not fewer than three and not
|
||||||
|
-- more than about seven issues should be current at any time.
|
||||||
|
alter table issues add column current boolean default false;
|
||||||
|
|
||||||
|
insert into issues (id, content, current) values ('Currency',
|
||||||
|
'Scotland could keep the Pound, or use the Euro. But we could also set up a new currency of our own.',
|
||||||
|
true);
|
||||||
|
|
||||||
|
insert into issues (id, content, current) values ('Monarchy',
|
||||||
|
'Scotland could keep the Queen. This is an issue to be decided after independence.',
|
||||||
|
true);
|
||||||
|
|
||||||
|
insert into issues (id, content, current) values ('Defence',
|
||||||
|
'Scotland will not have nuclear weapons, and will probably not choose to engage in far-off wars. But we could remain members of NATO.',
|
||||||
|
true);
|
||||||
|
|
||||||
|
|
||||||
|
insert into options (id) values ('Yes');
|
||||||
|
|
||||||
|
insert into options (id) values ('No');
|
||||||
|
|
11
resources/migrations/20170415102900-core-views.down.sql
Normal file
11
resources/migrations/20170415102900-core-views.down.sql
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
drop view if exists roles_by_canvasser;
|
||||||
|
|
||||||
|
drop view if exists teams_by_canvasser;
|
||||||
|
|
||||||
|
drop view if exists canvassers_by_team;
|
||||||
|
|
||||||
|
drop view if exists canvassers_by_introducer;
|
||||||
|
|
||||||
|
drop view if exists teams_by_organiser;
|
||||||
|
|
||||||
|
drop view if exists organisers_by_team;
|
59
resources/migrations/20170415102900-core-views.up.sql
Normal file
59
resources/migrations/20170415102900-core-views.up.sql
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
create view roles_by_canvasser as
|
||||||
|
select canvassers.id as canvasser, roles.name
|
||||||
|
from roles, rolememberships, canvassers
|
||||||
|
where roles.id = rolememberships.role_id
|
||||||
|
and canvassers.id = rolememberships.canvasser_id
|
||||||
|
and canvassers.authorised = true;
|
||||||
|
|
||||||
|
create view teams_by_canvasser as
|
||||||
|
select canvassers.id as canvasser, teams.id, teams.name, teams.latitude, teams.longitude
|
||||||
|
from teams, teammemberships, canvassers
|
||||||
|
where teams.id = teammemberships.team_id
|
||||||
|
and canvassers.id = teammemberships.canvasser_id;
|
||||||
|
|
||||||
|
create view canvassers_by_team as
|
||||||
|
select teams.id as team,
|
||||||
|
canvassers.id,
|
||||||
|
canvassers.username,
|
||||||
|
canvassers.fullname,
|
||||||
|
canvassers.email,
|
||||||
|
canvassers.phone
|
||||||
|
from teams, teammemberships, canvassers
|
||||||
|
where teams.id = teammemberships.team_id
|
||||||
|
and canvassers.id = teammemberships.canvasser_id
|
||||||
|
and canvassers.authorised = true;
|
||||||
|
|
||||||
|
create view canvassers_by_introducer as
|
||||||
|
select introducers.id as introducer,
|
||||||
|
canvassers.id as canvasser,
|
||||||
|
canvassers.username,
|
||||||
|
canvassers.fullname,
|
||||||
|
canvassers.email,
|
||||||
|
canvassers.phone,
|
||||||
|
canvassers.authorised
|
||||||
|
from canvassers, canvassers as introducers
|
||||||
|
where introducers.id = canvassers.introduced_by;
|
||||||
|
|
||||||
|
create view teams_by_organiser as
|
||||||
|
select canvassers.id as organiser,
|
||||||
|
teams.id,
|
||||||
|
teams.name,
|
||||||
|
teams.latitude,
|
||||||
|
teams.longitude
|
||||||
|
from teams, teamorganiserships, canvassers
|
||||||
|
where teams.id = teamorganiserships.team_id
|
||||||
|
and canvassers.id = teamorganiserships.canvasser_id
|
||||||
|
and canvassers.authorised = true;
|
||||||
|
|
||||||
|
create view organisers_by_team as
|
||||||
|
select teams.id as team,
|
||||||
|
canvassers.id,
|
||||||
|
canvassers.username,
|
||||||
|
canvassers.fullname,
|
||||||
|
canvassers.email,
|
||||||
|
canvassers.phone
|
||||||
|
from teams, teamorganiserships, canvassers
|
||||||
|
where teams.id = teamorganiserships.team_id
|
||||||
|
and canvassers.id = teamorganiserships.canvasser_id
|
||||||
|
and canvassers.authorised = true;
|
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS dwellings (
|
CREATE TABLE IF NOT EXISTS dwellings (
|
||||||
id serial NOT NULL primary key,
|
id serial NOT NULL primary key,
|
||||||
address_id integer NOT NULL,
|
address_id integer NOT NULL references addresses(id),
|
||||||
sub_address varchar(16)
|
sub_address varchar(16)
|
||||||
);
|
);
|
||||||
--;;
|
--;;
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#nav-menu {
|
#nav-menu {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav menu li {
|
#nav menu li {
|
||||||
|
|
|
@ -1,21 +1,309 @@
|
||||||
-- :name create-user! :! :n
|
------------------------------------------------------------------------------;
|
||||||
-- :doc creates a new user record
|
----
|
||||||
INSERT INTO users
|
---- youyesyet.routes.authenticated: routes and pages for authenticated users.
|
||||||
(id, first_name, last_name, email, pass)
|
----
|
||||||
VALUES (:id, :first_name, :last_name, :email, :pass)
|
---- 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
|
||||||
|
----
|
||||||
|
------------------------------------------------------------------------------;
|
||||||
|
|
||||||
-- :name update-user! :! :n
|
-- This file gets slurped in and converted into simple functions by the line
|
||||||
-- :doc update an existing user record
|
-- in youyesyet.db.core.clj:
|
||||||
UPDATE users
|
-- (conman/bind-connection *db* "sql/queries.sql")
|
||||||
SET first_name = :first_name, last_name = :last_name, email = :email
|
-- the functions then appeare in the youyesyet.db.core namespace.
|
||||||
|
|
||||||
|
-- :name create-address! :! :n
|
||||||
|
-- :doc creates a new address record
|
||||||
|
INSERT INTO addresses
|
||||||
|
(address, postcode, district_id, latitude, longitude)
|
||||||
|
VALUES (:address, :postcode, :district, :latitude, :longitude)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
-- :name update-address! :! :n
|
||||||
|
-- :doc update an existing address record
|
||||||
|
UPDATE addresses
|
||||||
|
SET address = :address, postcode = :postcode, latitude = :latitude, longitude = :longitude
|
||||||
WHERE id = :id
|
WHERE id = :id
|
||||||
|
|
||||||
-- :name get-user :? :1
|
-- :name get-address :? :1
|
||||||
-- :doc retrieve a user given the id.
|
-- :doc retrieve a address given the id.
|
||||||
SELECT * FROM users
|
SELECT * FROM addresses
|
||||||
WHERE id = :id
|
WHERE id = :id
|
||||||
|
|
||||||
-- :name delete-user! :! :n
|
-- :name get-addresses-by-postcode
|
||||||
-- :doc delete a user given the id
|
|
||||||
DELETE FROM users
|
-- :name delete-address! :! :n
|
||||||
|
-- :doc delete a address given the id
|
||||||
|
DELETE FROM addresses
|
||||||
WHERE id = :id
|
WHERE id = :id
|
||||||
|
|
||||||
|
|
||||||
|
-- :name create-authority! :! :n
|
||||||
|
-- :doc creates a new authority record
|
||||||
|
INSERT INTO authorities
|
||||||
|
(id)
|
||||||
|
VALUES (:id)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
-- :name update-authority! :! :n
|
||||||
|
-- :doc update an existing authority record
|
||||||
|
UPDATE authorities
|
||||||
|
SET id = :id
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-authority :? :1
|
||||||
|
-- :doc retrieve a authority given the id.
|
||||||
|
SELECT * FROM authorities
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-authorities :? :0
|
||||||
|
-- :doc retrieve all authorities
|
||||||
|
SELECT id FROM authorities
|
||||||
|
|
||||||
|
-- :name delete-authority! :! :n
|
||||||
|
-- :doc delete a authority given the id
|
||||||
|
DELETE FROM authorities
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
|
||||||
|
-- :name create-canvasser! :! :n
|
||||||
|
-- :doc creates a new canvasser record
|
||||||
|
INSERT INTO canvassers
|
||||||
|
(username, fullname, elector_id, address_id, phone, email, authority_id, authorised)
|
||||||
|
VALUES (:username, :fullname, :elector_id, :address_id, :phone, :email, :authority_id, :authorised)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
-- :name update-canvasser! :! :n
|
||||||
|
-- :doc update an existing canvasser record
|
||||||
|
UPDATE canvassers
|
||||||
|
SET username = :username, fullname = :fullname, elector_id = :elector_id, address_id = :address_id, phone = :phone, email = :email, authority_id = :authority_id, authorised = :authorised
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-canvasser :? :1
|
||||||
|
-- :doc retrieve a canvasser given the id.
|
||||||
|
SELECT * FROM canvassers
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-canvasser-by-username :? :1
|
||||||
|
-- :doc rerieve a canvasser given the username.
|
||||||
|
SELECT * FROM canvassers
|
||||||
|
WHERE username = :username
|
||||||
|
|
||||||
|
-- :name get-canvasser-by-email :? :1
|
||||||
|
-- :doc rerieve a canvasser given the email address.
|
||||||
|
SELECT * FROM canvassers
|
||||||
|
WHERE email = :email
|
||||||
|
|
||||||
|
-- :name delete-canvasser! :! :n
|
||||||
|
-- :doc delete a canvasser given the id
|
||||||
|
DELETE FROM canvassers
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
|
||||||
|
-- :name create-district! :! :n
|
||||||
|
-- :doc creates a new district record
|
||||||
|
INSERT INTO districts
|
||||||
|
(id, name)
|
||||||
|
VALUES (:id, :name)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
-- :name update-district! :! :n
|
||||||
|
-- :doc update an existing district record
|
||||||
|
UPDATE districts
|
||||||
|
SET name = :name
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-district :? :1
|
||||||
|
-- :doc retrieve a district given the id.
|
||||||
|
SELECT * FROM districts
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name delete-district! :! :n
|
||||||
|
-- :doc delete a district given the id
|
||||||
|
DELETE FROM districts
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
|
||||||
|
-- :name create-elector! :! :n
|
||||||
|
-- :doc creates a new elector record
|
||||||
|
INSERT INTO electors
|
||||||
|
(name, address_id, phone, email)
|
||||||
|
VALUES (:name, :address_id, :phone, :email)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
-- :name update-elector! :! :n
|
||||||
|
-- :doc update an existing elector record
|
||||||
|
UPDATE electors
|
||||||
|
SET name = :name, address_id = :address_id, phone = :phone, email = :email
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-elector :? :1
|
||||||
|
-- :doc retrieve a elector given the id.
|
||||||
|
SELECT * FROM electors
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name delete-elector! :! :n
|
||||||
|
-- :doc delete a elector given the id
|
||||||
|
DELETE FROM electors
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
|
||||||
|
-- :name create-followupaction! :! :n
|
||||||
|
-- :doc creates a new followupaction record
|
||||||
|
INSERT INTO followupactions
|
||||||
|
(request_id, actor, date, notes, closed)
|
||||||
|
VALUES (:request_id, :actor, :date, :notes, :closed)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
-- We don't update followup actions. They're permanent record.
|
||||||
|
|
||||||
|
-- :name get-followupaction :? :1
|
||||||
|
-- :doc retrieve a followupaction given the id.
|
||||||
|
SELECT * FROM followupactions
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- We don't delete followup actions. They're permanent record.
|
||||||
|
|
||||||
|
|
||||||
|
-- followup methods are reference data, do not need to be programmatically maintained.
|
||||||
|
|
||||||
|
|
||||||
|
-- :name create-followuprequest! :! :n
|
||||||
|
-- :doc creates a new followupaction record
|
||||||
|
INSERT INTO followuprequests
|
||||||
|
(elector_id, visit_id, issue_id, method_id)
|
||||||
|
VALUES (:elector_id, :visit_id, :issue_id, :method_id)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
-- We don't update followup requests. They're permanent record.
|
||||||
|
|
||||||
|
-- :name get-followuprequest :? :1
|
||||||
|
-- :doc retrieve a followupaction given the id.
|
||||||
|
SELECT * FROM followuprequests
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- We don't delete followup requests. They're permanent record.
|
||||||
|
|
||||||
|
|
||||||
|
-- :name create-issueexpertise! :! :n
|
||||||
|
-- :doc creates a new issueexpertise record
|
||||||
|
INSERT INTO issueexpertise
|
||||||
|
(canvasser_id, issue_id, method_id)
|
||||||
|
VALUES (:canvasser_id, :issue_id, :method_id)
|
||||||
|
-- issueexertise is a link table, doesn't have an id field.
|
||||||
|
|
||||||
|
-- :name update-issueexpertise! :! :n
|
||||||
|
-- :doc update an existing issueexpertise record
|
||||||
|
UPDATE issueexpertise
|
||||||
|
SET canvasser_id = :canvasser_id, issue_id = :issue_id, method_id = :method_id
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-issueexpertise :? :1
|
||||||
|
-- :doc retrieve a issueexpertise given the canvasser_id -
|
||||||
|
-- getting it by its own id is unlikely to be interesting or useful.
|
||||||
|
SELECT * FROM issueexpertise
|
||||||
|
WHERE canvasser_id = :canvasser_id
|
||||||
|
|
||||||
|
-- :name delete-issueexpertise! :! :n
|
||||||
|
-- :doc delete a issueexpertise given the id
|
||||||
|
DELETE FROM issueexpertise
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
|
||||||
|
-- :name create-issue! :! :n
|
||||||
|
-- :doc creates a new issue record
|
||||||
|
INSERT INTO issues
|
||||||
|
(id, url, content, current)
|
||||||
|
VALUES (:id, :url, :content, :current)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
|
||||||
|
-- :name update-issue! :! :n
|
||||||
|
-- :doc update an existing issue record
|
||||||
|
UPDATE issues
|
||||||
|
SET url = :url, content = :content, current = :current
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-issue :? :1
|
||||||
|
-- :doc retrieve a issue given the id -
|
||||||
|
SELECT * FROM issues
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name delete-issue! :! :n
|
||||||
|
-- :doc delete a issue given the id
|
||||||
|
DELETE FROM issues
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
|
||||||
|
-- options is virtually reference data; it's not urgent to create a programmatic means of editing
|
||||||
|
|
||||||
|
-- :name create-visit! :! :n
|
||||||
|
-- :doc creates a new visit record
|
||||||
|
INSERT INTO visits
|
||||||
|
(address_id, canvasser_id)
|
||||||
|
VALUES (:address_id, :canvasser_id)
|
||||||
|
RETURNING id
|
||||||
|
|
||||||
|
-- visits is audit data; we don't update it.
|
||||||
|
|
||||||
|
-- :name get-visit :? :1
|
||||||
|
-- :doc retrieve a visit given the id.
|
||||||
|
SELECT * FROM visits
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- visits is audit data; we don't delete it.
|
||||||
|
|
||||||
|
|
||||||
|
-- views are select only
|
||||||
|
|
||||||
|
-- :name get-roles-by-canvasser :? :*
|
||||||
|
-- :doc Get the role names for the canvasser with the specified id
|
||||||
|
select name from roles_by_canvasser
|
||||||
|
where canvasser = :canvasser
|
||||||
|
|
||||||
|
-- :name get-teams-by-canvasser :? :*
|
||||||
|
-- :doc Get details of the teams which the canvasser with the specified id is member of.
|
||||||
|
select * from teams_by_canvasser
|
||||||
|
where canvasser = :canvasser_id
|
||||||
|
|
||||||
|
-- :name get-canvassers-by-team :? :*
|
||||||
|
-- :doc Get details of all canvassers who are members of the team with the specified id
|
||||||
|
select * from canvassers_by_team
|
||||||
|
where team = :team_id
|
||||||
|
|
||||||
|
-- :name get-canvassers-by-team :? :*
|
||||||
|
-- :doc Get details of all authorised canvassers who are members of this team.
|
||||||
|
select * from canvassers_by_introducer
|
||||||
|
where introducer = :introducer_id
|
||||||
|
|
||||||
|
-- :name get-canvassers-by-search :? :*
|
||||||
|
-- :doc Get details of all authorised canvassers whose details match this search string.
|
||||||
|
select * from canvassers
|
||||||
|
where name like '%' || :search || '%'
|
||||||
|
or username like '%' || :search || '%'
|
||||||
|
or email like '%' || :search || '%'
|
||||||
|
|
||||||
|
-- :name get-teams_by_organiser :? :*
|
||||||
|
-- :doc Get details of all the teams organised by the canvasser with the specified id
|
||||||
|
select * from teams_by_organiser
|
||||||
|
where organiser = :organiser_id
|
||||||
|
|
||||||
|
-- :name get-organisers-by-team :? :*
|
||||||
|
-- :doc Get details of all organisers of the team with the specified id
|
||||||
|
select * from organisers_by_team
|
||||||
|
where team = :team_id
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base-authenticated.html" %}
|
||||||
<html>
|
{% block whole-page %}
|
||||||
<head>
|
|
||||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="stylesheet" type="text/css" href="css/yyy-common.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="css/yyy-app.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="css/spinner.css" />
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Archivo+Black|Archivo+Narrow" rel="stylesheet"/>
|
|
||||||
<title>You Yes Yet?</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div class="splash-screen">
|
<div class="splash-screen">
|
||||||
<div class="sk-fading-circle">
|
<div class="sk-fading-circle">
|
||||||
|
@ -33,21 +22,16 @@
|
||||||
You must enable JavaScript to use the You Yes Yet app.
|
You must enable JavaScript to use the You Yes Yet app.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
<!-- scripts and styles -->
|
{% block extra-script %}
|
||||||
<!-- ATTENTION \/ -->
|
<!-- scripts and styles -->
|
||||||
<!-- Leaflet -->
|
<!-- ATTENTION \/ -->
|
||||||
<link rel="stylesheet" href="vendor/leaflet/dist/leaflet.css" />
|
<!-- Leaflet -->
|
||||||
<script src="vendor/leaflet/dist/leaflet.js"></script>
|
<link rel="stylesheet" href="vendor/leaflet/dist/leaflet.css" />
|
||||||
<!-- ATTENTION /\ -->
|
<script src="vendor/leaflet/dist/leaflet.js"></script>
|
||||||
|
<!-- ATTENTION /\ -->
|
||||||
<script type="text/javascript">
|
{% script "/js/app.js" %}
|
||||||
var context = "{{servlet-context}}";
|
{% endblock %}
|
||||||
var csrfToken = "{{csrf-token}}";
|
|
||||||
</script>
|
|
||||||
{% script "/js/app.js" %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
<head>
|
<head>
|
||||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" type="text/css" href="css/yyy-static.css" />
|
<link rel="stylesheet" type="text/css" href="css/yyy-common.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/yyy-site.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="css/spinner.css" />
|
<link rel="stylesheet" type="text/css" href="css/spinner.css" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Archivo+Black|Archivo+Narrow" rel="stylesheet"/>
|
<link href="https://fonts.googleapis.com/css?family=Archivo+Black|Archivo+Narrow" rel="stylesheet"/>
|
||||||
<title>{{title}}</title>
|
<title>{{title}}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
{% block whole-page %}
|
||||||
<header>
|
<header>
|
||||||
<div id="nav">
|
<div id="nav">
|
||||||
<img id="nav-icon" src="img/threelines.png" alt="Menu"/>
|
<img id="nav-icon" src="img/threelines.png" alt="Menu"/>
|
||||||
|
@ -16,8 +18,12 @@
|
||||||
<li class=""><a href="index.html">Home</a></li>
|
<li class=""><a href="index.html">Home</a></li>
|
||||||
<li class=""><a href="library.html">Library</a></li>
|
<li class=""><a href="library.html">Library</a></li>
|
||||||
<li class=""><a href="register.html">Register</a></li>
|
<li class=""><a href="register.html">Register</a></li>
|
||||||
<li class=""><a href="login.html">Login</a></li>
|
<li class="">{% if user %}<a href="logout.html">Logout</a>
|
||||||
|
{% else %}<a href="login.html">Login</a>{% endif %}</li>
|
||||||
<li class=""><a href="about.html">About</a></li>
|
<li class=""><a href="about.html">About</a></li>
|
||||||
|
{% if user %}
|
||||||
|
<li id="user"><a href="profile">Logged in as {{user.username}}</a></li>
|
||||||
|
{% endif %}
|
||||||
</menu>
|
</menu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -28,12 +34,40 @@
|
||||||
|
|
||||||
<div id="main-container" class="container">
|
<div id="main-container" class="container">
|
||||||
<div id="big-links">
|
<div id="big-links">
|
||||||
{{big-links}}
|
{% block big-links %}
|
||||||
</div>
|
{% endblock %}
|
||||||
|
</div>
|
||||||
<div if="#content">
|
<div if="#content">
|
||||||
{{content}}
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
<div id="back-link-container">
|
||||||
|
<a href="javascript:history.back()" id="back-link">Back</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<footer>
|
||||||
|
<div id="credits">
|
||||||
|
<div>
|
||||||
|
<img src="img/credits/ric-logo.png" width="24" height="24"/>
|
||||||
|
A project of the
|
||||||
|
<a href="https://radical.scot/">Radical Independence Campaign</a> ||
|
||||||
|
Version {{version}}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img height="16" width="16" alt="Clojure" src="img/credits/luminus-logo.png"/>Built with <a href="http://www.luminusweb.net/">LuminusWeb</a> ||
|
||||||
|
<img height="16" width="16" alt="Clojure" src="img/credits/clojure-icon.gif"/> Powered by <a href="http://clojure.org">Clojure</a> ||
|
||||||
|
<img height="16" width="16" alt="GitHub" src="img/credits/github-logo-transparent.png"/>Find me/fork me on <a href="https://github.com/simon-brooke/smeagol">Github</a> ||
|
||||||
|
<img height="16" width="16" alt="Free Software Foundation" src="img/credits/gnu.small.png"/>Licensed under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU General Public License version 2.0</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
{% endblock %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
var context = "{{servlet-context}}";
|
||||||
|
var csrfToken = "{{csrf-token}}";
|
||||||
|
</script>
|
||||||
|
{% block extra-script %}
|
||||||
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="css/yyy-common.css" />
|
<link rel="stylesheet" type="text/css" href="css/yyy-common.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="css/yyy-site.css" />
|
<link rel="stylesheet" type="text/css" href="css/yyy-site.css" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Archivo+Black|Archivo+Narrow" rel="stylesheet"/>
|
<link href="https://fonts.googleapis.com/css?family=Archivo+Black|Archivo+Narrow" rel="stylesheet"/>
|
||||||
<title>{{title}}</title>
|
<title>{% block title %}{% endblock %}{{title}}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
|
60
resources/templates/canvasser.html
Normal file
60
resources/templates/canvasser.html
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{% extends "base-authenticated.html" %}
|
||||||
|
{% block title %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<form action="edit-canvasser" method="post">
|
||||||
|
{% if canvasser %}
|
||||||
|
<input type="hidden" name="id" id="id" value="{{canvasser.id}}"/>
|
||||||
|
{% endif %}
|
||||||
|
<p class="widget">
|
||||||
|
<label for="fullname">Full name</label>
|
||||||
|
<input type="text" name="fullname" id="fullname" value="{{canvasser.fullname}}"/>
|
||||||
|
</p>
|
||||||
|
<p class="widget">
|
||||||
|
(TODO: Not absolutely sure what I'm going to do for an elector id widget yet.)
|
||||||
|
</p>
|
||||||
|
<p class="widget">
|
||||||
|
<label for="address">Address</label>
|
||||||
|
{% if address.id %}
|
||||||
|
<!-- if we already have an address, just show it with a link to edit it -->
|
||||||
|
<span class="pseudo-widget" id="address">
|
||||||
|
{{address.address}}
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
(TODO: Some sort of address lookup widget goes here.)
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<p class="widget">
|
||||||
|
<label for="phone">Phone number</label>
|
||||||
|
<input type="tel" name="phone" id="phone" value="{{canvasser.phone}}"/>
|
||||||
|
</p>
|
||||||
|
<p class="widget">
|
||||||
|
<label for="email">Email address</label>
|
||||||
|
<input type="email" name="email" id="email" value="{{canvasser.email}}"/>
|
||||||
|
</p>
|
||||||
|
<p class="widget">
|
||||||
|
<label for="authority_id">Authorised by</label>
|
||||||
|
<select name="authority_id" id="authority_id">
|
||||||
|
{% for authority in authorities %}
|
||||||
|
<option value="{{authority.id}}"
|
||||||
|
{% ifequal authority.id canvasser.authority_id %}selected {% endifequal %}>
|
||||||
|
{{authority.id}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
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
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -1,9 +1,4 @@
|
||||||
{% extends "base-unauthenticated.html" %}
|
{% extends "base-unauthenticated.html" %}
|
||||||
{% block big-links %}
|
|
||||||
<div id="back-link-container">
|
|
||||||
<a href="javascript:history.back()" id="back-link">Back</a>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>
|
<p>
|
||||||
We're not going to do login in the long term; we're going to use oauth.
|
We're not going to do login in the long term; we're going to use oauth.
|
||||||
|
|
16
resources/templates/roles.html
Normal file
16
resources/templates/roles.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base-authenticated.html" %}
|
||||||
|
{% block title %}
|
||||||
|
{{ user }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block big-links %}
|
||||||
|
<div class="big-link-container">
|
||||||
|
<a href="app" class="big-link" id="big-link">Canvasser</a>
|
||||||
|
</div>
|
||||||
|
{% for role in roles %}
|
||||||
|
<div class="big-link-container">
|
||||||
|
<a href="{{role.name|lower}}" class="big-link" id="big-link">{{role.name}}</a>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
|
@ -1,4 +1,6 @@
|
||||||
(ns youyesyet.config
|
(ns ^{:doc "Read configuration."
|
||||||
|
:author "Simon Brooke"}
|
||||||
|
youyesyet.config
|
||||||
(:require [cprop.core :refer [load-config]]
|
(:require [cprop.core :refer [load-config]]
|
||||||
[cprop.source :as source]
|
[cprop.source :as source]
|
||||||
[mount.core :refer [args defstate]]))
|
[mount.core :refer [args defstate]]))
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
(ns youyesyet.db.core
|
(ns ^{:doc "Database access functions."
|
||||||
|
:author "Simon Brooke"}
|
||||||
|
youyesyet.db.core
|
||||||
(:require
|
(:require
|
||||||
[cheshire.core :refer [generate-string parse-string]]
|
[cheshire.core :refer [generate-string parse-string]]
|
||||||
[clojure.java.jdbc :as jdbc]
|
[clojure.java.jdbc :as jdbc]
|
||||||
|
@ -15,12 +17,10 @@
|
||||||
Timestamp
|
Timestamp
|
||||||
PreparedStatement]))
|
PreparedStatement]))
|
||||||
|
|
||||||
(def ^:dynamic *db* {:name "java:comp/env/jdbc/EmployeeDB"})
|
(defstate ^:dynamic *db*
|
||||||
;; (defstate ^:dynamic *db*
|
:start (conman/connect! {:jdbc-url (env :database-url)
|
||||||
;; :start (conman/connect! {:jdbc-url-env (env :database-url)
|
:driver-class-name "org.postgresql.Driver"})
|
||||||
;; :jdbc-url "jdbc:postgresql://127.0.0.1/youyesyet_dev?user=youyesyet&password=thisisnotsecure"
|
:stop (conman/disconnect! *db*))
|
||||||
;; :driver-class-name "org.postgresql.Driver"})
|
|
||||||
;; :stop (conman/disconnect! *db*))
|
|
||||||
|
|
||||||
(conman/bind-connection *db* "sql/queries.sql")
|
(conman/bind-connection *db* "sql/queries.sql")
|
||||||
|
|
||||||
|
|
|
@ -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]
|
(:require [clojure.java.jdbc :as sql]
|
||||||
[korma.core :as kc]
|
[korma.core :as kc]
|
||||||
[youyesyet.db.core :as yyydb]))
|
[youyesyet.db.core :as yyydb]))
|
||||||
|
|
|
@ -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]]
|
(:require [compojure.core :refer [routes wrap-routes]]
|
||||||
[youyesyet.layout :refer [error-page]]
|
[youyesyet.layout :refer [error-page]]
|
||||||
|
[youyesyet.routes.authenticated :refer [authenticated-routes]]
|
||||||
[youyesyet.routes.home :refer [home-routes]]
|
[youyesyet.routes.home :refer [home-routes]]
|
||||||
[youyesyet.routes.oauth :refer [oauth-routes]]
|
[youyesyet.routes.oauth :refer [oauth-routes]]
|
||||||
[compojure.route :as route]
|
[compojure.route :as route]
|
||||||
|
@ -10,6 +13,29 @@
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
[youyesyet.config :refer [env]]))
|
[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
|
(mount/defstate init-app
|
||||||
:start ((or (:init defaults) identity))
|
:start ((or (:init defaults) identity))
|
||||||
:stop ((or (:stop defaults) identity)))
|
:stop ((or (:stop defaults) identity)))
|
||||||
|
@ -38,6 +64,7 @@
|
||||||
(wrap-routes middleware/wrap-csrf)
|
(wrap-routes middleware/wrap-csrf)
|
||||||
(wrap-routes middleware/wrap-formats))
|
(wrap-routes middleware/wrap-formats))
|
||||||
#'oauth-routes
|
#'oauth-routes
|
||||||
|
#'authenticated-routes
|
||||||
(route/not-found
|
(route/not-found
|
||||||
(:body
|
(:body
|
||||||
(error-page {:status 404
|
(error-page {:status 404
|
||||||
|
|
|
@ -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]
|
(:require [selmer.parser :as parser]
|
||||||
[selmer.filters :as filters]
|
[selmer.filters :as filters]
|
||||||
[markdown.core :refer [md-to-html-string]]
|
[markdown.core :refer [md-to-html-string]]
|
||||||
|
@ -12,6 +14,7 @@
|
||||||
(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field)))
|
(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field)))
|
||||||
(filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)]))
|
(filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)]))
|
||||||
|
|
||||||
|
|
||||||
(defn render
|
(defn render
|
||||||
"renders the HTML template located relative to resources/templates"
|
"renders the HTML template located relative to resources/templates"
|
||||||
[template & [params]]
|
[template & [params]]
|
||||||
|
@ -26,6 +29,7 @@
|
||||||
:version (System/getProperty "youyesyet.version"))))
|
:version (System/getProperty "youyesyet.version"))))
|
||||||
"text/html; charset=utf-8"))
|
"text/html; charset=utf-8"))
|
||||||
|
|
||||||
|
|
||||||
(defn error-page
|
(defn error-page
|
||||||
"error-details should be a map containing the following keys:
|
"error-details should be a map containing the following keys:
|
||||||
:status - error status
|
:status - error status
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
(ns youyesyet.middleware
|
(ns ^{:doc "Plumbing, mainly boilerplate from Luminus."
|
||||||
|
:author "Simon Brooke"}
|
||||||
|
youyesyet.middleware
|
||||||
(:require [youyesyet.env :refer [defaults]]
|
(:require [youyesyet.env :refer [defaults]]
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
[youyesyet.layout :refer [*app-context* error-page]]
|
[youyesyet.layout :refer [*app-context* error-page]]
|
||||||
|
|
11
src/clj/youyesyet/routes/administrator.clj
Normal file
11
src/clj/youyesyet/routes/administrator.clj
Normal file
|
@ -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]))
|
75
src/clj/youyesyet/routes/authenticated.clj
Normal file
75
src/clj/youyesyet/routes/authenticated.clj
Normal file
|
@ -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)))
|
|
@ -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]]
|
(:require [clojure.walk :refer [keywordize-keys]]
|
||||||
[noir.response :as nresponse]
|
[noir.response :as nresponse]
|
||||||
[noir.util.route :as route]
|
[noir.util.route :as route]
|
||||||
|
@ -8,12 +9,37 @@
|
||||||
[ring.util.http-response :as response]
|
[ring.util.http-response :as response]
|
||||||
[clojure.java.io :as io]))
|
[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 []
|
(defn app-page []
|
||||||
(layout/render "app.html"))
|
(layout/render "app.html"))
|
||||||
|
|
||||||
|
|
||||||
(defn about-page []
|
(defn about-page []
|
||||||
(layout/render "about.html"))
|
(layout/render "about.html"))
|
||||||
|
|
||||||
|
|
||||||
(defn call-me-page [request]
|
(defn call-me-page [request]
|
||||||
(if
|
(if
|
||||||
request
|
request
|
||||||
|
@ -25,28 +51,53 @@
|
||||||
;; TODO: Issues need to be fetched from the database
|
;; TODO: Issues need to be fetched from the database
|
||||||
:concerns nil})))
|
: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 []
|
(defn home-page []
|
||||||
(layout/render "home.html" {:title "You Yes Yet?"}))
|
(layout/render "home.html" {:title "You Yes Yet?"}))
|
||||||
|
|
||||||
|
|
||||||
(defn login-page
|
(defn login-page
|
||||||
"This is very temporary. We're going to do authentication by oauth."
|
"This is very temporary. We're going to do authentication by oauth."
|
||||||
[request]
|
[request]
|
||||||
(let [params (keywordize-keys (:form-params request))
|
(let [params (keywordize-keys (:form-params request))
|
||||||
session (:session request)
|
session (:session request)
|
||||||
username (:username params)
|
username (:username params)
|
||||||
|
user (if username (db-core/get-canvasser-by-username db-core/*db* {:username username}))
|
||||||
password (:password params)
|
password (:password params)
|
||||||
redirect-to (or (:redirect-to params) "app")]
|
redirect-to (or (:redirect-to params) "roles")]
|
||||||
(if
|
(cond
|
||||||
(and (= username "test") (= password "test"))
|
;; this is obviously, ABSURDLY, insecure. I don't want to put just-about-good-enough,
|
||||||
(do
|
;; it-will-do-for-now security in place; instead, I want this to be test code only
|
||||||
(assoc (response/found redirect-to) :session (assoc session :user username)))
|
;; until we have o-auth properly working.
|
||||||
(layout/render "login.html" {:title "Please log in" :redirect-to redirect-to}))))
|
(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
|
(defroutes home-routes
|
||||||
(GET "/" [] (home-page))
|
(GET "/" [] (home-page))
|
||||||
(GET "/home" [] (home-page))
|
(GET "/home" [] (home-page))
|
||||||
(GET "/about" [] (about-page))
|
(GET "/about" [] (about-page))
|
||||||
|
(GET "/roles" request (route/restricted (roles-page request)))
|
||||||
(GET "/app" [] (route/restricted (app-page)))
|
(GET "/app" [] (route/restricted (app-page)))
|
||||||
(GET "/call-me" [] (call-me-page nil))
|
(GET "/call-me" [] (call-me-page nil))
|
||||||
(POST "/call-me" request (call-me-page request))
|
(POST "/call-me" request (call-me-page request))
|
||||||
|
|
|
@ -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]]
|
(:require [compojure.core :refer [defroutes GET]]
|
||||||
[ring.util.http-response :refer [ok found]]
|
[ring.util.http-response :refer [ok found]]
|
||||||
[clojure.java.io :as io]
|
[clojure.java.io :as io]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
(ns youyesyet.routes.services
|
(ns ^{:doc "REST API."
|
||||||
|
:author "Simon Brooke"} youyesyet.routes.services
|
||||||
(:require [clj-http.client :as client]
|
(:require [clj-http.client :as client]
|
||||||
[ring.util.http-response :refer :all]
|
[ring.util.http-response :refer :all]
|
||||||
[compojure.api.sweet :refer :all]
|
[compojure.api.sweet :refer :all]
|
||||||
|
|
|
@ -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
|
(:require
|
||||||
#?(:clj [clojure.core]
|
#?(:clj [clojure.core]
|
||||||
:cljs [reagent.core :refer [atom]])))
|
:cljs [reagent.core :refer [atom]])))
|
||||||
|
@ -41,6 +43,7 @@
|
||||||
(reverse items)
|
(reverse items)
|
||||||
(list items))})))
|
(list items))})))
|
||||||
|
|
||||||
|
|
||||||
(defn add!
|
(defn add!
|
||||||
"Add this item to the queue."
|
"Add this item to the queue."
|
||||||
[q item]
|
[q item]
|
||||||
|
@ -49,7 +52,9 @@
|
||||||
(assoc a :items
|
(assoc a :items
|
||||||
(cons item (:items a))))))
|
(cons item (:items a))))))
|
||||||
|
|
||||||
(defn q?
|
|
||||||
|
(defn queue?
|
||||||
|
"True if x is a queue, else false."
|
||||||
[x]
|
[x]
|
||||||
(try
|
(try
|
||||||
(let [q (deref x)
|
(let [q (deref x)
|
||||||
|
@ -61,21 +66,25 @@
|
||||||
#?(:clj (print (.getMessage any))
|
#?(:clj (print (.getMessage any))
|
||||||
:cljs (js/console.log (str any))))))
|
:cljs (js/console.log (str any))))))
|
||||||
|
|
||||||
|
|
||||||
(defn peek
|
(defn peek
|
||||||
"Look at the next item which could be removed from the queue."
|
"Look at the next item which could be removed from the queue."
|
||||||
[q]
|
[q]
|
||||||
(last (:items (deref q))))
|
(last (:items (deref q))))
|
||||||
|
|
||||||
|
|
||||||
(defn locked?
|
(defn locked?
|
||||||
[q]
|
[q]
|
||||||
(:locked (deref q)))
|
(:locked (deref q)))
|
||||||
|
|
||||||
|
|
||||||
(defn unlock!
|
(defn unlock!
|
||||||
([q ]
|
([q ]
|
||||||
(unlock! q true))
|
(unlock! q true))
|
||||||
([q value]
|
([q value]
|
||||||
(swap! q (fn [a] (assoc a :locked (not (true? value)))))))
|
(swap! q (fn [a] (assoc a :locked (not (true? value)))))))
|
||||||
|
|
||||||
|
|
||||||
(defn lock!
|
(defn lock!
|
||||||
[q]
|
[q]
|
||||||
(unlock! q false))
|
(unlock! q false))
|
||||||
|
@ -86,6 +95,7 @@
|
||||||
[q]
|
[q]
|
||||||
(count (deref q)))
|
(count (deref q)))
|
||||||
|
|
||||||
|
|
||||||
(defn take!
|
(defn take!
|
||||||
"Return the first item from the queue, rebind the queue to the remaining
|
"Return the first item from the queue, rebind the queue to the remaining
|
||||||
items. If the queue is empty return nil."
|
items. If the queue is empty return nil."
|
||||||
|
@ -97,12 +107,13 @@
|
||||||
(assoc (assoc a :items new-queue) :v item))))
|
(assoc (assoc a :items new-queue) :v item))))
|
||||||
(:v (deref q)))
|
(:v (deref q)))
|
||||||
|
|
||||||
|
|
||||||
(defn maybe-process-next
|
(defn maybe-process-next
|
||||||
"Apply this process, assumed to be a function of one argument, to the 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
|
item in the queue, if the queue is not currently locked; return the value
|
||||||
returned by process."
|
returned by process."
|
||||||
[q process]
|
[q process]
|
||||||
(if (and (q? q)(not (locked? q)))
|
(if (and (queue? q)(not (locked? q)))
|
||||||
(try
|
(try
|
||||||
(lock! q)
|
(lock! q)
|
||||||
(let [v (apply process (list (peek q)))]
|
(let [v (apply process (list (peek q)))]
|
||||||
|
|
|
@ -1,3 +1,26 @@
|
||||||
(ns youyesyet.validation
|
(ns youyesyet.validation
|
||||||
(:require [bouncer.core :as b]
|
(:require [bouncer.core :as b]
|
||||||
[bouncer.validators :as v]))
|
[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
|
||||||
|
;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -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})))
|
|
||||||
|
|
||||||
|
|
46
src/cljs/youyesyet/canvasser_app/ajax.cljs
Normal file
46
src/cljs/youyesyet/canvasser_app/ajax.cljs
Normal file
|
@ -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})))
|
||||||
|
|
||||||
|
|
|
@ -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
|
(:require cljsjs.react-leaflet
|
||||||
[ajax.core :refer [GET POST]]
|
[ajax.core :refer [GET POST]]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
|
@ -7,21 +9,21 @@
|
||||||
[reagent.core :as r]
|
[reagent.core :as r]
|
||||||
[re-frame.core :as rf]
|
[re-frame.core :as rf]
|
||||||
[secretary.core :as secretary]
|
[secretary.core :as secretary]
|
||||||
[youyesyet.ajax :refer [load-interceptors!]]
|
[youyesyet.canvasser-app.ajax :refer [load-interceptors!]]
|
||||||
[youyesyet.handlers]
|
[youyesyet.canvasser-app.handlers]
|
||||||
[youyesyet.subscriptions]
|
[youyesyet.canvasser-app.subscriptions]
|
||||||
[youyesyet.ui-utils :as ui]
|
[youyesyet.canvasser-app.ui-utils :as ui]
|
||||||
[youyesyet.views.about :as about]
|
[youyesyet.canvasser-app.views.about :as about]
|
||||||
[youyesyet.views.electors :as electors]
|
[youyesyet.canvasser-app.views.electors :as electors]
|
||||||
[youyesyet.views.followup :as followup]
|
[youyesyet.canvasser-app.views.followup :as followup]
|
||||||
[youyesyet.views.issue :as issue]
|
[youyesyet.canvasser-app.views.issue :as issue]
|
||||||
[youyesyet.views.issues :as issues]
|
[youyesyet.canvasser-app.views.issues :as issues]
|
||||||
[youyesyet.views.map :as maps])
|
[youyesyet.canvasser-app.views.map :as maps])
|
||||||
(:import goog.History))
|
(: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
|
;;;; This program is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
;;;; modify it under the terms of the GNU General Public License
|
|
@ -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]]
|
(:require [cljs.reader :refer [read-string]]
|
||||||
[re-frame.core :refer [dispatch reg-event-db]]
|
[re-frame.core :refer [dispatch reg-event-db]]
|
||||||
[youyesyet.db :as db]
|
[youyesyet.canvasser-app.state :as db]
|
||||||
))
|
))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
@ -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
|
;;;; This program is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
;;;; modify it under the terms of the GNU General Public License
|
|
@ -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]]))
|
(:require [re-frame.core :refer [reg-sub]]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
@ -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]
|
(:require [reagent.core :as r]
|
||||||
[re-frame.core :as rf]))
|
[re-frame.core :as rf]))
|
||||||
|
|
|
@ -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]]
|
(:require [re-frame.core :refer [reg-sub subscribe]]
|
||||||
[markdown.core :refer [md->html]]
|
[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
|
;;;; This program is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
;;;; modify it under the terms of the GNU General Public License
|
|
@ -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]]))
|
(: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
|
;;;; This program is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
;;;; modify it under the terms of the GNU General Public License
|
|
@ -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]]
|
(:require [reagent.core :refer [atom]]
|
||||||
[re-frame.core :refer [reg-sub subscribe dispatch]]
|
[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
|
;;;; This program is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
;;;; modify it under the terms of the GNU General Public License
|
|
@ -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]]
|
(: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
|
;;;; This program is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
;;;; modify it under the terms of the GNU General Public License
|
|
@ -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]]
|
(:require [re-frame.core :refer [reg-sub subscribe]]
|
||||||
[markdown.core :refer [md->html]]
|
[markdown.core :refer [md->html]]
|
||||||
[youyesyet.ui-utils :as ui]
|
[youyesyet.canvasser-app.ui-utils :as ui]
|
||||||
[youyesyet.views.issues :as issues]))
|
[youyesyet.canvasser-app.views.issues :as issues]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;
|
;;;;
|
|
@ -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]]
|
(: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
|
;;;; This program is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
;;;; modify it under the terms of the GNU General Public License
|
|
@ -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]]
|
(:require [re-frame.core :refer [reg-sub subscribe dispatch]]
|
||||||
[reagent.core :as reagent]))
|
[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
|
;;;; This program is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
;;;; modify it under the terms of the GNU General Public License
|
|
@ -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]]
|
(:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
|
||||||
[reagent.core :as reagent :refer [atom]]
|
[reagent.core :as reagent :refer [atom]]
|
||||||
[youyesyet.core :as rc]))
|
[youyesyet.canvasser-app.core :as rc]))
|
||||||
|
|
||||||
(deftest test-home
|
(deftest test-home
|
||||||
(is (= true true)))
|
(is (= true true)))
|
6
test/cljs/youyesyet/canvasser_app/doo_runner.cljs
Normal file
6
test/cljs/youyesyet/canvasser_app/doo_runner.cljs
Normal file
|
@ -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)
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
(ns youyesyet.doo-runner
|
|
||||||
(:require [doo.runner :refer-macros [doo-tests]]
|
|
||||||
[youyesyet.core-test]))
|
|
||||||
|
|
||||||
(doo-tests 'youyesyet.core-test)
|
|
||||||
|
|
Loading…
Reference in a new issue