Browse Source

fix conflicts xpplanot again

ansible-setup-and-deploy
ange 6 years ago
parent
commit
6205fc7272
  1. BIN
      .DS_Store
  2. 1
      app/__init__.py
  3. 2
      app/cover.py
  4. 23
      app/extractText.py
  5. 23
      app/models.py
  6. 6
      app/static/css/style.css
  7. 8
      app/templates/base.html
  8. 1
      app/templates/header.html
  9. 10
      app/templates/home.html
  10. 7
      app/templates/import_csv.html
  11. 150
      app/templates/scape.html
  12. 47
      app/templates/show_book_detail.html
  13. 37
      app/templates/show_instances.html
  14. 216
      app/views.py
  15. 2
      init_db.sh
  16. 6
      requirements.txt
  17. 4
      run.py
  18. BIN
      whoosh/Book/MAIN_071mbyqoitijmyv4.seg
  19. BIN
      whoosh/Book/MAIN_2a7vygakwdvdefoe.seg
  20. BIN
      whoosh/Book/MAIN_5id3ip2mvmsu2mbl.seg
  21. BIN
      whoosh/Book/MAIN_6bsjkp9xdes7zrrx.seg
  22. BIN
      whoosh/Book/MAIN_es8t5xyup7nfqm7j.seg
  23. BIN
      whoosh/Book/MAIN_hljrpqpa46dk3vgc.seg
  24. BIN
      whoosh/Book/MAIN_q0ohjvxyt5ufv1vl.seg
  25. BIN
      whoosh/Book/_MAIN_293.toc

BIN
.DS_Store

Binary file not shown.

1
app/__init__.py

@ -36,4 +36,3 @@ socketio = SocketIO(app)
app.config.from_object(__name__) app.config.from_object(__name__)
from app import views from app import views

2
app/cover.py

@ -40,7 +40,7 @@ def get_cover(file_path, filename):
big_filename = "app/uploads/cover/"+page["filename"] + "_cover.png" big_filename = "app/uploads/cover/"+page["filename"] + "_cover.png"
small_filename = "app/uploads/cover/"+page["filename"] + "cover_small" + ".png" small_filename = "app/uploads/cover/"+page["filename"] + "cover_small" + ".png"
img = pdf_page_to_png(src_pdf, pagenum = page["pagenum"], resolution = 300) img = pdf_page_to_png(src_pdf, pagenum = page["pagenum"], resolution = 200)
img.save(filename = big_filename) img.save(filename = big_filename)
# Ensmallen # Ensmallen

23
app/extractText.py

@ -0,0 +1,23 @@
import PyPDF2
def get_text(file_path, filename):
read_pdf =file_path
with open(read_pdf,'rb') as pdf_file, open("app/uploads/"+filename+'.txt', 'w') as text_file:
read_pdf = PyPDF2.PdfFileReader(pdf_file)
number_of_pages = read_pdf.getNumPages()
for page_number in range(number_of_pages): # use xrange in Py2
page = read_pdf.getPage(page_number)
page_content = page.extractText()
text_file.write(page_content)
def extract_text(file_path, filename):
try:
get_text(file_path, filename)
except:
with open(filename+'.txt', 'w') as text_file:
page_content = ""
text_file.write(page_content)

23
app/models.py

