Skip to content

Commit 3c5cbc7

Browse files
committed
starting section 3
1 parent fea8a9d commit 3c5cbc7

File tree

3 files changed

+396
-0
lines changed

3 files changed

+396
-0
lines changed

WebApp2/main.go

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package main
2+
3+
import (
4+
"database/sql"
5+
"encoding/json"
6+
"encoding/xml"
7+
"io/ioutil"
8+
"log"
9+
"net/http"
10+
"net/url"
11+
"text/template"
12+
13+
_ "github.com/mattn/go-sqlite3"
14+
"github.com/urfave/negroni"
15+
)
16+
17+
var port = ":8080"
18+
19+
type Page struct {
20+
Name string
21+
DBStatus bool
22+
}
23+
24+
type SearchResult struct {
25+
Title string `xml:"title,attr"`
26+
Author string `xml:"author,attr"`
27+
Year string `xml:"hyr,attr"`
28+
ID string `xml:"owi,attr"`
29+
}
30+
31+
type ClassifySearchResponse struct {
32+
Results []SearchResult `xml:"works>work"`
33+
}
34+
35+
type ClassifyBookResponse struct {
36+
BookData struct {
37+
Title string `xml:"title,attr"`
38+
Author string `xml:"author,attr"`
39+
ID string `xml:"owi,attr"`
40+
} `xml:"work"`
41+
Classification struct {
42+
MostPopular string `xml:"sfa,attr"`
43+
} `xml:"recommandations>ddc>mostPopular"`
44+
}
45+
46+
var db *sql.DB
47+
48+
func verifyDBConnection(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
49+
err := db.Ping()
50+
if err != nil {
51+
log.Println("verifyDBConnection :: DB not connected")
52+
http.Error(w, err.Error(), http.StatusInternalServerError)
53+
} else {
54+
next(w, r)
55+
}
56+
57+
}
58+
59+
func main() {
60+
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Llongfile)
61+
62+
templates := template.Must(template.ParseFiles("templates/index.html"))
63+
64+
db, _ = sql.Open("sqlite3", "dev.db")
65+
66+
mux := http.NewServeMux()
67+
68+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
69+
err := templates.ExecuteTemplate(w, "index.html", nil)
70+
if err != nil {
71+
http.Error(w, err.Error(), http.StatusInternalServerError)
72+
return
73+
}
74+
})
75+
76+
mux.HandleFunc("/search", func(w http.ResponseWriter, r *http.Request) {
77+
qs := r.FormValue("queryString")
78+
log.Println("/search => qs = ", qs)
79+
results, err := search(qs)
80+
if err != nil {
81+
http.Error(w, err.Error(), http.StatusInternalServerError)
82+
return
83+
}
84+
encoder := json.NewEncoder(w)
85+
err = encoder.Encode(results)
86+
if err != nil {
87+
http.Error(w, err.Error(), http.StatusInternalServerError)
88+
return
89+
}
90+
})
91+
92+
mux.HandleFunc("/books/add", func(w http.ResponseWriter, r *http.Request) {
93+
qs := r.FormValue("id")
94+
log.Println("/book/add => qs = ", qs)
95+
96+
book, err := find(qs)
97+
if err != nil {
98+
log.Println("/books/add qs = ", qs, " error while finding ", " error = ", err.Error())
99+
http.Error(w, err.Error(), http.StatusInternalServerError)
100+
return
101+
}
102+
103+
if book.BookData.Title == "" {
104+
log.Println("/books/add qs = ", qs, " This book is not popular")
105+
http.Error(w, "This book is not popular", http.StatusNoContent)
106+
return
107+
}
108+
109+
_, err = db.Exec("insert into books (pk, title, author, id, classification) values (?, ?, ?, ?, ?)",
110+
nil, book.BookData.Title, book.BookData.Author, book.BookData.ID, book.Classification.MostPopular)
111+
if err != nil {
112+
log.Println("/books/add qs = ", qs, " error while inserting into DB error = ", err.Error())
113+
http.Error(w, err.Error(), http.StatusInternalServerError)
114+
return
115+
}
116+
117+
log.Println("/books/add qs = ", qs, " successfully inserted into db")
118+
119+
encoder := json.NewEncoder(w)
120+
err = encoder.Encode(book)
121+
if err != nil {
122+
http.Error(w, err.Error(), http.StatusInternalServerError)
123+
return
124+
}
125+
})
126+
127+
n := negroni.Classic()
128+
n.Use(negroni.HandlerFunc(verifyDBConnection))
129+
n.UseHandler(mux)
130+
n.Run(port)
131+
}
132+
133+
func search(query string) (results []SearchResult, err error) {
134+
var searchURL = "http://classify.oclc.org/classify2/Classify?&summary=true&title="
135+
var body []byte
136+
var csr ClassifySearchResponse
137+
138+
searchURL = searchURL + url.QueryEscape(query)
139+
140+
log.Println("func search ::url = ", searchURL)
141+
142+
body, err = classifyAPI(searchURL)
143+
if err != nil {
144+
log.Println("func search :: err while requesting ", "url = ", searchURL, " error = ", err.Error())
145+
return
146+
}
147+
148+
err = xml.Unmarshal(body, &csr)
149+
if err != nil {
150+
log.Println("func search :: err while Unmarshalling ", "url = ", searchURL, " error = ", err.Error())
151+
return
152+
}
153+
results = csr.Results
154+
155+
return
156+
}
157+
158+
func find(id string) (cbr ClassifyBookResponse, err error) {
159+
var searchURL = "http://classify.oclc.org/classify2/Classify?&summary=true&owi="
160+
var body []byte
161+
162+
searchURL = searchURL + url.QueryEscape(id)
163+
164+
log.Println("func find ::url = ", searchURL)
165+
166+
body, err = classifyAPI(searchURL)
167+
if err != nil {
168+
log.Println("func find :: err while requesting ", "url = ", searchURL, " error = ", err.Error())
169+
return
170+
}
171+
172+
//log.Println("func find ::url = ", searchURL, " obtained body body = ", string(body))
173+
174+
err = xml.Unmarshal(body, &cbr)
175+
if err != nil {
176+
log.Println("func find :: err while Unmarshalling ", "url = ", searchURL, " error = ", err.Error())
177+
return
178+
}
179+
180+
log.Println("func find ::url = ", searchURL, " successfully unmarshalled cbr = ", cbr)
181+
182+
return
183+
}
184+
185+
func classifyAPI(url string) (body []byte, err error) {
186+
var resp *http.Response
187+
188+
resp, err = http.Get(url)
189+
if err != nil {
190+
log.Println("func classifyAPI :: err while requesting ", "url = ", url, " error = ", err.Error())
191+
return
192+
}
193+
defer resp.Body.Close()
194+
195+
body, err = ioutil.ReadAll(resp.Body)
196+
if err != nil {
197+
log.Println("func classifyAPI :: err while parsing the body ", "url = ", url, " error = ", err.Error())
198+
return
199+
}
200+
201+
return
202+
}

