Clojure Enlive: Applying snippet on a list - html

I am trying to define an enlive template for a html table, that shows data from a map.
template-div for this example is right here. Dummy content that for the cells in the template is here.
defsnippet for cell value and deftemplate are defined as:
(require '[net.cgrand.enlive-html :as html])
(html/defsnippet value-cell (template-div) [:div.Row :div.Cell] [value]
(html/content value))
However, when I try the snippet
(value-cell (mapv vals (:event-data dummy-content)))
All the values are in one tag like this
({:tag :div, :attrs {:class "Cell"},
:content ("end time 1" "date 1" "event name 1" "start time 1" "performer 1" "end time 2" "date 2" "event name 2" "start time 2" "performer 2")})
And I need every item from a list to be a value in the tag.

You are passing a list of values to value-cell, so value-cell should look something like:
(html/defsnippet value-cell (template-div)
[:div.Row :div.Cell]
[values]
(html/clone-for [value values]
(html/content value)))

Related

Clojure table rows plus/minus for same column value

I have a table in clojure which displays data from a API. I want add plus(+) symbol beside Name column which should hide all the rows which have same name. It expand the same name rows on clicking plus and collapse on clicking minus(-).
(defn ^:private get-table
[request]
{:title "Table"
:rows (transform(get-body (get-data request)))
:columns [{:title "Name" :field :name}
{:title "Age" :field :age}
{:title "Height" :field :height}
{:title "Weight" :field :weight}
{:title "Phone" :field :phone}]})
Appreciate any help!

How to set array as state in Reagent using ClojureScript

I'm trying to create a state called items however my code gives an error and I'm unsure of why when I try to access the items and iterate over them. What am I doing wrong here?
(def items (r/atom ["test" "test2"]))
(defn home-page []
[:div#main
[:section.section
[:h1#s-one-greeting "Hello, I'm testing"]
[:h2#s-one-greeting-two "blah blah blah"]]
[:section.section
[:p "Work history"]
[:p "yada"]
[:p "yada"]
[:ul
(for [item items]
^{:key item} [:li "item " item])]]])
You are accessing items without derefing it. So you are trying to for loop over the reagent atom which doesn't work. Just switch it to (for [item #items] ...) and you should be fine.

Select/unselect checkbox in re-frame

I'm adding a search id element to my app-db with:
[:input {:type "checkbox" :on-change #(reframe/dispatch [:add-elm {:subject_id subject-id}])}]
but I don't know how to recognize when the final user is checking or unchecking the box in order to put or withdraw the element from app-db. I want to avoid searching in the app-db to see if the element is already there.
1) Add an ID to the checkbox:
[:input {:type "checkbox" :title (:subject row-subject)
:id (str "subjects_" (:id row-subject))
:on-change #(rf/dispatch [:add-search-elm {"subjects" (:id row-subject)}])}]
2) Get and read the DOM element:
(let [ksection (first (first updates)) ;; key subject
vsection (get updates ksection) ;; value subject
elm (str ksection "_" vsection)
checkbox (gdom/getElement elm)
checked (.. checkbox -checked)]
(.log js/console (str ">>> ksection >>>>> " ksection ";; vsection >> " vsection ";; elm >> " elm " checkbox >>> " checkbox ";; checked >> " checked))

Accessing nested JSON Fields in Common Lisp

I have been playing with the spotify API in an effort to get comfortable with common Lisp. The language is a large departure from what I am used to and I am afraid I may be missing some understanding of how to parse objects and lists.
When I poll the spotify API looking for a song by title. It returns a lot of json which I access using cl-json decode method. However in order to access any of the fields I find myself doing long nested car cdr combinations. The cl-json returns not a listp or lista but a giant list of nested cons. It seems like a nightmare to parse. I am attaching the code below as an example. So what I want to ask is, What am I missing? This code looks way to cumbersome and messy to be the way its done in LISP, and I assume there is a nice way to map all the JSON to some type of object or hierarchical tree? Which would make querying easier.
(ql:quickload '(:cl-json :drakma))
(defvar *auth-token*)
(defvar *results*)
(setq *url* "https://api.spotify.com/v1/search")
(push (cons "application" "json") drakma:*text-content-types*)
(setf drakma:*header-stream* *standard-output*)
(defun search-spotify (&optional (search-param "blue moon") (search-type "track") (limit 5))
"Perform a search on the API for track, artist, or album information"
(defvar complete-url "")
(defvar url nil)
(defvar params nil)
(setq url "https://api.spotify.com/v1/search")
(setq params (format nil "?q=~a&type=~a&limit=~a" (replace-all search-param " " "+") (replace-all search-type " " "+") limit))
(setq complete-url (format nil "~a~a" url params))
(print complete-url)
(setq *results* (cl-json:decode-json-from-string
(drakma:http-request complete-url
:method :get
:additional-headers `(("Authorization" . ,(format nil "Bearer ~a" (cdar *auth-token*))))
))))
(defun print-spotify-artist ()
(loop for result in (cdr (caddar *results*))
do (format t "Artist Name: ~a Spotify ID: ~a ~%" (cdadr (cddadr (caddar result))) (cdar (cdaadr (caddar result))))))
Running the above code and calling the print-spotify-artist function will print a list as follows:
Artist Name: Eve 6 Spotify ID:
https://open.spotify.com/artist/4Eqd24yS5YcxI8b6Xfuwr8 Artist Name:
Bill Murray Spotify ID:
https://open.spotify.com/artist/3wkZ8WTrs7WcfE13voUCK1 Artist Name:
Various Artists Spotify ID:
https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of Artist Name:
Pat Martino Spotify ID:
https://open.spotify.com/artist/4DlMMgnldzX6OkCskmeGKz Artist Name:
Jess & Zeb Spotify ID:
https://open.spotify.com/artist/1oAndP8vmGtTlB6mbpieJs
Example JSON return, you get this returned for each song, for this example data I set the maximum results to 1. Notice that it is heavily nested. The below is what is returned from decode json function of cl-json.
((:TRACKS
(:HREF
. "https://api.spotify.com/v1/search?query=open%2Broad%2Bsong&type=track&offset=0&limit=1")
(:ITEMS
((:ALBUM (:ALBUM--TYPE . "album")
(:ARTISTS
((:EXTERNAL--URLS
(:SPOTIFY . "https://open.spotify.com/artist/4Eqd24yS5YcxI8b6Xfuwr8"))
(:HREF . "https://api.spotify.com/v1/artists/4Eqd24yS5YcxI8b6Xfuwr8")
(:ID . "4Eqd24yS5YcxI8b6Xfuwr8") (:NAME . "Eve 6") (:TYPE . "artist")
(:URI . "spotify:artist:4Eqd24yS5YcxI8b6Xfuwr8")))
(:AVAILABLE--MARKETS "AD" "AR" "AT" "AU" "BE" "BG" "BO" "BR" "CA" "CH"
"CL" "CO" "CR" "CY" "CZ" "DE" "DK" "DO" "EC" "EE" "ES" "FI" "FR" "GB"
"GR" "GT" "HK" "HN" "HU" "ID" "IE" "IL" "IS" "IT" "JP" "LI" "LT" "LU"
"LV" "MC" "MT" "MY" "NI" "NL" "NO" "NZ" "PA" "PE" "PH" "PL" "PT" "PY"
"RO" "SE" "SG" "SK" "SV" "TH" "TR" "TW" "US" "UY" "VN" "ZA")
(:EXTERNAL--URLS
(:SPOTIFY . "https://open.spotify.com/album/1qJOmC60ez9RNWPg4ELMBW"))
(:HREF . "https://api.spotify.com/v1/albums/1qJOmC60ez9RNWPg4ELMBW")
(:ID . "1qJOmC60ez9RNWPg4ELMBW")
(:IMAGES
((:HEIGHT . 639)
(:URL
. "https://i.scdn.co/image/3f7891ffa993954dd72ed50245280b15f5db5844")
(:WIDTH . 621))
((:HEIGHT . 300)
(:URL
. "https://i.scdn.co/image/16f1a6277b827fd97cb450c5dc246d29c2ed1d52")
(:WIDTH . 291))
((:HEIGHT . 64)
(:URL
. "https://i.scdn.co/image/935aca4d861213415fc64f780b0e9e5a0e8d865c")
(:WIDTH . 62)))
(:NAME . "Eve 6") (:RELEASE--DATE . "1998")
(:RELEASE--DATE--PRECISION . "year") (:TYPE . "album")
(:URI . "spotify:album:1qJOmC60ez9RNWPg4ELMBW"))
(:ARTISTS
((:EXTERNAL--URLS
(:SPOTIFY . "https://open.spotify.com/artist/4Eqd24yS5YcxI8b6Xfuwr8"))
(:HREF . "https://api.spotify.com/v1/artists/4Eqd24yS5YcxI8b6Xfuwr8")
(:ID . "4Eqd24yS5YcxI8b6Xfuwr8") (:NAME . "Eve 6") (:TYPE . "artist")
(:URI . "spotify:artist:4Eqd24yS5YcxI8b6Xfuwr8")))
(:AVAILABLE--MARKETS "AD" "AR" "AT" "AU" "BE" "BG" "BO" "BR" "CA" "CH" "CL"
"CO" "CR" "CY" "CZ" "DE" "DK" "DO" "EC" "EE" "ES" "FI" "FR" "GB" "GR" "GT"
"HK" "HN" "HU" "ID" "IE" "IL" "IS" "IT" "JP" "LI" "LT" "LU" "LV" "MC" "MT"
"MY" "NI" "NL" "NO" "NZ" "PA" "PE" "PH" "PL" "PT" "PY" "RO" "SE" "SG" "SK"
"SV" "TH" "TR" "TW" "US" "UY" "VN" "ZA")
(:DISC--NUMBER . 1) (:DURATION--MS . 194866) (:EXPLICIT)
(:EXTERNAL--IDS (:ISRC . "USRC19806851"))
(:EXTERNAL--URLS
(:SPOTIFY . "https://open.spotify.com/track/7kAKO1EYHt2MVlombUuoLN"))
(:HREF . "https://api.spotify.com/v1/tracks/7kAKO1EYHt2MVlombUuoLN")
(:ID . "7kAKO1EYHt2MVlombUuoLN") (:IS--LOCAL) (:NAME . "Open Road Song")
(:POPULARITY . 44)
(:PREVIEW--URL
. "https://p.scdn.co/mp3-preview/b793285aeeb4a1176b93dc739ffb361c6aabf4e5?cid=b067abcbc67f4ceba0d61e414926c9f5")
(:TRACK--NUMBER . 5) (:TYPE . "track")
(:URI . "spotify:track:7kAKO1EYHt2MVlombUuoLN")))
(:LIMIT . 1)
(:NEXT
. "https://api.spotify.com/v1/search?query=open%2Broad%2Bsong&type=track&offset=1&limit=1")
(:OFFSET . 0) (:PREVIOUS) (:TOTAL . 43)))
The Access library might help, it allows to access nested data structures.
(defparameter my-plist (list :foo "foo" :bar "bar"))
;; bar is a plist
(defclass obj-test ()
((foo :accessor foo :initarg :foo :initform :foo)
(bar :accessor bar :initarg :bar :initform (copy-list MY-PLIST))))
(defvar my-obj ((make-instance 'obj-test))
(accesses MY-OBJ 'bar 'foo) ;; => "foo"
There is a way to use dotted paths, if that is to your liking.
Access is battle tested, it is the heart of the Djula templating engine, one of the most downloaded libraries on Quicklisp.
Access also allows consistent access across data structures.
Deeper overview: https://lisp-journey.gitlab.io/blog/generice-consistent-access-of-data-structures-dotted-path/

clojure loop through json data

I have a problem parsing json data in a loop. Iam a clojure beginner and need some hint for looping through json data.
The data looks like this:
{"photoset" {"primary" "8455893107", "total" "2", "pages" 1, "perpage" 500, "page" 1,
"per_page" 500, "photo"
[{"id" "8455893107", "secret" "1a3236df06", "server" "8087",
"farm" 9, "title" "IMG_0137", "isprimary" "1"}
{"id" "8469482476", "secret" "4c1bf59214",
"server" "8235", "farm" 9, "title" "HippieBus", "isprimary" "0"}]
, "owner"
"93029076#N07", "id" "72157632724688181", "ownername" "clojureB5"}, "stat" "ok"}
What I want to do is loop through the two photos and build a new url with the id and farm value like http://www.flickr.com/farm/id
I know that I can get one value like this:
(-> (get-in (cheshire.core/parse-string (:body picList)) ["photoset" "photo"]) first (get "id"))
But I can I now loop through it?
You can simply use map.
(->> (get-in data ["photoset" "photo"])
(map #(str "http://www.flickr.com/" (get % "farm") "/" (get % "id"))))
It will yield the following list:
("http://www.flickr.com/9/8455893107" "http://www.flickr.com/9/8469482476")