diff --git a/BUGs b/BUGs index 45c59b8..1acd464 100644 --- a/BUGs +++ b/BUGs @@ -1,3 +1,16 @@ +### fix to get this working with bs 5... +editing a book needs to use input groups + + buttons (add/rem say authors) is in wrong spot + - buttons at bottom are broken still +edit series needs input groups +sub-books table looks off (notes/etc. not stretching full width) +even navbar is not inset from left-margin (see pa-dev) +get rid of font-awesome and use bootstrap5 icons +sort through dataTables calls in books_for_series.html (ever called without base?) -- Maybe as part of an ajax call? + +upgrade bootstrap, dataTables, etc. + + #### BUGS (next-20) ### DB/back-end diff --git a/Dockerfile b/Dockerfile index 46ec821..99dcb04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 WORKDIR /code USER root ENV PJM_UID=500 diff --git a/README b/README index 571dcbb..3345a22 100644 --- a/README +++ b/README @@ -28,6 +28,15 @@ sudo docker-compose -f /srv/docker/config/docker-compose.yml up bookdb_web ########################################################## Remember: MUST use form.errors when we have a validator that is fancier than not empty (year_published in book and num_books in series SO FAR) +##### make backup / export: +ddp@mara:/srv/docker/container/bookdb_dev$ sudo docker exec -it bookdb_dev bash + root@5eff50ab6cdc:/# pg_dump --user=ddp library > /docker-entrypoint-initdb.d/tables.sql + +#### if we want to upgrade postgres, do this extra step +sudo docker-compose -f /srv/docker/config/docker-compose.yml stop bookdb_dev +sudo rm -rf /srv/docker/container/bookdb_dev/data +#tweak docker-compose.yml to upgrade PG... +sudo docker-compose -f /srv/docker/config/docker-compose.yml up -d bookdb_dev ########################################################### TODOS (next 29): Validation: diff --git a/author.py b/author.py index 59560de..1e4ac7a 100644 --- a/author.py +++ b/author.py @@ -16,7 +16,7 @@ class Author(db.Model): firstnames = db.Column(db.String(50), unique=False, nullable=False) def __repr__(self): - return "".format(self.id,self.firstnames, self.surname) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Author into json / useful in jinja2 @@ -63,11 +63,11 @@ def new_author(): try: db.session.add(author) db.session.commit() - st.SetMessage( "Created new Author ({})".format(author) ) + st.SetMessage( f"Created new Author ({author.surname}, {author.firstnames})" ) return redirect( '/authors' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add Author: {}".format( e.orig) ) + st.SetMessage( f"Failed to add Author: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) ################################################################################ @@ -83,18 +83,18 @@ def author(id): try: author = Author.query.get(id) if 'delete' in request.form: - st.SetMessage("Successfully deleted Author: ({})".format( author ) ) + st.SetMessage( f"Successfully deleted Author: ({author.surname}, {author.firstnames})" ) author = Author.query.filter(Author.id==id).delete() if 'submit' in request.form: - st.SetMessage("Successfully Updated Author: (From: {}".format(author) ) + st.SetMessage( f"Successfully Updated Author: (From: {author.surname}, {author.firstnames}" ) author.surname = request.form['surname'] author.firstnames = request.form['firstnames'] - st.AppendMessage(" To: {}".format(author) ) + st.AppendMessage( f" To: {author.surname}, {author.firstnames})" ) db.session.commit() return redirect( '/authors' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Author: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Author: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) else: author = Author.query.get(id) diff --git a/condition.py b/condition.py index eb850c1..ba92cf8 100644 --- a/condition.py +++ b/condition.py @@ -15,7 +15,7 @@ class Condition(db.Model): name = db.Column(db.String(50), unique=True, nullable=False) def __repr__(self): - return "".format(self.id,self.name) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Condition into json / useful in jinja2 @@ -58,11 +58,11 @@ def new_condition(): try: db.session.add(condition) db.session.commit() - st.SetMessage( "Created new Condition (id={})".format(condition.id) ) + st.SetMessage( f"Created new Condition (id={condition.id})" ) return redirect( '/conditions' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add condition: {}".format( e.orig) ) + st.SetMessage( f"Failed to add condition: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) ################################################################################ @@ -78,16 +78,16 @@ def condition(id): try: condition = Condition.query.get(id) if 'delete' in request.form: - st.SetMessage("Successfully deleted (id={}, name={})".format( condition.id, condition.name ) ) + st.SetMessage(f"Successfully deleted (id={condition.id}, name={condition.name})" ) condition = Condition.query.filter(Condition.id==id).delete() if 'submit' in request.form: - st.SetMessage("Successfully Updated Condition (id={})".format(id) ) + st.SetMessage( f"Successfully Updated Condition (id={id})" ) condition.name = request.form['name'] db.session.commit() return redirect( '/conditions' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Condition: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Condition: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) else: condition = Condition.query.get(id) diff --git a/covertype.py b/covertype.py index 4c0bd58..1d29aa4 100644 --- a/covertype.py +++ b/covertype.py @@ -15,7 +15,7 @@ class Covertype(db.Model): name = db.Column(db.String(50), unique=True, nullable=False) def __repr__(self): - return "".format(self.id,self.name) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Covertype into json / useful in jinja2 @@ -58,11 +58,11 @@ def new_covertype(): try: db.session.add(covertype) db.session.commit() - st.SetMessage( "Created new Covertype (id={})".format(covertype.id) ) + st.SetMessage( f"Created new Covertype (id={covertype.id})" ) return redirect( '/covertypes' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add covertype: {}".format( e.orig) ) + st.SetMessage( f"Failed to add covertype: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) @@ -79,16 +79,16 @@ def covertype(id): try: covertype = Covertype.query.get(id) if 'delete' in request.form: - st.SetMessage("Successfully deleted (id={}, name={})".format( covertype.id, covertype.name ) ) + st.SetMessage( f"Successfully deleted (id={covertype.id}, name={covertype.name})" ) covertype = Covertype.query.filter(Covertype.id==id).delete() if 'submit' in request.form: - st.SetMessage("Successfully Updated Covertype (id={})".format(id) ) + st.SetMessage( f"Successfully Updated Covertype (id={id})" ) covertype.name = request.form['name'] db.session.commit() return redirect( '/covertypes' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Covertype: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Covertype: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) else: covertype = Covertype.query.get(id) diff --git a/genre.py b/genre.py index c6ca2dd..31525d7 100644 --- a/genre.py +++ b/genre.py @@ -15,7 +15,7 @@ class Genre(db.Model): name = db.Column(db.String(50), unique=True, nullable=False) def __repr__(self): - return "".format(self.id,self.name) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Genre into json / useful in jinja2 @@ -58,11 +58,11 @@ def new_genre(): try: db.session.add(genre) db.session.commit() - st.SetMessage( "Created new Genre (id={})".format(genre.id) ) + st.SetMessage( f"Created new Genre (id={genre.id})" ) return redirect( '/genres' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add Genre: {}".format( e.orig) ) + st.SetMessage( f"Failed to add Genre: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) ################################################################################ @@ -78,16 +78,16 @@ def genre(id): try: genre = Genre.query.get(id) if 'delete' in request.form: - st.SetMessage("Successfully deleted (id={}, name={})".format( genre.id, genre.name ) ) + st.SetMessage( f"Successfully deleted (id={genre.id}, name={genre.name})" ) genre = Genre.query.filter(Genre.id==id).delete() if 'submit' in request.form: - st.SetMessage("Successfully Updated Genre (id={})".format(id) ) + st.SetMessage( f"Successfully Updated Genre (id={id})" ) genre.name = request.form['name'] db.session.commit() return redirect( '/genres' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Genre: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Genre: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) else: genre = Genre.query.get(id) diff --git a/loan.py b/loan.py index 4a64818..7a49d89 100644 --- a/loan.py +++ b/loan.py @@ -20,7 +20,7 @@ class Loan(db.Model): date_lent = db.Column(db.Date, nullable=False ) def __repr__(self): - return "".format(self.id,self.firstnames, self.surname) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Loan into json / useful in jinja2 @@ -69,11 +69,11 @@ def new_loan(): try: db.session.add(loan) db.session.commit() - st.SetMessage( "Created new Loan (id={})".format(loan.id) ) + st.SetMessage( f"Created new Loan (id={loan.id})" ) return redirect( '/loans' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add Loan: {}".format( e.orig) ) + st.SetMessage( f"Failed to add Loan: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) @@ -90,12 +90,12 @@ def loan(id): loan = Loan.query.get(id) try: if 'delete' in request.form: - st.SetMessage("Successfully deleted (id={}, who={} {})".format( loan.id, loan.firstnames, loan.surname ) ) + st.SetMessage( f"Successfully deleted (id={loan.id}, who={loan.firsnames} {loan.surname})" ) # fall back to direct sql because loan.py is imported before Book_Loan_Link exists - db.engine.execute("delete from book_loan_link where loan_id = {}".format( loan.id )) + db.engine.execute( f"delete from book_loan_link where loan_id = {loan.id}" ) loan = Loan.query.filter(Loan.id==id).delete() if 'submit' in request.form: - st.SetMessage("Successfully Updated Loan (id={})".format(id) ) + st.SetMessage( f"Successfully Updated Loan (id={id})" ) loan.firstnames = request.form['firstnames'] loan.surname = request.form['surname'] loan.surname = request.form['surname'] @@ -105,7 +105,7 @@ def loan(id): return redirect( '/loans' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Loan: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Loan: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) else: loan = Loan.query.get(id) diff --git a/owned.py b/owned.py index 4012575..1f8c18d 100644 --- a/owned.py +++ b/owned.py @@ -15,7 +15,7 @@ class Owned(db.Model): name = db.Column(db.String(50), unique=True, nullable=False) def __repr__(self): - return "".format(self.id,self.name) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Owned into json / useful in jinja2 @@ -58,11 +58,11 @@ def new_owned(): try: db.session.add(owned) db.session.commit() - st.SetMessage( "Created new Owned Type (id={})".format(owned.id) ) + st.SetMessage( f"Created new Owned Type (id={owned.id})" ) return redirect( '/owneds' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add Ownership type: {}".format( e.orig) ) + st.SetMessage( f"Failed to add Ownership type: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) ################################################################################ @@ -77,16 +77,16 @@ def owned(id): try: owned = Owned.query.get(id) if 'delete' in request.form: - st.SetMessage("Successfully deleted (id={}, name={})".format( owned.id, owned.name ) ) + st.SetMessage( f"Successfully deleted (id={owned.id}, name={owned.name})" ) owned = Owned.query.filter(Owned.id==id).delete() if 'submit' in request.form: owned.name = request.form['name'] - st.SetMessage("Successfully Updated Owned (id={})".format(id) ) + st.SetMessage( f"Successfully Updated Owned (id={id})" ) db.session.commit() return redirect( '/owneds' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Ownership Type: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Ownership Type: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title='Edit Ownership Type', alert=st.GetAlert(), message=st.GetMessage() ) else: obj = Owned.query.get(id) diff --git a/publisher.py b/publisher.py index 54e8461..a4c2672 100644 --- a/publisher.py +++ b/publisher.py @@ -15,7 +15,7 @@ class Publisher(db.Model): name = db.Column(db.String(50), unique=True, nullable=False) def __repr__(self): - return "".format(self.id,self.name) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Publisher into json / useful in jinja2 @@ -58,11 +58,11 @@ def new_publisher(): try: db.session.add(publisher) db.session.commit() - st.SetMessage( "Created new Publisher (id={})".format(publisher.id) ) + st.SetMessage( f"Created new Publisher (id={publisher.id})" ) return redirect( '/publishers' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add Publisher: {}".format( e.orig) ) + st.SetMessage( f"Failed to add Publisher: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) ################################################################################ @@ -78,16 +78,16 @@ def publisher(id): try: publisher = Publisher.query.get(id) if 'delete' in request.form: - st.SetMessage("Successfully deleted (id={}, name={})".format( publisher.id, publisher.name ) ) + st.SetMessage( f"Successfully deleted (id={publisher.id}, name={publisher.name})" ) publisher = Publisher.query.filter(Publisher.id==id).delete() if 'submit' in request.form: - st.SetMessage("Successfully Updated Publisher (id={})".format(id) ) + st.SetMessage( f"Successfully Updated Publisher (id={id})" ) publisher.name = request.form['name'] db.session.commit() return redirect( '/publishers' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Publisher: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Publisher: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) else: publisher = Publisher.query.get(id) diff --git a/rating.py b/rating.py index 616370d..755d66d 100644 --- a/rating.py +++ b/rating.py @@ -15,7 +15,7 @@ class Rating(db.Model): name = db.Column(db.String(50), unique=True, nullable=False) def __repr__(self): - return "".format(self.id,self.name) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Rating into json / useful in jinja2 @@ -58,11 +58,11 @@ def new_rating(): try: db.session.add(rating) db.session.commit() - st.SetMessage( "Created new Rating (id={})".format(rating.id) ) + st.SetMessage( f"Created new Rating (id={rating.id})" ) return redirect( '/ratings' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add rating: {}".format( e.orig) ) + st.SetMessage( f"Failed to add rating: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) ################################################################################ @@ -78,16 +78,16 @@ def rating(id): try: rating = Rating.query.get(id) if 'delete' in request.form: - st.SetMessage("Successfully deleted (id={}, name={})".format( rating.id, rating.name ) ) + st.SetMessage( f"Successfully deleted (id={rating.id}, name={rating.name})" ) rating = Rating.query.filter(Rating.id==id).delete() if 'submit' in request.form: - st.SetMessage("Successfully Updated Rating (id={})".format(id) ) + st.SetMessage( f"Successfully Updated Rating (id={id})" ) rating.name = request.form['name'] db.session.commit() return redirect( '/ratings' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Rating: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Rating: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) else: rating = Rating.query.get(id) diff --git a/series.py b/series.py index e5cc997..1706f76 100644 --- a/series.py +++ b/series.py @@ -18,7 +18,7 @@ class Series(db.Model): note = db.Column(db.Text) def __repr__(self): - return "".format(self.id, self.title, self.num_books, self.calcd_rating, self.note) + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Series into json / useful in jinja2 @@ -41,7 +41,7 @@ class SeriesForm(FlaskForm): delete = SubmitField('Delete' ) def CalcAvgRating(sid): - res=db.engine.execute("select round(avg(to_number(r.name, '99')),1) as rating from book b, rating r, series s, book_series_link bsl where s.id={} and s.id = bsl.series_id and bsl.book_id = b.id and b.rating = r.id and r.name ~ E'^\\\\d+$'".format(sid)) + res=db.engine.execute( f"select round(avg(to_number(r.name, '99')),1) as rating from book b, rating r, series s, book_series_link bsl where s.id={sid} and s.id = bsl.series_id and bsl.book_id = b.id and b.rating = r.id and r.name ~ E'^\\\\d+$'" ) for row in res: rating = row.rating return rating @@ -79,11 +79,11 @@ def new_series(): try: db.session.add(series) db.session.commit() - st.SetMessage( "Created new Series (id={})".format(series.id) ) + st.SetMessage( f"Created new Series (id={series.id})" ) return redirect( '/seriess' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to add Series: {}".format( e.orig) ) + st.SetMessage( f"Failed to add Series: {e.orig}" ) return render_template("series.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) ################################################################################ @@ -99,10 +99,10 @@ def series(id): try: series = Series.query.get(id) if 'delete' in request.form: - st.SetMessage("Successfully deleted (id={}, title={})".format( series.id, series.title ) ) + st.SetMessage( f"Successfully deleted (id={series.id}, title={series.title})" ) series = Series.query.filter(Series.id==id).delete() if 'submit' in request.form and form.validate(): - st.SetMessage("Successfully Updated Series (id={})".format(id) ) + st.SetMessage( f"Successfully Updated Series (id={id})" ) series.title = request.form['title'] series.num_books = request.form['num_books'] series.calcd_rating = CalcAvgRating(id) @@ -110,7 +110,7 @@ def series(id): else: message="Failed to update Series:" for field in form.errors: - message = "{}
{}={}".format( message, field, form.errors[field] ) + message= f"{message}
{field}={form.errors[field]}" st.SetAlert("danger") st.SetMessage(message) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) @@ -118,7 +118,7 @@ def series(id): return redirect( '/seriess' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) - st.SetMessage( "Failed to modify Series: {}".format(e.orig) ) + st.SetMessage( f"Failed to modify Series: {e.orig}" ) return render_template("edit_id_name.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) else: series = Series.query.get(id)