updated with covers and authors

This commit is contained in:
Alex 2018-05-23 14:12:24 +02:00
parent 9461301d21
commit 7e1c504e09
17 changed files with 221 additions and 26 deletions

View File

@ -7,6 +7,7 @@ from werkzeug.utils import secure_filename
basedir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.abspath(os.path.dirname(__file__))
UPLOAD_FOLDER = os.path.join(basedir, 'uploads') UPLOAD_FOLDER = os.path.join(basedir, 'uploads')
UPLOAD_FOLDER_COVER = os.path.join(basedir, 'uploads/cover')
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__) app = Flask(__name__)

Binary file not shown.

Binary file not shown.

View File

@ -1,8 +1,21 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, FileField from wtforms import StringField, FileField
from wtforms.validators import InputRequired from wtforms.validators import InputRequired, DataRequired
from wtforms import FieldList
from wtforms import Form as NoCsrfForm
from wtforms.fields import StringField, FormField, SubmitField
from app.models import Book, BookSchema, Author
# - - - Forms - - -
class AuthorForm(NoCsrfForm):
# this forms is never exposed so we can user the non CSRF version
author_name = StringField('Author Name', validators=[DataRequired()])
class UserForm(FlaskForm): class UserForm(FlaskForm):
title = StringField('title', validators=[InputRequired()]) title = StringField('title', validators=[InputRequired()])
author = StringField('author', validators=[InputRequired()]) author = FieldList(FormField(AuthorForm, default=lambda: Author()), min_entries=1)
file = FileField() file = FileField()
class UserForm_Edit(FlaskForm):
title = StringField('title', validators=[InputRequired()])
author = FieldList(FormField(AuthorForm, default=lambda: Author()), min_entries=1)

View File

@ -2,26 +2,44 @@ from app import db
from marshmallow import Schema, fields, ValidationError, pre_load from marshmallow import Schema, fields, ValidationError, pre_load
class Book(db.Model): class Book(db.Model):
__tablename__ = 'books'
id = db.Column(db.Integer, primary_key = True) id = db.Column(db.Integer, primary_key = True)
title = db.Column(db.String(255)) title = db.Column(db.String(255))
author = db.Column(db.String(255))
file = db.Column(db.String(255)) file = db.Column(db.String(255))
cover = db.Column(db.String(255))
fileformat = db.Column(db.String(255))
author = db.relationship('Author')
def __init__(self, title, file, cover, fileformat):
def __init__(self, title, author, file):
self.title = title self.title = title
self.author = author
self.file = file self.file = file
self.cover = cover
self.fileformat = fileformat
def __repr__(self): def __repr__(self):
return '<Title %r>' % self.title return '<Title %r>' % self.title
def get_id(self):
return self.id
class Author(db.Model):
__tablename__ = 'authors'
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('books.id'))
author_name = db.Column(db.String(50))
def __init__(self, author_name):
self.author_name = author_name
class BookSchema(Schema): class BookSchema(Schema):
id = fields.Int(dump_only=True) id = fields.Int(dump_only=True)
title = fields.Str() title = fields.Str()
author = fields.Str() author = fields.Str()
file = fields.Str() file = fields.Str()
cover = fields.Str()
fileformat = fields.Str()
def must_not_be_blank(data): def must_not_be_blank(data):
if not data: if not data:

View File

@ -31,8 +31,42 @@ font-style: italic;
display: table; display: table;
} }
.container{
.header{ padding: 0px 8px;
}
.header input{
height:40px;
width: 500px;
font-size: 30px;
font-weight: bold;
}
.author input{
height:20px;
width: 500px;
font-size: 16px;
}
.footer{
width: 100%;
font-family:'Courier New';
font-weight:100;
font-size:12px;
}
.footer pre{
width: 60px;
margin:0 auto;
font-family:'Courier New';
}
.footer p{
width: 30%;
margin:0 auto;
text-align: center;
font-family:'Courier New';
} }

View File

