conflicts

This commit is contained in:
Alex 2018-06-10 12:32:32 +02:00
commit 932ce9102a
19 changed files with 339 additions and 55 deletions

View File

@ -40,4 +40,3 @@ app.config.from_object(__name__)
from app import views
flask_whooshalchemyplus.init_app(app) # initialize

View File

@ -4,7 +4,7 @@ from wtforms.validators import InputRequired, DataRequired
from wtforms import FieldList
from wtforms import Form as NoCsrfForm
from wtforms.fields import StringField, FormField, SubmitField, SelectField
from app.models import Book, BookSchema, Author
from app.models import Book, BookSchema, Author, Stack, StackSchema
# - - - Forms - - -
class AuthorForm(NoCsrfForm):
@ -30,6 +30,17 @@ class ChatForm(FlaskForm):
message = StringField('message', validators=[InputRequired()])
send = SubmitField(label='Send')
class StackForm(FlaskForm):
stack_name = StringField('Stack', validators=[InputRequired()])
stack_description = StringField('Description', validators=[InputRequired()])
create = SubmitField(label='Create')
class AddtoStackForm(FlaskForm):
select_stack = SelectField('Stacks', validators=[InputRequired()])
class EditStackForm(FlaskForm):
edit_stack_name = StringField('Stack', validators=[InputRequired()])
edit_stack_description = StringField('Description', validators=[InputRequired()])
class SearchForm(FlaskForm):
choices = [('All', 'All'),

View File

@ -119,7 +119,7 @@ class Stack(db.Model):
self.stack_description = stack_description
def __repr__(self):
return '<Stack %r>' % self.stack
return '<Stack %r>' % self.stack_name
class AuthorSchema(Schema):
id = fields.Int(dump_only=True)

View File

@ -97,10 +97,17 @@ background-color: #E8E8E8!important;
.library_table li{
list-style-type:none;
text-align: center;
padding-right: 5px;
display: inline-block;
}
#plus {
text-align: center;
font-size: 10px;
}
.library_table tr:nth-child(even){
background-color: #fafafa;
}
@ -176,15 +183,16 @@ font-family:'Courier New';
font-size: 12px;
}
.ui-tabs-vertical { width: 55em; }
.ui-tabs-vertical .ui-tabs-nav { padding: .2em .1em .2em .2em; float: left; width: 12em; }
.ui-tabs-vertical .ui-tabs-nav li { clear: left; width: 100%; border-bottom-width: 1px !important; border-right-width: 0 !important; margin: 0 -1px .2em 0; }
.ui-tabs-vertical { width: 100em; border-top: 0;}
.ui-tabs-vertical .ui-tabs-nav { padding: .2em .2em .2em .2em; float: left; width: 15em; }
.ui-tabs-vertical .ui-tabs-nav li { clear: left; width: 100%; border-bottom-width: 0 !important; border-right-width: 0 !important; margin: 0 -1px .2em 0; }
.ui-tabs-vertical .ui-tabs-nav li a { display:block; }
.ui-tabs-vertical .ui-tabs-nav li.ui-tabs-active { padding-bottom: 0; padding-right: .1em; border-right-width: 1px; }
.ui-tabs-vertical .ui-tabs-panel { padding: 1em; float: right; width: 40em;}
.ui-tabs-vertical .ui-tabs-nav li.ui-tabs-active { padding-bottom: 0; padding-right: .1em; border-right-width: 0; background-color: #A9A9A9 !important;}
.ui-tabs-vertical .ui-tabs-panel { padding: 1em; float: left; width: 50em; font-size: 12px;}
#draggable { width: 100px; height: 100px; padding: 0.5em; float: left; margin: 10px 10px 10px 0; }
#droppable { width: 150px; height: 150px; padding: 0.5em; float: left; margin: 10px; }
#newstext{
width: 100%;
margin: 0;
@ -300,3 +308,19 @@ box-sizing: border-box;
font-size: 16px;
font-style: italic;
}
.widget {
resize: both;
overflow: hidden;
width: 300px;
height: 300px;
display: inline-block;
}
.widget iframe {
width: 100%;
height: 100%;
border: none;
}