@ -15,6 +15,11 @@ stacks = db.Table('books_stacks',
db.Column('stack_id', db.Integer, db.ForeignKey('stacks.id'), primary_key=True) db.Column('stack_id', db.Integer, db.ForeignKey('stacks.id'), primary_key=True)
) )
instances = db.Table('books_instances',
db.Column('book_id', db.Integer, db.ForeignKey('books.id'), primary_key=True),
db.Column('instance_id', db.Integer, db.ForeignKey('instances.id'), primary_key=True)
)
class Book(db.Model): class Book(db.Model):
__tablename__ = 'books' __tablename__ = 'books'
@ -26,10 +31,14 @@ class Book(db.Model):
category = db.Column(db.String(255)) category = db.Column(db.String(255))
year_published = db.Column(db.Numeric(4,0)) year_published = db.Column(db.Numeric(4,0))
description = db.Column(db.String(2500)) description = db.Column(db.String(2500))
html = db.Column(db.String(255))
downloads = db.Column(db.Numeric(100,0))
authors = db.relationship('Author', secondary=authors,cascade="delete", lazy='subquery', authors = db.relationship('Author', secondary=authors,cascade="delete", lazy='subquery',
backref=db.backref('books', lazy=True),passive_deletes=True) backref=db.backref('books', lazy=True),passive_deletes=True)
stacks = db.relationship('Stack', secondary=stacks, lazy='subquery', stacks = db.relationship('Stack', secondary=stacks, lazy='subquery',
backref=db.backref('books', lazy=True)) backref=db.backref('books', lazy=True))
instances = db.relationship('Instance', secondary=instances, lazy='subquery',
backref=db.backref('books', lazy=True))
scapeX = db.Column(db.Numeric(10,2)) scapeX = db.Column(db.Numeric(10,2))
scapeY = db.Column(db.Numeric(10,2)) scapeY = db.Column(db.Numeric(10,2))
message = db.Column(db.String(1000)) message = db.Column(db.String(1000))
@ -47,6 +56,7 @@ class Book(db.Model):
self.fileformat = fileformat self.fileformat = fileformat
self.category = category self.category = category
self.year_published = year_published self.year_published = year_published
self.download = None
self.scapeX = 0 self.scapeX = 0
self.scapeY = 0 self.scapeY = 0
self.message = message self.message = message
@ -73,6 +83,19 @@ class Author(db.Model):
def __init__(self, author_name): def __init__(self, author_name):
self.author_name = author_name self.author_name = author_name
class Instance(db.Model):
__tablename__ = 'instances'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50))
ip = db.Column(db.String(50))
action = db.Column(db.String(50))
def __init__(self, ip, action):
self.name = ip
self.ip = ip
self.action = action
class UserIns(db.Model): class UserIns(db.Model):
__tablename__ = 'userins' __tablename__ = 'userins'

6
app/static/css/style.css

@ -199,6 +199,7 @@ font-size: 12px;
.ui-tabs-vertical { width: 100em; border-top: 0;} .ui-tabs-vertical { width: 100em; border-top: 0;}
<<<<<<< HEAD <<<<<<< HEAD
<<<<<<< HEAD
.ui-tabs-vertical .ui-tabs-nav { padding: .2em .2em .2em .2em; float: left; width: 15em; } .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 { clear: left; width: 100%; border-bottom-width: 0 !important; border-right-width: 0 !important; margin: 0 -1px .2em 0; }
======= =======
@ -206,6 +207,11 @@ font-size: 12px;
.ui-tabs-vertical .ui-tabs-nav { padding: .2em .2em .2em .2em; float: left; width: 15em; -webkit-appearance: none;} .ui-tabs-vertical .ui-tabs-nav { padding: .2em .2em .2em .2em; float: left; width: 15em; -webkit-appearance: none;}
.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; list-style-type: none; } .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; list-style-type: none; }
>>>>>>> stack_stuff >>>>>>> stack_stuff
=======
.ui-tabs-vertical a { text-decoration: none; color: black;}
.ui-tabs-vertical .ui-tabs-nav { padding: .2em .2em .2em .2em; float: left; width: 15em; -webkit-appearance: none;}
.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; list-style-type: none; }
>>>>>>> master
.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: 0; background-color: yellow !important; list-style-type: none;} .ui-tabs-vertical .ui-tabs-nav li.ui-tabs-active { padding-bottom: 0; padding-right: .1em; border-right-width: 0; background-color: yellow !important; list-style-type: none;}
.ui-tabs-vertical .ui-tabs-panel { padding: 1em; float: left; width: 50em; font-size: 12px; list-style-type: none;} .ui-tabs-vertical .ui-tabs-panel { padding: 1em; float: left; width: 50em; font-size: 12px; list-style-type: none;}

8
app/templates/base.html

@ -14,7 +14,6 @@
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![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.js"> <link rel="stylesheet" href="/static/js/jquery-ui-1.12.1.custom/jquery-ui.js">
{% block css %} {% endblock%} {% block css %} {% endblock%}
</head> </head>
@ -62,7 +61,7 @@ console.log(time)
return ('0'+time.getDate()).slice(-2) + '.' + ('0'+(time.getMonth()+1)).slice(-2) + '.' + time.getFullYear() +" " + ('0'+time.getHours()).slice(-2)+":"+ ('0'+time.getMinutes()).slice(-2); return ('0'+time.getDate()).slice(-2) + '.' + ('0'+(time.getMonth()+1)).slice(-2) + '.' + time.getFullYear() +" " + ('0'+time.getHours()).slice(-2)+":"+ ('0'+time.getMinutes()).slice(-2);
} }
//change addr when ONLINE::::::-> //change addr when ONLINE::::::->
var socket = io.connect('http://localhost:5000'); var socket = io.connect('{{server}}');
var app = new Vue({ var app = new Vue({
el: "#app", el: "#app",
delimiters: ['[[', ']]'], delimiters: ['[[', ']]'],
@ -112,6 +111,11 @@ socket.on('channel-' + app.channel, function(msg) {
scrollTop: $('.messages')[0].scrollHeight scrollTop: $('.messages')[0].scrollHeight
}); });
}); });
document.getElementById("file_import_csv").onchange = function() {
document.getElementById("form_import_csv").submit();
};
</script> </script>
</body> </body>
</html> </html>