@ -1 +1,32 @@
/* Add your Application JavaScript */ /* Add your Application JavaScript */
$(function() {
$("div[data-toggle=fieldset]").each(function() {
var $this = $(this);
//Add new entry
$this.find("button[data-toggle=fieldset-add-row]").click(function() {
var target = $($(this).data("target"))
console.log(target);
var oldrow = target.find("[data-toggle=fieldset-entry]:last");
var row = oldrow.clone(true, true);
console.log(row.find(":input")[0]);
var elem_id = row.find(":input")[0].id;
var elem_num = parseInt(elem_id.replace(/.*-(\d{1,4})-.*/m, '$1')) + 1;
row.attr('data-id', elem_num);
row.find(":input").each(function() {
console.log(this);
var id = $(this).attr('id').replace('-' + (elem_num - 1) + '-', '-' + (elem_num) + '-');
$(this).attr('name', id).attr('id', id).val('').removeAttr("checked");
});
oldrow.after(row);
}); //End add new entry
//Remove row
$this.find("button[data-toggle=fieldset-remove-row]").click(function() {
if($this.find("[data-toggle=fieldset-entry]").length > 1) {
var thisRow = $(this).closest("[data-toggle=fieldset-entry]");
thisRow.remove();
}
}); //End remove row
});
});

View File

@ -17,7 +17,24 @@
<form method="POST" action="{{ url_for('add_book') }}" enctype=multipart/form-data> <form method="POST" action="{{ url_for('add_book') }}" enctype=multipart/form-data>
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="form-group">{{ form.title.label }} {{ form.title(size=20, class="form-control") }}</div> <div class="form-group">{{ form.title.label }} {{ form.title(size=20, class="form-control") }}</div>
<div class="form-group">{{ form.author.label }} {{ form.author(size=20, class="form-control") }}</div> <br>
<div data-toggle="fieldset" id="phone-fieldset">
{{ form.author.label }} <button type="button" data-toggle="fieldset-add-row"
data-target="#phone-fieldset">+</button>
<table>
<tr>
<th></th>
<th></th>
</tr>
{% for author in form.author %}
<tr data-toggle="fieldset-entry">
<td>{{ author.author_name }}</td>
<td><button type="button" data-toggle="fieldset-remove-row" id="phone-{{loop.index0}}-remove">-</button></td>
</tr>
{% endfor %}
</table>
</div>
<br>
{{ form.file }} {{ form.file }}
<button type="submit" class="btn btn-primary">Upload</button> <button type="submit" class="btn btn-primary">Upload</button>
</form> </form>

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>XPUB LIB</title> <title>XPPL</title>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
@ -14,12 +14,15 @@
<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">
{% block css %} {% endblock%} {% block css %} {% endblock%}
</head> </head>
<body> <body>
{% block header %}
<header> <header>
{% include "header.html" %} {% include "header.html" %}
</header> </header>
{% endblock %}
<main> <main>
<div class="container"> <div class="container">
{% block main %}{% endblock %} {% block main %}{% endblock %}
@ -32,5 +35,7 @@
</footer> </footer>
{% block js %} {% endblock%} {% block js %} {% endblock%}
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="{{ url_for("static", filename="js/app.js") }}"></script>
</body> </body>
</html> </html>

View File

@ -1,7 +1,8 @@
<br> <br>
<pre style="font-family:'Courier New'; font-weight:100; font-size:12px;"> <div class="footer">
<pre>
, , , ,
/////| /////|
///// | ///// |
@ -9,7 +10,9 @@
|===| | |===| |
|x | | |x | |
| p | | | p | |
|u b| / |p l| /
|===|/ |===|/
'---' '---'
</pre> </pre>
<p>XPPL. MADE POSSIBLE BY EXPERIMENTAL PUBLISHING, PZI.</p>
</div>

View File

@ -6,5 +6,4 @@
<li><a href="{{ url_for('about') }}">About</a></li> <li><a href="{{ url_for('about') }}">About</a></li>
</ul> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
</nav> </nav>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block main %} {% block main %}
<h1 class="header">XPUB LIB</h1> <h1 class="header">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>
This might only be one interface to this library: This might only be one interface to this library:

View File