View File

@ -157,3 +157,37 @@ $(document).ready(function()
}
);
// Autocomplete for search - Contact Joca in case of trouble
$('#search').on("input", function() {
var query = this.value;
$.ajax({
url: "/autocomplete_suggestions",
data: $('form').serialize(),
type: "POST",
success: function(response) {
//console.log("Got your query!");
}
});
$.ajax({
type: "GET",
url: "/autocomplete_suggestions",
dataType: "json",
success: function(data) {
// Start autocomplete
var availableTags = data;
console.log(availableTags);
$( "#search" ).autocomplete({
source: availableTags
});
// End of autocomplete
}
});
});

View File

@ -2,5 +2,12 @@
{% block main %}
<h1 class="page-header">About</h1>
<p>This an interface to the XPUB Library.</p>
<p>
XPPL is a project aimed at people who are studying the field of media culture, or as we like to call them: knowledge comrades.
<br>
This digital library gathers all the books and articles floating around on PZI shelves and our hard drives and memory sticks, so that they can be shared.
<br>
Its web interface hosts a curated catalogue of books and articles, and its distributed architecture provides instances for uploading and downloading.
<br>
It starts at XPUB, but can go anywhere we want it to.</p>
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends 'base.html' %}
{% block main %}
<div class="container">
{% from "_formhelpers.html" import render_field %}
<h1 class="page-header">Add Stack</h1>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger">
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('add_stack') }}" enctype=multipart/form-data>
{{form.hidden_tag()}}
<br>
{{ render_field(form.stack_name)}}
{{ render_field(form.stack_description)}}
<button type="submit" class='button'>Create</button>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% block main %}
<div class="container">
<div> Chosen book:
<h1 class="header">{{ book.title }}</h1>
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="150" onerror="if (this.src != '../static/img/{{ book.cover }}') this.src = '../static/img/default_cover.png';">
</div>
<p>These are all the stacks that have been built so far.</p>
{% from "_formhelpers.html" import render_field %}
<form method="POST">
<div class="search"> {{ render_field(add_form.select_stack) }} </div>
<button type="submit" class="button" value="Stack" name="add_book">Add to stack</button>
</form>
</div>
{% endblock %}

View File

@ -15,6 +15,7 @@
<![endif]-->
<link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/js/jquery-ui-1.12.1.custom/jquery-ui.css">
<link rel="stylesheet" href="/static/js/jquery-ui-1.12.1.custom/jquery-ui.js">
{% block css %} {% endblock%}
</head>
<body>

View File

@ -0,0 +1,25 @@
{% extends 'base.html' %}
{% block main %}
<div class="container">
<!--
{% from "_formhelpers.html" import render_field %} -->
<!--
{{ render_field(form.edit_stack_name)}}
{{ render_field(form.edit_stack_description)}} -->
<form method="POST" action="{{ url_for('edit_stack_by_id', id=stack.id )}}">
{{ form.csrf_token }}
<br> <br>
<div class="form-group">
{{ form.edit_stack_name.label }} {{ form.edit_stack_name(size=20, class="form-control") }}
</div><br>
<div class="form-group">
{{ form.edit_stack_description.label }} {{ form.edit_stack_description(size=20, class="form-control") }}
</div>
<br>
<button type="submit" class="btn btn-primary">Update</button>
</form>
</div>
{% endblock %}

View File

@ -33,6 +33,8 @@
<th>Filetype</th>
<th>Category</th>
<th>Stack</th>
<th>Add to stack</th>
</tr>
{% for book in books %}
<tr>
@ -51,6 +53,10 @@
<li><a href="{{url_for('show_stack_by_id', id=stack.id)}}">{{ stack.stack_name }}</a> </li>
{% endfor %}
</td>
<td id='plus'><a href='{{url_for('add_to_stack', id=book.id)}}'>
==>
</a></td>
{% endfor %}
</table>
@ -68,6 +74,8 @@
<th>Filetype</th>
<th>Category</th>
<th>Stack</th>
<th>Add to stack</th>
</tr>
{% for book in books_all %}
<tr>
@ -86,6 +94,9 @@
<li><a href="{{url_for('show_stack_by_id', id=stack.id)}}">{{ stack.stack_name }}</a> </li>
{% endfor %}
</td>
<td id='plus'><a href='{{url_for('add_to_stack', id=book.id)}}'>
==>
</a></td>
{% endfor %}
</table>
<p>

View File

@ -3,6 +3,7 @@
{% block main %}
<div class="container">
{% from "_formhelpers.html" import render_field %}
<form method="POST">
<div>{{ form.select(style="width: 100px; margin: 10px; float: left; font-size: 20px") }}</div>
<div class="search">
@ -32,6 +33,7 @@
<th>Filetype</th>
<th>Category</th>
<th>Stack</th>
<th>Add to stack</th>
</tr>
</thead>
<tbody>
@ -59,6 +61,10 @@
<li><a href="{{url_for('show_stack_by_id', id=stack.id)}}">{{ stack.stack_name }}</a> </li>
{% endfor %}
</td>
<td id='plus'><a href='{{url_for('add_to_stack', id=book.id)}}'>
==>
</a></td>
</tr>
{% endfor %}

View File

@ -5,24 +5,26 @@
<h1 class="header">{{ stack.stack_name }}</h1>
<p>Stack description:
{% for stack in stacks %}
{{stack.stack_description}}
{% endfor %}
</p>
<p>{{ stack.stack_description }} </p>
<p>Books in this stack: {% for book in stack.books %}
<li> <a href="{{url_for('show_book_by_id', id=book.id)}}">{{book.title}}</a> </li>
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="150" onerror="if (this.src != '../static/img/{{ book.cover }}') this.src = '../static/img/default_cover.png';">
<div class='widget'>
<iframe src="../uploads/{{ book.file }}" width="50%" ></iframe>
</div>
{% endfor %}</p>
<br>
<br>
<p>
<a href="{{ url_for('remove_stack_by_id', id=stack.id )}}">Delete</a> </p>
<a href="{{ url_for('edit_stack_by_id', id=stack.id )}}">Edit</a> </p>
<p><a href="{{url_for('show_stacks')}}">Go back to stacks</p>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
{% block main %}
<div class="container">
<h1 class="header">
<a href="{{url_for('show_stack_by_id', id=stack.id)}}">
{{ stack.stack_name }} </a>
</h1>
<p>{{ stack.stack_description }} </p>
<p>Books in this stack: {% for book in stack.books %}
<li style="font-size: 18px;"> <a href="{{url_for('show_book_by_id', id=book.id)}}">{{book.title}}</a> </li>
<p style="font-size: 10px;"><a href='{{url_for('add_to_stack', id=book.id)}}'>
Add to another stack
</a></p>
{% endfor %}</p>
</div>
{% endblock %}

View File

@ -4,46 +4,30 @@
<div class="container">
<h1 class="page-header">Stacks</h1>
<p>These are all the stacks that have been built so far.</p>
<p><a href= {{ url_for('add_stack') }}>Add a new stack</a></p>
<table style="width:100%">
<!--
<td> {% for stack in stacks %}
<li><a href="{{url_for('show_stack_by_id', id=stack.id)}}">{{ stack.stack_name }}</a> </li>
{% endfor %}
</td>
</table>
-->
<div id="tabs">
<ul>
{% for stack in stacks %}
<li><a href="#tabs-1">{{ stack.stack_name }}</a></li>
<li> <a href="stacks/tab/{{ stack.id }}">
{{ stack.stack_name }}
</a></td>
{% endfor %}
</ul>
<div id="tabs-1">
<h2>Stack description</h2>
<p>This stack is nice.</p>
</div>
</div>
<br>
<br>
<h1 class="page-header">Build a stack</h1>
<div id="draggable" class="ui-widget-content">
<p>List of books</p>
</div>
<div id="droppable" class="ui-widget-header">
<p>Stack</p>
</div>
</div>

View File

@ -6,10 +6,10 @@ 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
from flask import Flask, Response, session, 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
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
@ -20,6 +20,8 @@ import time
from csv import DictWriter, DictReader
import io
from sqlalchemy.inspection import inspect
import autocomplete
import sys
import os
from werkzeug.utils import secure_filename
@ -307,13 +309,60 @@ 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/<int:id>', methods=['POST', 'GET'])
def show_stack_in_tab(id):
return show_stack_by_id(id, is_tab=True)
@app.route('/stacks/<int:id>', methods=['POST', 'GET'])
def show_stack_by_id(id):
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():
@ -350,6 +399,8 @@ def show_instances():
@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)
if request.method == 'POST':
@ -383,6 +434,46 @@ def search_results(searchtype, query):
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 test1():
if request.method == 'POST':
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)
session['autocomplete_suggestions'] = str(autocomplete_suggestions)
print(session['autocomplete_suggestions'])
return Response(json.dumps(session['autocomplete_suggestions']), mimetype='application/json')
## STACKS!
@app.route('/add_to_stack/<int:id>', 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)
@app.route('/export/csv', methods=['GET'])

View File

@ -20,7 +20,7 @@ with open(args.csv) as f:
print ('get_cover', fullpath, name)
cover = get_cover(fullpath, name)
book = Book(row['Title'], row['Filename'], cover, row['Format'], row['Shelf'], None)
book = Book(row['Title'], row['Filename'], cover, row['Format'], row['Category'], None)
db.session.add(book)
authors = row['Author'].split(',')
@ -39,7 +39,7 @@ with open(args.csv) as f:
if stack:
b = db.session.query(Stack).filter_by(stack_name=stack).first()
if b == None:
b = Stack(stack_name=stack, stack_description="test")
b = Stack(stack_name=stack, stack_description=stack_description)
db.session.add(b)
book.stacks.append(b)

View File

@ -16,6 +16,12 @@ WTForms==2.1
flask-marshmallow==0.9.0
Wand==0.4.4
PyPDF2==1.26.0
<<<<<<< HEAD
flask-socketio==2.9.2
flask-whooshalchemyplus==0.7.5
python-dotenv==0.7.1
=======
autocomplete==0.0.104
>>>>>>> 30d8bade54d8646ad4a5e314022d62e2dbf81755

View File

@ -1,5 +1,4 @@
Title,Author,Shelf,Format,OCR,Downloaded,Origin,Filename,Stack
Mac OS X Leopard Edition,David Pogue,Technical,pdf,1,1,LibGen,,
Title,Author,Category,Format,OCR,Downloaded,Origin,Filename,Stack
The Qmail Handbook,Dave Sill,Technical,pdf,1,1,LibGen,,
Hardening Network Infrastructure: Bulletproof Your Systems Before You Are Hacked!,Wes Noonan,Technical,"chm, pdf",1,1,LibGen,,Make a library
Cocoa Programming for Mac OS X Second Edition,Aaron Hillegaas,Technical,pdf,1,1,LibGen,,

1 Title Author Shelf Category Format OCR Downloaded Origin Filename Stack
Mac OS X Leopard Edition David Pogue Technical pdf 1 1 LibGen
2 The Qmail Handbook Dave Sill Technical pdf 1 1 LibGen
3 Hardening Network Infrastructure: Bulletproof Your Systems Before You Are Hacked! Wes Noonan Technical chm, pdf 1 1 LibGen Make a library
4 Cocoa Programming for Mac OS X Second Edition Aaron Hillegaas Technical pdf 1 1 LibGen