# goats-requery.tcl # # This whole thing is derived from code I wrote as part of the # Ars Digita Community System training problems sets, # (see http://www.arsdigita.com/boot-camp/psets/3.xpset ) # specifically, the assignment to compare prices on a given book # from several on-line book stores. Consequently, this code is # ultimately derived from code distributed with the ACS, so proper # attribution is due. set exception_count 0 # parse form input set_the_usual_form_variables #sanity checks. "Graphable" flags this update for inclusion in # the hourly charts. It may only be set by requests comming from # the server itself. This is done by a cron job. Having a graphable # flag is more robust (in my opinion) than selecting by the exact # time, since it will still work if the automatic update runs a # early or late. if { ![info exists graphable] || [empty_string_p $graphable] } { incr exception_count append exception_text "No value for graphable." } if { ![regexp -nocase {^y$} $graphable] && ![regexp -nocase {^n$} $graphable]} { incr exception_count append exception_text "Invalid graphable $graphable." } if {[regexp -nocase {^y$} $graphable] && [string compare "137.22.99.75" [ns_conn peeraddr]] != 0} { incr exception_count append exception_text "Your IP address is not authorized to set graphable. " } set graphable [string toupper $graphable] if { $exception_count > 0 } { ad_return_complaint $exception_count $exception_text # terminate execution of this thread (a goto!) return } # return the URL of the goats page. Why use a function instead of a variable? Because # it made sense as part of a more complex ADT which was used in the code I wrote for # something else and subsequently modified to do this. proc amzn_goats_url {} { return "http://www.amazon.com/paypage/PC87DYXDCIC29" # return "http://s1.amazon.com/exec/varzea/pay/T1IPCIVVU7AQLY" } # extract total money from Amazon web page proc amzn_parse_money { page } { if {[regexp {Total Collected:.*?\$(.*?)} $page match price]} { return $price } else { ns_returnerror 408 "Unidentified error parsing amazon.com for total collected. Here is is, may you parse better:
$page" } } # extract number of payments from Amazon web page proc amzn_parse_payments { page } { if {[regexp {of Payments:.*?size=-1>(.*?)} $page match price]} { return $price } else { ns_returnerror 408 "Unidentified error parsing amazon.com for number of payments. Here is is, may you parse better:
$page" } } # Another curiosity which is reveals the heritage of this code. Don't worry about it. # it's part of a larger system designed to make the addition of other operations and # bookstores easy and transparent to client code. proc amzn_func {operation parameter} { switch $operation { "url" { return [amzn_goats_url] } "url_body" { if [catch {set temp [ns_httpget $parameter 3]}] { ns_returnerror 408 "unable to contact amazon.com server with url $parameter" } else { return $temp } } "body_money" { return [amzn_parse_money $parameter] } "body_payments" { return [amzn_parse_payments $parameter] } default { ns_returnerror 500 "Invalid operator $operation on bookstore amzn" return } } } # The only proc you need to know: an ADT defining basic server-specific ops for all servers so far proc bookstore { storename operation parameter } { switch $storename { "amzn" { return [amzn_func $operation $parameter] } default { ns_returnerror 500 "Invalid call to bookstore. Storename $storename not recognized" return } } } # set_the_usual_form_variables # See, here's where the wierdness comes in. If you used all the funky code # above to define a new bookstore, you could use this code right here # with no changes other than replacing the parameter "amzn" in the [bookstore ] # command. And you could put several bookstores in a list, and loop through # them automatically. set amzn_url1 [bookstore amzn url bob] set amzn_body1 [bookstore amzn url_body $amzn_url1] set amzn_money [bookstore amzn body_money $amzn_body1] set amzn_payments [bookstore amzn body_payments $amzn_body1] # More exception handling if { ![info exists amzn_money] || [empty_string_p $amzn_money] } { incr exception_count append exception_text "No Money!" } if { ![info exists amzn_payments] || [empty_string_p $amzn_payments] } { incr exception_count append exception_text "No Payments!" } if { $exception_count > 0 } { ad_return_complaint $exception_count $exception_text # terminate execution of this thread (a goto!) return } # This all just takes a comma out the value, so that 1,234 dolllars gets # turned into 1 and 234, and then in 1234, so that the comma doesn't # confuse Oracle. regexp {([0-9]*),(.*$)} $amzn_money match big little set amzn_money "$big$little" # figure out what page this was called from so we can # bounce the user right back there. That way, this same # page can be linked to by both the daily and hourly # graph pages, and the user will be sent back to the # (now updated) page s/he was at before. Which is what # we want. set referer [ns_set get [ns_conn headers] Referer] # This is debugging material it never gets actually returned. set page_content " test! $referer Query shows \$$amzn_money in $amzn_payments payments." # Here's the beef. Insert the parsed values into Oracle db_transaction { db_dml lock_table "lock table goats_money in exclusive mode" set new_key [db_string max_check_id "select nvl(max(check_id)+1,1) from goats_money"] db_dml insert_quote "insert into goats_money (check_id, insertion_date, total_money, num_payments,graphable) values (:new_key, sysdate, :amzn_money , :amzn_payments, :graphable) " } db_release_unused_handles #ns_return 200 text/html $page_content #ns_returnredirect goats-2.tcl # And send the user back to whence they came ns_returnredirect $referer