WebApp2/templates/index.html

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<html>
2+
3+
<head>
4+
<title>
5+
Search Books
6+
</title>
7+
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
8+
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
9+
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
10+
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css" integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous">
11+
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
12+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
13+
</head>
14+
15+
<body onload="onLoadFunction()">
16+
<nav class="navbar navbar-dark bg-dark">
17+
<a class="navbar-brand" href="#">
18+
<span class="mr-sm-2">
19+
<i class="fas fa-book-reader"></i>
20+
</span>
21+
Search Books
22+
</a>
23+
<form id="search-form" class="form-inline" onsubmit="return false">
24+
<input class="form-control mr-sm-2" type="search" name="queryString" placeholder="Search" aria-label="Search">
25+
<button class="btn btn-outline-success" title="Search" type="submit" onclick="return submitSearch()">
26+
Search
27+
</button>
28+
</form>
29+
</nav>
30+
<ul class="list-group">
31+
<li class="list-group-item">
32+
<div class="container">
33+
<div class="modal fade" id="addBookResultModal" role="dialog">
34+
<div class="modal-dialog">
35+
<div class="modal-content">
36+
<div class="modal-header">
37+
<h4 class="modal-title" id="addBookResultModalHead"></h4>
38+
<button type="button" class="close" data-dismiss="modal">&times;</button>
39+
</div>
40+
<div class="modal-body">
41+
<a id="addBookResultModalBody"></a>
42+
</div>
43+
<div class="modal-footer">
44+
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
45+
</div>
46+
</div>
47+
</div>
48+
</div>
49+
</div>
50+
<ul class="list-group">
51+
<table class="table table-hover table-bordered table-dark">
52+
<thead id="table-head">
53+
<tr>
54+
<th scope="col">Title</th>
55+
<th scope="col">Author</th>
56+
<th scope="col">Year</th>
57+
<th scope="col">ID</th>
58+
<th scope="col">Action</th>
59+
</tr>
60+
</thead>
61+
<tbody id="search-results">
62+
63+
</tbody>
64+
</table>
65+
</ul>
66+
</li>
67+
</ul>
68+
<script type="text/javascript" src="js/helper.js"></script>
69+
<script type="text/javascript">
70+
function onLoadFunction() {
71+
$("#table-head").hide();
72+
}
73+
74+
function submitSearch() {
75+
$.ajax({
76+
url: "/search",
77+
method: "POST",
78+
data: $("#search-form").serialize(),
79+
success: function (rawData) {
80+
var searchResults = $("#search-results");
81+
var tableHeading = $("#table-head")
82+
searchResults.empty();
83+
84+
var parsed = JSON.parse(rawData);
85+
if (!parsed) return;
86+
87+
$("#table-head").show();
88+
89+
parsed.forEach(function (result) {
90+
var actionForm = `
91+
<form id="${'search-form-' + result.ID}" class="form-inline" onsubmit="return false">
92+
<input class="form-control mr-sm-2" type="text" name="id" value="${result.ID}" hidden>
93+
<button class="btn btn-outline-success" title="Add this book" type="submit" onclick="return addBook(${result.ID})">
94+
<i class="fas fa-plus-square"></i>
95+
</button>
96+
</form>
97+
`
98+
var row = $("<tr><td>" + result.Title + "</td><td>" + result.Author + "</td><td>" + result.Year + "</td><td>" + result.ID + "</td><td>" + actionForm + "</td></tr>");
99+
searchResults.append(row);
100+
});
101+
}
102+
});
103+
return false;
104+
}
105+
106+
function addBook(id) {
107+
$.ajax({
108+
url: "/books/add",
109+
method: "POST",
110+
data: $("#search-form-" + id).serialize(),
111+
success: function (rawData) {
112+
if(rawData){
113+
var parsed = JSON.parse(rawData);
114+
if (parsed && parsed.BookData && parsed.BookData.Title) {
115+
$('#addBookResultModalHead').html(`Success <i class="far fa-smile"></i>`);
116+
$('#addBookResultModalBody').text(`${parsed.BookData.Title} has been added to database`);
117+
$('#addBookResultModal').modal('show');
118+
return;
119+
}
120+
}
121+
$('#addBookResultModalHead').html(`Sorry <i class="far fa-sad-tear"></i>`);
122+
$('#addBookResultModalBody').text(`Failed to add the book you selected. It is not popular. Please try again...`);
123+
$('#addBookResultModal').modal('show');
124+
}
125+
});
126+
return false;
127+
}
128+
</script>
129+
</body>
130+
131+
</html>

0 commit comments

Comments
 (0)