Considerable progress on the issue experts workflow, not perfect yet.
This commit is contained in:
parent
3df314ecfc
commit
54ad57349c
|
@ -67,6 +67,13 @@ WHERE canvasser_id = :id
|
||||||
ORDER BY date desc
|
ORDER BY date desc
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
|
|
||||||
|
--:name get-locality-for-visit :? :1
|
||||||
|
--:doc returns the locality of the address of this visit
|
||||||
|
SELECT addresses.locality
|
||||||
|
FROM addresses, visits
|
||||||
|
WHERE visits.address_id = addresses.id
|
||||||
|
AND visits.id = :id
|
||||||
|
|
||||||
-- I don't know why this next one isn't autogenerating, but it isn't and it's critical.
|
-- I don't know why this next one isn't autogenerating, but it isn't and it's critical.
|
||||||
|
|
||||||
-- :name list-roles-by-canvasser :? :*
|
-- :name list-roles-by-canvasser :? :*
|
||||||
|
|
25
resources/sql/youyesyet.postgres.overrides.sql
Normal file
25
resources/sql/youyesyet.postgres.overrides.sql
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- convenience view lv_followupactions of entity followupactions for
|
||||||
|
-- lists, et cetera
|
||||||
|
-- ADL is not yet correctly chaining tables when generating convenience
|
||||||
|
-- views, so the auto-generated convenience view is a horrible
|
||||||
|
-- cross-product join
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
DROP VIEW lv_followupactions;
|
||||||
|
CREATE VIEW lv_followupactions AS
|
||||||
|
SELECT electors.name ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| visits.date ||', '|| issues.id AS request_id_expanded,
|
||||||
|
followupactions.request_id,
|
||||||
|
canvassers.username ||', '|| canvassers.fullname ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| canvassers.phone ||', '|| canvassers.email AS actor_expanded,
|
||||||
|
followupactions.actor,
|
||||||
|
followupactions.date,
|
||||||
|
followupactions.notes,
|
||||||
|
followupactions.closed,
|
||||||
|
followupactions.id
|
||||||
|
FROM followuprequests, visits, canvassers, addresses, followupactions, issues, electors
|
||||||
|
WHERE followupactions.request_id = followuprequests.id
|
||||||
|
AND followuprequests.elector_id = electors.id
|
||||||
|
AND followuprequests.visit_id = visits.id
|
||||||
|
AND followuprequests.issue_id = issues.id
|
||||||
|
AND visits.address_id = addresses.id
|
||||||
|
AND followupactions.actor = canvassers.id
|
||||||
|
;
|
|
@ -13,16 +13,19 @@
|
||||||
<form action='{{servlet-context}}/issue-expert/followup-action' method='POST'>
|
<form action='{{servlet-context}}/issue-expert/followup-action' method='POST'>
|
||||||
{% csrf-field %}
|
{% csrf-field %}
|
||||||
<input id='id' name='id' type='hidden' value='{{record.id}}'/>
|
<input id='id' name='id' type='hidden' value='{{record.id}}'/>
|
||||||
|
<input id='request_id' name='request_id' type='hidden' value='{{record.id}}'/>
|
||||||
|
<input id='elector_id' name='elector_id' type='hidden' value='{{record.elector_id}}'/>
|
||||||
|
<input id='visit_id' name='visit_id' type='hidden' value='{{record.visit_id}}'/>
|
||||||
<p class='widget'>
|
<p class='widget'>
|
||||||
<label for='elector_id'>
|
<label for='elector_id'>
|
||||||
Elector
|
Elector
|
||||||
</label>
|
</label>
|
||||||
{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %}
|
{% ifmemberof canvassers teamorganisers issueexperts analysts issueeditors admin %}
|
||||||
<span id='elector_id' name='elector_id' class='pseudo-widget disabled'>
|
<span id='elector' name='elector' class='pseudo-widget disabled'>
|
||||||
{{elector.name}} ({{elector.gender}})
|
{{elector.name}} ({{elector.gender}})
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span id='elector_id' name='elector_id' class='pseudo-widget not-authorised'>
|
<span id='elector' name='elector' class='pseudo-widget not-authorised'>
|
||||||
You are not permitted to view elector of followuprequests
|
You are not permitted to view elector of followuprequests
|
||||||
</span>
|
</span>
|
||||||
{% endifmemberof %}
|
{% endifmemberof %}
|
||||||
|
@ -121,7 +124,21 @@
|
||||||
<input id='closed' name='closed' type='checkbox' maxlength='' size='16'/>
|
<input id='closed' name='closed' type='checkbox' maxlength='' size='16'/>
|
||||||
{% endifmemberof %}
|
{% endifmemberof %}
|
||||||
</p>
|
</p>
|
||||||
|
<p class='widget'>
|
||||||
|
<label for='intention'>
|
||||||
|
What is the elector's voting intention now?
|
||||||
|
</label>
|
||||||
|
{% ifmemberof admin issueexperts %}
|
||||||
|
<select id='option_id' name='option_id'>
|
||||||
|
<option value="">Not stated</option>
|
||||||
|
{% for option in options %}
|
||||||
|
<img src="{{servlet-context}}/img/option/{{option.id}}-{% ifequal record.option_id option.id %}selected{% else %}unselected{% endifequal %}.png" alt="{{option.id}}"/>
|
||||||
|
<option value='{{option.id}}' {% ifequal record.option_id option.id%}selected='selected'{% endifequal %} style="background-image: url('{{servlet-context}}/img/option/{{option.id}}-{% ifequal record.option_id option.id %}selected{% else %}unselected{% endifequal %}.png')">
|
||||||
|
{{option.id}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
{% endifmemberof %}
|
||||||
|
</p>
|
||||||
{% ifmemberof admin issueexperts %}
|
{% ifmemberof admin issueexperts %}
|
||||||
<p class='widget action-safe'>
|
<p class='widget action-safe'>
|
||||||
<label for='save-button' class='action-safe'>
|
<label for='save-button' class='action-safe'>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
[clojure.string :as s]
|
[clojure.string :as s]
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
[clojure.walk :refer [keywordize-keys]]
|
[clojure.walk :refer [keywordize-keys]]
|
||||||
|
[java-time :as jt]
|
||||||
[markdown.core :refer [md-to-html-string]]
|
[markdown.core :refer [md-to-html-string]]
|
||||||
[noir.util.route :as route]
|
[noir.util.route :as route]
|
||||||
[ring.util.http-response :as response]
|
[ring.util.http-response :as response]
|
||||||
|
@ -39,6 +40,11 @@
|
||||||
;;;;
|
;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;;; TODO: Must lock record to expert when taking it from the list, unlock after
|
||||||
|
;;; thirty minutes or when the action is posted, whichever is sooner. If
|
||||||
|
;;; an expert opens a locked record, it must show up as locked. Also, expert should
|
||||||
|
;;; not be able to open a closed request.
|
||||||
|
|
||||||
(defn list-page [request]
|
(defn list-page [request]
|
||||||
(layout/render
|
(layout/render
|
||||||
"issue-expert/list.html"
|
"issue-expert/list.html"
|
||||||
|
@ -48,10 +54,10 @@
|
||||||
:records (db/list-open-requests db/*db* {:expert (:id user)})})))
|
:records (db/list-open-requests db/*db* {:expert (:id user)})})))
|
||||||
|
|
||||||
|
|
||||||
(defn followup-request-page [request]
|
(defn get-followup-request-page [request]
|
||||||
(let
|
(let
|
||||||
[params (support/massage-params request)
|
[params (support/massage-params request)
|
||||||
id (:id (keywordize-keys params))
|
id (:id params)
|
||||||
record (db/get-followuprequest db/*db* {:id id})
|
record (db/get-followuprequest db/*db* {:id id})
|
||||||
elector (if
|
elector (if
|
||||||
record
|
record
|
||||||
|
@ -65,15 +71,11 @@
|
||||||
db/*db* {:id (:visit_id record)})))]
|
db/*db* {:id (:visit_id record)})))]
|
||||||
(layout/render
|
(layout/render
|
||||||
"issue-expert/request.html"
|
"issue-expert/request.html"
|
||||||
{:title (str "Request from " (:name elector) " at " (:date visit))
|
{:actions (map
|
||||||
:user (:user (:session request))
|
|
||||||
:visit visit
|
|
||||||
:actions (map
|
|
||||||
;; HTML-ise the notes in each action record
|
;; HTML-ise the notes in each action record
|
||||||
#(merge % {:notes (md-to-html-string (:notes %))})
|
#(merge % {:notes (md-to-html-string (:notes %))})
|
||||||
(db/list-followupactions-by-followuprequest
|
(db/list-followupactions-by-followuprequest
|
||||||
db/*db* {:id id}))
|
db/*db* {:id id}))
|
||||||
:record record
|
|
||||||
:elector elector
|
:elector elector
|
||||||
:issue (let
|
:issue (let
|
||||||
[raw-issue (if
|
[raw-issue (if
|
||||||
|
@ -82,13 +84,48 @@
|
||||||
(if raw-issue
|
(if raw-issue
|
||||||
(merge
|
(merge
|
||||||
raw-issue
|
raw-issue
|
||||||
{:brief (md-to-html-string (:brief raw-issue))})))})))
|
{:brief (md-to-html-string (:brief raw-issue))})))
|
||||||
|
:options (db/list-options db/*db* params)
|
||||||
|
:record record
|
||||||
|
:title (str "Request from " (:name elector) " at " (:date visit))
|
||||||
|
:user (:user (:session request))
|
||||||
|
:visit visit})))
|
||||||
|
|
||||||
|
|
||||||
|
(defn post-followup-action
|
||||||
|
"From this `request`, create a `followupaction` record, and, if an
|
||||||
|
`option_id` is present in the params, an `intention` record; show
|
||||||
|
the request list on success, to the request form on failure."
|
||||||
|
[request]
|
||||||
|
(support/do-or-log-error
|
||||||
|
(let
|
||||||
|
[params (support/massage-params request)
|
||||||
|
locality (:locality (db/get-locality-for-visit db/*db* {:id (:visit_id params)}))]
|
||||||
|
(log/debug "post-followup-request-page with request " request)
|
||||||
|
(db/create-followupaction!
|
||||||
|
db/*db*
|
||||||
|
(assoc
|
||||||
|
params
|
||||||
|
:actor (:id (:user (:session request)))
|
||||||
|
:date (jt/to-sql-timestamp (jt/local-date-time))
|
||||||
|
:closed (= (:closed params) "on")))
|
||||||
|
(if-not
|
||||||
|
(zero? (count (:option_id params)))
|
||||||
|
(if
|
||||||
|
(zero? (count (:signature (db/get-elector db/*db* {:id (:elector_id params)}))))
|
||||||
|
;; the elector has NOT recorded GDPR consent: explicitly bind elector_id to nil
|
||||||
|
(db/create-intention! db/*db* (assoc params :locality locality :elector_id nil))
|
||||||
|
;; else the elector HAS recorded GDPR consent
|
||||||
|
(db/create-intention! db/*db* (assoc params :locality locality))))
|
||||||
|
(list-page request))
|
||||||
|
:error-return
|
||||||
|
(get-followup-request-page request)))
|
||||||
|
|
||||||
|
|
||||||
(defroutes issue-expert-routes
|
(defroutes issue-expert-routes
|
||||||
(GET "/issue-expert/list" request
|
(GET "/issue-expert/list" request
|
||||||
(route/restricted (list-page request)))
|
(route/restricted (list-page request)))
|
||||||
(GET "/issue-expert/followup-request" request
|
(GET "/issue-expert/followup-request" request
|
||||||
(route/restricted (followup-request-page request)))
|
(route/restricted (get-followup-request-page request)))
|
||||||
(POST "/issue-expert/followup-request" request
|
(POST "/issue-expert/followup-action" request
|
||||||
(route/restricted (followup-request-page request))))
|
(route/restricted (post-followup-action request))))
|
||||||
|
|
Loading…
Reference in a new issue