@ -4,9 +4,19 @@
<div class="container"> <div class="container">
<h1 class="header">{{ book.title }}</h1> <h1 class="header">{{ book.title }}</h1>
<p>Author: {{ book.author }}</p>
<a href="../uploads/{{ book.file }}">download file</a> <img src="../uploads/cover/{{ book.cover }}" width="200">
<p>Author(s): {% for author in book.author %}
<li> {{ author.author_name }}</li>
{% endfor %}</p>
<a href="../uploads/{{ book.file }}">download {{ book.fileformat }}</a>
<br>
<br>
<a href="{{ url_for('edit_book_by_id', id=book.id )}}">edit</a>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -17,13 +17,21 @@
<table style="width:100%"> <table style="width:100%">
<tr> <tr>
<th>Cover</th>
<th>Title</th> <th>Title</th>
<th>Author</th> <th>Author</th>
<th>Filetype</th>
</tr> </tr>
{% for book in books %} {% for book in books %}
<tr> <tr>
<td><img src="../uploads/cover/{{ book.cover }}" width="80"></td>
<td><a href="books/{{ book.id }}">{{ book.title }}</a></td> <td><a href="books/{{ book.id }}">{{ book.title }}</a></td>
<td>{{ book.author }}</td> <td> {% for author in book.author %}
<li> {{ author.author_name }}</li>
{% endfor %}</td>
<td>{{ book.fileformat }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View File

@ -8,11 +8,13 @@ This file creates your application.
from app import app, db from app import app, db
from flask import Flask, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort from flask import Flask, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort
from app.forms import UserForm from app.forms import UserForm, UserForm_Edit
from app.models import Book, BookSchema from app.models import Book, BookSchema, Author
from app.cover import get_cover
import os import os
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
# import sqlite3 # import sqlite3
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
@ -31,6 +33,9 @@ def home():
"""Render website's home page.""" """Render website's home page."""
return render_template('home.html') return render_template('home.html')
@app.route('/hello/<name>')
def hello(name):
return "Hello " + name
@app.route('/about/') @app.route('/about/')
def about(): def about():
@ -42,20 +47,61 @@ def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], return send_from_directory(app.config['UPLOAD_FOLDER'],
filename) filename)
@app.route('/uploads/cover/<filename>')
def uploaded_file_cover(filename):
return send_from_directory(app.config['UPLOAD_FOLDER_COVER'],
filename)
@app.route('/books') @app.route('/books')
def show_books(): def show_books():
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('show_books.html', books=books) return render_template('show_books.html', books=books)
@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)
if not book: if not book:
abort(404) return render_template('red_link.html', id=id)
else: else:
return render_template('show_book_detail.html', book=book) return render_template('show_book_detail.html', book=book)
@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()
author_table = Author.query.filter_by(user_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 = UserForm_Edit(title = book_to_edit.title, author =book_to_edit.author)
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']
author = user_form.author.data # You could also have used request.form['email']
# save user to database
#book = Book(title, author, filename, cover, file_extension)
book_to_edit.title = title
db.session.commit()
book = Book.query.filter_by(title=title).first()
author_table = Author.query.filter_by(user_id=book.id).delete()
for this_author in author:
this_author = Author(this_author.get('author_name'))
book.author.append(this_author)
db.session.commit()
flash("%s updated" % (title))
return redirect(url_for('show_books'))
return render_template('edit_book_detail.html', book=book_to_edit, form=user_form)
@app.route('/add-book', methods=['POST', 'GET']) @app.route('/add-book', methods=['POST', 'GET'])
def add_book(): def add_book():
@ -77,15 +123,25 @@ def add_book():
if file and allowed_file(file.filename): if file and allowed_file(file.filename):
filename = secure_filename(file.filename) filename = secure_filename(file.filename)
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)
file.save(fullpath) file.save(fullpath)
cover = get_cover(fullpath, name)
title = user_form.title.data # You could also have used request.form['name'] title = user_form.title.data # You could also have used request.form['name']
author = user_form.author.data # You could also have used request.form['email'] author = user_form.author.data # You could also have used request.form['email']
# save user to database print(author)
book = Book(title, author, filename) print(len(author))
book = Book(title, filename, cover, file_extension)
db.session.add(book) db.session.add(book)
db.session.commit() db.session.commit()
book = Book.query.filter_by(title=title).first()
for this_author in author:
this_author = Author(this_author.get('author_name'))
book.author.append(this_author)
db.session.commit()
#author = "hallo"
# save user to database
flash("%s added to the library" % (title)) flash("%s added to the library" % (title))
return redirect(url_for('show_books')) return redirect(url_for('show_books'))
else: else: