+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..9037b5b
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..14e5437
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index a6c95a5..318b24d 100644
--- a/README.md
+++ b/README.md
@@ -21,13 +21,3 @@ The installation instructions above will allow you to run this app. I've include
if __name__ == '__main__':
app.run(debug=True)
```
-This will allow you to see errors in the browser if the app encounters one.
-
-## Next Steps
-### pagination
-I plan to add pagination -- currently, each request to the google books api only returns 10 results, and can only return up to 40 results. however, searches can have thousands of results. I plan to add forward and backwards buttons that allow new requests to be sent for the same query, that way the user can navigate through the entire list of results.
-### Design
-I'd also like to make the app more visually appealing. It's currently very barebones on html and css.
-
-### Code Refactor
-the BookSearch class is currently doing a lot. It would be better to have an APIQuery class, can be extended by separate classes for each api the app uses currently (google books and goodreads). This makes the code a little more future-proof.
diff --git a/app.py b/app.py
index 1b31eff..3db1a8e 100644
--- a/app.py
+++ b/app.py
@@ -5,16 +5,17 @@
import sys
app = Flask(__name__)
-app.secret_key = 'QVwI5uhPuU'
-
+# app.secret_key = 'QVwI5uhPuU'
+app.secret_key = 'AIzaSyBKFOVhps_PAJaA5mq9n440F_ILdj8BCMM'
app.logger.addHandler(logging.StreamHandler(sys.stdout))
app.logger.setLevel(logging.ERROR)
-@app.route('/', methods=["GET", "POST"])
+
+@app.route("/", methods=["GET", "POST"])
def home():
form = SearchForm()
results = []
- if request.method == 'POST': #if submitting the form
+ if request.method == 'POST':
if form.validate() == False:
return render_template("index.html", form=form)
else:
@@ -24,8 +25,9 @@ def home():
results = book_search.get_search_results()
return render_template("index.html", form=form, results=results)
- elif request.method == 'GET': #if returning results
+ elif request.method == 'GET':
return render_template("index.html", form=form, results=results)
+
if __name__ == '__main__':
app.run()
diff --git a/booksearch.py b/booksearch.py
index 0496def..7df83f7 100644
--- a/booksearch.py
+++ b/booksearch.py
@@ -1,25 +1,29 @@
import requests
+import dateutil.parser as dparser
class BookSearch:
books_api = 'https://www.googleapis.com/books/v1/volumes'
- parameters = { 'q' : '',
- 'fields' : 'kind,totalItems,items(kind,volumeInfo(title,subtitle,authors,publisher,industryIdentifiers,imageLinks/thumbnail))'
- }
- search = '' #user's search query, populated in __init__
- results = '' #response from google books, populated by parse_results()
+ parameters = dict(q='')
+ startIndex = 0
+ search = '' # user's search query, populated in __init__
+ results = '' # response from google books, populated by parse_results()
+ responseTime = 0
+ maxDate = None
+ minDate = None
+ mostProlific = ''
def __init__(self, search=''):
self.search = search
-
-
-
def make_a_search(self):
self.construct_request()
self.send_request()
+ # self.send_all_requests()
self.parse_results()
+ if self.results['items']:
+ self.analyze_results()
# adds user's search phrase to parameters
def construct_request(self):
@@ -27,54 +31,106 @@ def construct_request(self):
def send_request(self):
self.search = requests.get(self.books_api, params=self.parameters)
+ self.responseTime = self.search.elapsed.total_seconds()
- #store the results in a python dictionary
+ def send_all_requests(self):
+ self.parameters['startIndex'] = 0
+ self.parameters['maxResults'] = 40
+ self.search = requests.get(self.books_api, params=self.parameters)
+ if 'totalItems' in self.search.json() and self.search.json()['totalItems'] > 0:
+ self.results = self.search.json()
+ num_results = self.results['totalItems']
+ while self.parameters['startIndex'] < num_results:
+ self.parameters['startIndex'] += 40
+ next_results = requests.get(self.books_api, params=self.parameters).json()
+ if 'items' in next_results:
+ for item in next_results['items']:
+ self.results['items'].append(item)
+
+ # store the results in a python dictionary
def parse_results(self):
self.results = self.search.json()
-
-
-
def get_search_results(self):
- search_results = []
-
if self.results['totalItems'] == 0:
return 'no results'
+
+ search_results = {
+ 'totalResults': self.results['totalItems'],
+ 'maxDate': self.maxDate,
+ 'minDate': self.minDate,
+ 'mostProlific': self.mostProlific,
+ 'responseTime': self.responseTime,
+ 'items': []
+ }
+
num_results = len(self.results['items'])
- for result in range(num_results):
+ # grab results by index from each of the resulting lists
+ # send these back to the app for rendering front end
+ for result in range(num_results):
formatted_result = {
'title': self.get_result_title(result),
+ 'description': self.get_result_description(result),
'authors': self.get_result_authors(result),
- 'publisher': self.get_result_publisher(result),
'thumbnail': self.get_thumbnail_url(result),
- 'goodreads': self.make_goodreads_url(result)
}
- search_results.append(formatted_result)
-
+ search_results['items'].append(formatted_result)
return search_results
+ # do a bit of processing on each json object
+ def analyze_results(self):
+ authors = {}
+ dates = []
+ for result in self.results['items']:
+ if 'authors' in result['volumeInfo']:
+ for author in result['volumeInfo']['authors']:
+ if author in authors:
+ authors[author] += 1
+ else:
+ authors[author] = 1
+ if 'publishedDate' in result['volumeInfo']:
+ dates.append(dparser.parse(result['volumeInfo']['publishedDate']))
+
+ # sort dates to get the earliest and latest
+ dates = sorted(dates)
+ self.maxDate = dates[-1]
+ self.minDate = dates[0]
+
+ # having counted our authors in this traunch, sort and grab the highest count
+ if authors:
+ sorted_authors = sorted(authors, key=authors.get, reverse=True)
+ # self.mostProlific = sorted(authors, key=authors.get, reverse=True)[0]
+ plurality = " work." if authors.get(sorted_authors[0]) is 1 else " works."
+ self.mostProlific = sorted_authors[0] + " who has contributed to " + str(authors.get(sorted_authors[0])) + plurality
+
+ # collate title and subtitles per volume
def get_result_title(self, result):
title = self.results['items'][result]['volumeInfo']['title']
-
if 'subtitle' in self.results['items'][result]['volumeInfo']:
title += ': ' + self.results['items'][result]['volumeInfo']['subtitle']
+ return title
- return 'title: ' + title
+ # check for and return a description if present, message otherwise
+ def get_result_description(self, result):
+ desc = 'No description provided.'
+ if 'description' in self.results['items'][result]['volumeInfo']:
+ desc = self.results['items'][result]['volumeInfo']['description']
+ return desc
+ # join all authors with a comma per volume
def get_result_authors(self, result):
- authors = 'unkown'
+ authors = 'unknown'
if 'authors' in self.results['items'][result]['volumeInfo']:
authors = ', '.join(self.results['items'][result]['volumeInfo']['authors'])
- return 'authors: ' + authors
+ return authors
+ # null check publisher, return if present
def get_result_publisher(self, result):
publisher = 'unknown'
-
if 'publisher' in self.results['items'][result]['volumeInfo']:
publisher = self.results['items'][result]['volumeInfo']['publisher']
-
- return 'publisher: ' + publisher
+ return publisher
def get_thumbnail_url(self, result):
thumbnail = ''
@@ -82,6 +138,7 @@ def get_thumbnail_url(self, result):
thumbnail = self.results['items'][result]['volumeInfo']['imageLinks']['thumbnail']
return thumbnail
+ # unclear if relevant, consider removal
def make_goodreads_url(self, result):
goodreads = 'https://www.goodreads.com/book/show/'
id = str(self.get_goodreads_id(result))
diff --git a/booksearch.pyc b/booksearch.pyc
new file mode 100644
index 0000000..8741db6
Binary files /dev/null and b/booksearch.pyc differ
diff --git a/form.py b/form.py
index eefdd72..21d9ce9 100644
--- a/form.py
+++ b/form.py
@@ -6,3 +6,9 @@
class SearchForm(Form):
search_query = StringField('Search', validators=[DataRequired("Enter a search phrase."), Length(max=68)])
submit = SubmitField('search')
+
+
+class PaginateForm(Form):
+ search_query = StringField('Search', validators=[DataRequired("Enter a search phrase."), Length(max=68)])
+ submit = SubmitField('nextResults')
+
diff --git a/form.pyc b/form.pyc
new file mode 100644
index 0000000..c9f66d9
Binary files /dev/null and b/form.pyc differ
diff --git a/requirements.txt b/requirements.txt
index e6267ac..111694f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,3 +12,4 @@ requests==2.21.0
urllib3==1.24.1
Werkzeug==0.15.3
WTForms==2.2.1
+python-dateutil==2.8.1
diff --git a/templates/index.html b/templates/index.html
index a23e640..843640b 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -4,21 +4,17 @@