|
|
@@ -64,7 +64,7 @@
|
|
|
`(200 (:content-type "application/json") (,(jojo:to-json entity)))
|
|
|
+404+)))
|
|
|
(otherwise +404+))
|
|
|
- +400+)))
|
|
|
+ +404+)))
|
|
|
|
|
|
(defun post-entity (params)
|
|
|
(let ((id (parse-integer (getf params :id) :junk-allowed t))
|
|
|
@@ -119,7 +119,6 @@
|
|
|
(and (or (not from-date) (> (getf visit :|visited_at|) from-date))
|
|
|
(or (not to-date) (< (getf visit :|visited_at|) to-date))))
|
|
|
|
|
|
-
|
|
|
(defun user-visits (params)
|
|
|
(let ((id (parse-integer (getf params :id) :junk-allowed t)))
|
|
|
(if id
|
|
|
@@ -130,30 +129,95 @@
|
|
|
(locations (gethash :|locations| *storage*))
|
|
|
(visits (gethash :|visits| *storage*))
|
|
|
(query-string (getf myway:*env* :query-string))
|
|
|
- (query-params (and query-string (quri:url-decode-params query-string)))
|
|
|
- (from-date (may-integer (aget query-params "fromDate")))
|
|
|
- (to-date (may-integer (aget query-params "toDate")))
|
|
|
- (to-distance (may-integer (aget query-params "toDistance")))
|
|
|
- (country (aget query-params "country"))
|
|
|
- (user-visits (loop for v-id in user-visit-ids
|
|
|
- for visit = (gethash v-id visits)
|
|
|
- for loc = (gethash (getf visit :|location|) locations)
|
|
|
- when (and visit (matching-visit visit from-date to-date))
|
|
|
- when (and loc (matching-location loc country to-distance))
|
|
|
- collect (list :|mark| (getf visit :|mark|)
|
|
|
- :|visited_at| (getf visit :|visited_at|)
|
|
|
- :|place| (getf loc :|place|)
|
|
|
- :|loc| loc))))
|
|
|
- (sort user-visits #'< :key (lambda (v) (getf v :|visited_at|)))
|
|
|
- `(200 (:content-type "application/json") (,(jojo:to-json (list :|visits| user-visits)))))
|
|
|
+ (query-params (and query-string (quri:url-decode-params query-string))))
|
|
|
+ (let ((from-date (may-integer (aget query-params "fromDate")))
|
|
|
+ (to-date (may-integer (aget query-params "toDate")))
|
|
|
+ (to-distance (may-integer (aget query-params "toDistance")))
|
|
|
+ (country (aget query-params "country")))
|
|
|
+ ;; TODO: Smarter indexes
|
|
|
+ (let ((user-visits (loop for v-id in user-visit-ids
|
|
|
+ for visit = (gethash v-id visits)
|
|
|
+ for loc = (gethash (getf visit :|location|) locations)
|
|
|
+ when (and visit (matching-visit visit from-date to-date))
|
|
|
+ when (and loc (matching-location loc country to-distance))
|
|
|
+ collect (list :|mark| (getf visit :|mark|)
|
|
|
+ :|visited_at| (getf visit :|visited_at|)
|
|
|
+ :|place| (getf loc :|place|)))))
|
|
|
+ (sort user-visits #'< :key (lambda (v) (getf v :|visited_at|)))
|
|
|
+ `(200 (:content-type "application/json") (,(jojo:to-json (list :|visits| user-visits)))))))
|
|
|
(error () +400+))
|
|
|
+404+))
|
|
|
- +400+)))
|
|
|
+ +404+)))
|
|
|
+
|
|
|
+(defun smart-f (arg &optional digits)
|
|
|
+ (with-output-to-string (s)
|
|
|
+ (prin1 (cond ((= (round arg) arg) (round arg))
|
|
|
+ (digits (float (/ (round (* arg (expt 10 digits)))
|
|
|
+ (expt 10 digits))))
|
|
|
+ (t arg))
|
|
|
+ s)))
|
|
|
+
|
|
|
+(defvar *unix-epoch-difference*
|
|
|
+ (encode-universal-time 0 0 0 1 1 1970 0))
|
|
|
+
|
|
|
+(defvar *year*
|
|
|
+ (round (* 60 60 24 365.25)))
|
|
|
+
|
|
|
+(defun universal-to-unix-time (universal-time)
|
|
|
+ (- universal-time *unix-epoch-difference*))
|
|
|
+
|
|
|
+(defun unix-to-universal-time (unix-time)
|
|
|
+ (+ unix-time *unix-epoch-difference*))
|
|
|
+
|
|
|
+(defun get-unix-time ()
|
|
|
+ (universal-to-unix-time (get-universal-time)))
|
|
|
+
|
|
|
+(defmethod jojo::%to-json ((ratio ratio))
|
|
|
+ (jojo:%write-string (smart-f ratio 5)))
|
|
|
+
|
|
|
+(defun matching-user (user from-age to-age gender now)
|
|
|
+ (let ((age (/ (- now (getf user :|birth_date|)) *year*)))
|
|
|
+ (and (or (not gender) (equal (getf user :|gender|) gender))
|
|
|
+ (or (not from-age) (> age from-age))
|
|
|
+ (or (not to-age) (< age to-age)))))
|
|
|
+
|
|
|
+(defun location-avg-mark (params)
|
|
|
+ (let ((id (parse-integer (getf params :id) :junk-allowed t)))
|
|
|
+ (if id
|
|
|
+ (let ((location (gethash id (gethash :|locations| *storage*))))
|
|
|
+ (if location
|
|
|
+ (handler-case
|
|
|
+ (let* ((location-visit-ids (gethash id (gethash :location-visits *storage*)))
|
|
|
+ (users (gethash :|users| *storage*))
|
|
|
+ (visits (gethash :|visits| *storage*))
|
|
|
+ (query-string (getf myway:*env* :query-string))
|
|
|
+ (query-params (and query-string (quri:url-decode-params query-string))))
|
|
|
+ (let ((from-date (may-integer (aget query-params "fromDate")))
|
|
|
+ (to-date (may-integer (aget query-params "toDate")))
|
|
|
+ (now (get-unix-time))
|
|
|
+ (from-age (may-integer (aget query-params "fromAge")))
|
|
|
+ (to-age (may-integer (aget query-params "toAge")))
|
|
|
+ (gender (aget query-params "gender")))
|
|
|
+ (let ((marks (loop for v-id in location-visit-ids
|
|
|
+ for visit = (gethash v-id visits)
|
|
|
+ for user = (gethash (getf visit :|user|) users)
|
|
|
+ when (and visit (matching-visit visit from-date to-date))
|
|
|
+ when (and user (matching-user user from-age to-age gender now))
|
|
|
+ collect (getf visit :|mark|))))
|
|
|
+ `(200 (:content-type "application/json")
|
|
|
+ (,(jojo:to-json (list :|avg|
|
|
|
+ (if marks
|
|
|
+ (/ (apply #'+ marks) (length marks))
|
|
|
+ 0.0))))))))
|
|
|
+ (error () +400+))
|
|
|
+ +404+))
|
|
|
+ +404+)))
|
|
|
|
|
|
(defvar *mapper* (myway:make-mapper))
|
|
|
(myway:connect *mapper* "/:entity/:id" 'post-entity :method :post)
|
|
|
(myway:connect *mapper* "/:entity/:id" 'get-entity)
|
|
|
(myway:connect *mapper* "/users/:id/visits" 'user-visits)
|
|
|
+(myway:connect *mapper* "/locations/:id/avg" 'location-avg-mark)
|
|
|
|
|
|
(defun main ()
|
|
|
(setf *storage* (load-data "data.zip"))
|