Coverage report: /home/samppa/personal/opiskelu/ohtuprojekti/pulsu/trunk/controller.lisp
Kind | Covered | All | % |
expression | 144 | 429 | 33.6 |
branch | 20 | 58 | 34.5 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
; This file contains the database part of the controller
3
(in-package :puls.controller)
5
(defparameter *database-table* "plus")
7
(defun connect-to-db (&optional (host "127.0.0.1"))
8
(clsql:connect (list host "pulsu" "remote" "remotepulsu"
9
13326) :database-type :mysql :pool t)
10
;; UFFI seems to send utf-8 to the database, regardless of the
12
(clsql-sys:execute-command "set names utf8"))
15
;#.(locally-enable-sql-reader-syntax)
17
(defun get-event (docno event_id)
18
"Gets a single ie-event, identified by document number and event_id
19
Returns something like (:field1 \"content\" :field2 \"content\" ...)"
21
(my-query (format nil "select * from ~A
22
where docno = '~A' and event_id = '~A'"
27
(defun set-event (docno event_id row)
28
"Sets fields of event identified by (docno,event_id). Row is a plist
30
Example: (set-event \"20050111_timesofindia_562b0491fe61ac9f38b60e45008bdd63\" 2 '(:group_number 0 :verify \"verified\"))"
32
(format nil "UPDATE ~A SET ~A WHERE docno='~A' and event_id=~A"
38
(defun add-event (docno row)
39
"Adds a new event to document docno with attributes from row.
40
NB! docno and event_id should _not_ be set in row"
44
(format nil "SELECT MAX(event_id)+1
48
SELECT * FROM ~Adeleted) AS FOO
55
"INSERT INTO ~A SET ~A,docno = '~A',event_id = ~A"
60
(reunify-event docno event_id))))
63
(defun get-max-event (docno)
64
(query (format nil "SELECT MAX(event_id) FROM plus WHERE docno = '~A'" docno)))
66
;; NB! the following three functions are not yet scenario-generic!!
67
(defun count-unified-case (group_number)
69
(format nil "UPDATE unified SET member_count = (SELECT count(*) FROM ~A WHERE group_number=unified.id) WHERE id=~A"
73
(defun get-unification-candidates (row)
76
"SELECT id FROM unified WHERE
77
disease='~A' AND country='~A' AND
78
start_time='~A' AND end_time='~A'"
79
(getf row :disease_canon)
81
(getf row :norm_stime)
82
(getf row :norm_etime))))
84
(defun reunify-event (docno event_id)
85
"Find a new unified group for event identified by (docno,event_id). If no matching ones found, create a new one"
87
(let* ((row (get-event docno event_id))
88
(oldgroup (getf row :group_number))
89
(candidates (get-unification-candidates row))
90
; insert real heuristic here:
91
(chosen (getf (first candidates) :id)))
92
; ensure that a group for the event exists
95
(format nil "INSERT INTO unified
96
(disease,country,start_time,end_time,member_count)
98
('~A','~A','~A','~A',0)"
99
(getf row :disease_canon)
101
(getf row :norm_stime)
102
(getf row :norm_etime)))
103
(setf chosen (getf (first (get-unification-candidates row)) :id))
108
(format nil "UPDATE ~A, unified SET group_number = ~A
110
~A.docno = '~A' AND ~A.event_id = ~A"
117
(count-unified-case chosen)
118
(when (not (null oldgroup))
119
(count-unified-case oldgroup)))))
122
(defun get-newest-events (&key (skip 0) (n 0) filter (show-accepted "all"))
123
"Gets newest events, max. n results. Skips skip newest ones.
124
Returns something like ((:field1 \"content\" :field2 \"content\" ...) ...)"
127
"SELECT * FROM ~A WHERE ~A ~A
128
ORDER BY doc_date DESC
133
(format-filter filter))
135
((string= show-accepted "unverified")
136
"AND verify IS NULL")
137
((string= show-accepted "verified")
138
"AND verify IS NOT NULL")
144
(defun get-document-events (docno)
145
"Returns all events from a single document."
153
(defun reject-event (docno event_id)
156
"INSERT INTO ~Adeleted SELECT * FROM ~A WHERE docno='~A' AND event_id=~A"
163
"DELETE FROM ~A WHERE docno='~A' AND event_id=~A"
171
(defun get-unified-case (group_number
172
&key (show-accepted "all")
174
"Get list of rows that is the unified case identified by group_number.
175
show-accepted is \"all\", \"verified\", \"unverified\""
176
(my-query (format nil "SELECT * from ~A where group_number = ~A ~A ~A"
181
(concatenate 'string " AND "
182
(format-filter filter)))
184
((string= show-accepted "unverified")
185
"AND verify IS NULL")
186
((string= show-accepted "verified")
187
"AND verify IS NOT NULL")
191
(deftest test-get-newest-unified-cases ()
192
(puls.test::map-check
195
(apply #'= (mapcar (lambda (x) (getf x :group_number))
197
(get-newest-unified-cases :skip 10 :n 10)))
198
(defun get-newest-unified-cases
199
(&key (skip 0) (n 0) (show-accepted "all") filter)
200
"Like get-newest-events, but each event gets replaced by a list of
201
events with the same group_number. show-accepted controls whether to
202
include verified cases."
204
(my-query (format nil "SELECT group_number FROM ~A
206
GROUP BY group_number
208
ORDER BY doc_date DESC
212
(cond ((string= show-accepted "verified")
213
"HAVING count(verify)>0")
218
(format nil "WHERE ~A" (format-filter filter)))
221
(get-unified-case (getf row :group_number)
222
:show-accepted show-accepted
226
(defun verify-unified-case (group_number string)
228
(format nil "UPDATE unified SET verify='~A' WHERE id=~A"
233
;#.(restore-sql-reader-syntax-state)
235
;;;; internal helper functions start here
237
(defun keywordify (string)
238
"Maps \"asdf\" => :asdf"
239
(intern (string-upcase string) :keyword))
241
(defun row-plist (row field-keywords)
242
"Maps (:fieldname1 :fieldname2 :fieldname3) (val1 val2 val3)
243
=> (:fieldname1 val1 :fieldname2 val2 :fieldname3 val3)"
245
:for j :in field-keywords
249
(deftest test-format-plist ()
250
(check (equal (format-plist '(:lol "moi" :foo 2))
251
"LOL = 'moi',FOO = 2")))
252
(deftest test2-format-plist ()
253
(check (equal (format-plist '(:lol "moi" :foo 2) " AND ")
254
"LOL = 'moi' AND FOO = 2")))
255
(defun format-plist (plist &optional (separator ","))
256
"Maps (:field1 val1 :field2 val2) => \"field1=val1,field2=val2\"
257
separator \",\" can be changed to what is needed. Example to \" AND \""
258
(apply #'concatenate 'string
260
(loop :for (i j) :in (plist-avpairs plist)
262
(format nil "~A = ~A"
264
(puls.database:database-format-value j))
265
:collecting separator))))
267
(defun format-filter (plists)
268
"Format a list of plists into a filter (a sql expression)"
271
(apply #'concatenate 'string
273
(loop :for p :in plists
275
:collect (format-plist p " AND ") :and
278
(deftest test-plist-avpairs ()
280
(equalp (plist-avpairs '(:foo "LOL" :bar 1))
281
'((:foo "LOL") (:bar 1)))))
282
(defun plist-avpairs (plist)
283
"Maps (:field1 val1 :field2 val2) => ((:field1 val1) (:field2 val2))"
284
(loop :for (field val) :on plist :by #'cddr
285
:collect (list field val)))
287
(defun my-query (&rest args)
288
"Wrapper around clsql:query that returns a list of plists
289
(my-query \"select * from plus\") =>
290
((:DOCNO \"20051231_NEWScomAU_73ef10bed6da82fc1bba1c8c779c133a\" :DOC_DATE
291
\"2005.12.31\" :DISEASE_NAME \"bird flu\" ...)
292
(:DOCNO \"20051231_TheAustralian_0c5cfe218c0da516616cec1bdfa58cf5\"... )
294
(multiple-value-bind (rows cols) (apply #'query args)
295
(loop :for row :in rows
296
:collect (row-plist row (mapcar #'keywordify cols)))))
298
(defun my-select (&rest args)
299
"Same as my-query but for clsql:select"
300
(multiple-value-bind (rows cols) (apply #'select args)
301
(loop :for row :in rows
302
:collect (row-plist row (mapcar #'keywordify cols)))))
304
(deftest test-subseqs ()
305
(check (equal (subseqs "abcdefghijkl" '(1 2 5 8))
307
(defun subseqs (seq li)
308
"(subseqs \"abcdefghij\" '(1 3 5 10)) => (\"bc\" \"fghij\")"
309
(loop :for (beg end) :on li :by #'cddr
310
:collect (subseq seq beg end)))
312
(defun get-map-kml (maplist &optional (table-name *database-table*))
313
"Fetches cases related to MAPLIST from TABLE-NAME and generates
314
kml data to *STANDARD-OUTPUT*. MAPLIST is list of attributes.
315
Example: (get-map-kml '(:country \"zambia\" :disease_canon \"hiv\"))"
316
(let* ((events (clsql:query
317
(format nil "select country, location, ~
318
count(*), disease_canon from ~A where ~A ~
319
group by country, location, disease_canon"
320
table-name (format-plist maplist " AND "))))
322
(locs (loop :for (a b c d ) :in events
323
:collect (puls.maps::make-puls-maps-location
324
a b c (list :disease d)))))
325
(puls.maps::write-kml locs
326
:country-description-fn
327
'puls.maps::puls-maps-example-disease-country-description-fn
329
'puls.maps::puls-maps-example-disease-loc-description-fn)))
335
(defun loc-describe())
338
(defun make-map-kml (events country-description-fn loc-description-fn)
339
"Fetches cases related to MAPLIST from TABLE-NAME and generates
340
kml data to *STANDARD-OUTPUT*. MAPLIST is list of attributes.
341
Example: (get-map-kml '(:country \"zambia\" :disease_canon \"hiv\"))"
342
(let* ((locs (loop :for case :in events
343
:collect (puls.maps::make-puls-maps-location (getf case :country) (getf case :location) 1 (list :disease (getf case :disease_canon))))))
345
(puls.maps::write-kml locs
346
:country-description-fn country-description-fn
347
;'puls.maps::puls-maps-example-disease-country-description-fn
348
:loc-description-fn loc-description-fn
349
;'puls.maps::puls-maps-example-disease-loc-description-fn
353
(defun authenticate-user ()
354
"user authentication"
355
(multiple-value-bind (user password)
356
(hunchentoot:authorization)
359
(string (first (first (clsql:query (format nil "select password from user where name = '~A'" user)))))))
360
(hunchentoot:require-authorization))))
364
(deftest test-controller ()
369
(test-get-newest-unified-cases)
371
(test-get-document-auxil)
372
(test-get-values-date-start)
373
(test-get-values-date-end)
374
(test-get-values-disease)
375
(test-get-values-state)