1
app/templates/header.html

@ -6,6 +6,7 @@
<li><a href="{{ url_for('add_book') }}">Add Book</a></li> <li><a href="{{ url_for('add_book') }}">Add Book</a></li>
<li><a href="{{ url_for ('add_stack') }}">Add Stack</a></li> <li><a href="{{ url_for ('add_stack') }}">Add Stack</a></li>
<li><a href="{{ url_for('about') }}">About</a></li> <li><a href="{{ url_for('about') }}">About</a></li>
<li><a href="{{ url_for('show_instances') }}">Instances</a></li>
</ul> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
</nav> </nav>

10
app/templates/home.html

@ -4,6 +4,8 @@
<div id="home_content"> <div id="home_content">
<h1 class="header" id="title_xppl">XPPL</h1> <h1 class="header" id="title_xppl">XPPL</h1>
<p class="lead">This is the awesome library of Experimental Publishing. <br> <p class="lead">This is the awesome library of Experimental Publishing. <br>
On instance: {{server}} / From: {{client}}
<br>
This might only be one interface to this library: This might only be one interface to this library:
</p> </p>
@ -12,6 +14,14 @@ This might only be one interface to this library:
<a href="{{url_for('show_books')}}">List</a> <a href="{{url_for('show_books')}}">List</a>
<br><br><br> <br><br><br>
<form method="GET" action="/export/csv">
<button type="submit">Export Catalogue (.CSV)</button>
</form>
<form method="POST" action="/import/csv" id="form_import_csv" enctype=multipart/form-data>
<input type="file" id="file_import_csv" name=file style="display:none;" />
<button type = "button" onclick="document.getElementById('file_import_csv').click()">Import Catalogue (.CSV)</button>
</form>
</div> </div>
<div id="app" class="container"> <div id="app" class="container">

7
app/templates/import_csv.html

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block main %}
<h1 class="page-header">Imported CSV</h1>
<p>{{numberadded}} books added!</p>
{% endblock %}

150
app/templates/scape.html

