What is happening to my SQL connection in a for construct? - sql

I'm working on a program that converts the USDA nutrition database files from ASCII into an sqlite database. Information about those files can be found at http://www.ars.usda.gov/Services/docs.htm?docid=8964 and amounts to something really awesome.
However, in the code below, I have my sql connection "disappearing" as soon as I enter a for construct in the function that does conversions.
(defn sanitize-field [field]
(cond
(= field "~~") ""
(= field "") 0
(and (.startsWith field "~") (.endsWith field "~"))
(.substring field 1 (- (.length field) 1))
:default (try
(Double. field)
(catch NumberFormatException n 0.0))))
(defn field-separator [line]
(.split line "\\^"))
(defn parse-file [file]
(map (fn [l] (map sanitize-field (field-separator l)))
(.split (slurp (.getCanonicalPath file)) "\r\n")))
(defn convert-food-description [source-dir]
(println (sql/connection))
(for [entry (parse-file (File. source-dir "FOOD_DES.txt"))]
(do
(println entry)
(println (sql/connection))
(sql/insert-rows "food_des" entry))))
(Class/forName "org.sqlite.JDBC")
(def db {:classname "com.sqlite.JDBC" :subprotocol "sqlite" :subname "nutrition.sqlite"})
(defn convert []
(sql/with-connection db
(convert-food-description (File. "/home/savanni/Downloads/nutrient_database"))))
When I run the convert operation, here is what I get:
user=> (convert)
(convert)
#<Conn org.sqlite.Conn#180e426>
((01001 0100 Butter, salted BUTTER,WITH SALT Y 0.0 6.38 4.27 8.79 3.87)
java.lang.Exception: no current database connection
So, my only println commands are in convert-food-description. It looks like the first one right after the function begins executes fine, printing out the connection that I created with the with-connection statement. But then the for loop begins, I print out the first line of data that I'm going to insert into the database, and then it throws an exception when trying to print the SQL connection again.
What could be causing the connection to disappear there? Is something else entirely happening?
UPDATE: the sql/ functions here all come from clojure.contrib.sql

for is lazy. Your connection handle is being opened and closed by with-connection before for in convert-food-description evaluates.
You have to force evaluation while the connection handle is still alive. Try changing for to doseq, for example.
user> (require '(clojure.contrib [sql :as sql]))
nil
user> (import 'org.sqlite.JDBC)
org.sqlite.JDBC
user> (def db {:classname "com.sqlite.JDBC"
:subprotocol "sqlite"
:subname "foo.sqlite"})
#'user/db
user> (sql/with-connection db (sql/create-table "foo" ["val" "int"]))
(0)
;; Bad
user> (defn foo []
(sql/with-connection db
(for [x (range 10)]
(sql/insert-records "foo" {:val x}))))
#'user/foo
user> (foo)
; Evaluation aborted.
;;java.lang.Exception: no current database connection
;; Good
user> (defn foo []
(sql/with-connection db
(doseq [x (range 10)]
(sql/insert-rows "foo" [x]))))
#'user/foo
user> (foo)
nil

First of all, your (println (sql/connection)) is not printing your current connection. Instead it creates new one and prints it.
Second, you have defined db as (def db {:classname "com.sqlite.JDBC" :subprotocol "sqlite" :subname "nutrition.sqlite"})
This doesn't open connection, this is just a connection description.
Third, you should pass db as a parameter to convert-food-description method, and use that for actual queries.

Related

How to preview an image and filename for a file upload in Clojure?

I want to upload an image, show a preview of the image, and the associated filename, all in Clojure.
The example at http://jsfiddle.net/LvsYc/638/ demonstrates what I want to do, except I want it in Clojure/ClojureScript.
I have attempted to rewrite the code from the above link, and have managed to output the file-name to the console using (js/console.log). I have two global atoms in my file, one for the image name, and one for the image itself. My code below is my attempt so far.
What do I need to do to 1). get the name of the file from the console log and display it in the :span.file-name using these atoms, and 2), display a preview of the image?
(def image (atom "#"))
(def image-name (atom nil))
(def file-upload-selector
"Implements a file-upload button, allowing the user to
navigate to a file on their desktop and select it."
(let
[reader (js/FileReader.)
_ (set! (.-onload reader)
(fn [e]
(reset! image
(-> e .-target .-result ))))
read-url
(fn [input]
(reset! image-name (-> input .-target .-files (aget 0) .-name))
(js/console.log (-> input .-target .-files (aget 0) .-name))
(when-let [file-input (.-files input)]
(.readAsDataURL reader (aget file-input 0))))]
(fn []
[:div.file.has-name.is-boxed
[:label.file-label
[:input.file-input {:type "file"
:name "User_Photo"
:id "file"
:on-change read-url}]
[:span.file-cta
[:span.file-icon
[:i.fas.fa-upload]]
[:span.file-label "Upload profile picture" ]
][:img {:src image :alt "your image" }]
[:span.file-name {:id "filename" } image-name
]]])))
Found a solution that worked for me. After researching online, the following github link was what I needed.
https://github.com/jlangr/image-upload/blob/master/src/cljs/image_upload/core.cljs
Relevant code from #jlangr.
(let [file (first (array-seq (.. file-added-event -target -files)))
file-reader (js/FileReader.)]
(set! (.-onload file-reader)
(fn [file-load-event]
(reset! preview-src (-> file-load-event .-target .-result))
(let [img (.getElementById js/document img-id)]
(set! (.-onload img)
(fn [image-load]
(.log js/console "dimensions:" (.-width img) "x" (.-height img)))))))
(.readAsDataURL file-reader file)))
(defn image-upload-fn []
[:div [:h2 "image-upload"]
[:input {:type "file" :on-change load-image}]
[:img {:id img-id :src #preview-src}]])

Elixir - testing a full script

I'm writing a test to check a function (called automatically by GenServer when a new file enters a folder) that calls other functions in the same module with pipes in order to read a file, process its content to insert it if needed and returns a list (:errors and :ok maps).
results looks like :
[
error: "Data not found",
ok: %MyModule{
field1: field1data,
field2: field2data
},
ok: %MyModule{
field1: field1data,
field2: field2data
},
error: "Data not found"
the code :
def processFile(file) do
insertResultsMap =
File.read!(file)
|> getLines()
|> extractMainData()
|> Enum.map(fn(x) -> insertLines(x) end)
|> Enum.group_by(fn x -> elem(x, 0) end)
handleErrors(Map.get(insertResultsMap, :error))
updateAnotherTableWithLines(Map.get(insertResultsMap, :ok))
end
defp getLines(docContent) do
String.split(docContent, "\n")
end
defp extractMainData(docLines) do
Enum.map(fn(x) -> String.split(x, ",") end)
end
defp insertLines([field1, field2, field3, field4]) do
Attrs = %{
field1: String.trim(field1),
field2: String.trim(field2),
field3: String.trim(field3),
field4: String.trim(field4)
}
mymodule.create_stuff(Attrs)
end
defp handleErrors(errors) do
{:ok, file} = File.open(#errorsFile, [:append])
saveErrors(file, errors)
File.close(file)
end
defp saveErrors(_, []), do: :ok
defp saveErrors(file, [{:error, changeset}|rest]) do
changes = for {key, value} <- changeset.changes do
"#{key} #{value}"
end
errors = for {key, {message, _}} <- changeset.errors do
"#{key} #{message}"
end
errorData = "data: #{Enum.join(changes, ", ")} \nErrors: #{Enum.join(errors, ", ")}\n\n"
IO.binwrite(file, errorData)
saveErrors(file, rest)
end
defp updateAnotherTableWithLines(insertedLines) do
Enum.map(insertedLines, fn {:ok, x} -> updateOtherTable(x) end)
end
defp updateOtherTable(dataForUpdate) do
"CLOSE" -> otherModule.doStuff(dataForUpdate.field1, dataForUpdate.field2)
end
I have several questions, and some will be pretty basic since I'm still learning :
What do you think of the code ? Any advices ? (take into account I voluntarily obfuscated names).
If I want to test this, is it the right way to test only processFile function ? Or should I make public more of them and test them individually ?
When I test the processFile function, I check that I'm receiving a list. Any way to make sure this list has only elements I'm waiting for, thus error: "String" or ok: %{}" ?
What do you think of the code? Any advices? (take into account I voluntarily obfuscated names).
Opinion based.
If I want to test this, is it the right way to test only processFile function?
Yes.
Or should I make public more of them and test them individually?
No, this is an implementation detail and testing it is an anti-pattern.
When I test the processFile function, I check that I'm receiving a list. Any way to make sure this list has only elements I'm waiting for, thus error: "String" or ok: %{}"?
You receive a Keyword. To check the explicit value, one might use:
foo = processFile(file)
assert not is_nil(foo[:ok])
OTOH, I’d better return a map from there and pattern match it:
assert %{ok: _} = processFile(file)
To assert that the result does not have anything save for :oks and :errors, one might use list subtraction:
assert Enum.uniq(Keyword.keys(result)) -- [:ok, :error] == []

Why this list has only void items in Racket

I tried to have user input function using code on this page: A simple Racket terminal interaction
(define entry_list (for/list ([line (in-lines)]
#:break (string=? line "done"))
(println line)))
(println entry_list)
Output is:
this
"this "
is
"is "
a
"a "
test
"test"
for testing only
"for testing only"
done
'(#<void> #<void> #<void> #<void> #<void>)
Why is the list consisting of only "void" items?
That is because the println function returns #<void>. If instead of println you put something that returned a different value for each line you would end up with a more interesting list.
For example, the following code should return a list with the lines that you typed in:
(define entry_list
(for/list ([line (in-lines)]
#:break (string=? line "done"))
line))
If you just want to print the lines then you could have used for instead of for/list, to avoid creating an useless list of voids at the end:

In racket, how do you allow file upload to the web server?

I'm trying to enable file upload to a web server using Racket's file-upload formlet (http://docs.racket-lang.org/web-server/formlets.html). The trouble is that formlet-process just returns the name of the file instead of its contents.
Here's what I have so far:
#lang web-server/insta
(require web-server/formlets
web-server/http
xml)
; start: request -> doesn't return
(define (start request)
(show-page request))
; show-page: request -> doesn't return
(define (show-page request)
; Response generator
(define (response-generator embed/url)
(response/xexpr
`(html
(head (title "File upload example"))
(body (h1 "File upload example"))
(form
([action ,(embed/url upload-handler)])
,#(formlet-display file-upload-formlet)
(input ([type "submit"] [value "Upload"]))))))
(define (upload-handler request)
(define a-file (formlet-process file-upload-formlet request))
(display a-file)
(response/xexpr
`(html
(head (title "File Uploaded"))
(body (h1 "File uploaded")
(p "Some text here to say file has been uploaded")))))
(send/suspend/dispatch response-generator))
; file-upload-formlet: formlet (binding?)
(define file-upload-formlet
(formlet
(div ,{(required (file-upload)) . => . a-file})
a-file))
In this case, a-file gets set to a byte string with the name of the file, instead of the contents of the file. How do I get the contents of the file so that I can write it to a file on the server?
Thanks in advance for your help!
OK, here's something that works, though I'm not sure it's the best way of doing things. Basically I had to
add method="POST" and enctype="multipart/form-data" to the form field html (yeah, schoolboy error to omit these, but I'm new to this stuff)
use binding:file-filename and binding:file-contents to extract the file name and contents from the binding returned by the file-upload formlet.
References that helped in figuring this out were
http://lists.racket-lang.org/users/archive/2009-August/034925.html
and
http://docs.racket-lang.org/web-server/http.html
So here's the working code. Obviously WORKINGDIR needs to be set to some working path.
#lang web-server/insta
(require web-server/formlets)
; start: request -> doesn't return
(define (start request)
(show-page request))
; show-page: request -> doesn't return
(define (show-page request)
; Response generator
(define (response-generator embed/url)
(response/xexpr
`(html
(head (title "File upload example"))
(body (h1 "File upload example"))
(form
([action ,(embed/url upload-handler)]
[method "POST"]
[enctype "multipart/form-data"])
,#(formlet-display file-upload-formlet)
(input ([type "submit"] [value "Upload"]))))))
(define (upload-handler request)
(define-values (fname fcontents)
(formlet-process file-upload-formlet request))
(define save-name (string-append "!uploaded-" fname))
(current-directory WORKINGDIR)
(display-to-file fcontents save-name #:exists 'replace)
(response/xexpr
`(html
(head (title "File Uploaded"))
(body (h2 "File uploaded")
(p ,fname)
(h2 "File size (bytes)")
(p ,(number->string (file-size save-name)))))))
(send/suspend/dispatch response-generator))
; file-upload-formlet: formlet (binding?)
(define file-upload-formlet
(formlet
(div ,{(file-upload) . => . binds})
; (formlet-process file-upload-formlet request)
; returns the file name and contents:
(let
([fname (bytes->string/utf-8 (binding:file-filename binds))]
[fcontents (binding:file-content binds)])
(values fname fcontents))))

Clojure: read metadata of a database

I am trying to get metadata from an sqlite database. The main purpose for the moment was to get a list of tables.
Code below (from here: link):
(defn db-get-tables
"Demonstrate getting table info"
[]
(sql/with-connection db
(into []
(resultset-seq
(-> (sql/connection)
(.getMetaData)
(.getTables nil nil nil (into-array ["TABLE" "VIEW"])))))))
This gives me a list of maps with metadata regarding the tables in the database. However, if I try to iterate this list (using 'for' or 'first') it gives me:
"Don't know how to create ISeq from proj00.operations.database$tables-list"
I believe that there must be an easy way to do this. But I just cannot find the right information on the web. Also, I cannot understand where that error is coming from.
It might be because the connection with the database is only open within the scope of "sql/with connection db". If I iterate through the collection like this with Microsoft SQL, I get an error that the connection is closed.
If you wrap the resultset-seq in a doall, this should be fixed. This breaks lazyness though in favor of getting all results into memory and being able to close the connection. If you want to keep lazyness, you should put the iteration within the "with-connection" scope, but you'll keep the connection open until you're done.
Also, you can generalize this function into supporting all metadata methods by making a macro of this (thanks to Verneri Åberg's answer to a question of mine):
(defmacro get-sql-metadata [db method & args]
`(with-connection
~db
(doall
(resultset-seq
(~method
(.getMetaData (connection))
~#args)))))
So now you can call the metadata with the metadata method and its own parameters like so:
(get-sql-metadata db .getTables nil nil nil (into-array ["TABLE" "VIEW"]))
or
(get-sql-metadata db .getColumns nil nil nil nil)
Followup:
Created a testdatabase, connected, everything should work like this.
Leiningen
(defproject sqlitetest "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]
[org.xerial/sqlite-jdbc "3.6.16"]
[org.clojure/java.jdbc "0.1.0"]])
Program
(ns sqlitetest
(:use [clojure.java.jdbc]))
(def db { :classname "org.sqlite.JDBC"
:subprotocol "sqlite"
:subname "E:/temp/chinook.db"})
(defmacro get-sql-metadata [db method & args]
`(with-connection
~db
(doall
(resultset-seq
(~method
(.getMetaData (connection))
~#args)))))
(def tables-list
(get-sql-metadata db .getTables nil nil nil (into-array ["TABLE" "VIEW"])))
REPL
sqlitetest=>(map :table_name tables-list)
("SQLITE_SEQUENCE" "ALBUM" "ARTIST" "CUSTOMER" "EMPLOYEE" "GENRE" "INVOICE" "INVOICELINE" "MEDIATYPE" "PLAYLIST" "PLAYLISTTRACK" "TRACK")
sqlitetest=>(first tables-list)
{:self_referencing_col_name nil, :table_name "SQLITE_SEQUENCE", :type_schem nil, :ref_generation nil, :table_type "TABLE", :table_schem nil, :table_cat nil, :type_cat nil, :type_name nil, :remarks nil}
Remark on your comment and answer to the question
The error is caused by doing defn instead of def over the table-list as in your comment. I run into the same error if I use defn.