(module web-services racket
(require net/cgi
net/url
(rename-in xml
[struct:element struct:xelement]
[element xelement]
[make-element make-xelement]
[element-name xelement-name]
[element-attributes xelement-attributes]
[element-content xelement-content]
[element? xelement?]
[struct:attribute struct:xattribute]
[attribute xattribute]
[make-attribute make-xattribute]
[attribute-name xattribute-name]
[attribute-value xattribute-value]
[attribute? xattribute?]
[struct:pcdata struct:xpcdata?]
[pcdata xpcdata]
[make-pcdata make-xpcdata]
[pcdata-string xpcdata-string]
[pcdata? xpcdata?]
[xml->xexpr old-xml->xexpr])
racket/gui/base
(only-in lang/htdp-beginner-abbr [cons student-cons]
[cons? student-cons?]))
(define debug #f)
(define EBAY-APP-ID "Universi-6f31-46da-b1de-005829958052")
(define EBAY-REQUEST-URL
"http://svcs.ebay.com/services/search/FindingService/v1")
(define GOOGLE-REQUEST-URL
"http://maps.google.com/maps/api/staticmap?")
(define CACHE (make-hash))
(define-struct ebay-item
(title
price
pcode
location)
#:transparent)
(define-struct element (name attributes contents)
#:transparent)
(define-struct pcdata (string)
#:transparent)
(define-struct attribute (name value)
#:transparent)
(define (fn-for-element e)
(... (element-name e) (element-attributes e) (element-contents e)))
(define (fn-for-attribute att)
(... (attribute-name att) (attribute-value att)))
(define (fn-for-loa loa)
(cond [(empty? loa) (...)]
[else (... (fn-for-attribute (first loa))
(fn-for-loa (rest loa)))]))
(define (fn-for-content cont)
(cond [(element? cont) (fn-for-element cont)]
[(pcdata? cont) (fn-for-pcdata cont)]))
(define (fn-for-loc loc)
(cond [(empty? loc) (...)]
[else (... (fn-for-content (first c))
(fn-for-loc (rest c)))]))
(define (fn-for-pcdata pcd)
(... (pcdata-string pcd)))
(define (debug-msg msg)
(if debug
(begin (display msg) (newline))
#f))
(define (send-request-port req-str)
(debug-msg req-str)
(get-pure-port (string->url req-str)))
(define (send-request-xml req-str)
(hash-ref-or-new CACHE req-str
(lambda ()
(let ([result
(xelement->element
(document-element
(read-xml
(send-request-port req-str))))])
result))))
(define (send-request-png req-str)
(hash-ref-or-new CACHE req-str
(lambda ()
(load-image-from-port (send-request-port req-str)))))
(define (load-image-from-port port)
(let ([file (make-temporary-file)])
(call-with-output-file file
(lambda (out)
(let loop ((b (read-byte port)))
(if (eof-object? b)
'done
(begin
(write-byte b out)
(loop (read-byte port))))))
#:mode 'binary
#:exists 'replace)
(begin0
(make-object image-snip%
file)
(delete-file file))))
(define (get-elt-element elt str)
(if (string=? (element-name elt) str)
elt
(get-elt-loc (element-contents elt) str)))
(define (get-elt-loc loc str)
(cond [(empty? loc) false]
[else (if (not (false? (get-elt-content (first loc) str)))
(get-elt-content (first loc) str)
(get-elt-loc (rest loc) str))]))
(define (get-elt-content cont str)
(cond [(element? cont) (get-elt-element cont str)]
[(pcdata? cont) false]))
(define (get-pcdata-element elt str)
(if (not (false? (get-elt-element elt str)))
(get-pcdata-loc (element-contents (get-elt-element elt str)))
""))
(define (get-pcdata-loc loc)
(cond [(empty? loc) ""]
[else (if (not (string=? "" (get-pcdata-content (first loc))))
(get-pcdata-content (first loc))
(get-pcdata-loc (rest loc)))]))
(define (get-pcdata-content cont)
(cond [(element? cont) ""]
[(pcdata? cont) (pcdata-string cont)]))
(define (get-search-result elt)
(if (not (false? (get-elt-element elt "searchResult")))
(element-contents (get-elt-element elt "searchResult"))
empty))
(define (xelement->element xe)
(make-element (symbol->string (xelement-name xe))
(xatts->atts (xelement-attributes xe))
(loxc->loc (xelement-content xe))))
(define (xatts->atts loxa)
(map (lambda (xa) (xatt->att xa)) loxa))
(define (xatt->att xa)
(make-attribute (symbol->string (xattribute-name xa)) (xattribute-value xa)))
(define (loxc->loc loxc)
(map (lambda (xc) (xcontent->content xc)) loxc))
(define (xcontent->content xc)
(cond [(xelement? xc)
(xelement->element xc)]
[(xpcdata? xc)
(xpcdata->pcdata xc)]))
(define (xpcdata->pcdata xpcd)
(make-pcdata (xpcdata-string xpcd)))
(define (make-query-url host path args)
(let ([urlstr
(url->string
(make-url "http" #f host #f #t
(map (lambda (n) (make-path/param n empty))
(regexp-split "/" path))
(massage-args args)
#f))])
urlstr))
(define (massage-args listof-nvp)
(local [ (define (lonvp l)
(map nvp l))
(define (nvp n)
(cons (string->symbol (car n))
(cadr n)))]
(lonvp listof-nvp)))
(define (label-marker-pairs lonvp)
(let loop ([lst lonvp]
[n 65])
(cond [(empty? lst) empty]
[else (student-cons (label-marker-pair (car lst) n)
(loop (cdr lst) (add1 n)))])))
(define (label-marker-pair nvp n)
(student-cons (car nvp)
(student-cons (string-append "label:"
(string (integer->char n))
"|"
(cadr nvp))
empty)))
(define (get-map lonvp)
(send-request-png (make-query-url "maps.google.com"
"maps/api/staticmap"
(student-cons (student-cons "size" (student-cons "256x256" empty))
(student-cons (student-cons "sensor" (student-cons "false" empty))
lonvp)))))
(define (convert-to-marker-pairs loei)
(cond [(empty? loei) empty]
[else (student-cons (convert-to-marker-pair (first loei))
(convert-to-marker-pairs (rest loei)))]))
(define (convert-to-marker-pair ebi)
(student-cons "markers"
(student-cons (string-append (ebay-item-pcode ebi)
","
(ebay-item-location ebi))
empty)))
(define (get-ebi-map str)
(get-map (convert-to-marker-pairs (get-ebay-items str))))
(define (get-labeled-ebi-map str)
(get-map (label-marker-pairs (convert-to-marker-pairs (get-ebay-items str)))))
(define (get-ebay-element s)
(send-request-xml (make-query-url "svcs.ebay.com"
"services/search/FindingService/v1"
(append (list (student-cons "OPERATION-NAME" (student-cons "findItemsByKeywords" empty))
(student-cons "SERVICE-VERSION" (student-cons "1.0.0" empty))
(student-cons "GLOBAL-ID" (student-cons "EBAY-ENCA" empty))
(student-cons "SECURITY-APPNAME" (student-cons "Universi-6f31-46da-b1de-005829958052" empty))
(student-cons "RESPONSE-DATA-FORMAT" (student-cons "XML" empty))
(student-cons "REST-PAYLOAD" (student-cons "true" empty))
(student-cons "paginationInput.entriesPerPage" (student-cons "26" empty))
(student-cons "paginationInput.pageNumber" (student-cons "1" empty))
(student-cons "buyerPostalCode" (student-cons "V6T1Z4" empty))
(student-cons "itemFilter(0).name" (student-cons "MaxDistance" empty))
(student-cons "itemFilter(0).value" (student-cons "25" empty))
(student-cons "itemFilter(1).name" (student-cons "Currency" empty))
(student-cons "itemFilter(1).value" (student-cons "USD" empty)))
(student-cons (student-cons "keywords" (student-cons s empty))
empty)))))
(define (element->ebay-items e)
(map (lambda (e)
(make-ebay-item
(get-pcdata-element e "title")
(get-pcdata-element e "currentPrice")
(get-pcdata-element e "postalCode")
(get-pcdata-element e "location")))
(get-search-result e)))
(define (get-ebay-items keyword)
(element->ebay-items (get-ebay-element keyword)))
(define (hash-ref-or-new hash key fn)
(if (hash-has-key? hash key)
(hash-ref hash key)
(let ([new (fn)])
(hash-set! hash key new)
new)))
(define (number->decimal-string n)
(real->decimal-string n 2))
(define (test1 keyword) (get-ebi-map keyword))
(define (test2 keyword) (get-labeled-ebi-map keyword))
(provide
get-ebay-items
number->decimal-string
send-request-png
send-request-xml
make-query-url
label-marker-pairs
(struct-out ebay-item)
(struct-out element)
(struct-out attribute)
(struct-out pcdata))
)