""" Flask Documentation: http://flask.pocoo.org/docs/ Jinja2 Documentation: http://jinja.pocoo.org/2/documentation/ Werkzeug Documentation: http://werkzeug.pocoo.org/documentation/ This file creates your application. """ from app import app, db, socketio, DOMAIN from flask import Flask, Response, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort import json from sqlalchemy.sql.expression import func, select from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema from app.cover import get_cover from os import environ from flask_socketio import SocketIO, emit import datetime import time import autocomplete import sys import os from werkzeug.utils import secure_filename # import sqlite3 ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'epub', 'chm', 'mobi']) author_schema = AuthorSchema() authors_schema = AuthorSchema(many=True) book_schema = BookSchema() books_schema = BookSchema(many=True) stack_schema = StackSchema() stacks_schema = StackSchema(many=True) chat_schema = ChatSchema() chats_schema = ChatSchema(many=True) def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS ### # Routing for your application. ### @app.route('/', methods= ['POST','GET']) def home(): chat_form = ChatForm() chat_messages = db.session.query(Chat).all() username = 'librarian' # if request.method == 'POST': # if chat_form.validate_on_submit(): # message = chat_form.message.data # msg = Chat(message) # db.session.add(msg) # db.session.commit() return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username=username) @app.route('/hello/') def hello(name): return "Hello " + name @app.route('/about/') def about(): """Render the website's about page.""" return render_template('about.html', name="Mary Jane") @app.route('/uploads/') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) @app.route('/uploads/cover/') def uploaded_file_cover(filename): return send_from_directory(app.config['UPLOAD_FOLDER_COVER'], filename) @app.route('/updates', methods=['POST', 'GET']) def get_updates(): userin = UserIns.query.filter_by(title="lastViewed").first() allbooks = db.session.query(Book).all() id = len(allbooks) latest_upload = allbooks[-1] return "This is the XPPL ~ Library XPUB ~ Updates / / / / / / / Last viewed: " + userin.info + " / / / / / / / " + str(len(allbooks)) + " Books online "+ " / / / / / / / " + "Latest upload: " + latest_upload.title @app.route('/scape', methods=['POST', 'GET']) def scape(): if request.method == 'POST': data = request.form book = Book.query.get(data['id']) print(book.scapeX) book.scapeX = data['x'] book.scapeY = data['y'] db.session.commit() books = db.session.query(Book).all() # or you could have used User.query.all() return render_template('scape.html', books=books) @app.route('/books_grid') def show_books_grid(): books = db.session.query(Book).all() # or you could have used User.query.all() return render_template('show_books_grid.html', books=books) @app.route('/books/') def show_book_by_id(id): book = Book.query.get(id) userin = UserIns.query.filter_by(title="lastViewed").first() if userin != None: userin.info = book.title db.session.commit() else: user_info = UserIns("lastViewed", book.title) db.session.add(user_info) db.session.commit() if not book: return render_template('red_link.html', id=id) else: return render_template('show_book_detail.html', book=book) @app.route('/books//delete', methods=['POST', 'GET']) def remove_book_by_id(id): book_to_edit = Book.query.filter_by(id=id).first() title = book_to_edit.title Book.query.filter_by(id=id).delete() #author_table = Author.query.filter_by(book_id=book_to_edit.id).delete() db.session.commit() flash("%s deleted from library" % (title)) return redirect(url_for('show_books')) @app.route('/books//edit', methods=['POST', 'GET']) def edit_book_by_id(id): book_to_edit = Book.query.filter_by(id=id).first() user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published) if request.method == 'POST': if user_form.validate_on_submit(): # on submit, check fields title = user_form.title.data input_authors = user_form.author.data category = user_form.category.data year_published = user_form.year_published.data if year_published=="": year_published = None book = Book.query.filter_by(id=id).first() book.title = title book.category = category book.year_published = year_published #authors update book.authors.clear() for i, author in enumerate(input_authors): author_name = author.get("author_name") if author_name: a = db.session.query(Author).filter_by(author_name=author_name).first() if a == None: a = Author(author_name=author_name) db.session.add(a) book.authors.append(a) # editing / uploading new file if user_form.file.data: file = request.files['file'] if file.filename == '': flash('No selected file') return redirect(request.url) if file and allowed_file(file.filename): allbooks = db.session.query(Book).all() id = book.id filename = str(id) + "_" + secure_filename(file.filename) fullpath = os.path.join(app.config['UPLOAD_FOLDER'], filename) name, file_extension = os.path.splitext(filename) file.save(fullpath) book.cover = get_cover(fullpath, name) book.file = filename else: flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) db.session.commit() flash("%s updated" % (title)) return redirect(url_for('show_book_by_id', id=id)) return render_template('edit_book_detail.html', book=book_to_edit, form=user_form) @app.route('/add-book', methods=['POST', 'GET']) def add_book(): upload_form = UploadForm() if request.method == 'POST': if upload_form.validate_on_submit(): #get data from form title = upload_form.title.data authors = upload_form.author.data category = upload_form.category.data year_published = upload_form.year_published.data if year_published=="": year_published = None #if upload with file if upload_form.upload.data: # check if the post request has the file part if 'file' not in request.files: flash('No file part') return redirect(request.url) file = request.files['file'] # if user does not select file, browser also # submit a empty part without filename if file.filename == '': flash('No selected file') return redirect(request.url) if file and allowed_file(file.filename): allbooks = db.session.query(Book).all() id = len(allbooks)+1 filename = str(id) + "_" + secure_filename(file.filename) fullpath = os.path.join(app.config['UPLOAD_FOLDER'], filename) name, file_extension = os.path.splitext(filename) file.save(fullpath) cover = get_cover(fullpath, name) else: flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) #if upload without file -> wishform, with potential PDF if upload_form.wish.data: #TO DO: make pdf generator #file = open('app/uploads/potential.pdf') #filename = 'potential.pdf' #file_extension = '.pdf' filename = '' file_extension = '' cover = '' book = Book(title, filename, cover, file_extension, category,year_published) db.session.add(book) for author in authors: author_name = author.get("author_name") if author_name: a = db.session.query(Author).filter_by(author_name=author_name).first() if a == None: a = Author(author_name=author_name) db.session.add(a) book.authors.append(a) db.session.commit() flash("%s added to the library" % (title)) return redirect(url_for('show_books')) flash_errors(upload_form) return render_template('add_book.html', form=upload_form) # Flash errors from the form if validation fails def flash_errors(form): for field, errors in form.errors.items(): for error in errors: flash(u"Error in the %s field - %s" % ( getattr(form, field).label.text, error )) #Authors @app.route('/authors/') def show_author_by_id(id): author = Author.query.get(id) if not author: abort (404) else: return render_template('show_author_detail.html', author=author) @app.route('/authors//edit', methods=['POST', 'GET']) def edit_author_by_id(id): return "Ask the programmer." ##stacks @app.route('/stacks') def show_stacks(): stacks = db.session.query(Stack).all() return render_template('show_stacks.html', stacks=stacks) @app.route('/stacks/add_stack', methods=['POST', 'GET']) def add_stack(): form = StackForm() stacks = db.session.query(Stack).all() if form.validate_on_submit(): stack_name = form.stack_name.data stack_description = form.stack_description.data stack = Stack(stack_name, stack_description) if form.stack_name.data: stack = Stack(stack_name, stack_description) db.session.add(stack) stacks = db.session.query(Stack).all() return redirect(url_for('show_stacks')) flash("%s stack created" % (stack_name)) return render_template('add_stack.html', stacks=stacks, form=form) @app.route('/stacks/tab/', methods=['POST', 'GET']) def show_stack_in_tab(id): return show_stack_by_id(id, is_tab=True) @app.route('/stacks/', methods=['POST', 'GET']) def show_stack_by_id(id, is_tab=False): stack = Stack.query.get(id) if not stack: abort (404) else: if is_tab == False: return render_template('show_stack_detail.html', stack=stack) else: return render_template('show_stack_detail_tab.html', stack=stack) @app.route('/stacks//delete', methods=['POST', 'GET']) def remove_stack_by_id(id): Stack.query.filter_by(id=id).delete() db.session.commit() return redirect(url_for('show_stacks')) @app.route('/stacks//edit', methods=['POST', 'GET']) def edit_stack_by_id(id): stack = Stack.query.filter_by(id=id).first() form = EditStackForm(edit_stack_name = stack.stack_name, edit_stack_description = stack.stack_description) if request.method == 'POST': if form.validate_on_submit(): stack_name = form.edit_stack_name.data stack_description = form.edit_stack_description.data stack.stack_name = stack_name stack.stack_description = stack_description db.session.commit() return redirect(url_for('show_stack_by_id', id=id)) return render_template('edit_stack_detail.html', stack=stack, form=form) @app.route('/stacks//remove/', methods=['POST', 'GET']) def remove_from_stack(bookid, stackid): book = Book.query.get(bookid) stack = Stack.query.get(stackid) if book not in stack.books: return render_template('show_book_detail.html', book=book) stack.books.remove(book) db.session.commit() return render_template('show_book_detail.html', book=book) ## search view = ['1'] @app.route('/books', methods= ['POST','GET']) def show_books(): books = db.session.query(Book).order_by(Book.title) search = SearchForm(request.form) view.append('1') viewby = '1' if search.grid.data: viewby = '2' view.append('2') return render_template ('show_books_grid.html', books=books, form=search) if search.listview.data: viewby = '1' view.append('1') return render_template ('show_books.html', books=books, form=search) if request.method == 'POST': newmsg = 'searched for: ' + search.search.data # message = search.search.data # newmessage = Chat(message) # db.session.add(newmessage) # db.session.commit() # Send search to socket chat socketio.emit('channel-' + str(1), { 'username': 'Search form', 'text': search.search.data, 'time': str(datetime.datetime.utcnow().strftime("%d.%m.%Y %H:%M"))}, broadcast=True) # Save message my_new_chat = Chat(message=newmsg) db.session.add(my_new_chat) try: db.session.commit() except: db.session.rollback() return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) return render_template('show_books.html', books=books, form=search) @app.route('/search///', methods=['POST', 'GET']) def search_results(searchtype, query, viewby): search = SearchForm(request.form, search=query) random_order=Book.query.order_by(func.random()).limit(14) results=Book.query.filter(Book.title.contains(query)).order_by(Book.title) viewby = view[-1] if searchtype == 'Title': results=Book.query.filter(Book.title.contains(query)).order_by(Book.title) if searchtype == 'Category': results=Book.query.filter(Book.category.contains(query)).order_by(Book.title) if searchtype== 'Author': results=db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query)).order_by(Book.title) if searchtype== 'Stack': results=db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query)).order_by(Book.title) if searchtype== 'All': # results=Book.query.whoosh_search(query) results=Book.query.filter(Book.title.contains(query)) results=results.union(Book.query.filter(Book.category.contains(query))) results=results.union(db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query))) results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query))).order_by(Book.title) if results.count() == 0: upload_form = UploadForm(title= query, author='') return render_template('red_link.html', form=upload_form, title=query) count = results.count() whole = Book.query.count() percentage = float(count / whole * 100) if search.listview.data: view.append('1') return render_template('results.html', books=results, form=search, query=query, books_all=random_order, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) if search.grid.data: view.append('2') return render_template('results_grid.html', books=results, form=search, query=query, books_all=random_order, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) if request.method == 'POST': newmsg = 'searched for: ' + search.search.data socketio.emit('channel-' + str(1), { 'username': 'Search form', 'text': search.search.data, 'time': str(datetime.datetime.utcnow().strftime("%d.%m.%Y %H:%M"))}, broadcast=True) # Save message my_new_chat = Chat(message=newmsg) db.session.add(my_new_chat) try: db.session.commit() except: db.session.rollback() query = search.search.data results = [] if viewby == '1': print (view[-1]) return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) else: return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) if viewby == '2': return render_template('results_grid.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) else: return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) # ## Search - autocomplete # autocomplete_suggestions = [] # @app.route('/autocomplete_suggestions', methods=['GET', 'POST']) # def test1(): # if request.method == 'POST': # autocomplete.load() # query = request.form['search'] # query_tokenized = query.lower().split() # print(query_tokenized) # word_1 = query_tokenized[-2] # word_2 = query_tokenized[-1] # #print(word_1) # autocomplete_output = autocomplete.predict(word_1 , word_2) # autocomplete_suggestions.clear() # for suggestion, score in autocomplete_output: # autocomplete_suggestions.append(suggestion) # print(autocomplete_suggestions) # return Response(json.dumps(autocomplete_suggestions), mimetype='application/json') @app.route('/add_to_stack/', methods=['GET', 'POST']) def add_to_stack(id): stacks = db.session.query(Stack).all() add_form = AddtoStackForm(request.form) add_form.select_stack.choices = [(stack.id, stack.stack_name) for stack in stacks] if request.method == 'GET': book = Book.query.get(id) return render_template('add_to_stacks.html', id=id, stacks=stacks, book=book, add_form=add_form) else: stack = Stack.query.get(int(add_form.select_stack.data)) book = Book.query.get(id) stack.books.append(book) db.session.commit() return render_template('show_stack_detail.html', stack=stack) from csv import DictWriter import io @app.route('/export/csv', methods=['GET']) def export_csv (): output = io.StringIO() fieldnames = ['title', 'authors'] csv = DictWriter(output,fieldnames) csv.writeheader() # for i in range(10): # csv.writerow({'ID': i, 'fruit': "Tomato"}) for book in Book.query.order_by("title"): authors = ", ".join([x.author_name for x in book.authors]) csv.writerow({"title": book.title, "authors": authors}) resp = Response(output.getvalue(), mimetype="text/plain") # resp.headers["Content-Disposition"] = "attachment;filename=export.csv" return resp ### # The API ### @app.route('/api/books', methods=['GET']) def get_books(): books = Book.query.all() data = books_schema.dump(books) #print(errors) return jsonify({'books': data}) @app.route('/api/books/', methods=['GET']) def get_book_by_id(id): book = Book.query.get(id) data = book_schema.dump(book) if not data: return jsonify({"message": "Book could not be found."}), 400 else: return jsonify({'book': data }) @app.route('/api/chats', methods=['GET']) def get_chat(): chats = Chat.query.all() data = chats_schema.dump(chats) #print(errors) return jsonify({'chat': data}) ### # The functions below should be applicable to all Flask apps. ### @app.route('/.txt') def send_text_file(file_name): """Send your static text file.""" file_dot_text = file_name + '.txt' return app.send_static_file(file_dot_text) @app.after_request def add_header(response): """ Add headers to both force latest IE rendering engine or Chrome Frame, and also to cache the rendered page for 10 minutes. """ response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1' response.headers['Cache-Control'] = 'public, max-age=600' return response @app.errorhandler(404) def page_not_found(error): """Custom 404 page.""" return render_template('404.html'), 404 @socketio.on('new_message') def new_message(message): # Send message to alls users print("new message") emit('channel-' + str(message['channel']), { 'username': message['username'], 'text': message['text'], 'time': str(datetime.datetime.utcnow().strftime("%d.%m.%Y %H:%M")) }, broadcast=True ) # Save message my_new_chat = Chat( message=message['text'] ) db.session.add(my_new_chat) try: db.session.commit() except: db.session.rollback() if __name__ == '__main__': socketio.run(app) #app.run(debug=True,host="0.0.0.0",port="8080")