Varia library working group XPPL. https://gitea.xpub.nl/XPUB/XPPL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

655 lines
23 KiB

7 years ago
"""
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.
"""
7 years ago
from app import app, db, socketio, DOMAIN
from flask import Flask, Response, session, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort
7 years ago
import json
7 years ago
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, Instance
from app.cover import get_cover
from app.extractText import extract_text
7 years ago
from os import environ
from flask_socketio import SocketIO, emit
import datetime
import time
from csv import DictWriter, DictReader
import io
from sqlalchemy.inspection import inspect
import autocomplete
from autocomplete import models
import sys
7 years ago
import os
from werkzeug.utils import secure_filename
7 years ago
# import sqlite3
7 years ago
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'epub', 'chm', 'mobi'])
7 years ago
7 years ago
author_schema = AuthorSchema()
authors_schema = AuthorSchema(many=True)
7 years ago
book_schema = BookSchema()
books_schema = BookSchema(many=True)
7 years ago
stack_schema = StackSchema()
stacks_schema = StackSchema(many=True)
7 years ago
chat_schema = ChatSchema()
chats_schema = ChatSchema(many=True)
7 years ago
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
###
# Routing for your application.
###
7 years ago
@app.route('/', methods= ['POST','GET'])
7 years ago
def home():
7 years ago
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()
#client = request.remote_addr
server = request.host
if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
client =request.environ['REMOTE_ADDR']
else:
client = request.environ['HTTP_X_FORWARDED_FOR']
7 years ago
return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username="librarian", client=client, server=server)
7 years ago
@app.route('/hello/<name>')
def hello(name):
return "Hello " + name
7 years ago
@app.route('/about/')
def about():
"""Render the website's about page."""
return render_template('about.html', name="Mary Jane")
@app.route('/uploads/<filename>')
def uploaded_file(filename):
book = Book.query.filter_by(file=filename).first()
i = Instance(request.host, "download")
existing_ip = db.session.query(Instance).filter_by(ip=request.host).first()
if existing_ip:
i.name = existing_ip.name
book.instances.append(i)
db.session.commit()
7 years ago
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
@app.route('/uploads/cover/<filename>')
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()
7 years ago
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
7 years ago
@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()
all_instances = db.session.query(Instance).all()
instances = []
for instance in all_instances:
exists = False
for existing_inst in instances:
if existing_inst.name == instance.name:
exists = True
break
else:
exists = False
if not exists:
instances.append(instance)
return render_template('scape.html', books=books, instances=instances)
7 years ago
@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)
7 years ago
@app.route('/books/<int:id>')
def show_book_by_id(id):
book = Book.query.get(id)
all_instances = db.session.query(Instance).all()
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()
7 years ago
if not book:
return render_template('red_link.html', id=id)
7 years ago
else:
return render_template('show_book_detail.html', book=book, all_instances=all_instances)
7 years ago
@app.route('/books/<int:id>/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()
7 years ago
#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/<int:id>/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():
# check if the post request has the file part
title = user_form.title.data # You could also have used request.form['name']
7 years ago
input_authors = user_form.author.data # You could also have used request.form['email']
7 years ago
category = user_form.category.data
year_published = user_form.year_published.data
if year_published=="":
year_published = None
# save user to database
#book = Book(title, author, filename, cover, file_extension)
book = Book.query.filter_by(id=id).first()
book.title = title
7 years ago
book.category = category
book.year_published = year_published
7 years ago
#authors update
7 years ago
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)
i = Instance(request.host, "edit")
existing_ip = db.session.query(Instance).filter_by(ip=request.host).first()
if existing_ip:
i.name = existing_ip.name
book.instances.append(i)
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)
7 years ago
@app.route('/add-book', methods=['POST', 'GET'])
def add_book():
7 years ago
upload_form = UploadForm()
7 years ago
7 years ago
if request.method == 'POST':
7 years ago
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
7 years ago
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)
7 years ago
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)
7 years ago
file.save(fullpath)
try:
cover = get_cover(fullpath, name)
except:
cover = ''
extract_text(fullpath, name)
7 years ago
else:
flash('allowed file formats: %s' % ALLOWED_EXTENSIONS)
#if upload without file -> wishform, with potential PDF
7 years ago
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)
i = Instance(request.host, "add")
existing_ip = db.session.query(Instance).filter_by(ip=request.host).first()
if existing_ip:
i.name = existing_ip.name
book.instances.append(i)
db.session.commit()
7 years ago
flash("%s added to the library" % (title))
return redirect(url_for('show_books'))
7 years ago
flash_errors(upload_form)
return render_template('add_book.html', form=upload_form)
7 years ago
# 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
))
7 years ago
#Authors
7 years ago
@app.route('/authors/<int:id>')
def show_author_by_id(id):
author = Author.query.get(id)
if not author:
abort (404)
7 years ago
else:
return render_template('show_author_detail.html', author=author)
7 years ago
@app.route('/authors/<int:id>/edit', methods=['POST', 'GET'])
def edit_author_by_id(id):
7 years ago
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)
7 years ago
@app.route('/stacks/add_stack', methods=['POST', 'GET'])
def add_stack():
form = StackForm()
stacks = db.session.query(Stack).all()
if form.validate_on_submit():
7 years ago
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'))
7 years ago
flash("%s stack created" % (stack_name))
return render_template('add_stack.html', stacks=stacks, form=form)
@app.route('/stacks/tab/<int:id>', methods=['POST', 'GET'])
def show_stack_in_tab(id):
return show_stack_by_id(id, is_tab=True)
7 years ago
@app.route('/stacks/<int:id>', 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/<int:id>/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/<int:id>/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('/instances', methods=['POST', 'GET'])
def show_instances():
all_instances = db.session.query(Instance).all()
instances = []
for instance in all_instances:
exists = False
for existing_inst in instances:
if existing_inst.name == instance.name:
exists = True
break
else:
exists = False
if not exists:
instances.append(instance)
if request.method == 'POST':
for item in request.form.items():
for i, itm in enumerate(item):
if i == 0:
oldname = itm
if i == 1:
name = itm
all_instances = db.session.query(Instance).filter_by(name=oldname).all()
for instance in all_instances:
instance.name = name
print(oldname)
print(name)
db.session.commit()
return render_template('show_instances.html', instances=instances)
7 years ago
## search
7 years ago
@app.route('/books', methods= ['POST','GET'])
def show_books():
#autocomplete.load() #Train markov model once, for autocomplete in search
books = db.session.query(Book).all()
search = SearchForm(request.form)
7 years ago
if request.method == 'POST':
return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data)))
7 years ago
return render_template('show_books.html', books=books, form=search)
7 years ago
@app.route('/search/<searchtype>/<query>/', 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)).all()
if searchtype == 'Title':
results=Book.query.filter(Book.title.contains(query)).all()
if searchtype == 'Category':
results=Book.query.filter(Book.category.contains(query)).all()
if searchtype== 'All':
results=Book.query.whoosh_search(query).all()
if not results:
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)))
return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query)
## Search - autocomplete
autocomplete_suggestions = []
autocomplete.load() #Train markov model once, for autocomplete in search
@app.route('/autocomplete_suggestions', methods=['GET', 'POST'])
def autocomplete_search():
if request.method == 'GET':
#query = request.form['search']
query = request.args.get('q')
query_tokenized = query.lower().split()
#print(query_tokenized)
word_1 = query_tokenized[-2]
word_2 = query_tokenized[-1]
#print(word_1)
#print(word_2)
autocomplete_output = autocomplete.predict(word_1 , word_2)
autocomplete_suggestions.clear()
for suggestion, score in autocomplete_output:
autocomplete_suggestions.append(suggestion)
return Response(json.dumps(autocomplete_suggestions), mimetype='application/json')
## STACKS!
7 years ago
@app.route('/add_to_stack/<int:id>', methods=['GET', 'POST'])
def add_to_stack(id):
stacks = db.session.query(Stack).all()
7 years ago
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)
7 years ago
@app.route('/export/csv', methods=['GET'])
def export_csv():
output = io.StringIO()
#fieldnames = ['title', 'authors', 'file', 'fileformat', 'category', 'year_published', 'description' ]
fields = Book.__mapper__.columns
fieldnames = []
for columns in fields:
fieldnames.append(columns.name)
i = inspect(Book)
referred_classes = [r.mapper.class_ for r in i.relationships]
referred_classes_tablenames = [r.mapper.class_.__tablename__ for r in i.relationships]
print(fieldnames+referred_classes_tablenames)
csv = DictWriter(output,fieldnames+referred_classes_tablenames)
csv.writeheader()
for book in Book.query.order_by("title"):
row = {}
for col in fieldnames:
print(getattr(book, col))
row[col] = getattr(book, col)
for col in referred_classes:
subattr = []
for subcol in getattr(book, col.__tablename__):
for metacol in subcol.__mapper__.columns:
query = metacol.name
if query != "id":
this = getattr(subcol, query)
subattr.append(this)
row[col.__tablename__] = " | ".join(subattr)
csv.writerow(row)
#print(row)
resp = Response(output.getvalue(), mimetype="text/csv")
resp.headers["Content-Disposition"] = "attachment;filename=export.csv"
return resp
import codecs
@app.route('/import/csv', methods= ['POST','GET'])
def import_csv():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
else:
file = request.files['file']
for row in DictReader(codecs.iterdecode(file, 'utf-8')):
numberadded = 0;
book = Book.query.filter_by(title=row['title']).first()
if book:
print("allreadyexists")
else:
cover = ''
if row['file']:
fullpath = os.path.join(app.config['UPLOAD_FOLDER'], row['file'])
name, file_extension = os.path.splitext(row['file'])
print ('get_cover', fullpath, name)
cover = get_cover(fullpath, name)
if row['year_published']:
year_published = int(row['year_published'])
else:
year_published = None;
book = Book(row['title'], row['file'], cover, row['fileformat'], row['category'],year_published)
book.scapeX = float(row['scapeX'])
book.scapeY = float(row['scapeY'])
db.session.add(book)
numberadded = numberadded+1
authors = row['authors'].split('|')
authors = [x.strip() for x in authors]
for author in authors:
if author:
a = db.session.query(Author).filter_by(author_name=author).first()
if a == None:
a = Author(author_name=author)
db.session.add(a)
book.authors.append(a)
db.session.commit()
return render_template('import_csv.html', numberadded=numberadded)
@app.route('/empty_catalogue', methods= ['POST','GET'])
def empty_catalogue():
meta = db.metadata
for table in reversed(meta.sorted_tables):
if str(table) == "books" or str(table) == "authors" or str(table) == "books_authors":
print('Clear table %s' % table)
db.session.execute(table.delete())
db.create_all()
db.session.commit()
return "ALL CLEARED"
7 years ago
###
# The API
###
@app.route('/api/books', methods=['GET'])
def get_books():
books = Book.query.all()
7 years ago
data = books_schema.dump(books)
#print(errors)
7 years ago
return jsonify({'books': data})
@app.route('/api/books/<int:id>', methods=['GET'])
def get_book_by_id(id):
book = Book.query.get(id)
data = book_schema.dump(book)
7 years ago
if not data:
7 years ago
return jsonify({"message": "Book could not be found."}), 400
else:
7 years ago
return jsonify({'book': data })
7 years ago
7 years ago
@app.route('/api/chats', methods=['GET'])
def get_chat():
chats = Chat.query.all()
data = chats_schema.dump(chats)
#print(errors)
return jsonify({'chat': data})
7 years ago
###
# The functions below should be applicable to all Flask apps.
###
@app.route('/<file_name>.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
7 years ago
### SOCKET for the chat
7 years ago
@socketio.on('new_message')
def new_message(message):
7 years ago
# Send message to all users
# print("new message")
# channel is always 1 now, but might be interesting for further development
7 years ago
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']
)
7 years ago
db.session.add(my_new_chat)
try:
db.session.commit()
except:
db.session.rollback()
7 years ago
if __name__ == '__main__':
7 years ago
socketio.run(app)
#app.run(debug=True,host="0.0.0.0",port="8080")