@ -3,27 +3,100 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css"/>
<style>
<style>
#scape_container {
zoom: 1;
}
body .ui-selecting { border:2px solid yellow; } body .ui-selecting { border:2px solid yellow; }
body .ui-selected {border:2px solid black;} body .ui-selected {border:2px solid black;}
body {overflow: scroll;} body {overflow: scroll;}
#scape_container{overflow: scroll;width: 100%; height:100vh;} #scape_container{overflow: scroll;width: 100%; height:100vh;}
#zoombuttons{
position: fixed;
right:20px;
bottom: 20px;
z-index: +999999999999999999999999999999999999999999999;
user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
}
#zoom-in{
width: 30px;
height: 30px;
background-color: rgb(240,240,240);
cursor: pointer;
border: 1px solid black;
text-align: center;
padding-top: 10px;
}
#zoom-out{
width: 30px;
height: 30px;
background-color: rgb(240,240,240);
cursor: pointer;
border: 1px solid black;
text-align: center;
padding-top: 10px;
}
#zoom-in:hover{
background-color: rgb(230,230,230);
}
#zoom-out:hover{
background-color: rgb(230,230,230);
}
.booktitle a{
text-decoration: none;
color: black;
}
</style> </style>
</head> </head>
<body> <body>
<div id="zoombuttons">
<select name="selector" id="selector">
{% for instance in instances %}
<option value="{{instance.name}}">{{instance.name}}</option>
{% endfor %}
</select>
<div id="zoom-in">+</div>
<div id="zoom-out">-</div>
</div>
<div id ="scape_container"> <div id ="scape_container">
{% for book in books|sort(attribute='title', reverse = False) %} {% for book in books|sort(attribute='title', reverse = False) %}
<div class = "drag" id = "{{ book.id }}" style="position: absolute;width:40px;height:auto; top:{{ book.scapeY }}px; left:{{ book.scapeX }}px;"> <div class = "drag" id = "{{ book.id }}" style="position: absolute;width:70px;height:auto; top:{{ book.scapeY }}px; left:{{ book.scapeX }}px;">
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" style="width:100%;height:auto;" onerror="if (this.src != '../static/img/default_cover.png') this.src = '../static/img/default_cover.png';"> <img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" style="width:100%;height:auto;" onerror="if (this.src != '../static/img/default_cover.png') this.src = '../static/img/default_cover.png';">
<p style="font-size:7px;"><a href="books/{{ book.id }}">{{ book.title }}</a></p> <p class="booktitle" style="font-size:7px;"><a href="books/{{ book.id }}">{{ book.title }}</a></p>
{% set got = {} %}
{% set all = 1 %}
{% for instance in book.instances %}
{% if instance.name in got %}
{% set x=got.__setitem__(instance.name, got[instance.name]+1) %}
{% else %}
{% set x=got.__setitem__(instance.name, 1) %}
{% endif %}
{% set all = loop.index %}
{% endfor %}
{% for instance, value in got.items() %}
{% set result = value/(book.instances|length) %}
<p hidden="True" >{{ instance }}</p>
<p hidden="True" class = "{{ instance }}" >{{result}}</p>
{% endfor %}
</div> </div>
{% endfor %} {% endfor %}
<div id="random" style="padding:2px; margin: 0;position: absolute;width:120px;height:20px;background-color:yellow;z-index:999999999999999999900000000000000;cursor:pointer;"> <div id="random" style="padding:2px; margin: 0;position: absolute;width:120px;height:20px;background-color:yellow;z-index:999999999999999999900000000000000;cursor:pointer;">
@ -81,7 +154,29 @@
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script> <script>
$( function() { $( function() {
var currentZoom = $('#scape_container').css('zoom');
var zoom = parseFloat(currentZoom);
var pointerX;
var pointerY;
$( ".drag" ).draggable({ stack: ".drag", $( ".drag" ).draggable({ stack: ".drag",
start : function(evt, ui) {
pointerY = (evt.pageY - $('#scape_container').offset().top) / zoom - parseInt($(evt.target).css('top'));
pointerX = (evt.pageX - $('#scape_container').offset().left) / zoom - parseInt($(evt.target).css('left'));
},
drag : function(evt, ui) {
var canvasTop = $('#scape_container').offset().top;
var canvasLeft = $('#scape_container').offset().left;
var canvasHeight = $('#scape_container').height();
var canvasWidth = $('#scape_container').width();
// Fix for zoom
ui.position.top = Math.round((evt.pageY - canvasTop) / zoom - pointerY);
ui.position.left = Math.round((evt.pageX - canvasLeft) / zoom - pointerX);
// Finally, make sure offset aligns with position
ui.offset.top = Math.round(ui.position.top + canvasTop);
ui.offset.left = Math.round(ui.position.left + canvasLeft);
},
stop: function(){ stop: function(){
var offset = $(this).offset(); var offset = $(this).offset();
var id = $(this).attr('id'); var id = $(this).attr('id');
@ -108,12 +203,7 @@
} ); } );
$( function() {
$( ".drag" ).resizable({aspectRatio: true});
} );
/* $( function() {
$( "body" ).selectable();
} );*/
$( "#random" ).click(function() { $( "#random" ).click(function() {
console.log("hallo"); console.log("hallo");
@ -121,8 +211,8 @@ $( ".drag" ).each(function() {
var id = $(this).attr('id'); var id = $(this).attr('id');
var postForm = { //Fetch form data var postForm = { //Fetch form data
'id' : id, 'id' : id,
'x' : Math.floor(Math.random() * window.innerWidth) , //Store name fields value, 'x' : Math.floor(Math.random() * 2000) , //Store name fields value,
'y' : Math.floor(Math.random() * window.innerHeight) 'y' : Math.floor(Math.random() * 2000)
}; };
$( this ).css("top", postForm['y']); $( this ).css("top", postForm['y']);
$( this ).css("left", postForm['x']); $( this ).css("left", postForm['x']);
@ -137,6 +227,42 @@ $( ".drag" ).each(function() {
}); });
}); });
$("#zoom-in").click(function(){
var currentZoom = $('#scape_container').css('zoom');
var newnumber = parseFloat(currentZoom) + 0.1;
console.log(newnumber)
//$('#scape_container').css('zoom','90%'); /* Webkit browsers */
$('#scape_container').css('zoom',newnumber.toString()); /* Other non-webkit browsers */
// $('#scape_container').css('-moz-transform',scale(0.9, 0.9)); /* Moz-browsers */
});
$("#zoom-out").click(function(){
var currentZoom = $('#scape_container').css('zoom');
var newnumber = parseFloat(currentZoom) - 0.1;
console.log(currentZoom+", "+newnumber.toString())
//$('#scape_container').css('zoom','90%'); /* Webkit browsers */
$('#scape_container').css('zoom',newnumber.toString()); /* Other non-webkit browsers */
// $('#scape_container').css('-moz-transform',scale(0.9, 0.9)); /* Moz-browsers */
});
$("select[name=selector]").on('change', function() {
var selectedInstance = $('select[name=selector]').val()
$( ".drag" ).each(function() {
if($(this).find('.'+selectedInstance).length==0)
{
$(this).css("opacity", "0.02")
}
else{
var found = $(this).find('.'+selectedInstance).html()
$(this).css("opacity", found)
}
});
});
</script> </script>
</body> </body>

47
app/templates/show_book_detail.html

@ -35,11 +35,11 @@
<p style="font-size: 10px;"><a href="{{url_for('remove_from_stack', stackid=stack.id, bookid=book.id)}}"> – Remove from stack</a>{% endfor %}</td> <p style="font-size: 10px;"><a href="{{url_for('remove_from_stack', stackid=stack.id, bookid=book.id)}}"> – Remove from stack</a>{% endfor %}</td>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td style="font-style: italic;">Notes from uploader</td> <td style="font-style: italic;">Notes from uploader</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
@ -66,6 +66,7 @@
</tbody> </tbody>
</table> </table>
<<<<<<< HEAD
<<<<<<< HEAD <<<<<<< HEAD
<a href="/viewpdf/{{ book.file }}">download {{ book.fileformat }}</a> <a href="/viewpdf/{{ book.file }}">download {{ book.fileformat }}</a>
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
@ -92,18 +93,52 @@
{% else %} {% else %}
{% endif %} {% endif %}
>>>>>>> stack_stuff >>>>>>> stack_stuff
=======
<a href="../uploads/{{ book.file }}">download {{ book.fileformat }}</a>
<br>
<br>
<p>Instances:</p>
{% set got = {} %}
{% set all = 1 %}
{% for instance in book.instances %}
{% if instance.name in got %}
{% set x=got.__setitem__(instance.name, got[instance.name]+1) %}
{% else %}
{% set x=got.__setitem__(instance.name, 1) %}
{% endif %}
{% set all = loop.index %}
{% endfor %}
{% for instance, value in got.items() %}
{% set result = value/(book.instances|length) %}
{{ instance }}: {{ (result*100)|round|int }}%<br>
{% endfor %}
<br>
{% if book.file %}
<button id="myBtn" style= "width: 180px; font-size: 10pt;"><a> Download this {{ book.fileformat }}</a></button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h3>A message from the uploading librarian:</h3>
<span style="font-style: italic;">"{{book.message or 'Happy reading.'}}" </span><br>
<h4><a href="../uploads/{{ book.file }}"> >>>> Link to file <<<<</h4></a></div>
</div>
{% else %}
{% endif %}
>>>>>>> master
<button style= "font-size: 10pt;"> <a href="{{ url_for('edit_book_by_id', id=book.id )}}">edit</a></button> <button style= "font-size: 10pt;"> <a href="{{ url_for('edit_book_by_id', id=book.id )}}">edit</a></button>
<button style= "font-size: 10pt;"> <a href="{{ url_for('remove_book_by_id', id=book.id)}}">delete</a></button> <button style= "font-size: 10pt;"> <a href="{{ url_for('remove_book_by_id', id=book.id)}}">delete</a></button>
<br><br> <br><br>
<hr> <hr>
{% if previousbook %} {% if previousbook %}
<a href="{{ url_for('show_book_by_id', id=previousbook.id )}}" style="font-size: 9pt;"> < see the previous book added to XPPL: &nbsp;<i>{{ previousbook.title |truncate(40,True,'...') }} </i></a> {% endif %} <a href="{{ url_for('show_book_by_id', id=previousbook.id )}}" style="font-size: 9pt;"> < see the previous book added to XPPL: &nbsp;<i>{{ previousbook.title |truncate(40,True,'...') }} </i></a> {% endif %}
{% if nextbook %} {% if nextbook %}
<a href="{{ url_for('show_book_by_id', id=nextbook.id )}}" style="float:right; font-size: 9pt;"> see the next book added to XPPL: &nbsp;<i>{{ nextbook.title|truncate(40,True,'...')}} </i>> </a>{% endif %} <a href="{{ url_for('show_book_by_id', id=nextbook.id )}}" style="float:right; font-size: 9pt;"> see the next book added to XPPL: &nbsp;<i>{{ nextbook.title|truncate(40,True,'...')}} </i>> </a>{% endif %}
</div> </div>
{% endblock %} {% endblock %}

37
app/templates/show_instances.html

@ -0,0 +1,37 @@
{% extends 'base.html' %}
{% block main %}
<div class="container">
<h1 class="page-header">Instances</h1>
<p>All instances, that are using this library</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>
-->
<ul>
{% for instance in instances %}
<li>{{ instance.name }} / {{ instance.ip }}</li>
<form method="post">
<input id="{{ instance.name }}" type="text" name="{{ instance.name }}"><br>
<input type="submit" value="rename">
</form>
{% endfor %}
</ul>
</div>
{% endblock %}

216
app/views.py

@ -13,16 +13,23 @@ import os
from sqlalchemy.sql.expression import func, select from sqlalchemy.sql.expression import func, select
from sqlalchemy.sql import except_ from sqlalchemy.sql import except_
from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm
from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema, Potential from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema, Instance, Potential
from app.cover import get_cover from app.cover import get_cover
<<<<<<< HEAD
from app.getannot import get_annotations from app.getannot import get_annotations
from urllib.parse import quote as urlquote from urllib.parse import quote as urlquote
=======
from app.extractText import extract_text
>>>>>>> master
from os import environ from os import environ
from flask_socketio import SocketIO, emit from flask_socketio import SocketIO, emit
from weasyprint import HTML from weasyprint import HTML
import datetime import datetime
import time import time
import autocomplete from csv import DictWriter, DictReader
import io
from sqlalchemy.inspection import inspect
#import autocomplete
import sys import sys
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
@ -57,8 +64,14 @@ def home():
# msg = Chat(message) # msg = Chat(message)
# db.session.add(msg) # db.session.add(msg)
# db.session.commit() # 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']
return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username=username) return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username=username, client=client, server=server)
@app.route('/hello/<name>') @app.route('/hello/<name>')
def hello(name): def hello(name):
@ -71,6 +84,13 @@ def about():
@app.route('/uploads/<filename>') @app.route('/uploads/<filename>')
def uploaded_file(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()
return send_from_directory(app.config['UPLOAD_FOLDER'], return send_from_directory(app.config['UPLOAD_FOLDER'],
filename) filename)
@ -101,7 +121,19 @@ def scape():
book.scapeY = data['y'] book.scapeY = data['y']
db.session.commit() db.session.commit()
books = db.session.query(Book).all() # or you could have used User.query.all() books = db.session.query(Book).all() # or you could have used User.query.all()
return render_template('scape.html', books=books) 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)
@app.route('/books_grid') @app.route('/books_grid')
@ -112,6 +144,7 @@ def show_books_grid():
@app.route('/books/<int:id>') @app.route('/books/<int:id>')
def show_book_by_id(id): def show_book_by_id(id):
book = Book.query.get(id) book = Book.query.get(id)
all_instances = db.session.query(Instance).all()
previousbook = Book.query.filter_by(id=id - 1).first() previousbook = Book.query.filter_by(id=id - 1).first()
nextbook = Book.query.filter_by(id=id + 1).first() nextbook = Book.query.filter_by(id=id + 1).first()
allbooks = db.session.query(Book).all() allbooks = db.session.query(Book).all()
@ -132,7 +165,7 @@ def show_book_by_id(id):
if not book: if not book:
return render_template('red_link.html', id=id) return render_template('red_link.html', id=id)
else: else:
return render_template('show_book_detail.html', book=book, previousbook = previousbook, nextbook = nextbook) return render_template('show_book_detail.html', book=book, previousbook = previousbook, nextbook = nextbook, all_instances=all_instances)
@app.route('/books/<int:id>/delete', methods=['POST', 'GET']) @app.route('/books/<int:id>/delete', methods=['POST', 'GET'])
@ -176,6 +209,11 @@ def edit_book_by_id(id):
a = Author(author_name=author_name) a = Author(author_name=author_name)
db.session.add(a) db.session.add(a)
book.authors.append(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)
# editing / uploading new file # editing / uploading new file
if user_form.file.data: if user_form.file.data:
@ -255,7 +293,13 @@ def add_book():
fullpath = os.path.join(app.config['UPLOAD_FOLDER'], filename) fullpath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
name, file_extension = os.path.splitext(filename) name, file_extension = os.path.splitext(filename)
file.save(fullpath) file.save(fullpath)
cover = get_cover(fullpath, name) try:
cover = get_cover(fullpath, name)
except:
cover = ''
extract_text(fullpath, name)
else: else:
flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) flash('allowed file formats: %s' % ALLOWED_EXTENSIONS)
#if upload without file -> wishform, with potential PDF #if upload without file -> wishform, with potential PDF
@ -285,6 +329,11 @@ def add_book():
a = Author(author_name=author_name) a = Author(author_name=author_name)
db.session.add(a) db.session.add(a)
book.authors.append(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() db.session.commit()
flash("%s added to the library" % (title)) flash("%s added to the library" % (title))
@ -378,10 +427,40 @@ def edit_stack_by_id(id):
stack.stack_name = stack_name stack.stack_name = stack_name
stack.stack_description = stack_description stack.stack_description = stack_description
db.session.commit() db.session.commit()
return redirect(url_for('show_stack_by_id', id=id)) return redirect(url_for('show_stack_by_id', id=id))
return render_template('edit_stack_detail.html', stack=stack, form=form) 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)
@app.route('/stacks/<int:stackid>/remove/<int:bookid>', methods=['POST', 'GET']) @app.route('/stacks/<int:stackid>/remove/<int:bookid>', methods=['POST', 'GET'])
def remove_from_stack(bookid, stackid): def remove_from_stack(bookid, stackid):
book = Book.query.get(bookid) book = Book.query.get(bookid)
@ -392,18 +471,21 @@ def remove_from_stack(bookid, stackid):
db.session.commit() db.session.commit()
return render_template('show_book_detail.html', book=book) return render_template('show_book_detail.html', book=book)
## search ## search
view = ['1'] view = ['1']
@app.route('/books', methods= ['POST','GET']) @app.route('/books', methods= ['POST','GET'])
def show_books(): def show_books():
<<<<<<< HEAD
<<<<<<< HEAD <<<<<<< HEAD
autocomplete.load() #Train markov model once, for autocomplete in search autocomplete.load() #Train markov model once, for autocomplete in search
books = db.session.query(Book).all() books = db.session.query(Book).all()
======= =======
books = db.session.query(Book).order_by(Book.title) books = db.session.query(Book).order_by(Book.title)
>>>>>>> stack_stuff >>>>>>> stack_stuff
=======
books = db.session.query(Book).order_by(Book.title)
>>>>>>> master
search = SearchForm(request.form) search = SearchForm(request.form)
view.append('1') view.append('1')
viewby = '1' viewby = '1'
@ -514,6 +596,7 @@ def search_results(searchtype, query, viewby):
if viewby == '2': if viewby == '2':
return render_template('results_grid.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) return render_template('results_grid.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage)
<<<<<<< HEAD
<<<<<<< HEAD <<<<<<< HEAD
## Search - autocomplete ## Search - autocomplete
autocomplete_suggestions = [] autocomplete_suggestions = []
@ -541,6 +624,8 @@ def test1():
## STACKS! ## STACKS!
======= =======
=======
>>>>>>> master
else: else:
return render_template('results.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) return render_template('results.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage)
@ -562,11 +647,21 @@ def test1():
# autocomplete_suggestions.clear() # autocomplete_suggestions.clear()
# for suggestion, score in autocomplete_output: # for suggestion, score in autocomplete_output:
# autocomplete_suggestions.append(suggestion) # autocomplete_suggestions.append(suggestion)
#
# session['autocomplete_suggestions'] = str(autocomplete_suggestions)
#
# print(session['autocomplete_suggestions'])
#
# return Response(json.dumps(session['autocomplete_suggestions']), mimetype='application/json')
<<<<<<< HEAD
# print(autocomplete_suggestions) # print(autocomplete_suggestions)
# return Response(json.dumps(autocomplete_suggestions), mimetype='application/json') # return Response(json.dumps(autocomplete_suggestions), mimetype='application/json')
>>>>>>> stack_stuff >>>>>>> stack_stuff
=======
## STACKS!
>>>>>>> master
@app.route('/add_to_stack/<int:id>', methods=['GET', 'POST']) @app.route('/add_to_stack/<int:id>', methods=['GET', 'POST'])
def add_to_stack(id): def add_to_stack(id):
@ -583,23 +678,97 @@ def add_to_stack(id):
db.session.commit() db.session.commit()
return render_template('show_stack_detail.html', stack=stack) return render_template('show_stack_detail.html', stack=stack)
from csv import DictWriter
import io
@app.route('/export/csv', methods=['GET']) @app.route('/export/csv', methods=['GET'])
def export_csv (): def export_csv():
output = io.StringIO() output = io.StringIO()
fieldnames = ['title', 'authors'] #fieldnames = ['title', 'authors', 'file', 'fileformat', 'category', 'year_published', 'description' ]
csv = DictWriter(output,fieldnames) 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() csv.writeheader()
# for i in range(10):
# csv.writerow({'ID': i, 'fruit': "Tomato"})
for book in Book.query.order_by("title"): for book in Book.query.order_by("title"):
authors = ", ".join([x.author_name for x in book.authors]) row = {}
csv.writerow({"title": book.title, "authors": authors}) for col in fieldnames:
resp = Response(output.getvalue(), mimetype="text/plain") print(getattr(book, col))
# resp.headers["Content-Disposition"] = "attachment;filename=export.csv" 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 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, None, None, None, None, None, None)
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_catalogue487352698237465', 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"
### ###
# The API # The API
### ###
@ -673,10 +842,14 @@ def page_not_found(error):
"""Custom 404 page.""" """Custom 404 page."""
return render_template('404.html'), 404 return render_template('404.html'), 404
### SOCKET for the chat
@socketio.on('new_message') @socketio.on('new_message')
def new_message(message): def new_message(message):
# Send message to alls users # Send message to all users
print("new message") # print("new message")
# channel is always 1 now, but might be interesting for further development
emit('channel-' + str(message['channel']), { emit('channel-' + str(message['channel']), {
'username': message['username'], 'username': message['username'],
'text': message['text'], 'text': message['text'],
@ -688,6 +861,7 @@ def new_message(message):
my_new_chat = Chat( my_new_chat = Chat(
message=message['text'] message=message['text']
) )
db.session.add(my_new_chat) db.session.add(my_new_chat)
try: try:
db.session.commit() db.session.commit()

2
init_db.sh

@ -4,4 +4,4 @@ mkdir -p app/uploads/cover
chmod 777 app/uploads/ chmod 777 app/uploads/
chmod 777 app/uploads/cover chmod 777 app/uploads/cover
python3 init.py python3 init.py
python3 import_csv.py xpublibrary.csv #python3 import_csv.py xpublibrary.csv

6
requirements.txt

@ -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-whooshalchemyplus==0.7.5
python-dotenv==0.7.1
=======
autocomplete==0.0.104 autocomplete==0.0.104
>>>>>>> 30d8bade54d8646ad4a5e314022d62e2dbf81755

4
run.py

@ -1,9 +1,13 @@
#! /usr/bin/env python #! /usr/bin/env python
from app import app, socketio from app import app, socketio
<<<<<<< HEAD <<<<<<< HEAD
<<<<<<< HEAD
# socketio.run(app) # socketio.run(app)
app.run(debug=True,host="0.0.0.0",port=8080) app.run(debug=True,host="0.0.0.0",port=8080)
======= =======
socketio.run(app, port=8080) socketio.run(app, port=8080)
=======
socketio.run(app,host="0.0.0.0", port=8080)
>>>>>>> master
#app.run(debug=True,host="0.0.0.0",port=8080) #app.run(debug=True,host="0.0.0.0",port=8080)
>>>>>>> stack_stuff >>>>>>> stack_stuff

BIN
whoosh/Book/MAIN_071mbyqoitijmyv4.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_2a7vygakwdvdefoe.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_5id3ip2mvmsu2mbl.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_6bsjkp9xdes7zrrx.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_es8t5xyup7nfqm7j.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_hljrpqpa46dk3vgc.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_q0ohjvxyt5ufv1vl.seg

Binary file not shown.

BIN
whoosh/Book/_MAIN_293.toc

Binary file not shown.
Loading…
Cancel
Save