diff --git a/app/static/css/style.css b/app/static/css/style.css index b72808f..de6ccad 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -301,9 +301,11 @@ box-sizing: border-box; .widget { resize: both; overflow: hidden; - width: 300px; - height: 300px; + width: 800px; + height: 200px; display: inline-block; + position: relative; + left: 400px; } @@ -348,4 +350,4 @@ box-sizing: border-box; } .gridbox:hover{ opacity: 0.5; -} \ No newline at end of file +} diff --git a/app/templates/show_stack_detail.html b/app/templates/show_stack_detail.html index af144cd..fda0469 100644 --- a/app/templates/show_stack_detail.html +++ b/app/templates/show_stack_detail.html @@ -9,7 +9,7 @@

Books in this stack: {% for book in stack.books %}

  • {{book.title}}
  • - +
    diff --git a/app/views_2.py b/app/views_2.py new file mode 100755 index 0000000..edf46ac --- /dev/null +++ b/app/views_2.py @@ -0,0 +1,539 @@ +""" +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 +from csv import DictWriter +import io + +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() + + # 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="librarian") + +@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): + filename = secure_filename(file.filename) + allbooks = db.session.query(Book).all() + id = book.id + new_filename = str(id) +"_"+ filename + fullpath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename) + name, file_extension = os.path.splitext(new_filename) + file.save(fullpath) + book.cover = get_cover(fullpath, name) + book.file = new_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): + filename = secure_filename(file.filename) + allbooks = db.session.query(Book).all() + id = len(allbooks)+1 + new_filename = str(id) +"_"+ filename + fullpath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename) + name, file_extension = os.path.splitext(new_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): +# import ipdb; ipdb.set_trace() +# book = Book.query.get(id) +# stack = Stack.query.get(id) + +# stack = db.session.query(Stack).join(stack.books).filter_by(stackid=stackid) +# stack.books.delete(book) +# db.session.commit() +# return render_template('show_book_by_id.html', stackid=stack.id, bookid=book.id) + +@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) + +## search + +@app.route('/books', methods= ['POST','GET']) +def show_books(): + books = db.session.query(Book).all() + search = SearchForm(request.form) + if request.method == 'POST': + return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) + + return render_template('show_books.html', books=books, form=search) + +@app.route('/search///', methods=['POST', 'GET']) +def search_results(searchtype, query): + search = SearchForm(request.form) + random_order=Book.query.order_by(func.random()).limit(10) + results=Book.query.filter(Book.title.contains(query)) + + if searchtype == 'Title': + results=Book.query.filter(Book.title.contains(query)) + + if searchtype == 'Category': + results=Book.query.filter(Book.category.contains(query)) + + if searchtype== 'Author': + results=db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query)) + + if searchtype== 'Stack': + results=db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query)) + + 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))) + + if results.count() == 0: + upload_form = UploadForm(title= query, author='') + return render_template('red_link.html', form=upload_form, title=query) + + if request.method == 'POST': + query = search.search.data + results = [] + return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) + + count = results.count() + whole = Book.query.count() + percentage = float(count / whole * 100) + 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('/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")