Innocenty Enikeew 8 yıl önce
ebeveyn
işleme
f3b7c268f6
1 değiştirilmiş dosya ile 83 ekleme ve 19 silme
  1. 83 19
      travels.lisp

+ 83 - 19
travels.lisp

@@ -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"))