add/remove series from a book now works, havent thought about removing a parent book fro a series, but otherwise you can add more than one series, remove as well, and it works in the DB
This commit is contained in:
2
BUGs
2
BUGs
@@ -4,3 +4,5 @@
|
||||
* book form is not validating (year published)
|
||||
* add/remove authors, and after save they are ordered by author.id, not order of addition (prob. needs book_author_link to have an auth_num)
|
||||
* show books, only shows first author
|
||||
* starting on series remove button. If we click a subbook, then remove series button needs to be clever (might need client-side validation / confirmation, eg. this is a subbook, you want to remove all of the sub books from the series?)
|
||||
* series: 112, book/sub-book ordering is broken
|
||||
|
||||
2
README
2
README
@@ -29,7 +29,7 @@ python3 main.py
|
||||
offer to move parent/child series orders as well to match (think Thomas Covenant)
|
||||
|
||||
### TODO:
|
||||
- need to create series, loan, book classes (and make green + button work)
|
||||
- when remove a Parent book from a series, what do we do?
|
||||
- need to delete all classes (and watch for referential integrity)
|
||||
- need to add 1 author to book, book to sub_book, series
|
||||
- need to delete 1 author from book
|
||||
|
||||
19
main.py
19
main.py
@@ -5,6 +5,7 @@ from flask_bootstrap import Bootstrap
|
||||
from wtforms import SubmitField, StringField, HiddenField, SelectField, IntegerField, TextAreaField, validators
|
||||
from flask_wtf import FlaskForm
|
||||
from status import st, Status
|
||||
import re
|
||||
|
||||
####################################### Flask App globals #######################################
|
||||
app = Flask(__name__)
|
||||
@@ -26,7 +27,7 @@ from covertype import Covertype, CovertypeForm, CovertypeSchema, GetCovertypeByI
|
||||
from owned import Owned, OwnedForm, OwnedSchema, GetOwnedById
|
||||
from rating import Rating, RatingForm, RatingSchema
|
||||
from loan import Loan, LoanForm, LoanSchema
|
||||
from series import Series, SeriesForm, SeriesSchema
|
||||
from series import Series, SeriesForm, SeriesSchema, ListOfSeriesWithMissingBooks
|
||||
|
||||
####################################### CLASSES / DB model #######################################
|
||||
book_author_link = db.Table('book_author_link', db.Model.metadata,
|
||||
@@ -77,6 +78,7 @@ class Book(db.Model):
|
||||
genre = db.relationship('Genre', secondary=book_genre_link )
|
||||
loan = db.relationship('Loan', secondary=Book_Loan_Link.__table__);
|
||||
series = db.relationship('Series', secondary=Book_Series_Link.__table__);
|
||||
bsl = db.relationship('Book_Series_Link' )
|
||||
year_published = db.Column(db.Integer)
|
||||
condition = db.Column(db.Integer, db.ForeignKey('condition.id'))
|
||||
covertype = db.Column(db.Integer, db.ForeignKey('covertype.id'))
|
||||
@@ -212,6 +214,7 @@ app.jinja_env.globals['GetConditionById'] = GetConditionById
|
||||
app.jinja_env.globals['GetPublisherById'] = GetPublisherById
|
||||
app.jinja_env.globals['SeriesBookNum'] = SeriesBookNum
|
||||
app.jinja_env.globals['ClearStatus'] = st.ClearMessage
|
||||
app.jinja_env.globals['ListOfSeriesWithMissingBooks'] = ListOfSeriesWithMissingBooks
|
||||
|
||||
book_schema = BookSchema()
|
||||
|
||||
@@ -367,6 +370,18 @@ def book(id):
|
||||
if 'author-' in el:
|
||||
book.author.append( Author.query.get( request.form[el] ) )
|
||||
|
||||
print( "bsl={}".format( book.bsl ))
|
||||
# delete all bsls
|
||||
db.engine.execute("delete from book_series_link where book_id = {}".format( book.id ) )
|
||||
cnt=1
|
||||
for field in request.form:
|
||||
if 'bsl-book_id-' in field and field != 'bsl-book_id-NUM':
|
||||
cnt=int(re.findall( '\d+', field )[0])
|
||||
print( "found: bsl-{} book_id == {}, series_id={}, book_num={}".format(cnt, request.form['bsl-book_id-{}'.format(cnt)], request.form['bsl-series_id-{}'.format(cnt)], request.form['bsl-book_num-{}'.format(cnt)]) )
|
||||
sql="insert into book_series_link (book_id, series_id, book_num) values ( {}, {}, {} )".format( request.form['bsl-book_id-{}'.format(cnt)], request.form['bsl-series_id-{}'.format(cnt)], request.form['bsl-book_num-{}'.format(cnt)])
|
||||
db.engine.execute( sql )
|
||||
cnt=cnt+1
|
||||
|
||||
## TODO:
|
||||
# what about series?, subbooks?, loan?, etc.
|
||||
db.session.commit()
|
||||
@@ -382,7 +397,7 @@ def book(id):
|
||||
genre_list = GetGenres()
|
||||
|
||||
book_s = book_schema.dump(book)
|
||||
return render_template("book.html", b=book, books=book_s, book_form=book_form, author_list=author_list, genre_list=genre_list, alert=alert, message=message, n=book_form.notes )
|
||||
return render_template("book.html", b=book, books=book_s, book_form=book_form, author_list=author_list, genre_list=genre_list, alert=alert, message=message, n=book_form.notes, poss_series_list=ListOfSeriesWithMissingBooks() )
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def main_page():
|
||||
|
||||
@@ -44,6 +44,13 @@ def CalcAvgRating(sid):
|
||||
print( row )
|
||||
return row.rating
|
||||
|
||||
def ListOfSeriesWithMissingBooks():
|
||||
res=db.engine.execute("select id, title, num_books, count from ( select s.id, s.title, s.num_books, count(bsl.book_id) from series s, book_series_link bsl where s.id = bsl.series_id group by s.id ) as foo where num_books > count")
|
||||
tmp=[]
|
||||
for row in res:
|
||||
tmp.append({'id': row[0], 'title': row[1]})
|
||||
return tmp
|
||||
|
||||
################################################################################
|
||||
# Routes for series data
|
||||
#
|
||||
|
||||
@@ -2,6 +2,69 @@
|
||||
{% block main_content %}
|
||||
|
||||
<script>
|
||||
|
||||
function AddBookToSeries(bid) {
|
||||
console.log("add Book (" +bid + ") to series")
|
||||
|
||||
// Read the Number from that DIV's ID (i.e: 3 from "series-div-3") -- could
|
||||
// also be the template (series-div-0), thats ok too
|
||||
var last_div = $('div[id^="series-div-"]:last');
|
||||
var num = parseInt( last_div.prop("id").match(/\d+/g), 10 );
|
||||
|
||||
console.log( "num=" + num )
|
||||
console.log( "last_div is=" + last_div.prop('id') )
|
||||
|
||||
// Is it shown? IF yes, copy / add a new one ELSE its the first one, so don't make another, just show it
|
||||
$('#series_plus_only').hide();
|
||||
|
||||
num++
|
||||
|
||||
// if we have more than 1 series, lets fix the buttons
|
||||
if( num > 1 ) {
|
||||
// disable minus button and hide plus button in old div
|
||||
last_div.find('#series_minus_but').addClass('disabled')
|
||||
last_div.find('#series_minus_but').prop('disabled', true )
|
||||
last_div.find('#series_plus_but_div').hide()
|
||||
console.log( "reset buttons on last_div is=" + last_div.prop('id') )
|
||||
}
|
||||
|
||||
// clone the template
|
||||
var new_div = $('#series-div-0').clone()
|
||||
|
||||
// reset id/names to new series-div- created (for id on div, name on select
|
||||
// for series_id and input text for book_num)
|
||||
new_div.prop('id', 'series-div-'+num );
|
||||
new_div.find('#series_minus_but').attr('onclick', "RemoveBookFromSeries({{books.id}},'series-div-"+num+"')" )
|
||||
new_div.find('#bsl-book_id-NUM').prop('name', 'bsl-book_id-'+num )
|
||||
new_div.find('#bsl-series_id-NUM').prop('name', 'bsl-series_id-'+num )
|
||||
new_div.find('#bsl-book_num-NUM').prop('name', 'bsl-book_num-'+num )
|
||||
|
||||
// insert new_div after the old div
|
||||
last_div.after( new_div );
|
||||
new_div.show()
|
||||
}
|
||||
|
||||
function RemoveBookFromSeries(bid, sid) {
|
||||
console.log("remove Book: " + bid + " from Series: " + sid )
|
||||
$('#'+sid).remove()
|
||||
if( sid == 'series-div-1' ) {
|
||||
console.log("remove Book: and it was the only series, so hide series-div-1 and put 'plus / empty' div back" )
|
||||
if( $('#series_plus_only').length == 0 ) {
|
||||
new_div = $('#series_plus_only_tmpl').clone()
|
||||
new_div.prop('id', 'series_plus_only')
|
||||
$('#series-div-0').after( new_div )
|
||||
}
|
||||
$('#series_plus_only').show();
|
||||
} else {
|
||||
console.log("remove Book: and there are more than 1 'new' series (not in DB), so delete this one")
|
||||
//now find 'last div still visible, re-enable its minus button and add back plus button
|
||||
var div = $('div[id^="series-div-"]:last');
|
||||
div.find('#series_minus_but').removeClass('disabled')
|
||||
div.find('#series_minus_but').prop('disabled', false )
|
||||
div.find('#series_plus_but_div').show()
|
||||
}
|
||||
}
|
||||
|
||||
function RemoveAuthorFromBook(num) {
|
||||
console.log("remove an author at slot: " + num )
|
||||
$('#auth-div-'+num).remove()
|
||||
@@ -28,6 +91,13 @@ function AddAuthorToBook(num) {
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="series_plus_only_tmpl" class="col input-group px-0" style="display:none">
|
||||
<span class="form-control"> </span>
|
||||
<div id="series_plus_but_div" class="input-group-append">
|
||||
<button class="btn btn-outline-success" type="button" onClick="AddBookToSeries({{books.id}},1)"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% set keys = [ 'title', 'author', 'publisher', 'genre', 'owned', 'covertype', 'condition', 'year_published', 'rating', 'notes', 'blurb' ] %}
|
||||
<div class="container-fluid">
|
||||
{% if message|length %}
|
||||
@@ -111,22 +181,64 @@ function AddAuthorToBook(num) {
|
||||
{% endif %}
|
||||
</div class="form-row">
|
||||
{% endfor %}
|
||||
{% if books.series|length %}
|
||||
<div class="form-row">
|
||||
<label for="series" class="col-lg-2 col-form-label">Series:</label>
|
||||
<table>
|
||||
{% for s in books.series %}
|
||||
<tr><td>
|
||||
{% if SeriesBookNum( s.id, books.id ) %}
|
||||
<label class="form-control-plaintext">Book {{ SeriesBookNum( s.id, books.id ) }} of {{s.num_books}} in <a href="/series/{{s.id}}">{{s.title}}</a></label>
|
||||
{% else %}
|
||||
<label class="form-control-plaintext">Contains books in <a href='/series/{{s.id}}'>{{s.title}}</a></label>
|
||||
{% endif %}
|
||||
</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-row col mx-0">
|
||||
{# use series-div-0 as a template & keep it hidden always #}
|
||||
<div id="series-div-0" class="col input-group px-0" style="display:none" actual_series_data="0">
|
||||
<div class="input-group-prepend">
|
||||
<button id="series_minus_but" class="btn btn-outline-danger" type="button"><i class="fas fa-minus"></i></button>
|
||||
</div>
|
||||
<input type="hidden" id="bsl-book_id-NUM" name="bsl-book_id-NUM" value="{{books.id}}">
|
||||
<span class="form-control col-lg-2">Book </span>
|
||||
<input type="text" id="bsl-book_num-NUM" name="bsl-book_num-NUM" class="form-control col-lg-1" placeholder="number">
|
||||
<span class="form-control col-lg-1"> of </span>
|
||||
<select class="form-control col" id="bsl-series_id-NUM" name="bsl-series_id-NUM">
|
||||
{% for s in poss_series_list %}
|
||||
<option value="{{s.id}}">{{s.title}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div id="series_plus_but_div" class="input-group-append">
|
||||
<button class="btn btn-outline-success" type="button" onClick="AddBookToSeries({{books.id}},0)"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
{% if books.series|length %}
|
||||
{% for s in books.series %}
|
||||
<div id="series-div-{{loop.index}}" class="col input-group px-0" actual_series_data="1">
|
||||
{% if SeriesBookNum( s.id, books.id ) %}
|
||||
<div class="input-group-prepend">
|
||||
<button class="btn btn-outline-danger" type="button" onClick="RemoveBookFromSeries({{books.id}},'series-div-{{loop.index}}')"><i class="fas fa-minus"></i></button>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
Book {{ SeriesBookNum( s.id, books.id ) }} of {{s.num_books}} in <a href=/series/{{s.id}}>{{s.title}}</a>
|
||||
</div>
|
||||
<input type="hidden" name="bsl-book_num-{{loop.index}}" value={{SeriesBookNum( s.id, books.id )}}>
|
||||
{% else %}
|
||||
<div class="input-group-prepend">
|
||||
<button class="btn btn-outline-danger" type="button" onClick="RemoveBookFromSeries({{books.id}},'series-div-{{loop.index}}')"><i class="fas fa-minus"></i></button>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
Contains books in <a href='/series/{{s.id}}'>{{s.title}}</a>
|
||||
</div>
|
||||
<input type="hidden" name="bsl-book_num-{{loop.index}}" value="">
|
||||
{% endif %}
|
||||
<input type="hidden" name="bsl-book_id-{{loop.index}}" value={{books.id}}>
|
||||
<input type="hidden" name="bsl-series_id-{{loop.index}}" value={{s.id}}>
|
||||
{% endfor %}
|
||||
<div id="series_plus_but_div" class="input-group-append">
|
||||
<button class="btn btn-outline-success" type="button" onClick="AddBookToSeries({{books.id}},1)"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
{% else %}
|
||||
<div id="series_plus_only" class="col input-group px-0" actual_series_data="0">
|
||||
<span class="form-control"> </span>
|
||||
<div id="series_plus_but_div" class="input-group-append">
|
||||
<button class="btn btn-outline-success" type="button" onClick="AddBookToSeries({{books.id}},1)"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div class="form-row col mx-0">
|
||||
</div class="form-row">
|
||||
{% if books.child_ref|length %}
|
||||
<div class="form-row">
|
||||
<label class="col-lg-2 col-form-label">Sub Books:</label>
|
||||
@@ -134,6 +246,7 @@ function AddAuthorToBook(num) {
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="spacer"><br></div>
|
||||
<div class="form-row col-lg-12">
|
||||
{{ book_form.delete( class="btn btn-outline-danger offset-lg-2 col-lg-2" )}}
|
||||
{{ book_form.submit( class="btn btn-primary col-lg-2" )}}
|
||||
|
||||
Reference in New Issue
Block a user