conflicts

master
Alex 7 years ago
commit 932ce9102a

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

@ -4,7 +4,7 @@ from wtforms.validators import InputRequired, DataRequired
from wtforms import FieldList from wtforms import FieldList
from wtforms import Form as NoCsrfForm from wtforms import Form as NoCsrfForm
from wtforms.fields import StringField, FormField, SubmitField, SelectField 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 - - - # - - - Forms - - -
class AuthorForm(NoCsrfForm): class AuthorForm(NoCsrfForm):
@ -30,6 +30,17 @@ class ChatForm(FlaskForm):
message = StringField('message', validators=[InputRequired()]) message = StringField('message', validators=[InputRequired()])
send = SubmitField(label='Send') 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): class SearchForm(FlaskForm):
choices = [('All', 'All'), choices = [('All', 'All'),

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

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

@ -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
}
});
});

@ -2,5 +2,12 @@
{% block main %} {% block main %}
<h1 class="page-header">About</h1> <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 %} {% endblock %}

@ -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 %}

@ -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 %}

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

@ -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 %}

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

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

@ -5,24 +5,26 @@
<h1 class="header">{{ stack.stack_name }}</h1> <h1 class="header">{{ stack.stack_name }}</h1>
<p>{{ stack.stack_description }} </p>
<p>Stack description:
{% for stack in stacks %}
{{stack.stack_description}}
{% endfor %}
</p>
<p>Books in this stack: {% for book in stack.books %} <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> <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> {% endfor %}</p>
<br> <br>
<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> </div>
{% endblock %} {% endblock %}

@ -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 %}

@ -4,46 +4,30 @@
<div class="container"> <div class="container">
<h1 class="page-header">Stacks</h1> <h1 class="page-header">Stacks</h1>
<p>These are all the stacks that have been built so far.</p> <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%"> <table style="width:100%">
<!-- <div id="tabs">
<td> {% for stack in stacks %} <ul>
{% for stack in stacks %}
<li><a href="{{url_for('show_stack_by_id', id=stack.id)}}">{{ stack.stack_name }}</a> </li> <li> <a href="stacks/tab/{{ stack.id }}">
{% endfor %} {{ stack.stack_name }}
</td> </a></td>
</table>
-->
<div id="tabs">
<ul>
{% for stack in stacks %}
<li><a href="#tabs-1">{{ stack.stack_name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
<div id="tabs-1">
<h2>Stack description</h2>
<p>This stack is nice.</p>
</div>
</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> </div>

@ -6,10 +6,10 @@ This file creates your application.
""" """
from app import app, db, socketio, DOMAIN 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 import json
from sqlalchemy.sql.expression import func, select 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.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema, Instance
from app.cover import get_cover from app.cover import get_cover
from app.extractText import extract_text from app.extractText import extract_text
@ -20,6 +20,8 @@ import time
from csv import DictWriter, DictReader from csv import DictWriter, DictReader
import io import io
from sqlalchemy.inspection import inspect from sqlalchemy.inspection import inspect
import autocomplete
import sys
import os import os
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
@ -307,13 +309,60 @@ def show_stacks():
stacks = db.session.query(Stack).all() stacks = db.session.query(Stack).all()
return render_template('show_stacks.html', stacks=stacks) 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']) @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) stack = Stack.query.get(id)
if not stack: if not stack:
abort (404) abort (404)
else: else:
if is_tab == False:
return render_template('show_stack_detail.html', stack=stack) 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']) @app.route('/instances', methods=['POST', 'GET'])
def show_instances(): def show_instances():
@ -350,6 +399,8 @@ def show_instances():
@app.route('/books', methods= ['POST','GET']) @app.route('/books', methods= ['POST','GET'])
def show_books(): def show_books():
autocomplete.load() #Train markov model once, for autocomplete in search
books = db.session.query(Book).all() books = db.session.query(Book).all()
search = SearchForm(request.form) search = SearchForm(request.form)
if request.method == 'POST': 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) 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']) @app.route('/export/csv', methods=['GET'])

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

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

@ -1,5 +1,4 @@
Title,Author,Shelf,Format,OCR,Downloaded,Origin,Filename,Stack Title,Author,Category,Format,OCR,Downloaded,Origin,Filename,Stack
Mac OS X Leopard Edition,David Pogue,Technical,pdf,1,1,LibGen,,
The Qmail Handbook,Dave Sill,Technical,pdf,1,1,LibGen,, 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 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,, 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
Loading…
Cancel
Save